nut-2.7.4/0000755000175000017500000000000012670024742007337 500000000000000nut-2.7.4/LICENSE-GPL30000644000175000017500000010437412640443572011003 00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . nut-2.7.4/COPYING0000644000175000017500000000110212640443572010307 00000000000000 Most files are licensed under the GNU General Public License (GPL) version 2, or (at your option) any later version. See "LICENSE-GPL2" in the root of this distribution. The files in the scripts/python/ directory are released under GNU General Public License (GPL) version 3, or (at your option) any later version. See "LICENSE-GPL3" in the root of this distribution. The Perl client module (scripts/perl/Nut.pm) is released under the same license as Perl itself. That is to say either GPL version 1 or (at your option) any later version, or the "Artistic License". nut-2.7.4/depcomp0000755000175000017500000005601612640476420010645 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2013-05-30.07; # UTC # Copyright (C) 1999-2013 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: nut-2.7.4/README0000644000175000017500000004533712640473702010154 00000000000000Network UPS Tools Overview =========================== Description ----------- Network UPS Tools is a collection of programs which provide a common interface for monitoring and administering UPS, PDU and SCD hardware. It uses a layered approach to connect all of the parts. Drivers are provided for a wide assortment of equipment. They understand the specific language of each device and map it back to a compatibility layer. This means both an expensive high end UPS, a simple "power strip" PDU, or any other power device can be handled transparently with a uniform management interface. This information is cached by the network server `upsd`, which then answers queries from the clients. upsd contains a number of access control features to limit the abilities of the clients. Only authorized hosts may monitor or control your hardware if you wish. Since the notion of monitoring over the network is built into the software, you can hang many systems off one large UPS, and they will all shut down together. You can also use NUT to power on, off or cycle your data center nodes, individually or globally through PDU outlets. Clients such as `upsmon` check on the status of the hardware and do things when necessary. The most important task is shutting down the operating system cleanly before the UPS runs out of power. Other programs are also provided to log information regularly, monitor status through your web browser, and more. Installing ---------- If you are installing these programs for the first time, go read the <<_installation_instructions,installation instructions>> to find out how to do that. This document contains more information on what all of this stuff does. Upgrading --------- When upgrading from an older version, always check the <> to see what may have changed. Compatibility issues and other changes will be listed there to ease the process. Configuring and using --------------------- Once NUT is installed, refer to the <> for directions. Documentation ------------- This is just an overview of the software. You should read the man pages, included example configuration files, and auxiliary documentation for the parts that you intend to use. Network Information ------------------- These programs are designed to share information over the network. In the examples below, `localhost` is used as the hostname. This can also be an IP address or a fully qualified domain name. You can specify a port number if your upsd process runs on another port. In the case of the program `upsc`, to view the variables on the UPS called sparky on the `upsd` server running on the local machine, you'd do this: /usr/local/ups/bin/upsc sparky@localhost The default port number is 3493. You can change this with "configure --with-port" at compile-time. To make a client talk to upsd on a specific port, add it after the hostname with a colon, like this: /usr/local/ups/bin/upsc sparky@localhost:1234 This is handy when you have a mixed environment and some of the systems are on different ports. The general form for UPS identifiers is this: [@[:]] Keep this in mind when viewing the examples below. Manifest -------- This package is broken down into several categories: - *drivers* - These programs talk directly to your UPS hardware. - *server* - upsd serves data from the drivers to the network. - *clients* - They talk to upsd and do things with the status data. - *cgi-bin* - Special class of clients that you can use with your web server. - *scripts* - Contains various scripts, like the Perl and Python binding, integration bits and applications. Drivers ------- These programs provide support for specific UPS models. They understand the protocols and port specifications which define status information and convert it to a form that upsd can understand. To configure drivers, edit ups.conf. For this example, we'll have a UPS called "sparky" that uses the apcsmart driver and is connected to `/dev/ttyS1`. That's the second serial port on most Linux-based systems. The entry in `ups.conf` looks like this: [sparky] driver = apcsmart port = /dev/ttyS1 To start and stop drivers, use upsdrvctl. By default, it will start or stop every UPS in the config file: /usr/local/ups/sbin/upsdrvctl start /usr/local/ups/sbin/upsdrvctl stop However, you can also just start or stop one by adding its name: /usr/local/ups/sbin/upsdrvctl start sparky /usr/local/ups/sbin/upsdrvctl stop sparky To find the driver name for your device, refer to the section below called "HARDWARE SUPPORT TABLE". Extra Settings ~~~~~~~~~~~~~~ Some drivers may require additional settings to properly communicate with your hardware. If it doesn't detect your UPS by default, check the driver's man page or help (-h) to see which options are available. For example, the usbhid-ups driver allows you to use USB serial numbers to distinguish between units via the "serial" configuration option. To use this feature, just add another line to your ups.conf section for that UPS: [sparky] driver = usbhid-ups port = auto serial = 1234567890 Hardware Compatibility List ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The <> is available in the source directory ('nut-X.Y.Z/data/driver.list'), and is generally distributed with packages. For example, it is available on Debian systems as: /usr/share/nut/driver.list This table is also available link:http://www.networkupstools.org/stable-hcl.html[online]. If your driver has vanished, see the link:FAQ.html[FAQ] and <>. Generic Device Drivers ~~~~~~~~~~~~~~~~~~~~~~ NUT provides several generic drivers that support a variety of very similar models. - The `genericups` driver supports many serial models that use the same basic principle to communicate with the computer. This is known as "contact closure", and basically involves raising or lowering signals to indicate power status. + This type of UPS tends to be cheaper, and only provides the very simplest data about power and battery status. Advanced features like battery charge readings and such require a "smart" UPS and a driver which supports it. + See the linkman:genericups[8] man page for more information. - The `usbhid-ups` driver attempts to communicate with USB HID Power Device Class (PDC) UPSes. These units generally implement the same basic protocol, with minor variations in the exact set of supported attributes. This driver also applies several correction factors when the UPS firmware reports values with incorrect scale factors. + See the linkman:usbhid-ups[8] man page for more information. - The `blazer_ser` and `blazer_usb` drivers supports the Megatec / Q1 protocol that is used in many brands (Blazer, Energy Sistem, Fenton Technologies, Mustek and many others). + See the linkman:blazer[8] man page for more information. - The `snmp-ups` driver handles various SNMP enabled devices, from many different manufacturers. In SNMP terms, `snmp-ups` is a manager, that monitors SNMP agents. + See the linkman:snmp-ups[8] man page for more information. - The `powerman-pdu` is a bridge to the PowerMan daemon, thus handling all PowerMan supported devices. The PowerMan project supports several serial and networked PDU, along with Blade and IPMI enabled servers. + See the linkman:powerman-pdu[8] man page for more information. - The `apcupsd-ups` driver is a bridge to the Apcupsd daemon, thus handling all Apcupsd supported devices. The Apcupsd project supports many serial, USB and networked APC UPS. + See the linkman:apcupsd-ups[8] man page for more information. UPS Shutdowns ~~~~~~~~~~~~~ upsdrvctl can also shut down (power down) all of your UPS hardware. WARNING: if you play around with this command, expect your filesystems to die. Don't power off your computers unless they're ready for it: /usr/local/ups/sbin/upsdrvctl shutdown /usr/local/ups/sbin/upsdrvctl shutdown sparky You should read the <> chapter to learn more about when to use this feature. If called at the wrong time, you may cause data loss by turning off a system with a filesystem mounted read-write. Power distribution unit management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NUT also provides an advanced support for power distribution units. You should read the <> chapter to learn more about when to use this feature. Network Server -------------- `upsd` is responsible for passing data from the drivers to the client programs via the network. It should be run immediately after `upsdrvctl` in your system's startup scripts. `upsd` should be kept running whenever possible, as it is the only source of status information for the monitoring clients like `upsmon`. Monitoring client ----------------- `upsmon` provides the essential feature that you expect to find in UPS monitoring software: safe shutdowns when the power fails. In the layered scheme of NUT software, it is a client. It has this separate section in the documentation since it is so important. You configure it by telling it about UPSes that you want to monitor in upsmon.conf. Each UPS can be defined as one of two possible types: Master ~~~~~~ This UPS supplies power to the system running `upsmon`, and this system is also responsible for shutting it down when the battery is depleted. This occurs after any slave systems have disconnected safely. If your UPS is plugged directly into a system's serial port, the `upsmon` process on that system should define that UPS as a master. For a typical home user, there's one computer connected to one UPS. That means you run a driver, `upsd`, and `upsmon` in master mode. Slave ~~~~~ This UPS may supply power to the system running `upsmon`, but this system can't shut it down directly. Use this mode when you run multiple computers on the same UPS. Obviously, only one can be connected to the serial port on the UPS, and that system is the master. Everything else is a slave. For a typical home user, there's one computer connected to one UPS. That means you run a driver, upsd, and upsmon in master mode. Additional Information ~~~~~~~~~~~~~~~~~~~~~~ More information on configuring upsmon can be found in these places: - The linkman:upsmon[8] man page - <> - <> chapter - The stock `upsmon.conf` that comes with the package Clients ------- Clients talk to upsd over the network and do useful things with the data from the drivers. There are tools for command line access, and a few special clients which can be run through your web server as CGI programs. For more details on specific programs, refer to their man pages. upsc ~~~~ `upsc` is a simple client that will display the values of variables known to `upsd` and your UPS drivers. It will list every variable by default, or just one if you specify an additional argument. This can be useful in shell scripts for monitoring something without writing your own network code. `upsc` is a quick way to find out if your driver(s) and upsd are working together properly. Just run `upsc ` to see what's going on, i.e.: morbo:~$ upsc sparky@localhost ambient.humidity: 035.6 ambient.humidity.alarm.maximum: NO,NO ambient.humidity.alarm.minimum: NO,NO ambient.temperature: 25.14 ... If you are interested in writing a simple client that monitors `upsd`, the source code for `upsc` is a good way to learn about using the upsclient functions. See the linkman:upsc[8] man page and <> for more information. upslog ~~~~~~ `upslog` will write status information from `upsd` to a file at set intervals. You can use this to generate graphs or reports with other programs such as `gnuplot`. upsrw ~~~~~ `upsrw` allows you to display and change the read/write variables in your UPS hardware. Not all devices or drivers implement this, so this may not have any effect on your system. A driver that supports read/write variables will give results like this: $ upsrw sparky@localhost ( many skipped ) [ups.test.interval] Interval between self tests Type: ENUM Option: "1209600" Option: "604800" SELECTED Option: "0" ( more skipped ) On the other hand, one that doesn't support them won't print anything: $ upsrw fenton@gearbox ( nothing ) `upsrw` requires administrator powers to change settings in the hardware. Refer to linkman:upsd.users[5] for information on defining users in `upsd`. upscmd ~~~~~~ Some UPS hardware and drivers support the notion of an instant command - a feature such as starting a battery test, or powering off the load. You can use upscmd to list or invoke instant commands if your hardware/drivers support them. Use the -l command to list them, like this: $ upscmd -l sparky@localhost Instant commands supported on UPS [sparky@localhost]: load.on - Turn on the load immediately test.panel.start - Start testing the UPS panel calibrate.start - Start run time calibration calibrate.stop - Stop run time calibration ... `upscmd` requires administrator powers to start instant commands. To define users and passwords in `upsd`, see linkman:upsd.users[5]. CGI Programs ------------ The CGI programs are clients that run through your web server. They allow you to see UPS status and perform certain administrative commands from any web browser. Javascript and cookies are not required. These programs are not installed or compiled by default. To compile and install them, first run `configure --with-cgi`, then do `make` and `make install`. If you receive errors about "gd" during configure, go get it and install it before continuing. You can get the source here: http://www.libgd.org/ In the event that you need libpng or zlib in order to compile gd, they can be found at these URLs: http://www.libpng.org/pub/png/pngcode.html http://www.gzip.org/zlib/ Access Restrictions ~~~~~~~~~~~~~~~~~~~ The CGI programs use hosts.conf to see if they are allowed to talk to a host. This keeps malicious visitors from creating queries from your web server to random hosts on the Internet. If you get error messages that say "Access to that host is not authorized", you're probably missing an entry in your hosts.conf. upsstats ~~~~~~~~ `upsstats` generates web pages from HTML templates, and plugs in status information in the right places. It looks like a distant relative of APC's old Powerchute interface. You can use it to monitor several systems or just focus on one. It also can generate IMG references to `upsimage`. upsimage ~~~~~~~~ This is usually called by upsstats via IMG SRC tags to draw either the utility or outgoing voltage, battery charge percent, or load percent. upsset ~~~~~~ `upsset` provides several useful administration functions through a web interface. You can use `upsset` to kick off instant commands on your UPS hardware like running a battery test. You can also use it to change variables in your UPS that accept user-specified values. Essentially, `upsset` provides the functions of `upsrw` and `upscmd`, but with a happy pointy-clicky interface. `upsset` will not run until you convince it that you have secured your system. You *must* secure your CGI path so that random interlopers can't run this program remotely. See the `upsset.conf` file. Once you have secured the directory, you can enable this program in that configuration file. It is not active by default. Version Numbering ----------------- The version numbers work like this: if the middle number is odd, it's a development tree, otherwise it is the stable tree. The past stable trees were 1.0, 1.2, 1.4, 2.0, 2.2 and 2.4, with the latest stable tree designated 2.6. The development trees were 1.1, 1.3, 1.5, 2.1 and 2.3. As of the 2.4 release, there is no real development branch anymore since the code is available through a revision control system (namely Subversion) and snapshots. Major release jumps are mostly due to large changes to the features list. There have also been a number of architectural changes which may not be noticeable to most users, but which can impact developers. Backwards and Forwards Compatibility ------------------------------------ The old network code spans a range from about 0.41.1 when TCP support was introduced up to the recent 1.4 series. It used variable names like STATUS, UTILITY, and LOADPCT. Many of these names go back to the earliest prototypes of this software from 1997. At that point there was no way to know that so many drivers would come along and introduce so many new variables and commands. The resulting mess grew out of control over the years. During the 1.3 development cycle, all variables and instant commands were renamed to fit into a tree-like structure. There are major groups, like input, output and battery. Members of those groups have been arranged to make sense - input.voltage and output.voltage compliment each other. The old names were UTILITY and OUTVOLT. The benefits in this change are obvious. The 1.4 clients can talk to either type of server, and can handle either naming scheme. 1.4 servers have a compatibility mode where they can answer queries for both names, even though the drivers are internally using the new format. When 1.4 clients talk to 1.4 or 2.0 (or more recent) servers, they will use the new names. Here's a table to make it easier to visualize: [options="header"] |============================================= | 4+| Server version | *Client version* | 1.0 | 1.2 | 1.4 | 2.0+ | 1.0 | yes | yes | yes | no | 1.2 | yes | yes | yes | no | 1.4 | yes | yes | yes | yes | 2.0+ | no | no | yes | yes |============================================= Version 2.0, and more recent, do not contain backwards compatibility for the old protocol and variable/command names. As a result, 2.0 clients can't talk to anything older than a 1.4 server. If you ask a 2.0 client to fetch "STATUS", it will fail. You'll have to ask for "ups.status" instead. Authors of separate monitoring programs should have used the 1.4 series to write support for the new variables and command names. Client software can easily support both versions as long as they like. If upsd returns 'ERR UNKNOWN-COMMAND' to a GET request, you need to use REQ. Support / Help / etc. --------------------- If you are in need of help, refer to the <> in the user manual. Hacking / Development Info -------------------------- Additional documentation can be found in: - the linkdoc:developer-guide[Developer Guide], - the linkdoc:packager-guide[Packager Guide]. Acknowledgements / Contributions -------------------------------- The many people who have participated in creating and improving NUT are listed in the user manual <>. nut-2.7.4/install-sh0000755000175000017500000003325512640476420011274 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2011-11-20.07; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: nut-2.7.4/NEWS0000644000175000017500000021220612667574526010001 00000000000000If you're upgrading from an earlier version, see the UPGRADING file. For a complete and more detailed list of changes, please refer to the ChangeLog file. --------------------------------------------------------------------------- Release notes for NUT 2.7.4 - what's new since 2.7.3: - New class of device supported: ATS - Automatic Transfer Switch are now supported in NUT. Eaton ATS are supported, and APC ones should be too. Users are welcomed to test and provide feedback - NUT command and variable naming scheme: * Document battery.charger.status, which will in time replace the historic CHRG and DISCHRG flags published in ups.status * Many extensions to support outlets groups, thresholds / alarms (ambient, input, output, outlet and outlet.group) - support for new devices: AEG PROTECT B / NAS APC ATS AP7724 (should be supported) Asium P700 Eaton ATS Eaton 5E 1100iUSB Eaton E Series DX UPS 1-20 kVA Eaton Powerware 9125-5000g Electrys UPS 2500 Fideltronic INIGO Viper 1200 Legrand Keor Multiplug LYONN CTB-800V Micropower LCD 1000 NHS Laser Senoidal 5000VA Sweex model P220 TS Shara Various APCUPSD-controlled APC devices - snmp-ups: * Improve automatic detection algorithm * Provide access to Net-SNMP timeout and retries * Proper handling of integer RW variables * Implement support for alarms, through ups.alarm and outlet.n.alarm * Improve log/debug output trace * Fix loss of precision when setting values, using upsrw * Support for outlets group management * Many improvements and simplification * Add support for Tripplite units using IETF mib * Improve communication staleness detection and recovery * Add devices MAC address publication * Register values enumerations, when available * Many improvements and fixes to the SNMP subdriver creation script - Eaton: * 3ph SNMP: Many improvements to Powerware / XUPS MIB, for data and commands Add support for Eaton Power Xpert Gateway UPS Card Improve support for temperature and humidity, including low / high values Alarms handling * ePDU (G2 and G3): Improve support for ambient sensor, including thresholds and dry contacts Outlet groups handling, including data, thresholds, settings and commands Alarms handling * XML/PDC (netxml-ups): Fix Eaton XML published data Add some settings (R/W flags) on ambient thresholds - bcmxcp_usb: improvements for device claiming and multi-packets responses - dummy-ups: allow any variable to be modified - libnutclient: Fix for reads when the socket was closed by NUT server - macosx-ups: * fix for 10.10 (Yosemite), v1.1 * gracefully handle disconnection of UPS (return "data stale") - nutdrv_atcl_usb: point to nutdrv_qx (fuji) for 0001:0000 - nutdrv_qx: * Add new 'sgs' USB subdriver to support TS Shara units * various improvements and simplification, to the code and documentation - nut-ipmipsu: improve FreeIPMI support - nut-scanner: * Don't depend on development libraries, by looking at some known paths, including the one provided through --libdir, to find the correct libraries * Fix a crash on a 2nd call to libnutscan with SNMP method - powercom: fix the processing of input and output voltage for KIN units - solis: * many improvements and cleanup * resync with end-of-packet character * fixes for Microsol Back-Ups BZ1200-BR - tripplitesu: Fix initialization when tripplite firmware is buggy (for Tripplite SU1000RT2U and possibly more) - usbhid-ups: * various minor improvements * support for Eaton UPS with dual HID report descriptor in HID Parser * handle missing USB strings in APC code - SSL support through Mozilla NSS: Rework the NSS tests to ensure that NSS is actually installed and usable for enabling SSL support in NUT - Augeas support: Augeas lens for ups.conf was updated to add various missing global directives and ups fields - scripts/systemd/nut-server.service.in: Restore systemd relationship since it was preventing upsd from starting whenever one or more drivers, among several, was failing to start - Fix UPower device matching for recent kernels, since hiddev* devices now have class "usbmisc", rather than "usb" - Network protocol information: default to type NUMBER for variables that are not flagged as STRING . This point is subject to improvements or change in the next release 2.7.5. Refer to docs/net-protocol.txt for more information - As usual, more bugfixes, cleanup and improvements, on both source code and documentation. --------------------------------------------------------------------------- Release notes for NUT 2.7.3 - what's new since 2.7.2: - reverted POWERDOWNFLAG to /etc/killpower as in 2.6.5 (packagers may want to put this in another filesystem, though) - configure/make fixes for ${systemdsystemunitdir} - apcsmart: fix command set parsing for protocol version 4 (e.g. Smart-UPS RT 10000 XL) - upslog: SIGUSR1 forces an immediate log entry - riello_usb/_ser: USB interface claim fix; improved error handling - usbhid-ups: add support for OpenUPS2 (PID: D005), Liebert GXT3 (PID: 0008) APC AP9584 Serial->USB kit (PID: 0000), and some Powercom models (PID: 0001). Fixed scaling for Cyberpower 0764:0501. - USB core: do not call usb_set_altinterface(0) by default - nutdrv_qx: added fabula, fuji USB and Voltronic-QS-HEX subdrivers; add bestups subdriver to supersede the old standalone bestups driver - NUT Monitor: added FreeDesktop AppData file (including screenshots) - renamed udev rules file to 62-nut-usbups.rules (permissions fix) - added AIX packaging - asem: added a driver for the UPS in ASEM PB1300 embedded PCs - solis: updated to support APC Microsol units sold in Brazil - tripplite_usb: updated to use dv/dq charge calculation for all models (also exposes battery_min and battery max as configuration variables); added binary 3005 protocol support (such as for SMART500RT1U) - genericups: better debugging while parsing the cable description flags - all drivers: a new 'synchronous' driver flag is available for very verbose units, such as some ePDUs - Eaton: * Add support for EnergySaving features for Eaton UPSs (HID USB/SHUT and XCP USB/serial) * Fix and complete Eaton ePDUs G2/G3 support * ABM (Advanced Battery Monitoring) support through battery.charger.status in HID (USB and SHUT), XCP (USB and serial) and SNMP (Powerware XUPS MIB) - support for new devices: APC Back-UPS 1200BR and Back-UPS BZ2200BI-BR (Microsol) ASEM SPA PB1300 UPS Belkin Regulator PRO-USB Cyber Power Systems Value 1500ELCD-RU EUROCASE EA200N 2000VA Fideltronik LUPUS 500 Flight Technic & International (FTUPS) FT-1000BS and FT-1000BS(T) Grafenthal PR-3000-HS JAWAN JW-UPSLC02 Lacerda New Orion 800VA Mecer ME-1000-WTU NHS Sistemas de Energia Expert C Online 6000/8000/10000 NHS Sistemas de Energia Expert S Online 6000/8000/10000 Powercom BNT-xxxAP (USB product id: 0001) Rucelf UPOII-3000-96-EL Tripp Lite OMNIVSINT800 Voltronic Power Apex 1KVA and Imperial 1KVA --------------------------------------------------------------------------- Release notes for NUT 2.7.2 - what's new since 2.7.1: - This release is the second interim release of the 2.7 testing series. - libupsclient had undefined references related to functions of libcommon. This issue was reported on Debian (bug #731156) and is now fixed - support for new devices: CABAC UPS-1700DV2 Eaton Powerware 3105 Emerson Network Power Liebert PSI 1440 MicroDowell B.Box LP 500 Numeric Digital 800 plus OptiUPS VS 575C Tripp Lite SU10KRT3/1X - FreeDesktop Hardware Abstraction Layer (HAL) support was removed. - nutdrv_atcl_usb: new driver for 'ATCL FOR UPS' - al175: re-introduced this driver (actually, it was in 2.7.1) - upsdrvctl now provides retry options for upsdrvctl and driver(s) - snmp-ups: add support for XPPC-MIB and Tripp Lite SU10KRT3/1X. Also fix erroneous status in HP/Compaq SNMP MIB (with the most recent HP firmware (1.76) ; improved various MIBs (APC, HP/Compaq, ...) - nutdrv_qx: add new 'fallback' Q1 subdriver, with minimal 'Q1' support. General improvements on all subdrivers. - mge-shut: partially revert PnP/RTS change, for initializing the communication with the UPS. Note that nut-scanner similar function was not modified however. - FreeBSD DEVD support: generate devd.conf files for USB UPSes This adds a --with-devd-dir=PATH option to ./configure - The NUT website was moved to a standalone website. A separate code repository and source archive are now available. - As usual, more bugfixes, cleanup and improvements, on both source code and documentation. --------------------------------------------------------------------------- Release notes for NUT 2.7.1 - what's new since 2.6.5: - This release is an interim release, part of the testing series, and the first release after the transition from Subversion to Git. The last release (2.6.5) is almost a year old. A lot of work has been done, but a good amount remains to achieve 2.8.0 goals. Please read the UPGRADING notes. - Added support for SSL via the Mozilla NSS library, in addition to the existing OpenSSL support. - Added a new driver, nutdrv_qx, for Megatec/Qx devices. This driver will eventually replace the blazer_ser and blazer_usb drivers. In particular, it adds support for Voltronic Power devices. - Increased USB_TIMEOUT to standards-compliant 5.000 seconds in most drivers. This should reduce the number of timeouts on low-speed USB 1.1 devices. - The jNut Java source has been split into a separate GitHub repository. - Added many devices to the HCL. Of particular note are many Tripp Lite USB HID PDC models which were tested against NUT by Tripp Lite. - Reworked some visual elements of the HCL. The output is better tailored for graphical and text-only browsers, but suggestions are welcome for additional accessibility enhancements. - Also increased timeouts and added redundant commands to improve reliability of mge-utalk driver. - Added the apcupsd-ups driver to interoperate with apcupsd installations. - Added documentation on creating subdrivers for snmp-ups and nutdrv_qx. - Added new drivers for the Riello UPS product line (riello_ser/riello_usb). - Many improvements to the BCM/XCP drivers have been merged in. This includes an improved data reception loop, and additional mappings. - Added a few variables to the Powercom HID mappings. - Updated the apcsmart driver, and renamed the previous driver to apcsmart-old. - Fixed the battery percentage calculation in the bestfcom driver. - libnutclient has been added as a C++ alternative to libupsclient. - Packaging files for Solaris and HP-UX (sponsored by Eaton) - Fix shutdown of Eaton HID, using usbhid-ups and mge-shut - usbhid-ups: final fix for APC Back UPS ES. APC Back UPS ES devices have buggy firmware, and this version does not cause a regression. The max_report variable should be set automatically based on the USB identification values. - nut-scanner: fix crash - IPMI support can handle more different versions of FreeIPMI - Support power supplies scan over the network nut-scanner can now scan for power supplies with IPMI over LAN. This is currently limited to IPMI 1.5 only - Implement a framework to spell check documentation source files, using Aspell. This includes an interactive build target (make spellcheck-interactive), and an automated one (make spellcheck), mainly for QA / Buildbot purpose. Note that a base NUT dictionary is also available (docs/nut.dict), providing a glossary of terms related to power devices and management - Improve systemd integration - snmp-ups: Fixed a crash on outlet management, and added delta_ups MIB support. Also fixed mappings for upsBypassVoltage, upsBypassCurrent, and upsBypassPower in three-phase IETF MIB. --------------------------------------------------------------------------- Release notes for NUT 2.6.5 - what's new since 2.6.4: - This release fixes an important regression in upssched: any upssched.conf command that takes a second argument resulted in a defective frame sent to the parent process. Thus, the command was not executed (report and patch from Oliver Schonefeld) - Website hosting: free NUT from Eaton website hosting NUT website (http://www.networkupstools.org) is no longer hosted by Eaton. Arnaud Quette (NUT project leader) has taken over NUT hosting on his own, to give NUT back some independence. This effort is also part of a logic to stop crediting Eaton for contributions from others (especially Arnaud Quette, as an individual). The new hosting service is located, as for Arnaud's blog (http://arnaud.quette.fr) on Gandi servers, using PaaS. This will allow more flexibility and automation of the release process - macosx-ups: new OS X Power Sources meta-driver Mac OS X provides UPS status information in a format similar to what is shown for laptop batteries. This driver will convert that information into a format compatible with NUT (Charles Lepple). - support for new devices: Eaton ePDU Switched Online Zinto A (USB ID 0x06da:0x0601) REDi Blazer 400VA / 600VA / 800VA UNITEK Alpha650ipF and Alpha650ipE (USB ID 0x0f03:0x0001) - mge-shut driver has been replaced by a new implementation (newmge-shut). In case of issue with this new version, users can revert to oldmge-shut. - First NUT virtualization package: NUT now supports integration with VMware ESXI 5.0, through a native VIB package. This is, for the time being, an external effort from René Garcia (refer to the Download section on NUT website). But work is underway to improve this integration, and include it in the NUT source tree - IPMI support (nut-ipmipsu driver and nut-scanner): prepare for supporting API changes in upcoming FreeIPMI versions 1.1.x and 1.2.x. - snmp-ups now supports high precision values for APC, and more variables - the NUT variables and commands namespace has been fixed and completed, with the known and used variables that were missing. - more bugfixes, cleanup and improvements, on both source code and documentation. --------------------------------------------------------------------------- Release notes for NUT 2.6.4 - what's new since 2.6.3: - This release Fix an important vulnerability in upsd (CVE-2012-2944: upsd can be remotely crashed) NUT server (upsd), from versions 2.4.0 to 2.6.3, are exposed to crashes when receiving random data from the network. This issue is related to the way NUT parses characters, especially from the network. Non printable characters were missed from strings operation (such as strlen), but still copied to the buffer, causing an overflow. Thus, fix NUT parser, to only allow the subset Ascii charset from Space to ~ (Reported by Sebastian Pohle, Alioth bug #313636, CVE-2012-2944) A separate patch, which applies to any faulty version, is also available: http://trac.networkupstools.org/projects/nut/changeset/3633 For more information, refer to the Common Vulnerabilities and Exposures: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2944 - A static source code analysis has been done by Michal Hlavinka from RedHat, using Coverity (12 issues fixed). - Add new "LIST CLIENTS" and "NETVER" commands to NUT network protocol. "NETVER" allows to retrieve the Network protocol version, while "LIST CLIENTS" provides the list of clients connected to a device. Refer to the developer guide, "Network protocol information" section for more information. - Support of ranges of values for writable variables has been added, to complete the existing enumerated values mechanism. This will start to appear in some drivers soon, beginning with Eaton. Refer to the developer guide, "Creating a new driver..." section for more information. - PyNUT.py has been updated to version 1.2.2, adding support for LIST CLIENTS, FSD, HELP and VER (Rene Martín Rodríguez) - support for new devices: AEG Power Solutions PROTECT HOME more APC SNMP cards ATEK Defensor range all Borri models all COVER ENERGY SA CyberPower OR700LCDRM1U, PR6000LCDRTXL5U and CP1000PFCLCD Dell UPS Network Management Card Dynamix 1000VA USB Eaton Management Card Contact (ref 66104) EVER POWERLINE RT / 11 / 31 and DUO II Pro GE Digital Energy GT Series 1000-3000 VA Gtec models all recent HP serial / USB UPS (G2, G3 and R/T models, ) and HP UPS Management Module Ippon INNOVA RT KOLFF BLACK NOVA Lexis X-power Tigra 1kVA Microline C-Lion Innova Online Yunto YQ450 PowerShield Defender 1200VA PowerWalker Online VFI LCD, Line-Interactive VI LCD and Line-Interactive VI Riello Netman Plus 102 SNMP Card Tripp-Lite OMNISMART500 - apcsmart has received some fixes to work better on Mac OS X, and in general - bcmxcp has improved support for battery status, and better supports PW9120 units - bestfortress has improved Best Fortress LI675VA support - blazer_ser and blazer_usb now try to automatically estimate high and low voltages, to be able to calculate battery charge ; support for online Innova UPS (T, RT and 3/1 T) has been added ; Best UPS support has been improved, to prepare for superseeding bestups driver - bestups has also received some care, though users are encouraged to switch to blazer_ser, since bestups will soon be deprecated. - newmge-shut has been heavily improved. However, replacement of the current mge-shut has been postponed to the next release, due to the CVE issue. - oneac 0.80 improves support for all families of Oneac (EG, ON, OZ and OB), including more data and instant commands (Bill Elliot). - usbhid-ups: for Eaton devices, ups.start.auto is now automatically adjusted for shutdown.{return,stayoff} to behave as expected ; Liebert firmwares with incorrect exponents have also been addressed. - snmp-ups now provides support for UPS shutdown, based on usbhid-ups mechanisms (composite commands and fallback). Composite commands are also supported now. This means, for example, that if 'shutdown.return' is not supported, a combination of 'load.off' + 'load.on' may be used ; Actual validity of instant commands is now tested before commands addition ; Eaton/MGE MIB has been cleaned and completed ; 3-phases support has been added to Socomec Netvision MIB ; HP/Compaq MIB has been completed, with thresholds, nominal values and more commands. - nut-scanner now also has libupsclient has a weak runtime dependency ; more docs and bugfixes have also happened. - Provide an Uncomplicated Firewall (UFW) profile (nut.ufw.profile) - Riello protocols have been officially published in NUT protocols library: http://www.networkupstools.org/ups-protocols.html#_riello - Duplicate instances of upsd / upsmon are now detected upon startup - NUT variables namespace has been completed with missing variables and commands that are already known and standard - upslog now comes with a companion file, for logrotate configuration - more devices embed NUT for power protection, now including Thecus NAS range - more bugfixes, cleanup and improvements, on both source code and documentation, with a good bunch from Greg A. Woods. --------------------------------------------------------------------------- Release notes for NUT 2.6.3 - what's new since 2.6.2: - nut-scanner is now more portable, and provides more coherent option names. IPMI support has also been added, to discover local power supplies. This version brings weak runtime dependencies in libnutscan, which allows to compile nut-scanner with all options and to run according to the available dependencies (USB, SNMP, IPMI, ...). - libnutscan now provides pkg-config support and the needed header files are distributed. Some documentation is also available in the developer guide and manual pages have been updated and completed. - support for new devices: Cyber Power Systems with SNMP RMCARD (100, 201, 202 and 301) ; Dynamix 650VA USB ; LDLC UPS-1200D ; Tecnoware UPS ERA LCD 0.65 ; Powercom BNT-xxxAP (USB ID 0d9f:0004) ; Various USB devices using UPSilon 2000 software. - apcsmart has received minor correction. - bcmxcp_usb now handles disconnection issues and reconnection mechanism. - blazer_usb enables again inclusion of buggy USB Device and Vendor IDs in udev rules file ; language ID support has been added for USB units from LDLC, Dynamix and other no names. - nut-ipmipsu has also received some improvements. - snmp-ups has fixed outlets reported current in aphel_genesisII MIB ; MGE 3 phases handles better low battery condition ; support for Cyber Power Systems with SNMP RMCARD has been added ; support of the newer Eaton ePDUs has been improved. - upsd doesn't anymore fail to start if at least one of the listening interface is available. This is needed on systems where either IPv4 or IPv6 is disabled, and no explicit LISTEN directive has been specified. - Avahi support is now automatically enabled, upon detection - jNut (NUT Java interface) adds device discovery support, through a nut-scanner wrapper ; jNutWebAPI, a HTTP/JSON web service interface, has also been added to interact with upsd and nut-scanner. - Base files for HPUX packaging have been added. This is still a work in progress. - Compilation on IBM Aix has been fixed (namespace conflict with ctypes). - more bugfixes, cleanup and improvements, on both source code and documentation. --------------------------------------------------------------------------- Release notes for NUT 2.6.2 - what's new since 2.6.1: - NUT can now monitor power supply units (PSU) in servers, through IPMI, using the new experimental 'nut-ipmipsu' driver. Users are encouraged to test it, and send feedback and debug output to the development mailing list. This requires GNU FreeIPMI (0.8.5 or higher) development package or files. Thanks goes to Al Chu, FreeIPMI project leader, for his help during this development. - NUT now provides a tool, called 'nut-scanner', to discover supported devices, both local and remote. nut-scanner will help to ease the configuration step, and power infrastructure discovery. This development, sponsored by Eaton, supports the following methods: * USB, * SNMP, * XML/HTTP (from Eaton), * NUT servers, using the classic connect or Avahi / mDNS methods. IPMI support will be added in the next release. A separate library, called 'libnutscan', is also available to provide these feature. Future NUT releases will provides binding for the supported languages (Perl, Python and Java). - NUT now provides a Java interface called 'jNut'. This development, sponsored by Eaton, is currently limited to the client interface. But it will be broaden to device discovery and configuration in the future. For more info, refer to nut/scripts/java/README, or the developer guide (chapter 'Creating new client'). Javadoc documentation is also provided, along with Java archives (.jar) in the Download section. - support for new devices: Eaton 3S ; Cyber Power Systems CP1000AVRLCD ; various APC models equiped with APC AP9618 management card, including APC Smart-UPS RT XL ; Orvaldi 750 / 900SP ; POWEREX VI 1000 LED ; PowerWalker VI 850 LCD ; SVEN Power Pro+ series (USB ID ffff:0000). - A regression has been fixed in udev rules file. This previously caused permission issues to owners of some USB devices. - Avahi support has been added, for NUT mDNS publication, through a static service file (nut/scripts/avahi/nut.service). - usbhid-ups has had Eaton completion: some features have been improved, such as 'output.voltage.nominal' ; 3S Eco control support has been added, along with battery.runtime.low and end of battery life (life cycle monitoring) support ; new measurements for 5 PX are also supported now (outlet power factor, power, real power and current). - apcsmart has been updated to support more variables and features ; the previous driver is however still available as 'apcsmart-old', in case of issues. - bcmxcp now supports per outlet startup and shutdown delays setting ; shutdown delay is also used, when available, for outlet.n.shutdown.return instead of the default 3 seconds. - snmp-ups.c has a new initialization method, that uses sysObjectID, which is a pointer to the prefered MIB of the device, to detect supported devices. This speeds up even more init stage and should render void the use of 'mib' option. SNMP v3 session initialisation has also been fixed, and Eaton PDU support has been completed. - Initial support has been added for systemd, the System and Service Manager from RedHat. - The chapter 'NUT configuration management with Augeas' of the developer guide has received some completion: a complete Python Augeas example code is now provided. - Finally, after years of dedication to NUT, Arjen de Korte is now retired. Sincere thanks to you Arjen from us all. --------------------------------------------------------------------------- Release notes for NUT 2.6.1 - what's new since 2.6.0: - the various recent USB regressions have been definitely fixed. - NUT now propose a variable to expose UPS efficiency (ups.efficiency). Eaton 5 PX already uses it. - the Perl module from Gabor Kiss (rewritten from Kit Peters') is now distributed with NUT source code. - support for new devices: Eaton Ellipse ECO, Powerware 9140, Eaton 5 PX, and ambient sensor on Eaton ePDU managed ; GE EP series ; Inform Sinus SS 210 ; IPAR Mini Energy ME 800 ; Mustek Yukai PowerMust 1000 USB ; Numeric 3000 SW ; SVEN Power Pro+ series (recent models) ; Vivaldi EA200 LED. - liebert-esp2: Improved Liebert ESP II support, including UPS shutdown (poweroff), 1 and 3-phase input and output variables, and most input / output / bypass / nominal variables. There is also a fix for the USB to serial cable (Farkas Levente and Spiros Ioannou). - powercom has improved PowerCom BNT 1500A and BNT-other support, along with driver documentation and code conformance to the NUT rules (Keven L. Ates). - apcsmart has more improved UPS poweroff support and options (Michal Soltys). - blazer has also seen some improvements. - usbhid-ups has completed a bit supported variables for APC and Eaton / MGE. - on the quality assurance side, Eaton has worked on fixing a few non conformances, like C++ style comments and warnings, using a newly developed verification tool (Prachi Gandhi). - fix remaining references to LIBSSL_LDFLAGS, instead of LIBSSL_LIBS, which cause unresolved symbol on libupsclient users (Fabrice Coutadeur). - the website has now a better support for Internet Explorer 6. - graphic illustrations, used for the Features page on the website Features and chapter of the user manual, have been refreshed (courtesy of Eaton). - more bugfixes, cleanup and improvements, on both source code and documentation. --------------------------------------------------------------------------- Release notes for NUT 2.6.0 - what's new since 2.4.3: - the main focus of this release is the complete documentation revamping, using AsciiDoc. This includes a new website, user manual, developer guide, packager guide and manual pages, available in various formats (single and multiple pages HTML, and PDF at the moment). Be sure to check the --with-doc configure option help, and docs/configure.txt for more information. - Add Augeas support, to provide easy NUT configuration management, through tools and development APIs. For more information, refer to the developer guide, or scripts/augeas/README in the source directory. - support for new devices: APC 5G; Eaton PowerWare 5119 RM (smart mode using upscode2 driver), Eaton Best Ferrups (using older ConnectUPS card), Eaton 9395 (serial interface), Eaton ConnectUPS X / BD / E Slot; HP T1000 INTL, HP T1500 INTL, HP T750 G2, HP R1500 G2 INTL; iDowell iBox UPS; Tripp Lite SmartOnline SU1000XLA, Tripp Lite Smart1000LCD, and some more USB/HID devices IDs; CyberPower CP1500AVRLCD and CP1350AVRLCD; PowerWalker Line-Interactive VI 1400 ; Rocketfish RF-1000VA / RF-1025VA. - usbhid-ups has better support for shutting down APC SmartUPS RM series, and finally fix the "buffer size" issue, which was breaking some devices data retrieval, or truncating some data on others. - snmp-ups now support SNMP v3 and its security parameters. IETF MIB support has also been extended. - fix dummy-ups simulation driver status handling bug, and add the capability to remove exposed variables on the fly. - the belkin driver now support control commands and status reporting for beeper and battery test. - the powerpanel driver supports more older CyberPower units. - mge-utalk, upscode2, blazer and liebert-esp2 have also received some care, and been improved. - NUT-Monitor and the PyNUT client module have been updated to 1.3, adding more features like automatic connection to the first local device and i18n support. - improve configure time dependencies checking and processing. - improve older Unix systems support (HP-UX, Aix, ...) for missing functions. - refresh and improve USB helper files (udev and UPower). - more generation automation: the ChangeLog file is now generated automatically at distribution time, along with the files needed for the website hardware compatibility list. - SSL support has also received some improvements. - tcp-wrapper now allows hostnames in /etc/hosts.allow too (not only IPv4 and/or IPv6 addresses). - many bugfixes, cleanup and improvements. --------------------------------------------------------------------------- Release notes for NUT 2.4.3 - what's new since 2.4.2: - this is a bugfix release that only solves the regression on IPv6 activation. --------------------------------------------------------------------------- Release notes for NUT 2.4.2 - what's new since 2.4.1: - the general USB support has been vastly improved, including many bug fixes, better OS support, new features and devices. - NUT now talks to Solar Controller Devices with the new ivtscd driver. - the snmp-ups driver supports more PDU, with a smaller disk footprint. - apcsmart supports more older SmartUPS and Matrix units. - the bestfortress driver is resurrected. - the virtual driver has been renamed to 'clone'. - the netxml-ups driver has received some care. - various debugging and development improvements have been done, around driver output; dummy-ups with more interaction and scripting and the device-recorder.sh script. - the build system has received many bugfixes and improvements. - the UPower (previously known as DeviceKit-power) rules file is now generated by NUT. - support for new devices: Apollo 1000A and 1000F; various Baytech RPC; old Best Power Fortress; Cyber Power Systems PR3000E, CP 1500C and OR2200LCDRM2U; all the new Dell UPS range (serial, USB and network); Eaton E Series NV and DX UPS, and Powerware 9130; older HP T500 and T750, newer T750 INTL (USB) and R1500 G2 (serial); Inform Informer Compact 1000VA; many serial and USB devices from Ippon, like Back Comfo Pro, Smart Power Pro and Smart Winner; IVT SCD series; Liebert GXT2-3000RT230 and PowerSure PSA; Mustek PowerMust 424 / 636 / 848 USB; all new PowerCOM USB devices with HID PDC interface; Tripp-Lite INTERNETOFFICE700, SMART700USB and ECO550UPS; UPSonic DS-800 (USB). --------------------------------------------------------------------------- Release notes for NUT 2.4.1 - what's new since 2.4.0: - the microdowell driver has appeared to support various MicroDowell Enterprise units (see the "new devices" list below). - support for new devices: MicroDowell Enterprise B8, B10, N8, N11, N15, N20, N22, N30, N40, N50, N60 and HiBox ST. - NUT-Monitor now better handles the ups.status field, and has switched to version 1.1. - the situation of the build toolchain has been fixed, with regard to the "make clean" target and the wrongly removed generated USB files. This brokes further configure call. --------------------------------------------------------------------------- Release notes for NUT 2.4.0 - what's new since 2.2.2: - preliminary support for Power Distribution Units (PDUs): NUT now support PDUs, either natively (ie using NUT snmp-ups driver), or through a binding to the Powerman daemon. The list of supported PDUs is already quite long, including: Eaton ePDUs (Managed and Monitored), some Aphel models, some Raritan PDUs, and the whole list of Powerman supported devices: http://powerman.sourceforge.net/supported.html - support for new devices: the various PDUs cited above, Chloride Desk Power 650, Cyber Power Systems Value 400E/600E/800E (USB models), Delta GES602N, Digitus DN-170020, the whole Eaton ranges (mostly composed of MGE Office Protection Systems and Powerware units) including BladeUPS, Forza Power Technologies SL-1001, HP PowerTrust 2997A, HP R/T 2200 G2, Infosec XP 1000 and XP 500, Ippon Back Power Pro (serial and USB), Kebo 1200D/D Series, Liebert PowerSure Personal XT, MGE Office Protection Systems Protection Station, Neus 400va and 600va, Phasak 400VA and 600VA, Plexus 500VA, Powercom Black Knight PRO / King PRO and Imperial, PowerKinetics BlackOut Buster, Sweex 1000 USB, UNITEK Alpha 500, WinPower CPM-800. - NUT now embeds Python client support through the PyNUTClient module and the NUT-Monitor application. Both are from David Goncalves, and are still available from http://www.lestat.st. For more information, refer to scripts/python/README. - the dummy-ups driver now support a "repeater" mode. This allows it to act as a NUT client, and to forward data. This can be useful for supervision and load sharing purposes. - tcp-wrappers support has been added to the upsd server, to grant users access by source IP for commands that require to be logged into the server. This replaces the previous internal implementation (ACL in upsd.conf). - the nut.conf file has been introduced to standardize startup configuration across the various systems. - NUT now ships a bash completion function for 'upsc' command (scripts/misc/nut.bash_completion). Simply copy it to /etc/bash_completion.d - many internal changes to improve maintenability, while lowering the maintenance cost (thus allowing developers to focus on what matters: the code!). Examples of this are: - the USB information automatic extraction to generate the various USB helper files, - the upsdrv_info_t structure to track more driver information, and remove the need for the upsdrv_banner() function - common USB code refactoring, as it is done for the serial functions. - tons of bugfixes, cleanup and improvements to make NUT stronger than ever! --------------------------------------------------------------------------- Release notes for NUT 2.2.2 - what's new since 2.2.1: - support for new devices: APC BACK-UPS XS LCD, Atlantis Land, Mustek Powermust Office 650, Oneac XAU models, Powerware PW5115 and PW9120 (USB), Nitram Elite 2005 - Integrated Power Management (NUT HAL integration) has reached a major milestone: it is now the most advanced UPS integration into Power Management layer known in existing OSs. It has received many corrections and improvements, and allows to PowerOff the UPS at the end of a power cycle (which is the most important feature, not supported on other systems). The various files are now installed into the correct location. - the usbhid-ups driver has received attention. Most notably, the shutdown handling has been reworked, and support for MGE UPS SYSTEMS 3 phases units has been added. - snmp-ups now supports MGE* Environment Sensor (ref 66 846). The ambient.temperature reporting has also been fixed for units other than APC. - the netxml-ups driver has appeared to support MGE* network HTTP/XML cards. - NUT now distributes by default the shared version of libupsclient (version 1.0.0), and use this for the provided clients (upsmon, upsc, upsrw, upscmd). This is part of an effort to reduce NUT's footprint, both on disk and in memory. - powerpanel has reach a new step toward the replacement of nitram and cpsups drivers. The final step is scheduled for NUT 2.4. - many changes, cleanup and fixes to the NUT core and various drivers. --------------------------------------------------------------------------- Release notes for NUT 2.2.1 - what's new since 2.2.0: - support for new devices: all MGE Office Protection Systems units, Advice TopGuard 2000, Belkin F6H375-USB, Dynamix UPS1700D, Effekta RM2000MH, Jageson Technology Jasuny USPS, Powercom SMK-1500A and SXL-1500A, PowerWalker Line-Interactive VI 400/800 and 600, Powerware 9110, UNITEK Alpha 2600, UPSonic CXR1000, some vintage serial APC UPSs. - the usbhid-ups driver has been improved, and fixed in many areas, through a backport of the development (trunk) version. - the udev rules, for Linux hotplug support of the USB UPSs, has been updated to support kernel newer than 2.6.22. - the megatec and megatec_usb drivers have also been backported from the development (trunk) version. - the client development files have also received some care: the upsclient pkg-config file has been fixed, and the upsclient.h file allows older NUT clients to continue using the UPSCONN structure. --------------------------------------------------------------------------- Release notes for NUT 2.2.0 - what's new since 2.0.5: - The new build infrastructure, using automake, is now used. This has major impact on the compilation and installation procedures, and thus on the NUT packaging. For more information, refer to UPGRADING and packaging/debian/ for an example of migration. - NUT now provides support for FreeDesktop Hardware Abstraction Layer (HAL) which brings full Plug And Play experience to USB UPS owners. For more information, refer to docs/nut-hal.txt. - support for new devices: Ablerex 625L, ActivePower 400VA, 2000VA; Belkin Home Office F6H350-SER, F6H500-SER, F6H650-SER; Belkin Office Series F6C550-AVR; Belkin Universal UPS F6C100-UNV (USB), F6C1100-UNV (USB), F6C1200-UNV (USB), F6H350deUNV (serial), F6H350ukUNV (serial), F6H650ukUNV (serial); Compaq R3000h; Cyber Power Systems PR2200; Dynex DX-800U; Geek Squad GS1285U; Krauler UP-M500VA; Mecer ME-2000; MGE UPS SYSTEMS Ellipse MAX; Online Zinto D; PowerTech SMK-800; SVEN Power Pro+ series, Power Smart RM 2000; Tripp-Lite SmartOnline SU1500RTXL2ua, smart2200RMXL2U. - added IPv6 support, - the newmge-shut driver has appeared. This one uses the same HID core as usbhid-ups, but communicate over a serial link. It will eventually replace the current mge-shut driver. - client commands (upsc, upsrw and upscmd): hostname is now optional, and defaults to "localhost" - many drivers have been improved and have received bug fixes: powerpanel, megatec, megatec_usb, safenet, tripplite_usb, gamatronic, - the hotplug and udev scripts, in charge of setting the right permissions on the USB devices, are now installed automatically when appropriate. - more generally, the NUT core and documentation, including the manpages, have been improved and updated. --------------------------------------------------------------------------- Release notes for NUT 2.0.5 - what's new since 2.0.4: This release is a backport of the development version. Many changes have already been backported previously. Thus it is more a synchronisation release, though it includes many bugfixes and support for new models. - support for new devices: APC Smart-UPS with 6TI firmware; Belkin Small Enterprise F6C1500-TW-RK; Compaq R3000 XR, R5500 XR; Cyber Power 550SL, 725SL, 685AVR, 800AVR, 1200AVR, AE550; Eltek; Inform GUARD; Microsol Rhino; Opti-UPS PowerES 420E; PowerMan RealSmart, BackPro; Powerware PW9315 3-phase; SOLA 305; Tripp-Lite SMART550USB, SMART2200RMXL2U, OMNI1000LCD, OMNI900LCD, OMNI650LCD, 1500 LCD, AVR550U; Viewsonic PowerES 420E. - bcmxcp: added 3-phase support - megatec: better hardware support, more instant commands - mge-hid: support more instant commands - newhidups: fixed APC and Tripp Lite bugs, various memory bugs, improved report buffering, improved Solaris support, added '-x explore' option for easy diagnosis of new devices - solis: shutdown programming, support new cables, Solaris support - tripplite_usb: updated SMARTPRO support, fixed OL/OB reporting, better error handling, some memory bugs - new dummy-ups driver simulator - added HTML interface for access to CGI scripts --------------------------------------------------------------------------- Release notes for NUT 2.0.4 - what's new since 2.0.3: - The newhidups critical bug (segmentation fault) has been fixed. It has also received some more care, like buxfixes and new models support and enhancement for Solaris. [Peter Selinger and Arnaud Quette] - A bug has been fixed in NUT core to support resuming from suspend-to-disk. This should also fix other similar issues, like time synchronisation through the NTP - Network Time Protocol. [Arjen de Korte] - The mge-shut driver now better detects the Low Battery status, support new models and fixes some wrong status and data. It also fixes some issue where the UPS wasn't restarting (refer to mge-shut manpage). [Arnaud Quette] - The genericups custom configuration through ups.conf is working again [Arjen de Korte] - The genericups driver type 22 also support CyberPower 725SL (and maybe others SL models) [David Kaufman] - The new megatec driver, which will replace a bunch of drivers by nut 2.2 (refer to docs/megatec.txt and UPGRADING) has been backported from the trunk (Development tree). The powermust driver has also received some attention. [Carlos Rodrigues] - The new rhino driver was added to support Microsol Rhino UPS hardware The solis has also been improved for solaris compatibility, and internal / external shutdown programming. solis can now save external shutdown programming to ups, and support new cables for solis 3 [Silvino B. Magalhães] - Several fixes and improvements have been made to upsrw, upsset, cpsups, tripplite_usb and the FAQ. [Arjen de Korte and Charles Lepple] --------------------------------------------------------------------------- Release notes for NUT 2.0.3 - what's new since 2.0.2: - The recent and major newhidups changes have been backported from the Development tree. It now: - supports models from MGE UPS SYSTEMS, APC and Belkin. Mustek and Unitek units are also recognized for development purpose, - handles better device reopening, after a disconnection, - handles multiple devices, with several parameters to find the right UPS. [Peter Selinger, Charles Lepple and Arnaud Quette] - The bcmxcp_usb driver has been added to support Powerware USB units. [Wolfgang Ocker and Kjell Claesson] - The tripplite_usb driver has been added to support Tripp Lite USB units. [Charles Lepple] - The sec driver is back as gamatronic [Gamatronic, Nadav Moskovitch] - The genericups driver has received official care from Gamatronic to add support for the Gamatronic UPS with alarm interface. [Gamatronic, Nadav Moskovitch] - The powermust driver now supports Soyntec Sekury C 500 and C 800 units. [Hanno Borns] - The mge-shut driver has received a bit of attention too, and enhance ups.model retrieval for some specific case (release 0.65) - The drivers don't change to the "statepath" directory anymore at initialisation time if called using -k. This avoid unneeded failure to poweroff the UPS if /var is already unmounted. [Gaspar Bakos] - The belkinunv driver now supports Belkin F6C1100-UNV [Dave Breiland] - The isbmex driver has been upgraded to version 0.05, which fixes various errors in formulas, add shutdown capability and revert back baudrate to B9600 (instead of B2400), as it broke the communication [Ricardo Martinezgarza] - The support of Sysgration UPGUARDS Pro650 in fentonups has been fixed [Simon J. Rowe] - The packaging files for Red Hat have received various fixes [Thomas Jarosch] - The solis driver has been fixed to avoid a naming colision and compile on Solaris [Paweł Kierdelewicz] - The snmp-ups driver has corrected the problem when exposing certain time data. --------------------------------------------------------------------------- Release notes for NUT 2.0.2 - what's new since 2.0.1: - the newhidups USB driver has been improved a lot and is no more experimental. It also now has a basic APC support, which will soon replace the legacy hidups driver. - The mge-utalk driver has improved its support for old units. - The mge-shut driver has been improved for restart/shutdown sequences which was previously blocking the serial port. - The general MGE support has been added Pulsar EXtreme C / EX RT, Comet EX RT, Pulsar SV, Pulsar PSX, Ellipse Office and NOVA AVR USB. - The genericups driver now supports Generic RUPS 2000, AEC MiniGuard UPS 700 (using Megatec M2501 cable), and Powerware 3110. [Nick Barnes, Paul Andreassen] - The powermust driver now supports SquareOne Power QP1000, Mustek PowerMust 1400VA Plus and 2000VA USB. [Carlos Rodrigues] - The fentonups driver has been enhanced and now supports Sysgration UPGUARDS Pro650. [Michel Bouissou, Simon J. Rowe] - The cpsups driver now supports MicroDowell B.Box BP 500/750/1000/1500. [Armin Diehl] - The snmp-ups driver now supports Socomec SNMP devices (Netvision MIB), and Powerware ConnectUPS SNMP cards. [Thanos Chatziathanassiou, Olli Salvia] - The bcmxcp driver is back with support for Powerware UPSs. [Tore Øpetveit, Kjell Claesson] - The cyberpower driver now supports CyberPower 1000AVR. [Dave Huang] - The new solis driver supports Microsol units: Solis 1.0, 1.5, 2.0 and 3.0. [Silvino B. Magalhaes] - The apcsmart driver has fixed APC600 support. - The etapro driver fixes brokeness due to ser_get_line use [Marek Michalkiewicz] - The new upscode2 driver supports Fiskars, Compaq and Powerware devices. [Niels Baggesen, Havard Lygre] - The tripplite driver has fixed a battery charge bug [Cedric Tefft] --------------------------------------------------------------------------- Release notes for NUT 2.0.1 - what's new since 2.0.0: - The bestuferrups driver has been forked into the new bestfcom driver which has better handling of the inverter status alarm messages and more. [Kent Hill] - Mustek UPS support returns with two drivers which have overlapping coverage: mustek and powermust. [powermust: Carlos Rodrigues, mustek: Martin Hajduch] - Additional CyberPower Systems hardware is supported with the new cpsups driver. Three recognized models are the CPS1500AVR, CPS1100VA, and OP500TE. [Walt Holman, Brad Sawatzky] - The genericups driver can now generate staleness warnings in specific cases where the UPS provides a way to test for its presence. See the "CON" setting in ups.conf for more details. [stan / saticed.me.uk] - Documentation for monitoring a Back-UPS RS 500 on a system without USB ports has been added to the cables directory. [Martin Edlman] - The everups driver now supports types 73-76 (NET 700/1000/1400/500-DPC) [hunter] - The new metasys driver supports Meta System models: Line, HF Millennium, HF Top Line, ECO Network, ECO, Ally HF, Megaline [BlaXwan] - The ippon driver now allows user-defined settings for the delay before switching off, and the delay before powering on. [Yuri Elizarov] - The victronups driver is now at version 0.1.9, which adds many instant commands: calibration control, battery and front panel tests, and bypass control. [Gert Lynge] - The tripplite driver has recieved a major overhaul to bring it up to working condition for the 2.0 tree, including code cleanups, several new variables, commands, and user-definable parameters. See ChangeLog for more. [Nicholas J Kain] - The mge-utalk driver has been upgraded to version 0.81, which fixes the lack of read-write variables and loss of sync on models which don't support restoring settings. [Arnaud Quette] - The Micro Ferrups model RE is now supported by the bestuferrups driver. The driver will also now read the ambient temperature and will no longer constantly report the data as stale. [Tim Thompson] - The fentonups driver's init sequence has been reworked to work better with some hardware, including a fix to the parser code. [MLH] - A workaround has been added to the hidups driver to avoid variables which are stuck by calling HIDIOCINITREPORT in every poll. [Stuart D. Gathman] - SOLA 610 UPS hardware and others which do not support the ID command may now be monitored by the bestups driver after forcing ID= in ups.conf. [Jason White] - "pollinterval" is now available via driver.parameter for consistency. [Arnaud Quette] - The mge-shut and newhidups drivers, along with the supporting hidparser/libhid code have received many updates, including lowering USB bandwidth consumption, driver unbinding (only in Linux), code cleanups, and more which can be seen in the ChangeLog file. [Arnaud Quette] - The fentonups driver now recognizes several more Megatec protocol units: SuperPower HP360, Hope-550 [Denis Zaika] Unitek Alpha 1000is [Antoine Cuvellard] - Some variables like uc_sigmask were renamed to avoid clashes with symbols on systems like HP/UX. - All man pages have been reworked to switch literal "-" characters to hyphens or "\-" as appropriate. [Shaul Karl] - upssched's CANCEL events were broken following the change to text-based socket messages in 1.5 and have been fixed. [Steven Schoch] - Calls to varargs functions with raw strings from the config files without an intervening "%s" have been fixed in upsmon, upssched, snmp-ups and upsd. [Ulf Harnhammar] --------------------------------------------------------------------------- Release notes for NUT 2.0.0 - what's new since 1.4.x: - The new naming scheme for variables and commands (introduced in 1.4) is now mandatory. The 1.4 tree supported both the old (STATUS) and the new (ups.status) as a transitional release, and now that time is over. This means that 2.0 is generally smaller than 1.4 code, since the interim compatibility hacks have been removed. - New serial handling code has been added, with greatly simplified operations. The old mess involving repeated calls to sigaction, alarm, and read has been condensed to a select-read loop. This change allows drivers which don't do any serial communications at all (hidups, snmp-ups) to drop that baggage, so they are a bit smaller when compiled. - The drivers now recognize "chroot=' and 'user=' in the global section of ups.conf. This means you don't have to use -r and -u when starting upsdrvctl. - upsmon now supports the -K argument to check for the presence of the POWERDOWNFLAG file. If it exists and contains the magic string, then upsmon will exit(EXIT_SUCCESS). Otherwise, it will exit(EXIT_FAILURE). This feature can be used to simplify shutdown scripts, since now you don't have to keep the script in sync with the upsmon.conf. - Many small things like signed value comparisons, int vs. size_t and proper use of const/struct were fixed throughout the source. These were mostly for correctness, but a few potential bugs involving very big or very small numbers were fixed at the same time. - The access control system in upsd.conf has been reworked and simplified. Since access levels have become meaningless in recent releases, the new system is just ACCEPT or REJECT . If you are upgrading from a previous version of the software, you will have to edit your upsd.conf to use this method. See the UPGRADING file for more details. - The build process now halts when make fails in one of the subdirectories. [Petter Reinholdtsen, Charles Lepple] - Helper data for using upsclient via pkgconfig is now created if pkgconfig is detected when configure runs. [Arnaud Quette] - The polling interval in drivers may now be set with 'pollinterval' in ups.conf. [Gabriel Faber] - Blazer UPS equipment is now supported with the blazer driver. [Phil Hutton] - Energizer USB UPS hardware is now supported on Linux with a new experimental driver. [Viktor T. Toth] - The newhidups driver has been merged as the first step towards portable USB UPS support. This will eventually replace the old Linux-only hidups driver. The newhidups driver is tagged experimental since it is under active development. [Arnaud Quette, Charles Lepple] - The newapc driver has been renamed to apcsmart, replacing the old driver with that name. If you used the newapc driver, be sure to delete the old binary and fix your ups.conf. - The apcsmart driver now supports asynchronous notification data from the hardware, which means it can wake up as soon as something happens. This affects the OL/OB/LB/RB data in ups.status, and generally reduces the latency in dispatching status changes by a few seconds. - The apcsmart driver can now support quirky hardware which does not provide the usual listing of valid command characters. This feature is necessary to monitor new models like the APC CS 350 and old ones like the Matrix 5000. It also now has sdtype=4 to handle the strange shutdown behavior on the CS series. - The belkin driver now works around broken firmware version 001, avoiding a lengthy delay at startup. It also implements the shutdown sequence differently, and should actually work on more hardware now. - The bestups driver has been slowed down to play nicer with the hardware, and is much more reliable as a result. Among other things, it should always detect the UPS on the first try, meaning no more "dot dot dot" when it starts. - The cyberpower driver is no longer tagged experimental, and now supports powering off the load. It also supports battery tests via instcmds. - Effekta MT 2000 RM hardware is now supported by the fentonups driver. [christoph moar] - The new safenet driver supports UPS hardware that uses the protocol of the same name. This includes models from many manufacturers, including Fairstone, Fenton, Gemini, Powerwell, Repotec, Soltec and Sweex. See the README or driver.list for the full details. [Arjen de Korte] - The genericups driver now has type 20 to monitor the Powerware 5119 RM. See http://lists.exploits.org/ups/Oct2003/00052.html. [Daniel Thompson] - The belkinunv driver has been added to allow monitoring Belkin Universal UPS hardware. [Peter Selinger] - Cyber Power Systems 1100AVR hardware which has a different protocol than the existing binary type (supported by 'cyberpower') is now supported by the experimental cyberpower1100 driver. [Walt Holman] - upsdrvctl now returns success or failure information in the exit code. Any failure during a requested operation will result in a nonzero value (specifically EXIT_FAILURE). --------------------------------------------------------------------------- Release notes for NUT 1.4.0 - what's new since 1.2.x: - The drivers and upsd now communicate over Unix domain sockets instead of state files, shared memory, or state files with mmap. This change makes many things possible, including the new dynamic variable and command naming scheme described below. There is a new development tool called sockdebug in the server directory for debugging driver-server communications on the sockets. - The old static variable scheme has been replaced by a new dynamic implementation. Vague names have been turned into meaningful names that fit into an organized system. UTILITY is now input.voltage. OUTVOLT is now output.voltage. This also applies to the names of instant commands. BTEST1 is test.battery.start, and BTEST0 is test.battery.stop. The old names are still supported for compatibility with older clients. This compatibility mode will be maintained throughout the 1.4 series, and will be gone by the release of 2.0. Users with older clients are encouraged to upgrade their software during this time. - The network protocol has been expanded to handle these new names. Older functions which only apply to the old names will continue to be supported through the 1.4 series. - The drivers and server (upsd) can now change their user ids and chroot themselves with the new -u and -r arguments. This lets you create a "chroot jail" with the bare minimum components. This technique is used to provide a higher degree of security. If someone exploited upsd to get a shell somehow, they would be stuck in the jail. - upssched now explicitly confirms reception of timer commands before exiting. This was done to avoid a race where one process would exit right when another one was starting. The second one would believe its command had been handled when it had been lost. - upslog has been reworked to use standard getopt parsing to provide the monitoring settings. The old way of specifying arguments is still supported for backwards compatibility. upslog has also been changed to only parse the format string once, rather than doing it every time through the loop. This should provide a minuscule drop in CPU utilization. - Usernames are now required in upsmon and upsd. This means that you must add a username to your MONITOR lines in upsmon.conf and then create a matching user in upsd.users. Installations from the 1.2 era probably already use usernames, so this mostly affects those from 1.0 and before. - Drivers are now pinged regularly by upsd when they aren't posting updates about the UPS status. This provides another check in the data validation process. If upsd fails to get a response within a few seconds, the UPS will be marked stale. - A few minor memory leaks were discovered with valgrind and squashed. - upsstats now reuses connections to upsd when cycling through multiple entries in the hosts.conf. This makes things a bit faster and avoids some of the noise in the syslog. This only applies to entries that are adjacent. To take advantage of this feature, you may have to rearrange them. MONITOR ups-1@host-1 ... MONITOR ups-1@host-2 ... MONITOR ups-2@host-2 ... MONITOR ups-3@host-3 ... Connection reuse for nonadjacent entries may be considered in the future. - upsd now warns about insecure configuration files at startup. These files (upsd.conf, upsd.users, and the certfile) should only be readable by upsd. Never make them world-readable. - The programs no longer print "shutting down" when they are just exiting. This was changed to avoid confusion about the term, since "shutting down" has a special meaning in UPS software. - Signal handlers no longer do any significant amount of work. Some of the programs used to do numerous things in there, raising concerns about reentrancy. They now set flags and allow the main loop to do the actual work from there. - A bug in upsmon where NOTIFYFLAG settings could be ignored was fixed. - Group handling has been changed. configure no longer accepts --with-group, and the programs no longer setgid() to a hardcoded value. They now setgid() to the primary group of whatever the user value may be. This may be compiled in with --with-user as before, and many programs accept -u to override it at runtime. - The state path is no longer created during 'make install'. Users are now expected to create it themselves. This removes a lot of evil complexity from the build and install sequences. - upsd no longer implements the DROP access command, as it could confuse the clients by getting them out of sync. DROP is now implemented as DENY, which sends an error message. If you use DROP, you should change it to DENY rather than relying on this compatibility measure. - The belkin driver no longer reports OFF and OL at the same time. - The bestups driver no longer sleeps during polls, which makes it more responsive to things like instant commands. - The cyberpower driver now has much better hardware detection code and no longer freezes at startup under some conditions. It also now supports the shutdown function. Instant commands for shutdowns and battery tests were also added. - The dummyups testing driver has been removed. The dummycons testing driver can do everything that dummyups once did and much more. dummycons is also now built by default for easier testing. - The newapc driver has been reworked to take advantage of the new internal driver state functions. Some variables without an obvious purpose were dropped. - The newapc driver now sends all five bytes when using sdtype 1. Previously it didn't send the entire string, and it didn't work. [Don Lewis] - The hidups driver has been expanded to allow for setting variables, a shutdown sequence, and more. [Arnaud Quette] - The mge-utalk driver had trouble establishing communications in some cases due to the RTS line being set. This has been fixed. The mge-shut driver has been added to the tree, and has replaced the older mge-ellipse driver. [Arnaud Quette, Philippe Marzouk] - Outlet-level control has been defined in the variable tree, and will be added to drivers where the hardware supports it. This can be used to shut down some components earlier than others to prolong your runtime on battery. This is supported in the mge-shut driver now, and may show up in others before long. [Arnaud Quette] - KIN-2200AP hardware is now recognized by the powercom driver. This change may also support other KIN-xxxxAP equipment. [Preston A. Elder] - The 1.1kVA UPS is now supported by the bestuferrups driver. This driver was also changed to allow easy addition of more models in the future. [Bob Apodaca] - The fentonups driver can now handle devices which implement the "I" detection differently, and now supports the Giant/WELI 500 as a result. [Joon Guillen] - The serial number of the UPS being monitored can now be specified with serial= in ups.conf in the genericups driver. [Shaul Karl] - The newapc driver now sends ESC to break out of menus when the initial detection fails. Some new APC models have interactive menus on the serial port, and the driver couldn't handle them before. - The snmp-ups driver now reports ambient temperature and humidity data for APC equipment. It also now supports the shutdown.reboot and shutdown.reboot.graceful commands. [Dmitry Frolov] - The list of supported variables and commands in the snmp-ups driver has been expanded. [Arnaud Quette, J.W. Hoogervorst] - Various drivers now report bypass mode with the BYP status word. [Arnaud Quette] - Energy Sistem equipment is now supported with the esupssmart driver. [Antonio Trujillo Coronado] - The Tripp-Lite SU series (SmartOnline) is supported with the new tripplitesu driver. [Allan Hessenflow] - The HP PowerTrust A2994A is now recognized by the hp driver. [Jan Sporbeck] - Many drivers were cleaned up to perform basic sanity checks on the status data before using it. - An explicit cleanup function has been added to the driver core to ensure that all dynamic resources are freed before exiting. This is part of the larger process to check for memory leaks and other bad things. [Arnaud Quette] - upsd now provides variable descriptions from an auxiliary file. This file is optional, which allows for a smaller memory footprint. It can also be edited for localization or other customizations. - upsimage and upsstats can now render BATTVOLT data. [Andrew R. Ghali] - String handling has been cleaned up throughout the tree. Calls to functions like strcpy and strcat were either replaced with other (range-checking) functions or were rewritten to avoid it. - Many compile-time defaults may now be overridden at runtime. In the environment NUT_CONFPATH and NUT_STATEPATH may be used. upsdrvctl has been changed to execve to pass these along to the drivers. ups.conf now supports driverpath=, and upsd.conf supports DATAPATH. [Bryan Henderson] - The configure --with-gd switches now actually do something useful when gd has been installed outside the default search directories. [Patrik Schindler] - The inline keyword is now handled properly on systems which do not support it or have it specified as another name. This was breaking compiles on some systems. [Petter Reinholdtsen] --------------------------------------------------------------------------- Release notes for NUT 1.2.2 - what's new since 1.2.1: - The snmp-ups driver has been upgraded and expanded. It now supports multiple MIBs, meaning it can handle RFC 1628, APCC, and MGE equipment. You can pick the right one with "mibs=" in ups.conf. Support for setting variable and instant commands is also available. [Arnaud Quette and Dmitry Frolov] - The powernet driver has been upgraded. It now supports more variables, has cleaner logging, and may now be considered stable. [Dmitry Frolov] - The hidups driver now supports physical port IDs. This avoids most of the problems where the hiddev* names can jump around too easily. It will now stay in the same place as long as you keep it plugged into the same physical port. See the ChangeLog file for more details. [David Brownell] - The hidups driver now also supports the MFR variable on APC Back-UPS ES equipment. [Jonathan A. Davis] - The sms driver has been updated to version 0.70. [Marcio Gomes] - The bestups driver now recognizes Best Power Axxium Rackmount equipment. [Ales Casar] - The liebert driver now uses O_NONBLOCK, and should now work consistently on OpenBSD as a result. [Alex Cichowski] - The liebert driver also now uses debouncing logic on the status lines. It was possible to get false readings that would start a shutdown or just annoy users with excessive onbatt/online notify messages. The new code forces the status to settle down for 3 polls before accepting the new value. This means that very short power events may not be detected. The alternative is having your machine shut down just because it decided to wiggle over to OB LB for a few seconds. - upsmon has had the disconnect logic fixed so the "communications lost" (COMMBAD) notify will actually go out when the connection fails. [Steve Monett] - upssched now uses a lock file to prevent a race where two could start at the same time. The second upssched would "win", and the first one would be unreachable. This had the side-effect of not being able to cancel timers on the first one. If you use upssched, you must define the LOCKFN directive when upgrading to this version, or it will not work. [Gaspar Bakos] - The packaging and scripts for Red Hat systems have been updated. [Antonino Albanese] - upsd is now a bit more lenient about access levels in the 'numlogins' check, which is what caused the problem in upsmon (next item). - upsmon no longer gets stuck in slavesync() when upsd is configured to drop certain queries. This usually happened at the worst possible time: in the middle of a shutdown. [John David Garza] - The upsclient functions now do more sanity checking on data from upsd so a short read won't return garbage to the callers. - upsset now works properly with ENUM/VARTYPE values for multiple UPSes on a single upsd. [Dmitry Frolov] - Various portability fixes for building on SGI were applied. [Andrea Suatoni] - upsd no longer tries to reference a deleted client structure if the client disconnects at the wrong time. Previously, it tried to use that pointer after the sendback() function had already failed on write and deleted the client. This could cause upsd to segfault depending on what areas were accessed. [Patrik Schindler] --------------------------------------------------------------------------- Release notes for NUT 1.2.1 - what's new since 1.2.0: - The sms driver is back, with support for Microlink Manager III hardware. [Marcio Gomes] - Fideltronik Ares Series hardware is now supported as genericups type 19. [Tomek Orzechowski and Arkadiusz Mikiewicz] - The drivers no longer silently drop instant commands or set commands from upsd that happen to get fragmented in transit. [linux@horizon.com] - The old multilink driver is back with a new name: liebert. It supports Liebert UPStation GXE hardware with the contact-closure cable. This is currently an experimental driver as there is no way to power down the load. - configure now picks up the right flags for gd automatically if gd 2.0.8 or higher is installed. This greatly simplifies the CGI build process for most users. - Shutdowns on FreeBSD using the genericups driver should work again. [Petri Riihikallio] --------------------------------------------------------------------------- nut-2.7.4/m4/0000755000175000017500000000000012670024740007655 500000000000000nut-2.7.4/m4/nut_check_libusb.m40000644000175000017500000000433112640473702013346 00000000000000dnl Check for LIBUSB compiler flags. On success, set nut_have_libusb="yes" dnl and set LIBUSB_CFLAGS and LIBUSB_LIBS. On failure, set dnl nut_have_libusb="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBUSB], [ if test -z "${nut_have_libusb_seen}"; then nut_have_libusb_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" AC_MSG_CHECKING(for libusb version via pkg-config) LIBUSB_VERSION="`pkg-config --silence-errors --modversion libusb 2>/dev/null`" if test "$?" = "0" -a -n "${LIBUSB_VERSION}"; then CFLAGS="`pkg-config --silence-errors --cflags libusb 2>/dev/null`" LIBS="`pkg-config --silence-errors --libs libusb 2>/dev/null`" else AC_MSG_CHECKING(via libusb-config) LIBUSB_VERSION="`libusb-config --version 2>/dev/null`" if test "$?" = "0" -a -n "${LIBUSB_VERSION}"; then CFLAGS="`libusb-config --cflags 2>/dev/null`" LIBS="`libusb-config --libs 2>/dev/null`" else LIBUSB_VERSION="none" CFLAGS="" LIBS="-lusb" fi fi AC_MSG_RESULT(${LIBUSB_VERSION} found) AC_MSG_CHECKING(for libusb cflags) AC_ARG_WITH(usb-includes, AS_HELP_STRING([@<:@--with-usb-includes=CFLAGS@:>@], [include flags for the libusb library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-usb-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libusb ldflags) AC_ARG_WITH(usb-libs, AS_HELP_STRING([@<:@--with-usb-libs=LIBS@:>@], [linker flags for the libusb library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-usb-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], []) AC_MSG_RESULT([${LIBS}]) dnl check if libusb is usable AC_CHECK_HEADERS(usb.h, [nut_have_libusb=yes], [nut_have_libusb=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(usb_init, [], [nut_have_libusb=no]) if test "${nut_have_libusb}" = "yes"; then dnl Check for libusb "force driver unbind" availability AC_CHECK_FUNCS(usb_detach_kernel_driver_np) LIBUSB_CFLAGS="${CFLAGS}" LIBUSB_LIBS="${LIBS}" fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_check_libnetsnmp.m40000644000175000017500000000370112640443572014243 00000000000000dnl Check for LIBNETSNMP compiler flags. On success, set dnl nut_have_libnetsnmp="yes" and set LIBNETSNMP_CFLAGS and dnl LIBNETSNMP_LIBS. On failure, set nut_have_libnetsnmp="no". dnl This macro can be run multiple times, but will do the checking only dnl once. AC_DEFUN([NUT_CHECK_LIBNETSNMP], [ if test -z "${nut_have_libnetsnmp_seen}"; then nut_have_libnetsnmp_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" dnl See which version of the Net-SNMP library (if any) is installed AC_MSG_CHECKING(for Net-SNMP version via net-snmp-config) SNMP_VERSION=`net-snmp-config --version 2>/dev/null` if test "$?" != "0" -o -z "${SNMP_VERSION}"; then SNMP_VERSION="none" fi AC_MSG_RESULT(${SNMP_VERSION} found) AC_MSG_CHECKING(for Net-SNMP cflags) AC_ARG_WITH(snmp-includes, AS_HELP_STRING([@<:@--with-snmp-includes=CFLAGS@:>@], [include flags for the Net-SNMP library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-snmp-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], [CFLAGS="`net-snmp-config --base-cflags 2>/dev/null`"]) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for Net-SNMP libs) AC_ARG_WITH(snmp-libs, AS_HELP_STRING([@<:@--with-snmp-libs=LIBS@:>@], [linker flags for the Net-SNMP library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-snmp-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], [LIBS="`net-snmp-config --libs 2>/dev/null`"]) AC_MSG_RESULT([${LIBS}]) dnl Check if the Net-SNMP library is usable AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [nut_have_libnetsnmp=yes], [nut_have_libnetsnmp=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(init_snmp, [], [nut_have_libnetsnmp=no]) if test "${nut_have_libnetsnmp}" = "yes"; then LIBNETSNMP_CFLAGS="${CFLAGS}" LIBNETSNMP_LIBS="${LIBS}" fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/ltoptions.m40000644000175000017500000003007312640476414012103 00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, # Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 7 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option `$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl `shared' nor `disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the `shared' and # `disable-shared' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the `static' and # `disable-static' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the `fast-install' # and `disable-fast-install' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the `pic-only' and `no-pic' # LT_INIT options. # MODE is either `yes' or `no'. If omitted, it defaults to `both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for lt_pkg in $withval; do IFS="$lt_save_ifs" if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS="$lt_save_ifs" ;; esac], [pic_mode=default]) test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) nut-2.7.4/m4/nut_check_asciidoc.m40000644000175000017500000000355612640473702013654 00000000000000dnl Check for LIBUSB compiler flags. On success, set nut_have_libusb="yes" dnl and set LIBUSB_CFLAGS and LIBUSB_LDFLAGS. On failure, set dnl nut_have_libusb="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_ASCIIDOC], [ if test -z "${nut_have_asciidoc_seen}"; then nut_have_asciidoc_seen=yes AC_PATH_PROGS([ASCIIDOC], [asciidoc]) if test -n "${ASCIIDOC}"; then AC_MSG_CHECKING([for asciiDoc version]) ASCIIDOC_VERSION="`${ASCIIDOC} --version 2>/dev/null`" dnl strip 'asciidoc ' from version string ASCIIDOC_VERSION="${ASCIIDOC_VERSION##* }" AC_MSG_RESULT(${ASCIIDOC_VERSION} found) fi AC_PATH_PROGS([A2X], [a2x]) if test -n "${A2X}"; then AC_MSG_CHECKING([for a2x version]) A2X_VERSION="`${A2X} --version 2>/dev/null`" dnl strip 'a2x ' from version string A2X_VERSION="${A2X_VERSION##* }" AC_MSG_RESULT(${A2X_VERSION} found) fi AC_PATH_PROGS([DBLATEX], [dblatex]) if test -n "${DBLATEX}"; then AC_MSG_CHECKING([for dblatex version]) DBLATEX_VERSION="`${DBLATEX} --version 2>/dev/null`" dnl strip 'dblatex version ' from version string DBLATEX_VERSION="${DBLATEX_VERSION##* }" AC_MSG_RESULT(${DBLATEX_VERSION} found) fi AC_PATH_PROGS([XSLTPROC], [xsltproc]) if test -n "${XSLTPROC}"; then AC_MSG_CHECKING([for xsltproc version]) XSLTPROC_VERSION="`${XSLTPROC} --version 2>/dev/null`" dnl strip 'xsltproc version ' from version string XSLTPROC_VERSION="${XSLTPROC_VERSION##* }" AC_MSG_RESULT(${XSLTPROC_VERSION} found) fi AC_PATH_PROGS([XMLLINT], [xmllint]) if test -n "${XMLLINT}"; then AC_MSG_CHECKING([for xmllint version]) XMLLINT_VERSION="`${XMLLINT} --version 2>/dev/null`" dnl strip 'xmllint version ' from version string XMLLINT_VERSION="${XMLLINT_VERSION##* }" AC_MSG_RESULT(${XMLLINT_VERSION} found) fi AC_PATH_PROGS([SOURCE_HIGHLIGHT], [source-highlight]) fi ]) nut-2.7.4/m4/ax_compare_version.m40000755000175000017500000001465212640443572013742 00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_compare_version.html # =========================================================================== # # SYNOPSIS # # AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # # DESCRIPTION # # This macro compares two version strings. Due to the various number of # minor-version numbers that can exist, and the fact that string # comparisons are not compatible with numeric comparisons, this is not # necessarily trivial to do in a autoconf script. This macro makes doing # these comparisons easy. # # The six basic comparisons are available, as well as checking equality # limited to a certain number of minor-version levels. # # The operator OP determines what type of comparison to do, and can be one # of: # # eq - equal (test A == B) # ne - not equal (test A != B) # le - less than or equal (test A <= B) # ge - greater than or equal (test A >= B) # lt - less than (test A < B) # gt - greater than (test A > B) # # Additionally, the eq and ne operator can have a number after it to limit # the test to that number of minor versions. # # eq0 - equal up to the length of the shorter version # ne0 - not equal up to the length of the shorter version # eqN - equal up to N sub-version levels # neN - not equal up to N sub-version levels # # When the condition is true, shell commands ACTION-IF-TRUE are run, # otherwise shell commands ACTION-IF-FALSE are run. The environment # variable 'ax_compare_version' is always set to either 'true' or 'false' # as well. # # Examples: # # AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) # AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) # # would both be true. # # AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) # AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) # # would both be false. # # AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) # # would be true because it is only comparing two minor versions. # # AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) # # would be true because it is only comparing the lesser number of minor # versions of the two values. # # Note: The characters that separate the version numbers do not matter. An # empty string is the same as version 0. OP is evaluated by autoconf, not # configure, so must be a string, not a variable. # # The author would like to acknowledge Guido Draheim whose advice about # the m4_case and m4_ifvaln functions make this macro only include the # portions necessary to perform the specific comparison specified by the # OP argument in the final configure script. # # LICENSE # # Copyright (c) 2008 Tim Toolan # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 11 dnl ######################################################################### AC_DEFUN([AX_COMPARE_VERSION], [ AC_REQUIRE([AC_PROG_AWK]) # Used to indicate true or false condition ax_compare_version=false # Convert the two version strings to be compared into a format that # allows a simple string comparison. The end result is that a version # string of the form 1.12.5-r617 will be converted to the form # 0001001200050617. In other words, each number is zero padded to four # digits, and non digits are removed. AS_VAR_PUSHDEF([A],[ax_compare_version_A]) A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/[[^0-9]]//g'` AS_VAR_PUSHDEF([B],[ax_compare_version_B]) B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ -e 's/[[^0-9]]//g'` dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary dnl # then the first line is used to determine if the condition is true. dnl # The sed right after the echo is to remove any indented white space. m4_case(m4_tolower($2), [lt],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` ], [gt],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` ], [le],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` ], [ge],[ ax_compare_version=`echo "x$A x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` ],[ dnl Split the operator from the subversion count if present. m4_bmatch(m4_substr($2,2), [0],[ # A count of zero means use the length of the shorter version. # Determine the number of characters in A and B. ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` # Set A to no more than B's length and B to no more than A's length. A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` ], [[0-9]+],[ # A count greater than zero means use only that many subversions A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` ], [.+],[ AC_WARNING( [illegal OP numeric parameter: $2]) ],[]) # Pad zeros at end of numbers to make same length. ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" B="$B`echo $A | sed 's/./0/g'`" A="$ax_compare_version_tmp_A" # Check for equality or inequality as necessary. m4_case(m4_tolower(m4_substr($2,0,2)), [eq],[ test "x$A" = "x$B" && ax_compare_version=true ], [ne],[ test "x$A" != "x$B" && ax_compare_version=true ],[ AC_WARNING([illegal OP parameter: $2]) ]) ]) AS_VAR_POPDEF([A])dnl AS_VAR_POPDEF([B])dnl dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. if test "$ax_compare_version" = "true" ; then m4_ifvaln([$4],[$4],[:])dnl m4_ifvaln([$5],[else $5])dnl fi ]) dnl AX_COMPARE_VERSION nut-2.7.4/m4/nut_check_libgd.m40000644000175000017500000000474412640443572013161 00000000000000dnl Check for LIBGD compiler flags. On success, set nut_have_libgd="yes" dnl and set LIBGD_CFLAGS and LIBGD_LDFLAGS. On failure, set dnl nut_have_libgd="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBGD], [ if test -z "${nut_have_libgd_seen}"; then nut_have_libgd_seen=yes CFLAGS_ORIG="${CFLAGS}" LDFLAGS_ORIG="${LDFLAGS}" LIBS_ORIG="${LIBS}" dnl Initial defaults. These are only used if gdlib-config is dnl unusable and the user fails to pass better values in --with dnl arguments CFLAGS="" LDFLAGS="-L/usr/X11R6/lib" LIBS="-lgd -lpng -lz -ljpeg -lfreetype -lm -lXpm -lX11" AC_MSG_CHECKING(for gd version via gdlib-config) GD_VERSION=`gdlib-config --version 2>/dev/null` if test "$?" != "0" -o -z "${GD_VERSION}"; then GD_VERSION="none" fi AC_MSG_RESULT(${GD_VERSION} found) case "${GD_VERSION}" in none) ;; 2.0.5 | 2.0.6 | 2.0.7) AC_MSG_WARN([[gd ${GD_VERSION} detected, unable to use gdlib-config script]]) AC_MSG_WARN([[If gd detection fails, upgrade gd or use --with-gd-includes and --with-gd-libs]]) ;; *) CFLAGS="`gdlib-config --includes 2>/dev/null`" LDFLAGS="`gdlib-config --ldflags 2>/dev/null`" LIBS="`gdlib-config --libs 2>/dev/null`" ;; esac dnl Now allow overriding gd settings if the user knows best AC_MSG_CHECKING(for gd include flags) AC_ARG_WITH(gd-includes, AS_HELP_STRING([@<:@--with-gd-includes=CFLAGS@:>@], [include flags for the gd library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-gd-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for gd library flags) AC_ARG_WITH(gd-libs, AS_HELP_STRING([@<:@--with-gd-libs=LDFLAGS@:>@], [linker flags for the gd library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-gd-libs - see docs/configure.txt) ;; *) LDFLAGS="${withval}" LIBS="" ;; esac ], []) AC_MSG_RESULT([${LDFLAGS} ${LIBS}]) dnl check if gd is usable AC_CHECK_HEADERS(gd.h gdfontmb.h, [nut_have_libgd=yes], [nut_have_libgd=no], [AC_INCLUDES_DEFAULT]) AC_SEARCH_LIBS(gdImagePng, gd, [], [nut_have_libgd=no]) if test "${nut_have_libgd}" = "yes"; then AC_DEFINE(HAVE_LIBGD, 1, [Define if you have Boutell's libgd installed]) LIBGD_CFLAGS="${CFLAGS}" LIBGD_LDFLAGS="${LDFLAGS} ${LIBS}" fi dnl put back the original versions CFLAGS="${CFLAGS_ORIG}" LDFLAGS="${LDFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/ltsugar.m40000644000175000017500000001042412640476414011527 00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59 which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) nut-2.7.4/m4/nut_check_libopenssl.m40000644000175000017500000000437412640443572014251 00000000000000dnl Check for OpenSSL (LIBOPENSSL) compiler flags. On success, set dnl nut_have_openssl="yes" and nut_ssl_lib="OpenSSL", and define WITH_SSL, dnl WITH_OPENSSL, LIBSSL_CFLAGS and LIBSSL_LIBS. On failure, set dnl nut_have_libssl="no". dnl This macro can be run multiple times, but will do the checking only once. AC_DEFUN([NUT_CHECK_LIBOPENSSL], [ if test -z "${nut_have_libopenssl_seen}"; then nut_have_libopenssl_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" AC_MSG_CHECKING(for OpenSSL version via pkg-config) OPENSSL_VERSION="`pkg-config --silence-errors --modversion openssl 2>/dev/null`" if test "$?" = "0" -a -n "${OPENSSL_VERSION}"; then CFLAGS="`pkg-config --silence-errors --cflags openssl 2>/dev/null`" LIBS="`pkg-config --silence-errors --libs openssl 2>/dev/null`" else OPENSSL_VERSION="none" CFLAGS="" LIBS="-lssl -lcrypto" fi AC_MSG_RESULT(${OPENSSL_VERSION} found) dnl allow overriding OpenSSL settings if the user knows best AC_MSG_CHECKING(for OpenSSL cflags) AC_ARG_WITH(openssl-includes, AS_HELP_STRING([@<:@--with-openssl-includes=CFLAGS@:>@], [include flags for the OpenSSL library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-openssl-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for OpenSSL ldflags) AC_ARG_WITH(openssl-libs, AS_HELP_STRING([@<:@--with-openssl-libs=LIBS@:>@], [linker flags for the OpenSSL library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-openssl-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], []) AC_MSG_RESULT([${LIBS}]) dnl check if openssl is usable AC_CHECK_HEADERS(openssl/ssl.h, [nut_have_openssl=yes], [nut_have_openssl=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(SSL_library_init, [], [nut_have_openssl=no]) if test "${nut_have_openssl}" = "yes"; then nut_with_ssl="yes" nut_ssl_lib="(OpenSSL)" AC_DEFINE(WITH_SSL, 1, [Define to enable SSL support]) AC_DEFINE(WITH_OPENSSL, 1, [Define to enable SSL support using OpenSSL]) LIBSSL_CFLAGS="${CFLAGS}" LIBSSL_LIBS="${LIBS}" fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/lt~obsolete.m40000644000175000017500000001375612640476415012434 00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) nut-2.7.4/m4/nut_check_libwrap.m40000644000175000017500000000251212640443572013527 00000000000000dnl Check for LIBWRAP compiler flags. On success, set nut_have_libwrap="yes" dnl and set LIBWRAP_CFLAGS and LIBWRAP_LIBS. On failure, set dnl nut_have_libwrap="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBWRAP], [ if test -z "${nut_have_libwrap_seen}"; then nut_have_libwrap_seen=yes dnl save LIBS LIBS_ORIG="${LIBS}" LIBS="" AC_CHECK_HEADERS(tcpd.h, [nut_have_libwrap=yes], [nut_have_libwrap=no], [AC_INCLUDES_DEFAULT]) AC_SEARCH_LIBS(yp_get_default_domain, nsl, [], [nut_have_libwrap=no]) dnl The line below does not work on Solaris 10. dnl AC_SEARCH_LIBS(request_init, wrap, [], [nut_have_libwrap=no]) AC_MSG_CHECKING(for library containing request_init) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include int allow_severity = 0, deny_severity = 0; ]], [[ request_init(0); ]])], [ AC_MSG_RESULT(none required) ], [ LIBS="${LIBS} -lwrap" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include int allow_severity = 0, deny_severity = 0; ]], [[ request_init(0); ]])], [ AC_MSG_RESULT(-lwrap) ], [ AC_MSG_RESULT(no) nut_have_libwrap=no ]) ]) if test "${nut_have_libwrap}" = "yes"; then AC_DEFINE(HAVE_WRAP, 1, [Define to enable libwrap support]) LIBWRAP_CFLAGS="" LIBWRAP_LIBS="${LIBS}" fi dnl restore original LIBS LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_type_socklen_t.m40000644000175000017500000000271412640473702013756 00000000000000dnl Check for socklen_t: historically on BSD it is an int, and in dnl POSIX 1g it is a type of its own, but some platforms use different dnl types for the argument to getsockopt, getpeername, etc. So we dnl have to test to find something that will work. dnl This code gets around. This instance came from rsync 2.5.6. AC_DEFUN([NUT_TYPE_SOCKLEN_T], [ AC_CHECK_TYPE([socklen_t], ,[ AC_MSG_CHECKING([for socklen_t equivalent]) AC_CACHE_VAL([nut_cv_socklen_t_equiv], [ # Systems have either "struct sockaddr *" or # "void *" as the second argument to getpeername nut_cv_socklen_t_equiv= for arg2 in "struct sockaddr" void; do for t in int size_t unsigned long "unsigned long"; do AC_TRY_COMPILE([ #include #include int getpeername (int, $arg2 *, $t *); ],[ $t len; getpeername(0,0,&len); ],[ nut_cv_socklen_t_equiv="$t" break ]) done done if test "x$nut_cv_socklen_t_equiv" = x; then AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) fi ]) AC_MSG_RESULT($nut_cv_socklen_t_equiv) AC_DEFINE_UNQUOTED(socklen_t, $nut_cv_socklen_t_equiv, [type to use in place of socklen_t if not defined])], [#include #include ]) ]) nut-2.7.4/m4/nut_check_libnss.m40000644000175000017500000000451412640473702013363 00000000000000dnl Check for Mozilla NSS (LIBNSS) compiler flags. On success, set dnl nut_have_libnss="yes" and nut_ssl_lib="Mozilla NSS", and define WITH_SSL, dnl WITH_NSS, LIBSSL_CFLAGS and LIBSSL_LIBS. On failure, set nut_have_libnss="no". dnl This macro can be run multiple times, but will do the checking only once. AC_DEFUN([NUT_CHECK_LIBNSS], [ if test -z "${nut_have_libnss_seen}"; then nut_have_libnss_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" AC_MSG_CHECKING(for Mozilla NSS version via pkg-config) NSS_VERSION="`pkg-config --silence-errors --modversion nss 2>/dev/null`" if test "$?" = "0" -a -n "${NSS_VERSION}"; then CFLAGS="`pkg-config --silence-errors --cflags nss 2>/dev/null`" LIBS="`pkg-config --silence-errors --libs nss 2>/dev/null`" else NSS_VERSION="none" CFLAGS="" LIBS="-lnss3 -lnssutil3 -lsmime3 -lssl3 -lplds4 -lplc4 -lnspr4" fi AC_MSG_RESULT(${NSS_VERSION} found) dnl allow overriding NSS settings if the user knows best AC_MSG_CHECKING(for Mozilla NSS cflags) AC_ARG_WITH(nss-includes, AS_HELP_STRING([@<:@--with-nss-includes=CFLAGS@:>@], [include flags for the Mozilla NSS library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-nss-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for Mozilla NSS ldflags) AC_ARG_WITH(nss-libs, AS_HELP_STRING([@<:@--with-nss-libs=LIBS@:>@], [linker flags for the Mozilla NSS library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-nss-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], []) AC_MSG_RESULT([${LIBS}]) dnl check if NSS is usable: we need both the runtime and headers AC_CHECK_FUNCS(NSS_Init, [nut_have_libnss=yes], [nut_have_libnss=no]) dnl libc6 also provides an nss.h file, so also check for ssl.h AC_CHECK_HEADERS([nss.h ssl.h], [], [nut_have_libnss=no], [AC_INCLUDES_DEFAULT]) if test "${nut_have_libnss}" = "yes"; then nut_with_ssl="yes" nut_ssl_lib="(Mozilla NSS)" AC_DEFINE(WITH_SSL, 1, [Define to enable SSL support]) AC_DEFINE(WITH_NSS, 1, [Define to enable SSL support using Mozilla NSS]) LIBSSL_CFLAGS="${CFLAGS}" LIBSSL_LIBS="${LIBS}" fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_check_libneon.m40000644000175000017500000000404512640443572013520 00000000000000dnl Check for LIBNEON compiler flags. On success, set nut_have_neon="yes" dnl and set LIBNEON_CFLAGS and LIBNEON_LIBS. On failure, set dnl nut_have_neon="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBNEON], [ if test -z "${nut_have_neon_seen}"; then nut_have_neon_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" dnl See which version of the neon library (if any) is installed AC_MSG_CHECKING(for libneon version via pkg-config (0.25.0 minimum required)) NEON_VERSION="`pkg-config --silence-errors --modversion neon 2>/dev/null`" if test "$?" != "0" -o -z "${NEON_VERSION}"; then NEON_VERSION="none" fi AC_MSG_RESULT(${NEON_VERSION} found) AC_MSG_CHECKING(for libneon cflags) AC_ARG_WITH(neon-includes, AS_HELP_STRING([@<:@--with-neon-includes=CFLAGS@:>@], [include flags for the neon library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-neon-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], [CFLAGS="`pkg-config --silence-errors --cflags neon 2>/dev/null`"]) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libneon ldflags) AC_ARG_WITH(neon-libs, AS_HELP_STRING([@<:@--with-neon-libs=LIBS@:>@], [linker flags for the neon library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-neon-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], [LIBS="`pkg-config --silence-errors --libs neon 2>/dev/null`"]) AC_MSG_RESULT([${LIBS}]) dnl check if neon is usable AC_CHECK_HEADERS(ne_xmlreq.h, [nut_have_neon=yes], [nut_have_neon=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(ne_xml_dispatch_request, [], [nut_have_neon=no]) if test "${nut_have_neon}" = "yes"; then dnl Check for connect timeout support in library (optional) AC_CHECK_FUNCS(ne_set_connect_timeout ne_sock_connect_timeout) LIBNEON_CFLAGS="${CFLAGS}" LIBNEON_LIBS="${LIBS}" fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_check_libfreeipmi.m40000644000175000017500000000722012640443572014357 00000000000000dnl Check for FreeIPMI (LIBFREEIPMI) compiler flags. On success, set dnl nut_have_freeipmi="yes" and nut_ipmi_lib="FreeIPMI", and define WITH_IPMI, dnl WITH_FREEIPMI, LIBIPMI_CFLAGS and LIBIPMI_LIBS. On failure, set dnl nut_have_freeipmi="no". dnl This macro can be run multiple times, but will do the checking only once. AC_DEFUN([NUT_CHECK_LIBFREEIPMI], [ if test -z "${nut_have_libfreeipmi_seen}"; then nut_have_libfreeipmi_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" AC_MSG_CHECKING(for FreeIPMI version via pkg-config) dnl pkg-config support requires Freeipmi 1.0.5, released on Thu Jun 30 2011 dnl but NUT should only require 0.8.5 (for nut-scanner) and 1.0.1 (for dnl nut-ipmipsu) (comment from upstream Al Chu) FREEIPMI_VERSION="`pkg-config --silence-errors --modversion libfreeipmi 2>/dev/null`" if test "$?" = "0" -a -n "${FREEIPMI_VERSION}"; then CFLAGS="`pkg-config --silence-errors --cflags libfreeipmi libipmimonitoring 2>/dev/null`" LIBS="`pkg-config --silence-errors --libs libfreeipmi libipmimonitoring 2>/dev/null`" else FREEIPMI_VERSION="none" CFLAGS="" LIBS="-lfreeipmi -lipmimonitoring" fi AC_MSG_RESULT(${FREEIPMI_VERSION} found) dnl allow overriding FreeIPMI settings if the user knows best AC_MSG_CHECKING(for FreeIPMI cflags) AC_ARG_WITH(freeipmi-includes, AS_HELP_STRING([@<:@--with-freeipmi-includes=CFLAGS@:>@], [include flags for the FreeIPMI library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-freeipmi-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for FreeIPMI ldflags) AC_ARG_WITH(freeipmi-libs, AS_HELP_STRING([@<:@--with-freeipmi-libs=LIBS@:>@], [linker flags for the FreeIPMI library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-freeipmi-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], []) AC_MSG_RESULT([${LIBS}]) dnl check if freeipmi is usable with our current flags AC_CHECK_HEADERS(freeipmi/freeipmi.h, [nut_have_freeipmi=yes], [nut_have_freeipmi=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS(ipmi_monitoring.h, [], [nut_have_freeipmi=no], [AC_INCLUDES_DEFAULT]) AC_SEARCH_LIBS([ipmi_ctx_create], [freeipmi], [], [nut_have_freeipmi=no]) dnl when version cannot be tested (prior to 1.0.5, with no pkg-config) dnl we have to check for some specific functions AC_SEARCH_LIBS([ipmi_ctx_find_inband], [freeipmi], [], [nut_have_freeipmi=no]) AC_SEARCH_LIBS([ipmi_monitoring_init], [ipmimonitoring], [nut_have_freeipmi_monitoring=yes], [nut_have_freeipmi_monitoring=no]) AC_SEARCH_LIBS([ipmi_monitoring_sensor_read_record_id], [ipmimonitoring], [], [nut_have_freeipmi_monitoring=no]) dnl Check for FreeIPMI 1.1.X / 1.2.X which implies API changes! AC_SEARCH_LIBS([ipmi_sdr_cache_ctx_destroy], [freeipmi], [nut_have_freeipmi_11x_12x=no], []) AC_SEARCH_LIBS([ipmi_sdr_ctx_destroy], [freeipmi], [nut_have_freeipmi_11x_12x=yes], [nut_have_freeipmi_11x_12x=no]) if test "${nut_have_freeipmi}" = "yes"; then nut_with_ipmi="yes" nut_ipmi_lib="(FreeIPMI)" nut_have_libipmi="yes" AC_DEFINE(HAVE_FREEIPMI, 1, [Define if FreeIPMI support is available]) LIBIPMI_CFLAGS="${CFLAGS}" LIBIPMI_LIBS="${LIBS}" fi if test "${nut_have_freeipmi_11x_12x}" = "yes"; then AC_DEFINE(HAVE_FREEIPMI_11X_12X, 1, [Define if FreeIPMI 1.1.X / 1.2.X support is available]) fi if test "${nut_have_freeipmi_monitoring}" = "yes"; then AC_DEFINE(HAVE_FREEIPMI_MONITORING, 1, [Define if FreeIPMI monitoring support is available]) fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_arg_with.m40000644000175000017500000000032112640443572012532 00000000000000dnl simplified declaration of some feature options AC_DEFUN([NUT_ARG_WITH], [ AC_ARG_WITH($1, AC_HELP_STRING([--with-$1], [$2 ($3)]), [nut_with_$1="${withval}"], [nut_with_$1="$3"] ) ]) nut-2.7.4/m4/nut_check_libavahi.m40000644000175000017500000000507712640443572013657 00000000000000dnl Check for LIBAVAHI compiler flags. On success, set nut_have_neon="yes" dnl and set LIBAVAHI_CFLAGS and LIBAVAHI_LIBS. On failure, set dnl nut_have_avahi="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBAVAHI], [ if test -z "${nut_have_avahi_seen}"; then nut_have_avahi_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" dnl See which version of the avahi library (if any) is installed AC_MSG_CHECKING(for avahi-core version via pkg-config (0.6.30 minimum required)) AVAHI_CORE_VERSION="`pkg-config --silence-errors --modversion avahi-core 2>/dev/null`" if test "$?" != "0" -o -z "${AVAHI_CORE_VERSION}"; then AVAHI_CORE_VERSION="none" fi AC_MSG_RESULT(${AVAHI_CORE_VERSION} found) AC_MSG_CHECKING(for avahi-client version via pkg-config (0.6.30 minimum required)) AVAHI_CLIENT_VERSION="`pkg-config --silence-errors --modversion avahi-client 2>/dev/null`" if test "$?" != "0" -o -z "${AVAHI_CLIENT_VERSION}"; then AVAHI_CLIENT_VERSION="none" fi AC_MSG_RESULT(${AVAHI_CLIENT_VERSION} found) AC_MSG_CHECKING(for avahi cflags) AC_ARG_WITH(avahi-includes, AS_HELP_STRING([@<:@--with-avahi-includes=CFLAGS@:>@], [include flags for the avahi library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-avahi-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], [CFLAGS="`pkg-config --silence-errors --cflags avahi-core avahi-client 2>/dev/null`"]) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for avahi ldflags) AC_ARG_WITH(avahi-libs, AS_HELP_STRING([@<:@--with-avahi-libs=LIBS@:>@], [linker flags for the avahi library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-avahi-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], [LIBS="`pkg-config --silence-errors --libs avahi-core avahi-client 2>/dev/null`"]) AC_MSG_RESULT([${LIBS}]) dnl check if avahi-core is usable AC_CHECK_HEADERS(avahi-common/malloc.h, [nut_have_avahi=yes], [nut_have_avahi=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(avahi_free, [], [nut_have_avahi=no]) if test "${nut_have_avahi}" = "yes"; then dnl check if avahi-client is usable AC_CHECK_HEADERS(avahi-client/client.h, [nut_have_avahi=yes], [nut_have_avahi=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(avahi_client_new, [], [nut_have_avahi=no]) if test "${nut_have_avahi}" = "yes"; then LIBAVAHI_CFLAGS="${CFLAGS}" LIBAVAHI_LIBS="${LIBS}" fi fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_check_libpowerman.m40000644000175000017500000000333412640443572014411 00000000000000dnl Check for LIBPOWERMAN compiler flags. On success, set nut_have_libpowerman="yes" dnl and set LIBPOWERMAN_CFLAGS and LIBPOWERMAN_LIBS. On failure, set dnl nut_have_libpowerman="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBPOWERMAN], [ if test -z "${nut_have_libpowerman_seen}"; then nut_have_libpowerman_seen=yes dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" AC_MSG_CHECKING(for libpowerman cflags) AC_ARG_WITH(powerman-includes, AS_HELP_STRING([@<:@--with-powerman-includes=CFLAGS@:>@], [include flags for the libpowerman library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-powerman-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], [CFLAGS="`pkg-config --silence-errors --cflags libpowerman 2>/dev/null`"]) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libpowerman libs) AC_ARG_WITH(powerman-libs, AS_HELP_STRING([@<:@--with-powerman-libs=LIBS@:>@], [linker flags for the libpowerman library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-powerman-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], [LIBS="`pkg-config --silence-errors --libs libpowerman 2>/dev/null`"]) AC_MSG_RESULT([${LIBS}]) dnl check if libpowerman is usable AC_CHECK_HEADERS(libpowerman.h, [nut_have_libpowerman=yes], [nut_have_libpowerman=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(pm_connect, [], [nut_have_libpowerman=no]) if test "${nut_have_libpowerman}" = "yes"; then LIBPOWERMAN_CFLAGS="${CFLAGS}" LIBPOWERMAN_LIBS="${LIBS}" fi dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/nut_check_libltdl.m40000644000175000017500000000273312640443572013522 00000000000000dnl Check for LIBLTDL compiler flags. On success, set nut_have_libltdl="yes" dnl and set LIBLTDL_CFLAGS and LIBLTDL_LIBS. On failure, set dnl nut_have_libltdl="no". This macro can be run multiple times, but will dnl do the checking only once. AC_DEFUN([NUT_CHECK_LIBLTDL], [ if test -z "${nut_have_libltdl_seen}"; then nut_have_libltdl_seen=yes dnl save LIBS LIBS_ORIG="${LIBS}" LIBS="" AC_MSG_CHECKING(for libltdl cflags) AC_ARG_WITH(libltdl-includes, AS_HELP_STRING([@<:@--with-libltdl-includes=CFLAGS@:>@], [include flags for the libltdl library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-libltdl-includes - see docs/configure.txt) ;; *) CFLAGS="${withval}" ;; esac ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libltdl ldflags) AC_ARG_WITH(libltdl-libs, AS_HELP_STRING([@<:@--with-libltdl-libs=LIBS@:>@], [linker flags for the libltdl library]), [ case "${withval}" in yes|no) AC_MSG_ERROR(invalid option --with(out)-libltdl-libs - see docs/configure.txt) ;; *) LIBS="${withval}" ;; esac ], []) AC_MSG_RESULT([${LIBS}]) AC_CHECK_HEADERS(ltdl.h, [nut_have_libltdl=yes], [nut_have_libltdl=no], [AC_INCLUDES_DEFAULT]) AC_SEARCH_LIBS(lt_dlinit, ltdl, [], [nut_have_libltdl=no]) if test "${nut_have_libltdl}" = "yes"; then AC_DEFINE(HAVE_LIBLTDL, 1, [Define to enable libltdl support]) LIBLTDL_CFLAGS="" LIBLTDL_LIBS="${LIBS}" fi dnl restore original LIBS LIBS="${LIBS_ORIG}" fi ]) nut-2.7.4/m4/libtool.m40000644000175000017500000106011112640476414011511 00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool 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. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]) # serial 57 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_CC_BASENAME(CC) # ------------------- # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. m4_defun([_LT_CC_BASENAME], [for cc_temp in $1""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from `configure', and `config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # `config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain="$ac_aux_dir/ltmain.sh" ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the `libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to `config.status' so that its # declaration there will have the same value as in `configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags="_LT_TAGS"dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the `libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into `config.status', and then the shell code to quote escape them in # for loops in `config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # `#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test $lt_write_fail = 0 && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ \`$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test $[#] != 0 do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try \`$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try \`$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test "$silent" = yes && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # _LT_COPYING _LT_LIBTOOL_TAGS # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) _LT_PROG_REPLACE_SHELLFNS mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test $_lt_result -eq 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS="$save_LDFLAGS" ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[[012]]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test "$lt_cv_ld_force_load" = "yes"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" m4_if([$1], [CXX], [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script which will find a shell with a builtin # printf (which we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case "$ECHO" in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified).], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case ${with_sysroot} in #( yes) if test "$GCC" = yes; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([${with_sysroot}]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and in which our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; *-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD="${LD-ld}_sol2" fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -eq 0; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -ne 0; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test "x$lt_cv_ar_at_file" = xno; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test x"[$]$2" = xyes; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" ]) if test x"[$]$2" = xyes; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n $lt_cv_sys_max_cmd_len ; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "$cross_compiling" = yes; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen="shl_load"], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen="dlopen"], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) ]) ]) ]) ]) ]) ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links="nottested" if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test "$hard_links" = no; then AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", [Define to the sub-directory in which libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then # We can hardcode non-existent directories. if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; *) lt_sed_strip_eq="s,=/,/,g" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[[89]] | openbsd2.[[89]].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([], [sys_lib_dlsearch_path_spec], [2], [Run-time system search path for libraries]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program which can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$1; then lt_cv_path_MAGIC_CMD="$ac_dir/$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac]) MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program which can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi]) if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh # decide which to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd="$ECHO" ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test "x$lt_cv_path_mainfest_tool" != xyes; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM="-lm") ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test "$GCC" = yes; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global defined # symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test "$with_gnu_ld" = yes; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test "$lt_use_gnu_ld_interface" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global # defined symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi _LT_TAGVAR(link_all_deplibs, $1)=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; hpux10*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS="$save_LDFLAGS"]) if test "$lt_cv_irix_exported_symbol" = yes; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' else case $host_os in openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ;; esac fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting ${shlibpath_var} if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC="$CC" AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report which library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC="$lt_save_CC" ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared # libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ func_to_tool_file "$lt_outputfile"~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd2*) # C++ shared libraries are fairly broken _LT_TAGVAR(ld_shlibs, $1)=no ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(GCC, $1)="$GXX" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case ${prev}${p} in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test "$pre_test_object_deps_done" = no; then case ${prev} in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)="${prev}${p}" else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)="$p" else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)="$p" else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test "X$F77" = "Xno"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_F77" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$G77" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" CFLAGS="$lt_save_CFLAGS" fi # test "$_lt_disable_F77" != yes AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test "X$FC" = "Xno"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_FC" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test "$_lt_disable_FC" != yes AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f $lt_ac_sed && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test $lt_ac_count -gt 10 && break lt_ac_count=`expr $lt_ac_count + 1` if test $lt_ac_count -gt $lt_ac_max; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [AC_MSG_CHECKING([whether the shell understands some XSI constructs]) # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,b/c, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes AC_MSG_RESULT([$xsi_shell]) _LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) AC_MSG_CHECKING([whether the shell understands "+="]) lt_shell_append=no ( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes AC_MSG_RESULT([$lt_shell_append]) _LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) # ------------------------------------------------------ # In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and # '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. m4_defun([_LT_PROG_FUNCTION_REPLACE], [dnl { sed -e '/^$1 ()$/,/^} # $1 /c\ $1 ()\ {\ m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) } # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: ]) # _LT_PROG_REPLACE_SHELLFNS # ------------------------- # Replace existing portable implementations of several shell functions with # equivalent extended shell implementations where those features are available.. m4_defun([_LT_PROG_REPLACE_SHELLFNS], [if test x"$xsi_shell" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"}]) _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl func_split_long_opt_name=${1%%=*} func_split_long_opt_arg=${1#*=}]) _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) fi if test x"$lt_shell_append" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl func_quote_for_eval "${2}" dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) # Save a `func_append' function call where possible by direct use of '+=' sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: else # Save a `func_append' function call even when '+=' is not available sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$_lt_function_replace_fail" = x":"; then AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) fi ]) # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine which file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS nut-2.7.4/m4/nut_report_feature.m40000644000175000017500000000166312640443572013766 00000000000000dnl automated feature report at the end of configure script. dnl it also AC_DEFINE() and AM_CONDITIONAL the matching variable. dnl for example, "usb" (--with-usb) will give dnl nut_with_usb and WITH_USB (both macros, and dnl AM_CONDITIONAL) AC_DEFUN([NUT_REPORT], [ if test -z "${nut_report_feature_flag}"; then nut_report_feature_flag="1" ac_clean_files="${ac_clean_files} conf_nut_report_feature" echo > conf_nut_report_feature echo "Configuration summary:" >> conf_nut_report_feature echo "======================" >> conf_nut_report_feature fi echo "$1: $2" >> conf_nut_report_feature ]) AC_DEFUN([NUT_REPORT_FEATURE], [ AC_MSG_CHECKING([whether to $1]) AC_MSG_RESULT([$2 $3]) NUT_REPORT([$1], [$2 $3]) AM_CONDITIONAL([$4], test "$2" = "yes") if test "$2" = "yes"; then AC_DEFINE_UNQUOTED($4, 1, $5) fi ]) AC_DEFUN([NUT_PRINT_FEATURE_REPORT], [ cat conf_nut_report_feature ]) nut-2.7.4/m4/nut_check_os.m40000644000175000017500000001160612640444140012504 00000000000000dnl Check for the exact system name and type. This is only used at the moment dnl to determine the packaging rule to be used through the OS_NAME variable. dnl Derived from dist.m4 - OpenSS7 (Ditributed under the GNU GPL v2) dnl Copyright (c) 2001-2006 OpenSS7 Corporation dnl Copyright (c) 1997-2000 Brian F. G. Bidulock AC_DEFUN_ONCE([NUT_OS_FUNCTIONS], [ os_get_name() { case "$[1]" in (*CentOS*|*CENTOS*) echo 'centos' ;; (*Lineox*|*LINEOX*) echo 'lineox' ;; (*White?Box*|*WHITE?BOX*) echo 'whitebox' ;; (*Fedora*|*FEDORA*) echo 'fedora' ;; (*Mandrake*|*Mandriva*|*MANDRAKE*|*MANDRIVA*) echo 'mandriva' ;; (*Red?Hat*|*RED?HAT*) echo 'redhat' ;; (*SuSE*|*SUSE*|*Novell*|*NOVELL*) echo 'suse' ;; (*Debian*|*DEBIAN*) echo 'debian' ;; (*Ubuntu*|*UBUNTU*) echo 'ubuntu' ;; (*Gentoo*|*gentoo*) echo 'gentoo' ;; # FIXME: *BSD, Solaris, HPUX, Aix, ... (*) # fallback for other systems case "${host_cpu}-${host_os}" in *-aix*) echo 'aix' ;; *-freebsd*) echo 'freebsd' ;; *-darwin*) echo 'darwin' ;; *solaris*) echo 'esyscmd(uname -sp)' ;; *-hpux*) echo 'hpux' ;; esac esac } # only list special cases. os_get_target() { case "$[1]" in # some may fall under generic-rpm (centos|lineox|whitebox|fedora|redhat) echo 'redhat' ;; (suse) echo 'opensuse' ;; (ubuntu) echo 'debian' ;; (*) echo '$[1]' ;; # FIXME: *BSD, Solaris, HPUX, Aix, ... esac } ])# _OS_FUNCTIONS AC_DEFUN([NUT_CHECK_OS], [ m4_pattern_allow([^PKG_TARGET$]) # Look for all possible source of OS name resolution # 1) we look for a LSB release info file eval "dist_search_path=\" /etc/lsb-release\"" dist_search_path=$(echo "$dist_search_path" | sed -e 's|\||g;s|//|/|g') for dist_file in $dist_search_path do if test -f "$dist_file" then dist_cv_build_lsb_file="$dist_file" break fi done if test -z "$dist_cv_build_lsb_file" ; then dist_cv_build_lsb_file='no' fi # 2) we look at specific release info file eval "dist_search_path=\" /etc/gentoo-release /etc/centos-release /etc/lineox-release /etc/whitebox-release /etc/fedora-release /etc/mandrake-release /etc/mandriva-release /etc/redhat-release /etc/SuSE-release /etc/debian_version\"" dist_search_path=$(echo "$dist_search_path" | sed -e 's|\||g;s|//|/|g') for dist_file in $dist_search_path do if test -f "$dist_file" then dist_cv_build_rel_file="$dist_file" break fi done if test -z "$dist_cv_build_rel_file" ; then dist_cv_build_rel_file='no' fi # 3) we try the generic issue info file eval "dist_search_path=\" /etc/issue /etc/issue.net\"" dist_search_path=$(echo "$dist_search_path" | sed -e 's|\||g;s|//|/|g') for dist_file in $dist_search_path do if test -f "$dist_file" then dist_cv_build_issue_file="$dist_file" break fi done if test -z "$dist_cv_build_issue_file" ; then dist_cv_build_issue_file='no' fi # Now we parse these content to search for the OS name AC_REQUIRE([NUT_OS_FUNCTIONS]) AC_CACHE_CHECK([for host system name], [dist_cv_build_flavor], [dnl if test -z "$dist_cv_build_flavor" -a ":${dist_cv_build_rel_file:-no}" != :no ; then if test `echo "$dist_cv_build_rel_file" | sed -e 's|.*/||'` != 'debian_version' ; then dist_cv_build_flavor=$(os_get_name "$(cat $dist_cv_build_rel_file)") fi fi if test -z "$dist_cv_build_flavor" -a ":${dist_cv_build_lsb_file:-no}" != :no ; then . "$dist_cv_build_lsb_file" dist_cv_build_flavor=$(os_get_name "${DISTRIB_DESCRIPTION:-unknown}") if test -z "$dist_cv_build_flavor" ; then dist_cv_build_flavor=$(echo "$DISTRIB_ID" | tr [[:upper:]] [[:lower:]] | sed -e 's|[[[:space:]]]*||g;s|linux||g') fi fi if test -z "$dist_cv_build_flavor" -a ":${dist_cv_build_issue_file:-no}" != :no ; then dist_cv_build_flavor=$(os_get_name "$(cat $dist_cv_build_issue_file | grep 'Linux\|Fedora\|Ubuntu' | head -1)") fi # do debian after lsb and issue for Ubuntu if test -z "$dist_cv_build_flavor" -a ":${dist_cv_build_rel_file:-no}" != :no ; then if test `echo "$dist_cv_build_rel_file" | sed -e 's|.*/||'` = 'debian_version' ; then dist_cv_build_flavor='debian' fi fi # FIXME if test -z "$dist_cv_build_flavor" ; then dist_cv_build_flavor=$(os_get_name "$(${CC-cc} $CFLAGS -v 2>&1 | grep 'gcc version')") fi # save the result if test -n "$dist_cv_build_flavor" ; then OS_NAME=$dist_cv_build_flavor PKG_TARGET=$(os_get_target "$dist_cv_build_flavor") fi ]) ])# NUT_CHECK_OS dnl checking for OS information file {/etc/lsb-release, /etc/xxx_version, /etc/issue, ...) dnl Checking for host system name dnl get the base type (linux, ...) from uname, dnl then check the exact linux type?! dnl FIXME: consider cross pf target dnl detect build env (pbuilder, .rpm, ...) nut-2.7.4/m4/ltversion.m40000644000175000017500000000126212640476415012074 00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 3337 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.2]) m4_define([LT_PACKAGE_REVISION], [1.3337]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.2' macro_revision='1.3337' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) nut-2.7.4/tests/0000755000175000017500000000000012670024742010501 500000000000000nut-2.7.4/tests/Makefile.in0000644000175000017500000011364412667762001012502 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: tests VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @HAVE_CPPUNIT_TRUE@TESTS = cppunittest$(EXEEXT) @HAVE_CPPUNIT_TRUE@check_PROGRAMS = $(am__EXEEXT_1) subdir = tests DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp $(top_srcdir)/test-driver ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @HAVE_CPPUNIT_TRUE@am__EXEEXT_1 = cppunittest$(EXEEXT) am__cppunittest_SOURCES_DIST = example.cpp cpputest.cpp @HAVE_CPPUNIT_TRUE@am__objects_1 = cppunittest-example.$(OBJEXT) @HAVE_CPPUNIT_TRUE@am_cppunittest_OBJECTS = $(am__objects_1) \ @HAVE_CPPUNIT_TRUE@ cppunittest-cpputest.$(OBJEXT) cppunittest_OBJECTS = $(am_cppunittest_OBJECTS) cppunittest_LDADD = $(LDADD) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = cppunittest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(cppunittest_CXXFLAGS) \ $(CXXFLAGS) $(cppunittest_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(cppunittest_SOURCES) DIST_SOURCES = $(am__cppunittest_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no am__tty_colors = { \ $(am__tty_colors_dummy); \ if test "X$(AM_COLOR_TESTS)" = Xno; then \ am__color_tests=no; \ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ am__color_tests=yes; \ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ am__color_tests=yes; \ fi; \ if test $$am__color_tests = yes; then \ red=''; \ grn=''; \ lgn=''; \ blu=''; \ mgn=''; \ brg=''; \ std=''; \ fi; \ } am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__recheck_rx = ^[ ]*:recheck:[ ]* am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* # A command that, given a newline-separated list of test names on the # standard input, print the name of the tests that are to be re-run # upon "make recheck". am__list_recheck_tests = $(AWK) '{ \ recheck = 1; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ { \ if ((getline line2 < ($$0 ".log")) < 0) \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ { \ recheck = 0; \ break; \ } \ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ { \ break; \ } \ }; \ if (recheck) \ print $$0; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # A command that, given a newline-separated list of test names on the # standard input, create the global log from their .trs and .log files. am__create_global_log = $(AWK) ' \ function fatal(msg) \ { \ print "fatal: making $@: " msg | "cat >&2"; \ exit 1; \ } \ function rst_section(header) \ { \ print header; \ len = length(header); \ for (i = 1; i <= len; i = i + 1) \ printf "="; \ printf "\n\n"; \ } \ { \ copy_in_global_log = 1; \ global_test_result = "RUN"; \ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".trs"); \ if (line ~ /$(am__global_test_result_rx)/) \ { \ sub("$(am__global_test_result_rx)", "", line); \ sub("[ ]*$$", "", line); \ global_test_result = line; \ } \ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ copy_in_global_log = 0; \ }; \ if (copy_in_global_log) \ { \ rst_section(global_test_result ": " $$0); \ while ((rc = (getline line < ($$0 ".log"))) != 0) \ { \ if (rc < 0) \ fatal("failed to read from " $$0 ".log"); \ print line; \ }; \ printf "\n"; \ }; \ close ($$0 ".trs"); \ close ($$0 ".log"); \ }' # Restructured Text title. am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } # Solaris 10 'make', and several other traditional 'make' implementations, # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it # by disabling -e (using the XSI extension "set +e") if it's set. am__sh_e_setup = case $$- in *e*) set +e;; esac # Default flags passed to test drivers. am__common_driver_flags = \ --color-tests "$$am__color_tests" \ --enable-hard-errors "$$am__enable_hard_errors" \ --expect-failure "$$am__expect_failure" # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). am__check_pre = \ $(am__sh_e_setup); \ $(am__vpath_adj_setup) $(am__vpath_adj) \ $(am__tty_colors); \ srcdir=$(srcdir); export srcdir; \ case "$@" in \ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ *) am__odir=.;; \ esac; \ test "x$$am__odir" = x"." || test -d "$$am__odir" \ || $(MKDIR_P) "$$am__odir" || exit $$?; \ if test -f "./$$f"; then dir=./; \ elif test -f "$$f"; then dir=; \ else dir="$(srcdir)/"; fi; \ tst=$$dir$$f; log='$@'; \ if test -n '$(DISABLE_HARD_ERRORS)'; then \ am__enable_hard_errors=no; \ else \ am__enable_hard_errors=yes; \ fi; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ am__expect_failure=yes;; \ *) \ am__expect_failure=no;; \ esac; \ $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) # A shell command to get the names of the tests scripts with any registered # extension removed (i.e., equivalently, the names of the test logs, with # the '.log' extension removed). The result is saved in the shell variable # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", # since that might cause problem with VPATH rewrites for suffix-less tests. # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ case '$@' in \ */*) \ case '$*' in \ */*) b='$*';; \ *) b=`echo '$@' | sed 's/\.log$$//'`; \ esac;; \ *) \ b='$*';; \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) TEST_LOGS = $(am__test_logs2:.test.log=.log) TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ $(TEST_LOG_FLAGS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ @HAVE_CPPUNIT_TRUE@cppunittest_CXXFLAGS = $(CPPUNIT_CFLAGS) @HAVE_CPPUNIT_TRUE@cppunittest_LDFLAGS = $(CPPUNIT_LIBS) # List of src files for CppUnit tests @HAVE_CPPUNIT_TRUE@CPPUNITTESTSRC = example.cpp @HAVE_CPPUNIT_TRUE@cppunittest_SOURCES = $(CPPUNITTESTSRC) cpputest.cpp @HAVE_CPPUNIT_FALSE@EXTRA_DIST = example.cpp cpputest.cpp all: all-am .SUFFIXES: .SUFFIXES: .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tests/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list cppunittest$(EXEEXT): $(cppunittest_OBJECTS) $(cppunittest_DEPENDENCIES) $(EXTRA_cppunittest_DEPENDENCIES) @rm -f cppunittest$(EXEEXT) $(AM_V_CXXLD)$(cppunittest_LINK) $(cppunittest_OBJECTS) $(cppunittest_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppunittest-cpputest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppunittest-example.Po@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< cppunittest-example.o: example.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-example.o -MD -MP -MF $(DEPDIR)/cppunittest-example.Tpo -c -o cppunittest-example.o `test -f 'example.cpp' || echo '$(srcdir)/'`example.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-example.Tpo $(DEPDIR)/cppunittest-example.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='example.cpp' object='cppunittest-example.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-example.o `test -f 'example.cpp' || echo '$(srcdir)/'`example.cpp cppunittest-example.obj: example.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-example.obj -MD -MP -MF $(DEPDIR)/cppunittest-example.Tpo -c -o cppunittest-example.obj `if test -f 'example.cpp'; then $(CYGPATH_W) 'example.cpp'; else $(CYGPATH_W) '$(srcdir)/example.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-example.Tpo $(DEPDIR)/cppunittest-example.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='example.cpp' object='cppunittest-example.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-example.obj `if test -f 'example.cpp'; then $(CYGPATH_W) 'example.cpp'; else $(CYGPATH_W) '$(srcdir)/example.cpp'; fi` cppunittest-cpputest.o: cpputest.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-cpputest.o -MD -MP -MF $(DEPDIR)/cppunittest-cpputest.Tpo -c -o cppunittest-cpputest.o `test -f 'cpputest.cpp' || echo '$(srcdir)/'`cpputest.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-cpputest.Tpo $(DEPDIR)/cppunittest-cpputest.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest.cpp' object='cppunittest-cpputest.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-cpputest.o `test -f 'cpputest.cpp' || echo '$(srcdir)/'`cpputest.cpp cppunittest-cpputest.obj: cpputest.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-cpputest.obj -MD -MP -MF $(DEPDIR)/cppunittest-cpputest.Tpo -c -o cppunittest-cpputest.obj `if test -f 'cpputest.cpp'; then $(CYGPATH_W) 'cpputest.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest.cpp'; fi` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-cpputest.Tpo $(DEPDIR)/cppunittest-cpputest.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest.cpp' object='cppunittest-cpputest.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-cpputest.obj `if test -f 'cpputest.cpp'; then $(CYGPATH_W) 'cpputest.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest.cpp'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags # Recover from deleted '.trs' file; this should ensure that # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells # to avoid problems with "make -n". .log.trs: rm -f $< $@ $(MAKE) $(AM_MAKEFLAGS) $< # Leading 'am--fnord' is there to ensure the list of targets does not # expand to empty, as could happen e.g. with make check TESTS=''. am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) am--force-recheck: @: $(TEST_SUITE_LOG): $(TEST_LOGS) @$(am__set_TESTS_bases); \ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ redo_bases=`for i in $$bases; do \ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ done`; \ if test -n "$$redo_bases"; then \ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ if $(am__make_dryrun); then :; else \ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ fi; \ if test -n "$$am__remaking_logs"; then \ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ "recursion detected" >&2; \ else \ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ fi; \ if $(am__make_dryrun); then :; else \ st=0; \ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ for i in $$redo_bases; do \ test -f $$i.trs && test -r $$i.trs \ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ test -f $$i.log && test -r $$i.log \ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ done; \ test $$st -eq 0 || exit 1; \ fi @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ ws='[ ]'; \ results=`for b in $$bases; do echo $$b.trs; done`; \ test -n "$$results" || results=/dev/null; \ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ success=true; \ else \ success=false; \ fi; \ br='==================='; br=$$br$$br$$br$$br; \ result_count () \ { \ if test x"$$1" = x"--maybe-color"; then \ maybe_colorize=yes; \ elif test x"$$1" = x"--no-color"; then \ maybe_colorize=no; \ else \ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ shift; \ desc=$$1 count=$$2; \ if test $$maybe_colorize = yes && test $$count -gt 0; then \ color_start=$$3 color_end=$$std; \ else \ color_start= color_end=; \ fi; \ echo "$${color_start}# $$desc $$count$${color_end}"; \ }; \ create_testsuite_report () \ { \ result_count $$1 "TOTAL:" $$all "$$brg"; \ result_count $$1 "PASS: " $$pass "$$grn"; \ result_count $$1 "SKIP: " $$skip "$$blu"; \ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ result_count $$1 "FAIL: " $$fail "$$red"; \ result_count $$1 "XPASS:" $$xpass "$$red"; \ result_count $$1 "ERROR:" $$error "$$mgn"; \ }; \ { \ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ $(am__rst_title); \ create_testsuite_report --no-color; \ echo; \ echo ".. contents:: :depth: 2"; \ echo; \ for b in $$bases; do echo $$b; done \ | $(am__create_global_log); \ } >$(TEST_SUITE_LOG).tmp || exit 1; \ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ if $$success; then \ col="$$grn"; \ else \ col="$$red"; \ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ if $$success; then :; else \ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ if test -n "$(PACKAGE_BUGREPORT)"; then \ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ fi; \ echo "$$col$$br$$std"; \ fi; \ $$success || exit 1 check-TESTS: @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ log_list=`for i in $$bases; do echo $$i.log; done`; \ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; recheck: all $(check_PROGRAMS) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ | $(am__list_recheck_tests)` || exit 1; \ log_list=`for i in $$bases; do echo $$i.log; done`; \ log_list=`echo $$log_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ am__force_recheck=am--force-recheck \ TEST_LOGS="$$log_list"; \ exit $$? cppunittest.log: cppunittest$(EXEEXT) @p='cppunittest$(EXEEXT)'; \ b='cppunittest'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) .test.log: @p='$<'; \ $(am__set_b); \ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) @am__EXEEXT_TRUE@.test$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: check-am install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \ clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ recheck tags tags-am uninstall uninstall-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/tests/example.cpp0000644000175000017500000000257012640443572012567 00000000000000/* example - CppUnit unit test example Copyright (C) 2012 Emilien Kia 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 class ExampleTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( ExampleTest ); CPPUNIT_TEST( testOne ); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void testOne(); }; // Registers the fixture into the 'registry' CPPUNIT_TEST_SUITE_REGISTRATION( ExampleTest ); void ExampleTest::setUp() { } void ExampleTest::tearDown() { } void ExampleTest::testOne() { // Set up int i = 1; float f = 1.0; // Process int cast = (int)f; // Check CPPUNIT_ASSERT_EQUAL( i, cast ); } nut-2.7.4/tests/Makefile.am0000644000175000017500000000056612640443572012467 00000000000000# Network UPS Tools: tests if HAVE_CPPUNIT TESTS = cppunittest check_PROGRAMS = $(TESTS) cppunittest_CXXFLAGS = $(CPPUNIT_CFLAGS) cppunittest_LDFLAGS = $(CPPUNIT_LIBS) # List of src files for CppUnit tests CPPUNITTESTSRC = example.cpp cppunittest_SOURCES = $(CPPUNITTESTSRC) cpputest.cpp else !HAVE_CPPUNIT EXTRA_DIST = example.cpp cpputest.cpp endif !HAVE_CPPUNIT nut-2.7.4/tests/cpputest.cpp0000644000175000017500000000311012640443572012772 00000000000000/* cpputest - basic runner for unit tests Copyright (C) 2012 Emilien Kia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include int main(int argc, char* argv[]) { /* Get the top level suite from the registry */ CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); /* Adds the test to the list of test to run */ CppUnit::TextUi::TestRunner runner; runner.addTest( suite ); /* Change the default outputter to a compiler error format outputter */ runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(), std::cerr ) ); /* Run the tests. */ bool wasSucessful = runner.run(); /* Return error code 1 if the one of test failed. */ return wasSucessful ? 0 : 1; } nut-2.7.4/config.guess0000755000175000017500000012355012640476420011606 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-03-23' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: nut-2.7.4/Makefile.in0000644000175000017500000010106412667762000011330 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # top-level Makefile for NUT VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = . DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/configure $(am__configure_deps) \ $(top_srcdir)/scripts/Aix/nut-aix.spec.in \ $(top_srcdir)/scripts/avahi/nut.service.in \ $(top_srcdir)/scripts/HP-UX/nut.psf.in \ $(top_srcdir)/scripts/HP-UX/postinstall.in \ $(top_srcdir)/scripts/ufw/nut.ufw.profile.in COPYING TODO \ compile config.guess config.sub depcomp install-sh missing \ ltmain.sh ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = scripts/Aix/nut-aix.spec \ scripts/avahi/nut.service scripts/HP-UX/nut.psf \ scripts/HP-UX/postinstall scripts/ufw/nut.ufw.profile CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # include directory for aclocal ACLOCAL_AMFLAGS = -I m4 # subdirectories to build and distribute. The order matters, as # several subdirectories depend on stuff in "common" or tools being built first SUBDIRS = include common clients conf data tools docs drivers \ lib scripts server tests # COPYING is included automatically. EXTRA_DIST = INSTALL.nut LICENSE-GPL2 LICENSE-GPL3 MAINTAINERS UPGRADING # ---------------------------------------------------------------------- # flags to pass to ./configure when calling "make distcheck" and "make # distcheck-light". Try to check as many features as possible! Also # need to give hotplug-dir and udev-dir, so that staged install does # not fail. DISTCHECK_FLAGS = --with-all --with-ssl --with-doc=auto DISTCHECK_LIGHT_FLAGS = --with-all=auto --with-ssl=auto --with-doc=auto DISTCHECK_CONFIGURE_FLAGS = ${DISTCHECK_FLAGS} \ --with-systemdsystemunitdir='$${prefix}/lib/systemd/system' \ --with-hotplug-dir='$${prefix}/etc/hotplug' \ --with-udev-dir='$${prefix}/etc/udev' \ --with-devd-dir='$${prefix}/etc/devd' # ---------------------------------------------------------------------- # Automatically generate the ChangeLog from Git logs: MAINTAINERCLEAN_FILES = ChangeLog # Older boundary of the ChangeLog commits range # It can be a tag ('v2.2.0'), a commit hash, a date, ... # See gitrevisions for more information on specifying ranges GITLOG_START_POINT = v2.6.0 # ---------------------------------------------------------------------- # targets from old build system (pre-automake). # supported for a period of time for backward "compatibility". WARN = "----------------------------------------------------------------------" all: all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): scripts/Aix/nut-aix.spec: $(top_builddir)/config.status $(top_srcdir)/scripts/Aix/nut-aix.spec.in cd $(top_builddir) && $(SHELL) ./config.status $@ scripts/avahi/nut.service: $(top_builddir)/config.status $(top_srcdir)/scripts/avahi/nut.service.in cd $(top_builddir) && $(SHELL) ./config.status $@ scripts/HP-UX/nut.psf: $(top_builddir)/config.status $(top_srcdir)/scripts/HP-UX/nut.psf.in cd $(top_builddir) && $(SHELL) ./config.status $@ scripts/HP-UX/postinstall: $(top_builddir)/config.status $(top_srcdir)/scripts/HP-UX/postinstall.in cd $(top_builddir) && $(SHELL) ./config.status $@ scripts/ufw/nut.ufw.profile: $(top_builddir)/config.status $(top_srcdir)/scripts/ufw/nut.ufw.profile.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-libtool \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip distcheck distclean distclean-generic \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am distcheck-light: $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_LIGHT_FLAGS)" distcheck # workaround the dist generated files that are also part of the distribution # Note that distcleancheck is disabled for now, while waiting for a proper # solution, that do not break older unix systems #distcleancheck_listfiles = \ # find . -type f -exec sh -c 'test -f $(srcdir)/{} || echo {}' ';' distcleancheck: @: # Force ChangeLog regeneration upon make dist (due to nonexistant 'dummy-stamp'), # in case it has already been generated previously dummy-stamp: ChangeLog: tools/gitlog2changelog.py dummy-stamp $(top_srcdir)/tools/gitlog2changelog.py $(GITLOG_START_POINT) || \ echo "gitlog2changelog.py failed to generate the ChangeLog. See https://github.com/networkupstools/nut/commits/master" > $@ # ---------------------------------------------------------------------- # Maintainers targets: distribution signature and hashes dist-sig: rm -f nut-@PACKAGE_VERSION@.tar.gz.sig gpg --detach-sign nut-@PACKAGE_VERSION@.tar.gz dist-hash: md5sum nut-@PACKAGE_VERSION@.tar.gz > nut-@PACKAGE_VERSION@.tar.gz.md5 sha256sum nut-@PACKAGE_VERSION@.tar.gz > nut-@PACKAGE_VERSION@.tar.gz.sha256 build: @echo $(WARN) @echo "Warning: 'make build' is deprecated. Use 'make all' instead." @echo $(WARN) $(MAKE) $(AM_MAKEFLAGS) all install-bin: @echo $(WARN) @echo "Warning: 'make install-bin' is deprecated." @echo "Use 'make install-exec' instead for a similar effect." @echo $(WARN) cd common; $(MAKE) $(AM_MAKEFLAGS) install cd drivers; $(MAKE) $(AM_MAKEFLAGS) install cd server; $(MAKE) $(AM_MAKEFLAGS) install cd clients; $(MAKE) $(AM_MAKEFLAGS) install install-man: install-data-recursive @echo $(WARN) @echo "Warning: 'make install-man' is deprecated." @echo "Use 'cd man; make install' instead." @echo $(WARN) cd man; $(MAKE) $(AM_MAKEFLAGS) install install-conf: @echo $(WARN) @echo "Warning: 'make install-conf' is deprecated." @echo "Use 'cd conf; make install' instead." @echo $(WARN) cd conf; $(MAKE) $(AM_MAKEFLAGS) install # The target install-data already has a standardized meaning under automake install-dirs: @echo $(WARN) @echo "Warning: 'make install-dirs' is deprecated." @echo "Use 'make installdirs' instead." @echo $(WARN) make installdirs cgi build-cgi install-cgi install-cgi-dir install-cgi-bin \ install-cgi-man install-cgi-conf install-cgi-html: @echo "Error: 'make $@' no longer exists." @echo "Use './configure --with-cgi' instead." install-lib: @echo "Error: 'make $@' no longer exists." @echo "Use './configure --with-dev' instead." usb build-usb install-usb: @echo "Error: 'make $@' no longer exists." @echo "Use './configure --with-usb' instead." snmp build-snmp install-snmp install-snmp-mgr install-snmp-man: @echo "Error: 'make $@' no longer exists." @echo "Use './configure --with-snmp' instead." setver: @echo "Error: 'make setver' no longer exists." @echo "Edit configure.in to set version number." package: if test `uname -s` = "HP-UX"; then \ cd scripts/HP-UX; \ make package; \ mv NUT_HPUX_package.depot NUT_HPUX_package@PACKAGE_VERSION@.depot; \ elif test `uname -s` = "SunOS"; then \ make; \ rm -rf @prefix@; \ make install; \ cd scripts/Solaris; \ make package; \ make uninstall; \ rm -rf @prefix@; \ elif test `uname -s` = "AIX"; then \ make dist; \ cp scripts/Aix/nut-aix.spec /usr/src/packages/SPECS; \ cp scripts/Aix/nut.init nut-*.tar.gz /usr/src/packages/SOURCES; \ rpm -ba /usr/src/packages/SPECS/nut-aix.spec; \ fi; # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/tools/0000755000175000017500000000000012670024741010476 500000000000000nut-2.7.4/tools/gitlog2changelog.py0000755000175000017500000001001112640444140014177 00000000000000#!/usr/bin/env python # Copyright 2008 Marcus D. Hanwell # Minor changes for NUT by Charles Lepple # Distributed under the terms of the GNU General Public License v2 or later import string, re, os from textwrap import TextWrapper import sys rev_range = '' if len(sys.argv) > 1: base = sys.argv[1] rev_range = '%s..HEAD' % base # Execute git log with the desired command line options. fin = os.popen('git log --summary --stat --no-merges --date=short %s' % rev_range, 'r') # Create a ChangeLog file in the current directory. fout = open('ChangeLog', 'w') # Set up the loop variables in order to locate the blocks we want authorFound = False dateFound = False messageFound = False filesFound = False message = "" messageNL = False files = "" prevAuthorLine = "" wrapper = TextWrapper(initial_indent="\t", subsequent_indent="\t ") # The main part of the loop for line in fin: # The commit line marks the start of a new commit object. if line.startswith('commit'): # Start all over again... authorFound = False dateFound = False messageFound = False messageNL = False message = "" filesFound = False files = "" continue # Match the author line and extract the part we want elif 'Author:' in line: authorList = re.split(': ', line, 1) author = authorList[1] author = author[0:len(author)-1] authorFound = True # Match the date line elif 'Date:' in line: dateList = re.split(': ', line, 1) date = dateList[1] date = date[0:len(date)-1] dateFound = True # The Fossil-IDs are ignored: elif line.startswith(' Fossil-ID:') or line.startswith(' [[SVN:'): continue # The svn-id lines are ignored elif ' git-svn-id:' in line: continue # The sign off line is ignored too elif 'Signed-off-by' in line: continue # Extract the actual commit message for this commit elif authorFound & dateFound & messageFound == False: # Find the commit message if we can if len(line) == 1: if messageNL: messageFound = True else: messageNL = True elif len(line) == 4: messageFound = True else: if len(message) == 0: message = message + line.strip() else: message = message + " " + line.strip() # If this line is hit all of the files have been stored for this commit elif re.search('files? changed', line) >= 0: filesFound = True continue # Collect the files for this commit. FIXME: Still need to add +/- to files elif authorFound & dateFound & messageFound: fileList = re.split(' \| ', line, 2) if len(fileList) > 1: if len(files) > 0: files = files + ", " + fileList[0].strip() else: files = fileList[0].strip() # All of the parts of the commit have been found - write out the entry if authorFound & dateFound & messageFound & filesFound: # First the author line, only outputted if it is the first for that # author on this day authorLine = date + " " + author if len(prevAuthorLine) == 0: fout.write(authorLine + "\n\n") elif authorLine == prevAuthorLine: pass else: fout.write("\n" + authorLine + "\n\n") # Assemble the actual commit message line(s) and limit the line length # to 80 characters. commitLine = "* " + files + ": " + message # Write out the commit line fout.write(wrapper.fill(commitLine) + "\n") #Now reset all the variables ready for a new commit block. authorFound = False dateFound = False messageFound = False messageNL = False message = "" filesFound = False files = "" prevAuthorLine = authorLine # Close the input and output lines now that we are finished. fin.close() fout.close() nut-2.7.4/tools/nut-ddl-dump.sh0000755000175000017500000000376612640473702013305 00000000000000#!/bin/sh ################################################################################ # A script to ease the generation of NUT device dumps for NUT Devices Dumps Library ################################################################################ # Author: (C) Arnaud Quette # License: GPL v2+ ################################################################################ # FIXME: # - check if a previous report exists, and increase report number #  - we currently use the .dev format ; but also consider the NDS format # http://www.networkupstools.org/ddl/ ################################################################################ strUsage="Usage: $0 " # Check command line parameter () if [ -z "$1" ]; then echo "$strUsage" exit else DDL_DEVICE_NAME=$1 fi # Test communication with the device testResult="`upsc ${DDL_DEVICE_NAME} 2> /dev/null`" if [ $? -gt 0 ]; then echo "Can't communicate with ${DDL_DEVICE_NAME}" exit fi # Build the filename # ________. # Process the Manufacturer name RAW_DDL_MFR="`upsc ${DDL_DEVICE_NAME} device.mfr 2>/dev/null`" if [ "${RAW_DDL_MFR}" = "EATON" ]; then RAW_DDL_MFR="Eaton" fi # Replace spaces with underscores DDL_MFR="`echo ${RAW_DDL_MFR} | sed s/\ /_/g`" # Process the Model name # Replace spaces with underscores RAW_DDL_MODEL="`upsc ${DDL_DEVICE_NAME} device.model 2>/dev/null`" DDL_MODEL="`echo ${RAW_DDL_MODEL} | sed s/\ /_/g`" # Process the driver name and NUT version DDL_DRIVER_NAME="`upsc ${DDL_DEVICE_NAME} driver.name 2>/dev/null`" DDL_NUT_VERSION="`upsc ${DDL_DEVICE_NAME} driver.version 2>/dev/null`" # TODO: check if a similar file exists, to update Report nb DDL_REPORT_NUMBER="01" DDL_FILENAME="${DDL_MFR}__${DDL_MODEL}__${DDL_DRIVER_NAME}__${DDL_NUT_VERSION}__${DDL_REPORT_NUMBER}.dev" # Dump device data into the file echo "${testResult}" > ${DDL_FILENAME} echo "${DDL_FILENAME} generated using ${DDL_DEVICE_NAME} " nut-2.7.4/tools/nut-recorder.sh0000755000175000017500000000441112640443572013372 00000000000000#!/bin/sh ################################################################################ # # nut-recorder # An utility to record device running sequence (Ie power failures or any # other change) and dump it in a .seq format # The .seq file can then be used by the 'dummy-ups driver to replay the # sequence. # ################################################################################ # FIXME: # - implement PAUSE / RESUME (do not increment TIMER) on pressing space (?) # - implement support for creating either .dev (static dump) or .seq # - implement dump of instcmd and upsrw ################################################################################ strUsage="Usage: nut-recorder [output-file] [interval]" # log data each 5 seconds DEFAULT_INTERVAL=5 # temporary files location TEMP_DIR="/tmp" # output this file by default DEFAULT_OUTPUT="dummy-device.seq" # Process command line parameters if [ -z "$1" ]; then echo "$strUsage" exit else devName=$1 fi if [ -z "$2" ]; then outFile=$DEFAULT_OUTPUT else outFile=$2 fi if [ -z "$3" ]; then pollInterval=$DEFAULT_INTERVAL else pollInterval=$3 fi # initialize TIMER value curTimer=0 # Test communication with the device testResult="`upsc $devName > /dev/null`" if [ $? -gt 0 ]; then echo "$devName: $testResult" exit fi # initialize output file echo "# dummy-ups sequence recorded with $0\n"> $outFile # initialize data upsc $devName >> $outFile cp -f $outFile ${TEMP_DIR}/prevDump.tmp echo "Initial data:\n" cat $outFile while (true) do # rest a bit before getting fresh data sleep $pollInterval # update the TIMER value curTimer=`expr $curTimer + $pollInterval` # dump the current data testResult="`upsc $devName > ${TEMP_DIR}/curDump.tmp`" if [ $? -gt 0 ]; then echo "$devName: $testResult" # FIXME: what to do (pause, exit)? fi # do the diff dataDiff="`diff --unchanged-line-format='' --old-line-format='' --new-line-format='%L' ${TEMP_DIR}/prevDump.tmp ${TEMP_DIR}/curDump.tmp`" # dump actual, if any if [ ! -z "${dataDiff}" ]; then # dump differences echo "TIMER $curTimer" >> $outFile echo "$dataDiff" >> $outFile # and echo out echo "TIMER $curTimer" echo "$dataDiff" curTimer=0 fi # rotate dumps mv ${TEMP_DIR}/curDump.tmp ${TEMP_DIR}/prevDump.tmp done nut-2.7.4/tools/nut-snmpinfo.py0000755000175000017500000001300212667537407013442 00000000000000#!/usr/bin/env python # Copyright (C) 2011 - Frederic Bohe # Copyright (C) 2016 - Arnaud Quette # # 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 # This program extracts all SNMP information related to NUT snmp-ups drivers. import glob import re import sys output_file_name="./nut-scanner/nutscan-snmp.h" output_file = open(output_file_name,'w') #expand #define constant def expand_define(filename,constant): ret_line = "" f = open(filename, 'r') for line in f: if constant in line and "#define" in line: line_without_carriage_return = re.sub("[\n\r]", "", line) line_with_single_blank = re.sub("[ \t]+", " ", line_without_carriage_return) define_line = line_with_single_blank.split(" "); #define_line[0] = "#define" #define_line[1] = const name #define_line[2...] = const value (may be other const name) if constant in define_line[1]: define_line.pop(0) #remove #define define_line.pop(0) #remove the constant name for elem in define_line: if elem[0] == "\"": clean_elem = re.sub("\"", "", elem) ret_line = ret_line + clean_elem else: ret_line = ret_line + expand_define(filename,elem); return ret_line output_file.write( "/* nutscan-snmp\n" ) output_file.write( " * Copyright (C) 2011 - Frederic Bohe \n" ) output_file.write( " * Copyright (C) 2016 - Arnaud Quette \n" ) output_file.write( " *\n" ) output_file.write( " * This program is free software; you can redistribute it and/or modify\n" ) output_file.write( " * it under the terms of the GNU General Public License as published by\n" ) output_file.write( " * the Free Software Foundation; either version 2 of the License, or\n" ) output_file.write( " * (at your option) any later version.\n" ) output_file.write( " *\n" ) output_file.write( " * This program is distributed in the hope that it will be useful,\n" ) output_file.write( " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" ) output_file.write( " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" ) output_file.write( " * GNU General Public License for more details.\n" ) output_file.write( " *\n" ) output_file.write( " * You should have received a copy of the GNU General Public License\n" ) output_file.write( " * along with this program; if not, write to the Free Software\n" ) output_file.write( " * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" ) output_file.write( " */\n" ) output_file.write( "\n" ) output_file.write( "#ifndef DEVSCAN_SNMP_H\n" ) output_file.write( "#define DEVSCAN_SNMP_H\n" ) output_file.write( "\n" ) output_file.write( "typedef struct {\n" ) output_file.write( " char * oid;\n" ) output_file.write( " char * mib;\n" ) output_file.write( " char * sysoid;\n" ) output_file.write( "} snmp_device_id_t;\n" ) output_file.write( "\n" ) output_file.write( "/* SNMP IDs device table */\n" ) output_file.write( "static snmp_device_id_t snmp_device_table[] = {\n" ) for filename in glob.glob('../drivers/*-mib.c'): list_of_line = open(filename,'r').read().split(';') for line in list_of_line: if "mib2nut_info_t" in line: # Discard commented lines # Note that we only search for the beginning of the comment, the # end can be in the following line, due to the .split(';') m = re.search(r'/\*.*', line) if m: #sys.stderr.write('discarding line'+line+'\n') continue #clean up line line2 = re.sub("[\n\t\r}]", "", line) # split line line = line2.split("{",1) #line[1] is the part between {} line2 = line[1].split(",") mib = line2[0] #line2[3] is the OID of the device model name which #could be made of #define const and string. source_oid = line2[3] #line2[5] is the SysOID of the device which #could be made of #define const and string. if len(line2) >= 6: source_sysoid = line2[5] else: source_sysoid = "NULL" #decode source_oid line = source_oid.lstrip(" ") line2 = line.split(" ") oid = "" for elem in line2: if elem[0] == "\"": clean_elem = re.sub("\"", "", elem) oid = oid+clean_elem else: oid = oid + expand_define(filename,elem); #decode source_sysoid line = source_sysoid.lstrip(" ") line = line.rstrip(" ") line2 = line.split(" ") sysoid = "" for elem in line2: if elem[0] == "\"": clean_elem = re.sub("\"", "", elem) sysoid = sysoid+clean_elem else: sysoid = sysoid + expand_define(filename,elem); if sysoid == "": sysoid = "NULL" else: sysoid = "\"" + sysoid + "\"" output_file.write( "\t{ \"" + oid + "\", " + mib + ", " + sysoid + "},\n" ) output_file.write( " /* Terminating entry */\n" ) output_file.write( " { NULL, NULL, NULL}\n" ) output_file.write( "};\n" ) output_file.write( "#endif /* DEVSCAN_SNMP_H */\n" ) nut-2.7.4/tools/driver-list-format.sh0000755000175000017500000000211412667537461014523 00000000000000#!/bin/sh ################################################################################ # # Ensure that driver.list and driver.list.in are properly formatted (with tabs) # ################################################################################ # Adapt path for either dist target or manual call CURRENT_PATH="`dirname $0`" DRVLIST_PATH="" if [ -f "${CURRENT_PATH}/data/driver.list.in" ]; then DRVLIST_PATH="${CURRENT_PATH}" elif [ -f "${CURRENT_PATH}/../data/driver.list.in" ]; then DRVLIST_PATH="${CURRENT_PATH}/.." else echo "Can't find driver.list in . or .." exit 1 fi echo "Checking whether driver.list[.in] are well formatted" for drvfile in driver.list.in driver.list do if [ -f "${DRVLIST_PATH}/data/${drvfile}" ]; then sed -e '/^#/!s/\" \+\"/\"\t\"/g' -e "/^#/!s/[[:blank:]]*$//" < "${DRVLIST_PATH}/data/${drvfile}" > "${DRVLIST_PATH}/data/${drvfile}.tabbed" mv -f "${DRVLIST_PATH}/data/${drvfile}.tabbed" "${DRVLIST_PATH}/data/${drvfile}" echo "Processed ${DRVLIST_PATH}/data/${drvfile}" else echo "Skipping ${drvfile} as it is missing..." fi done echo "done" nut-2.7.4/tools/Makefile.in0000644000175000017500000005656612667762001012511 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # TODO: remove redundancies! VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = tools DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # XXX this does not work with Automake!!! # # In fact the very concept is entirely antithetical to Automake. # # SUBDIRS are explicitly a listing of all the directories that make # must recurse into BEFORE processing the current directory. # # These python scripts must be moved into a sub-directory, and _only_ # executed IF they need to be, and all the nut-scanner sources need # to be moved out of a sub-directory into this directory. # # Anyway, for the time being, we force build in ./ before nut-scanner, # to have nutscan-{usb,snmp}.h built before going into the nut-scanner # sub-directory SUBDIRS = . nut-scanner EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh \ gitlog2changelog.py nut-snmpinfo.py driver-list-format.sh all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tools/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am dist-hook distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am all: nut-scanner-deps # XXX these rules are all bogus! They cause un-named target files to # always be rebuilt! None of that is ever the right way to use make, # and especially not Automake. Explicit filenames and their exact # dependencies need to be properly listed. nut-scanner-deps: @if python -c 1; then \ echo "Regenerating the SNMP helper files."; \ $(top_srcdir)/tools/nut-snmpinfo.py; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ echo "Skipping the SNMP helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi @if perl -e 1; then \ echo "Regenerating the USB helper files."; \ $(top_srcdir)/tools/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ echo "Skipping the USB helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi # call the USB info script upon "make dist", and if Perl is present # call the SNMP info script upon "make dist", and if Python is present # and call both for building nut-scanner # Also ensure that data/driver.list is well formatted dist-hook: @if python -c 1; then \ echo "Regenerating the SNMP helper files."; \ $(distdir)/nut-snmpinfo.py; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ echo "Skipping the SNMP helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi @if perl -e 1; then \ echo "Regenerating the USB helper files."; \ $(distdir)/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ echo "Skipping the USB helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi @$(distdir)/driver-list-format.sh; .PHONY: nut-scanner-deps nut-scanner-snmp-deps nut-scanner-usb-deps # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/tools/nut-usbinfo.pl0000755000175000017500000003035512640473702013237 00000000000000#!/usr/bin/env perl # Current Version : 1.3 # Copyright (C) 2008 - 2012 dloic (loic.dardant AT gmail DOT com) # Copyright (C) 2008 - 2015 Arnaud Quette # Copyright (C) 2013 - 2014 Charles Lepple # # Based on the usbdevice.pl script, made for the Ubuntu Media Center # for the final use of the LIRC project. # # 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 # TODO list: # - rewrite using glob, as in other helper scripts # - manage deps in Makefile.am use File::Find; use strict; # path to scan for USB_DEVICE pattern my $scanPath="../drivers"; # Hotplug output file my $outputHotplug="../scripts/hotplug/libhid.usermap"; # udev output file my $outputUdev="../scripts/udev/nut-usbups.rules.in"; # BSD devd output file my $output_devd="../scripts/devd/nut-usb.conf.in"; # UPower output file my $outputUPower="../scripts/upower/95-upower-hid.rules"; # tmp output, to allow generating the ENV{UPOWER_VENDOR} header list my $tmpOutputUPower; # mfr header flag my $upowerMfrHeaderDone = 0; # NUT device scanner - C header my $outputDevScanner = "./nut-scanner/nutscan-usb.h"; my $GPL_header = "\ * Copyright (C) 2011 - Arnaud Quette \ *\ * 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"; # array of products indexed by vendorID my %vendor; # contain for each vendor, its name (and...) my %vendorName; ################# MAIN ################# find(\&find_usbdevs,$scanPath); &gen_usb_files; ################# SUB METHOD ################# sub gen_usb_files { # Hotplug file header open my $outHotplug, ">$outputHotplug" || die "error $outputHotplug : $!"; print $outHotplug '# This file is generated and installed by the Network UPS Tools package.'."\n"; print $outHotplug "#\n"; print $outHotplug '# Sample entry (replace 0xVVVV and 0xPPPP with vendor ID and product ID respectively) :'."\n"; print $outHotplug '# libhidups 0x0003 0xVVVV 0xPPPP 0x0000 0x0000 0x00 0x00'; print $outHotplug ' 0x00 0x00 0x00 0x00 0x00000000'."\n"; print $outHotplug "#\n"; print $outHotplug '# usb module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi'; print $outHotplug ' bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass'; print $outHotplug ' bInterfaceProtocol driver_info'."\n"; # Udev file header open my $outUdev, ">$outputUdev" || die "error $outputUdev : $!"; print $outUdev '# This file is generated and installed by the Network UPS Tools package.'."\n\n"; print $outUdev 'ACTION!="add|change", GOTO="nut-usbups_rules_end"'."\n"; print $outUdev 'SUBSYSTEM=="usb_device", GOTO="nut-usbups_rules_real"'."\n"; print $outUdev 'SUBSYSTEM=="usb", GOTO="nut-usbups_rules_real"'."\n"; print $outUdev 'SUBSYSTEM!="usb", GOTO="nut-usbups_rules_end"'."\n\n"; print $outUdev 'LABEL="nut-usbups_rules_real"'."\n"; open my $out_devd, ">$output_devd" || die "error $output_devd : $!"; print $out_devd '# This file is generated and installed by the Network UPS Tools package.'."\n"; print $out_devd "# Homepage: http://www.networkupstools.org/\n\n"; # UPower file header open my $outputUPower, ">$outputUPower" || die "error $outputUPower : $!"; print $outputUPower '##############################################################################################################'."\n"; print $outputUPower '# Uninterruptible Power Supplies with USB HID interfaces'."\n#\n"; print $outputUPower '# This file was automatically generated by NUT:'."\n#".' https://github.com/networkupstools/nut/'."\n#\n"; print $outputUPower '# To keep up to date, monitor upstream NUT'."\n#".' https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.rules'."\n"; print $outputUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n\n"; print $outputUPower '# newer hiddev are part of the usbmisc class'."\n".'SUBSYSTEM=="usbmisc", GOTO="up_hid_chkdev"'."\n"; print $outputUPower '# only support USB, else ignore'."\n".'SUBSYSTEM!="usb", GOTO="up_hid_end"'."\n\n"; print $outputUPower '# if usbraw device, ignore'."\n".'LABEL="up_hid_chkdev"'."\n".'KERNEL!="hiddev*", GOTO="up_hid_end"'."\n\n"; print $outputUPower '# if an interface, ignore'."\n".'ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end"'."\n\n"; # Device scanner header open my $outputDevScanner, ">$outputDevScanner" || die "error $outputDevScanner : $!"; print $outputDevScanner '/* nutscan-usb'.$GPL_header."\n */\n\n"; print $outputDevScanner "#ifndef DEVSCAN_USB_H\n#define DEVSCAN_USB_H\n\n"; print $outputDevScanner "#include \n"; print $outputDevScanner "#include \"nut_stdint.h\"\t/* for uint16_t */\n\n"; # vid, pid, driver print $outputDevScanner "typedef struct {\n\tuint16_t\tvendorID;\n\tuint16_t\tproductID;\n\tchar*\tdriver_name;\n} usb_device_id_t;\n\n"; print $outputDevScanner "/* USB IDs device table */\nstatic usb_device_id_t usb_device_table[] = {\n\n"; # generate the file in alphabetical order (first for VendorID, then for ProductID) foreach my $vendorId (sort { lc $a cmp lc $b } keys %vendorName) { # Hotplug vendor header if ($vendorName{$vendorId}) { print $outHotplug "\n# ".$vendorName{$vendorId}."\n"; } # udev vendor header if ($vendorName{$vendorId}) { print $outUdev "\n# ".$vendorName{$vendorId}."\n"; } # devd vendor header if ($vendorName{$vendorId}) { print $out_devd "\n# ".$vendorName{$vendorId}."\n"; } # UPower vendor header flag $upowerMfrHeaderDone = 0; foreach my $productId (sort { lc $a cmp lc $b } keys %{$vendor{$vendorId}}) { # Hotplug device entry print $outHotplug "# ".$vendor{$vendorId}{$productId}{"comment"}."\n"; print $outHotplug "libhidups 0x0003 ".$vendorId." ".$productId." 0x0000 0x0000 0x00"; print $outHotplug " 0x00 0x00 0x00 0x00 0x00 0x00000000\n"; # udev device entry print $outUdev "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"driver"}."\n"; print $outUdev "ATTR{idVendor}==\"".removeHexPrefix($vendorId); print $outUdev "\", ATTR{idProduct}==\"".removeHexPrefix($productId)."\","; print $outUdev ' MODE="664", GROUP="@RUN_AS_GROUP@"'."\n"; # devd device entry print $out_devd "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"driver"}."\n"; print $out_devd "notify 100 {\n\tmatch \"system\"\t\t\"USB\";\n"; print $out_devd "\tmatch \"subsystem\"\t\"DEVICE\";\n"; print $out_devd "\tmatch \"type\"\t\t\"ATTACH\";\n"; print $out_devd "\tmatch \"vendor\"\t\t\"$vendorId\";\n"; # print $out_devd "\tmatch \"product\"\t\t\"$productId\";\n"; print $out_devd "\taction \"chgrp \@RUN_AS_GROUP\@ /dev/\$cdev; chmod g+rw /dev/\$cdev\";\n"; print $out_devd "};\n"; # UPower device entry (only for USB/HID devices!) if ($vendor{$vendorId}{$productId}{"driver"} eq "usbhid-ups") { if ($upowerMfrHeaderDone == 0) { # UPower vendor header if ($vendorName{$vendorId}) { $tmpOutputUPower = $tmpOutputUPower."\n# ".$vendorName{$vendorId}."\n"; } print $outputUPower "ATTRS{idVendor}==\"".removeHexPrefix($vendorId)."\", ENV{UPOWER_VENDOR}=\"".$vendorName{$vendorId}."\"\n"; $upowerMfrHeaderDone = 1; } $tmpOutputUPower = $tmpOutputUPower."ATTRS{idVendor}==\"".removeHexPrefix($vendorId); $tmpOutputUPower = $tmpOutputUPower."\", ATTRS{idProduct}==\"".removeHexPrefix($productId)."\","; $tmpOutputUPower = $tmpOutputUPower.' ENV{UPOWER_BATTERY_TYPE}="ups"'."\n"; } # Device scanner entry print $outputDevScanner "\t{ ".$vendorId.', '.$productId.", \"".$vendor{$vendorId}{$productId}{"driver"}."\" },\n"; } } # Udev footer print $outUdev "\n".'LABEL="nut-usbups_rules_end"'."\n"; # UPower... # ...flush device table print $outputUPower $tmpOutputUPower; # ...and print footer print $outputUPower "\n".'LABEL="up_hid_end"'."\n"; # Device scanner footer print $outputDevScanner "\t/* Terminating entry */\n\t{ -1, -1, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n"; } sub find_usbdevs { # maybe there's an option to turn off all .* files, but anyway this is stupid return $File::Find::prune = 1 if ($_ eq '.svn') || ($_ =~ /^\.#/) || ($_ =~ /\.orig$/); my $nameFile=$_; my $lastComment=""; open my $file,$nameFile or die "error open file $nameFile"; while(my $line=<$file>) { # catch comment (should permit comment on the precedent or on the current line of USB_DEVICE declaration) if($line =~/\s*\/\*(.+)\*\/\s*$/) { $lastComment=$1; } if($line =~/^\s*\{\s*USB_DEVICE\((.+)\,(.+)\)\s*/) # for example : { USB_DEVICE(MGE_VENDORID, 0x0001)... } { my $VendorID=trim($1); my $ProductID=trim($2); my $VendorName=""; # special thing for backward declaration using #DEFINE # Format: # /* vendor name */ # #define VENDORID 0x???? if(!($VendorID=~/\dx(\d|\w)+/)) { open my $fh,$nameFile or die "error open file $nameFile"; while(my $data=<$fh>) { # catch Vendor Name if($data =~/\s*\/\*(.+)\*\/\s*$/) { $VendorName=$1; } # catch VendorID if ($data =~ /(#define|#DEFINE)\s+$VendorID\s+(\dx(\d|\w)+)/) { $VendorID=$2; last; } } } # same thing for the productID if(!($ProductID=~/\dx(\d|\w)+/)) { my $data = do { open my $fh, $nameFile or die "error open file $nameFile"; join '', <$fh> }; if ($data =~ /(#define|#DEFINE)\s+$ProductID\s+(\dx(\d|\w)+)/) { $ProductID=$2; } else { die "In file $nameFile, for product $ProductID, can't find the declaration of the constant"; } } # store data (to be optimized) # and don't overwrite actual vendor names with empty values if( (!$vendorName{$VendorID}) or (($vendorName{$VendorID} eq "") and ($VendorName ne "")) ) { $vendorName{$VendorID}=trim($VendorName); } $vendor{$VendorID}{$ProductID}{"comment"}=$lastComment; # process the driver name my $driver=$nameFile; if($nameFile=~/(.+)-hid\.c/) { $driver="usbhid-ups"; } # FIXME: make a generic matching rule *.c => * elsif ($nameFile eq "bcmxcp_usb.c") { $driver="bcmxcp_usb"; } elsif ($nameFile eq "tripplite_usb.c") { $driver="tripplite_usb"; } elsif ($nameFile eq "blazer_usb.c") { $driver="blazer_usb"; } elsif ($nameFile eq "richcomm_usb.c") { $driver="richcomm_usb"; } elsif ($nameFile eq "nutdrv_atcl_usb.c") { $driver="nutdrv_atcl_usb"; } elsif ($nameFile eq "riello_usb.c") { $driver="riello_usb"; } elsif ($nameFile eq "nutdrv_qx.c") { $driver="nutdrv_qx"; } else { die "Unknown driver type: $nameFile"; } $vendor{$VendorID}{$ProductID}{"driver"}=$driver; } } } sub removeHexPrefix { # make a local copy, not to alter the original entry my $string = $_[0]; $string =~ s/0x//; return $string; } sub trim { my($str) = shift =~ m!^\s*(.+?)\s*$!i; defined $str ? return $str : return ''; } nut-2.7.4/tools/nut-scanner/0000755000175000017500000000000012670024741012733 500000000000000nut-2.7.4/tools/nut-scanner/nutscan-init.c0000644000175000017500000000662212667537407015457 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-init.c \brief init functions for nut scanner library \author Frederic Bohe */ #include "common.h" #include #include #include #include #include int nutscan_avail_avahi = 0; int nutscan_avail_ipmi = 0; int nutscan_avail_nut = 0; int nutscan_avail_snmp = 0; int nutscan_avail_usb = 0; int nutscan_avail_xml_http = 0; int nutscan_load_usb_library(const char *libname_path); int nutscan_load_snmp_library(const char *libname_path); int nutscan_load_neon_library(const char *libname_path); int nutscan_load_avahi_library(const char *libname_path); int nutscan_load_ipmi_library(const char *libname_path); int nutscan_load_upsclient_library(const char *libname_path); /* FIXME: would be good to get more from /etc/ld.so.conf[.d] */ char * search_paths[] = { LIBDIR, "/usr/lib64", "/lib64", "/usr/lib", "/lib", "/usr/local/lib", NULL }; const char * get_libname(const char* base_libname) { DIR *dp; struct dirent *dirp; int index = 0; char *libname_path = NULL; char current_test_path[LARGEBUF]; for(index = 0 ; (search_paths[index] != NULL) && (libname_path == NULL) ; index++) { memset(current_test_path, 0, LARGEBUF); if ((dp = opendir(search_paths[index])) == NULL) continue; while ((dirp = readdir(dp)) != NULL) { if(!strncmp(dirp->d_name, base_libname, strlen(base_libname))) { snprintf(current_test_path, LARGEBUF, "%s/%s", search_paths[index], dirp->d_name); libname_path = realpath(current_test_path, NULL); if (libname_path != NULL) break; } } closedir(dp); } /* fprintf(stderr,"Looking for lib %s, found %s\n", base_libname, (libname_path!=NULL)?libname_path:"NULL");*/ return libname_path; } void nutscan_init(void) { #ifdef WITH_USB nutscan_avail_usb = nutscan_load_usb_library(get_libname("libusb-0.1.so")); #endif #ifdef WITH_SNMP nutscan_avail_snmp = nutscan_load_snmp_library(get_libname("libnetsnmp.so")); #endif #ifdef WITH_NEON nutscan_avail_xml_http = nutscan_load_neon_library(get_libname("libneon.so")); #endif #ifdef WITH_AVAHI nutscan_avail_avahi = nutscan_load_avahi_library(get_libname("libavahi-client.so")); #endif #ifdef WITH_FREEIPMI nutscan_avail_ipmi = nutscan_load_ipmi_library(get_libname("libfreeipmi.so")); #endif nutscan_avail_nut = nutscan_load_upsclient_library(get_libname("libupsclient.so")); } void nutscan_free(void) { if( nutscan_avail_usb ) { lt_dlexit(); } if( nutscan_avail_snmp ) { lt_dlexit(); } if( nutscan_avail_xml_http ) { lt_dlexit(); } if( nutscan_avail_avahi ) { lt_dlexit(); } if( nutscan_avail_ipmi ) { lt_dlexit(); } if( nutscan_avail_nut ) { lt_dlexit(); } } nut-2.7.4/tools/nut-scanner/nutscan-init.h0000644000175000017500000000242712640473702015447 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-init.h \brief initialisation data \author Frederic Bohe */ #ifndef SCAN_INIT #define SCAN_INIT #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif extern int nutscan_avail_avahi; extern int nutscan_avail_ipmi; extern int nutscan_avail_nut; extern int nutscan_avail_snmp; extern int nutscan_avail_usb; extern int nutscan_avail_xml_http; void nutscan_init(void); void nutscan_free(void); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif nut-2.7.4/tools/nut-scanner/scan_snmp.c0000644000175000017500000004427312667537407015030 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file scan_snmp.c \brief detect NUT supported SNMP devices \author Frederic Bohe */ #include "common.h" #include "nut-scan.h" #ifdef WITH_SNMP #include #include #include #include /* workaround for buggy Net-SNMP config * from drivers/snmp-ups.h */ #ifdef PACKAGE_BUGREPORT #undef PACKAGE_BUGREPORT #endif #ifdef PACKAGE_NAME #undef PACKAGE_NAME #endif #ifdef PACKAGE_VERSION #undef PACKAGE_VERSION #endif #ifdef PACKAGE_STRING #undef PACKAGE_STRING #endif #ifdef PACKAGE_TARNAME #undef PACKAGE_TARNAME #endif #include #include #ifdef HAVE_PTHREAD #include #endif #include "nutscan-snmp.h" /* Address API change */ #ifndef usmAESPrivProtocol #define USMAESPRIVPROTOCOL "usmAES128PrivProtocol" #else #define USMAESPRIVPROTOCOL "usmAESPrivProtocol" #endif #define SysOID ".1.3.6.1.2.1.1.2.0" static nutscan_device_t * dev_ret = NULL; #ifdef HAVE_PTHREAD static pthread_mutex_t dev_mutex; #endif long g_usec_timeout ; /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; static void (*nut_init_snmp)(const char *type); static void (*nut_snmp_sess_init)(netsnmp_session * session); static void * (*nut_snmp_sess_open)(struct snmp_session *session); static int (*nut_snmp_sess_close)(void *handle); static struct snmp_session * (*nut_snmp_sess_session)(void *handle); static void * (*nut_snmp_parse_oid)(const char *input, oid *objid, size_t *objidlen); static struct snmp_pdu * (*nut_snmp_pdu_create) (int command ); netsnmp_variable_list * (*nut_snmp_add_null_var)(netsnmp_pdu *pdu, const oid *objid, size_t objidlen); static int (*nut_snmp_sess_synch_response) (void *sessp, netsnmp_pdu *pdu, netsnmp_pdu **response); static int (*nut_snmp_oid_compare) (const oid *in_name1, size_t len1, const oid *in_name2, size_t len2); static void (*nut_snmp_free_pdu) (netsnmp_pdu *pdu); static int (*nut_generate_Ku)(const oid * hashtype, u_int hashtype_len, u_char * P, size_t pplen, u_char * Ku, size_t * kulen); static const char * (*nut_snmp_api_errstring) (int snmp_errnumber); static int (*nut_snmp_errno); static oid * (*nut_usmAESPrivProtocol); static oid * (*nut_usmHMACMD5AuthProtocol); static oid * (*nut_usmHMACSHA1AuthProtocol); static oid * (*nut_usmDESPrivProtocol); /* return 0 on error */ int nutscan_load_snmp_library(const char *libname_path) { if( dl_handle != NULL ) { /* if previous init failed */ if( dl_handle == (void *)1 ) { return 0; } /* init has already been done */ return 1; } if (libname_path == NULL) { fprintf(stderr, "SNMP library not found. SNMP search disabled.\n"); return 0; } if( lt_dlinit() != 0 ) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_init_snmp) = lt_dlsym(dl_handle, "init_snmp"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_init) = lt_dlsym(dl_handle, "snmp_sess_init"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_open) = lt_dlsym(dl_handle, "snmp_sess_open"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_close) = lt_dlsym(dl_handle, "snmp_sess_close"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_session) = lt_dlsym(dl_handle, "snmp_sess_session"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_parse_oid) = lt_dlsym(dl_handle, "snmp_parse_oid"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_pdu_create) = lt_dlsym(dl_handle, "snmp_pdu_create"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_add_null_var) = lt_dlsym(dl_handle, "snmp_add_null_var"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_synch_response) = lt_dlsym(dl_handle, "snmp_sess_synch_response"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_oid_compare) = lt_dlsym(dl_handle, "snmp_oid_compare"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_free_pdu) = lt_dlsym(dl_handle,"snmp_free_pdu"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_generate_Ku) = lt_dlsym(dl_handle, "generate_Ku"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_api_errstring) = lt_dlsym(dl_handle, "snmp_api_errstring"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_errno) = lt_dlsym(dl_handle, "snmp_errno"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usmAESPrivProtocol) = lt_dlsym(dl_handle, USMAESPRIVPROTOCOL); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usmHMACMD5AuthProtocol) = lt_dlsym(dl_handle, "usmHMACMD5AuthProtocol"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usmHMACSHA1AuthProtocol) = lt_dlsym(dl_handle, "usmHMACSHA1AuthProtocol"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usmDESPrivProtocol) = lt_dlsym(dl_handle, "usmDESPrivProtocol"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: fprintf(stderr, "Cannot load SNMP library (%s) : %s. SNMP search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } /* end of dynamic link library stuff */ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response,char * mib) { nutscan_device_t * dev = NULL; struct snmp_session * session; char * buf; session = (*nut_snmp_sess_session)(sec->handle); if(session == NULL) { return; } /* SNMP device found */ dev = nutscan_new_device(); dev->type = TYPE_SNMP; dev->driver = strdup("snmp-ups"); dev->port = strdup(session->peername); buf = malloc( response->variables->val_len + 1 ); if( buf ) { memcpy(buf,response->variables->val.string, response->variables->val_len); buf[response->variables->val_len]=0; nutscan_add_option_to_device(dev,"desc",buf); free(buf); } nutscan_add_option_to_device(dev,"mibs",mib); /* SNMP v3 */ if( session->community == NULL || session->community[0] == 0) { if( sec->secLevel ) { nutscan_add_option_to_device(dev,"secLevel", sec->secLevel); } if( sec->secName ) { nutscan_add_option_to_device(dev,"secName", sec->secName); } if( sec->authPassword ) { nutscan_add_option_to_device(dev,"authPassword", sec->authPassword); } if( sec->privPassword ) { nutscan_add_option_to_device(dev,"privPassword", sec->privPassword); } if( sec->authProtocol ) { nutscan_add_option_to_device(dev,"authProtocol", sec->authProtocol); } if( sec->privProtocol ) { nutscan_add_option_to_device(dev,"privProtocol", sec->privProtocol); } } else { buf = malloc( session->community_len + 1 ); if( buf ) { memcpy(buf,session->community, session->community_len); buf[session->community_len]=0; nutscan_add_option_to_device(dev,"community",buf); free(buf); } } #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif dev_ret = nutscan_add_device_to_device(dev_ret,dev); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif } static struct snmp_pdu * scan_snmp_get_manufacturer(char* oid_str,void* handle) { size_t name_len; oid name[MAX_OID_LEN]; struct snmp_pdu *pdu, *response = NULL; int status; int index = 0; /* create and send request. */ name_len = MAX_OID_LEN; if (!(*nut_snmp_parse_oid)(oid_str, name, &name_len)) { index++; return NULL; } pdu = (*nut_snmp_pdu_create)(SNMP_MSG_GET); if (pdu == NULL) { index++; return NULL; } (*nut_snmp_add_null_var)(pdu, name, name_len); status = (*nut_snmp_sess_synch_response)(handle,pdu, &response); if( response == NULL ) { index++; return NULL; } if(status!=STAT_SUCCESS||response->errstat!=SNMP_ERR_NOERROR|| response->variables == NULL || response->variables->name == NULL || (*nut_snmp_oid_compare)(response->variables->name, response->variables->name_length, name, name_len) != 0 || response->variables->val.string == NULL ) { (*nut_snmp_free_pdu)(response); index++; return NULL; } return response; } static void try_all_oid(void * arg) { struct snmp_pdu *response = NULL; int index = 0; nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; while(snmp_device_table[index].oid != NULL) { response = scan_snmp_get_manufacturer(snmp_device_table[index].oid,sec->handle); if( response == NULL ) { index++; continue; } scan_snmp_add_device(sec,response,snmp_device_table[index].mib); (*nut_snmp_free_pdu)(response); response = NULL; index++; } } static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec) { (*nut_snmp_sess_init)(snmp_sess); snmp_sess->peername = sec->peername; if( sec->community != NULL || sec->secLevel == NULL ) { snmp_sess->version = SNMP_VERSION_1; if( sec->community != NULL ) { snmp_sess->community = (unsigned char *)sec->community; snmp_sess->community_len = strlen(sec->community); } else { snmp_sess->community = (unsigned char *)"public"; snmp_sess->community_len = strlen("public"); } } else { /* SNMP v3 */ snmp_sess->version = SNMP_VERSION_3; /* Security level */ if (strcmp(sec->secLevel, "noAuthNoPriv") == 0) snmp_sess->securityLevel = SNMP_SEC_LEVEL_NOAUTH; else if (strcmp(sec->secLevel, "authNoPriv") == 0) snmp_sess->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; else if (strcmp(sec->secLevel, "authPriv") == 0) snmp_sess->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; else { fprintf(stderr,"Bad SNMPv3 securityLevel: %s\n", sec->secLevel); return 0; } /* Security name */ if( sec->secName == NULL ) { fprintf(stderr,"securityName is required for SNMPv3\n"); return 0; } snmp_sess->securityName = strdup(sec->secName); snmp_sess->securityNameLen = strlen(snmp_sess->securityName); /* Everything is ready for NOAUTH */ if( snmp_sess->securityLevel == SNMP_SEC_LEVEL_NOAUTH ) { return 1; } /* Process mandatory fields, based on the security level */ switch (snmp_sess->securityLevel) { case SNMP_SEC_LEVEL_AUTHNOPRIV: if (sec->authPassword == NULL) { fprintf(stderr, "authPassword is required for SNMPv3 in %s mode\n", sec->secLevel); return 0; } break; case SNMP_SEC_LEVEL_AUTHPRIV: if ((sec->authPassword == NULL) || (sec->privPassword == NULL)) { fprintf(stderr, "authPassword and privPassword are required for SNMPv3 in %s mode\n", sec->secLevel); return 0; } break; default: /* nothing else needed */ break; } /* Process authentication protocol and key */ snmp_sess->securityAuthKeyLen = USM_AUTH_KU_LEN; /* default to MD5 */ snmp_sess->securityAuthProto = (*nut_usmHMACMD5AuthProtocol); snmp_sess->securityAuthProtoLen = sizeof((*nut_usmHMACMD5AuthProtocol))/ sizeof(oid); if( sec->authProtocol ) { if (strcmp(sec->authProtocol, "SHA") == 0) { snmp_sess->securityAuthProto = (*nut_usmHMACSHA1AuthProtocol); snmp_sess->securityAuthProtoLen = sizeof((*nut_usmHMACSHA1AuthProtocol))/ sizeof(oid); } else { if (strcmp(sec->authProtocol, "MD5") != 0) { fprintf(stderr, "Bad SNMPv3 authProtocol: %s", sec->authProtocol); return 0; } } } /* set the authentication key to a MD5/SHA1 hashed version of * our passphrase (must be at least 8 characters long) */ if ((*nut_generate_Ku)(snmp_sess->securityAuthProto, snmp_sess->securityAuthProtoLen, (u_char *) sec->authPassword, strlen(sec->authPassword), snmp_sess->securityAuthKey, &snmp_sess->securityAuthKeyLen) != SNMPERR_SUCCESS) { fprintf(stderr, "Error generating Ku from authentication pass phrase\n"); return 0; } /* Everything is ready for AUTHNOPRIV */ if( snmp_sess->securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV ) { return 1; } /* default to DES */ snmp_sess->securityPrivProto=(*nut_usmDESPrivProtocol); snmp_sess->securityPrivProtoLen = sizeof((*nut_usmDESPrivProtocol))/sizeof(oid); if( sec->privProtocol ) { if (strcmp(sec->privProtocol, "AES") == 0) { snmp_sess->securityPrivProto= (*nut_usmAESPrivProtocol); snmp_sess->securityPrivProtoLen = sizeof((*nut_usmAESPrivProtocol))/ sizeof(oid); } else { if (strcmp(sec->privProtocol, "DES") != 0) { fprintf(stderr, "Bad SNMPv3 authProtocol: %s\n" ,sec->authProtocol); return 0; } } } /* set the private key to a MD5/SHA hashed version of * our passphrase (must be at least 8 characters long) */ snmp_sess->securityPrivKeyLen = USM_PRIV_KU_LEN; if ((*nut_generate_Ku)(snmp_sess->securityAuthProto, snmp_sess->securityAuthProtoLen, (u_char *) sec->privPassword, strlen(sec->privPassword), snmp_sess->securityPrivKey, &snmp_sess->securityPrivKeyLen) != SNMPERR_SUCCESS) { fprintf(stderr, "Error generating Ku from private pass phrase\n"); return 0; } } return 1; } static void * try_SysOID(void * arg) { struct snmp_session snmp_sess; void * handle; struct snmp_pdu *pdu, *response = NULL, *resp = NULL; oid name[MAX_OID_LEN]; size_t name_len = MAX_OID_LEN; nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; int index = 0; int sysoid_found = 0; /* Initialize session */ if( !init_session(&snmp_sess,sec) ) { goto try_SysOID_free; } snmp_sess.retries = 0; snmp_sess.timeout = g_usec_timeout; /* Open the session */ handle = (*nut_snmp_sess_open)(&snmp_sess); /* establish the session */ if (handle == NULL) { fprintf(stderr,"Failed to open SNMP session for %s.\n", sec->peername); goto try_SysOID_free; } /* create and send request. */ if (!(*nut_snmp_parse_oid)(SysOID, name, &name_len)) { fprintf(stderr,"SNMP errors: %s\n", (*nut_snmp_api_errstring)((*nut_snmp_errno))); (*nut_snmp_sess_close)(handle); goto try_SysOID_free; } pdu = (*nut_snmp_pdu_create)(SNMP_MSG_GET); if (pdu == NULL) { fprintf(stderr,"Not enough memory\n"); (*nut_snmp_sess_close)(handle); goto try_SysOID_free; } (*nut_snmp_add_null_var)(pdu, name, name_len); (*nut_snmp_sess_synch_response)(handle, pdu, &response); if (response) { sec->handle = handle; /* SNMP device found */ /* SysOID is supposed to give the required MIB. */ /* Check if the received OID match with a known sysOID */ if(response->variables != NULL && response->variables->val.objid != NULL){ while(snmp_device_table[index].oid != NULL) { if(snmp_device_table[index].sysoid == NULL ) { index++; continue; } name_len = MAX_OID_LEN; if (!(*nut_snmp_parse_oid)( snmp_device_table[index].sysoid, name, &name_len)) { index++; continue; } if ( (*nut_snmp_oid_compare)( response->variables->val.objid, response->variables->val_len/sizeof(oid), name, name_len) == 0 ) { /* we have found a relevent sysoid */ resp = scan_snmp_get_manufacturer( snmp_device_table[index].oid, handle); if( resp != NULL ) { scan_snmp_add_device(sec,resp, snmp_device_table[index].mib); sysoid_found = 1; (*nut_snmp_free_pdu)(resp); } } index++; } } /* try a list of known OID */ if( !sysoid_found ) { try_all_oid(sec); } (*nut_snmp_free_pdu)(response); response = NULL; } (*nut_snmp_sess_close)(handle); try_SysOID_free: if( sec->peername ) { free(sec->peername); } free(sec); return NULL; } nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip,long usec_timeout, nutscan_snmp_t * sec) { int i; nutscan_snmp_t * tmp_sec; nutscan_ip_iter_t ip; char * ip_str = NULL; #ifdef HAVE_PTHREAD pthread_t thread; pthread_t * thread_array = NULL; int thread_count = 0; pthread_mutex_init(&dev_mutex,NULL); #endif if( !nutscan_avail_snmp ) { return NULL; } g_usec_timeout = usec_timeout; /* Initialize the SNMP library */ (*nut_init_snmp)("nut-scanner"); ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); while(ip_str != NULL) { tmp_sec = malloc(sizeof(nutscan_snmp_t)); memcpy(tmp_sec, sec, sizeof(nutscan_snmp_t)); tmp_sec->peername = ip_str; #ifdef HAVE_PTHREAD if (pthread_create(&thread,NULL,try_SysOID,(void*)tmp_sec)==0){ thread_count++; thread_array = realloc(thread_array, thread_count*sizeof(pthread_t)); thread_array[thread_count-1] = thread; } #else try_SysOID((void *)tmp_sec); #endif ip_str = nutscan_ip_iter_inc(&ip); }; #ifdef HAVE_PTHREAD for ( i=0; i < thread_count ; i++) { pthread_join(thread_array[i],NULL); } pthread_mutex_destroy(&dev_mutex); free(thread_array); #endif nutscan_device_t * result = nutscan_rewind_device(dev_ret); dev_ret = NULL; return result; } #else /* WITH_SNMP */ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip,long usec_timeout, nutscan_snmp_t * sec) { return NULL; } #endif /* WITH_SNMP */ nut-2.7.4/tools/nut-scanner/nut-scan.h0000644000175000017500000000657012640473702014566 00000000000000/* * Copyright (C) * 2011 - EATON * 2012 - Arnaud Quette * * 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 */ /*! \file nut-scan.h \brief general header for nut-scanner \author Frederic Bohe \author Arnaud Quette */ #ifndef NUT_SCAN_H #define NUT_SCAN_H #include #include #include #ifdef WITH_IPMI #include #endif #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /* SNMP structure */ typedef struct nutscan_snmp { char * community; char * secLevel; char * secName; char * authPassword; char * privPassword; char * authProtocol; char * privProtocol; char * peername; void * handle; } nutscan_snmp_t; /* IPMI structure */ /* Settings for OutofBand (remote) connection */ typedef struct nutscan_ipmi { char* username; /* IPMI 1.5 and 2.0 */ char* password; /* IPMI 1.5 and 2.0 */ int authentication_type; /* IPMI 1.5 */ int cipher_suite_id; /* IPMI 2.0 */ char* K_g_BMC_key; /* IPMI 2.0, optional key for 2 key auth. */ int privilege_level; /* for both */ unsigned int workaround_flags; /* for both */ int ipmi_version; /* IPMI 1.5 or 2.0? */ } nutscan_ipmi_t; /* IPMI auth defines, simply using FreeIPMI defines */ #ifndef IPMI_AUTHENTICATION_TYPE_NONE #define IPMI_AUTHENTICATION_TYPE_NONE 0x00 #define IPMI_AUTHENTICATION_TYPE_MD2 0x01 #define IPMI_AUTHENTICATION_TYPE_MD5 0x02 #define IPMI_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY 0x04 #define IPMI_AUTHENTICATION_TYPE_OEM_PROP 0x05 #define IPMI_AUTHENTICATION_TYPE_RMCPPLUS 0x06 #endif #ifndef IPMI_PRIVILEGE_LEVEL_ADMIN #define IPMI_PRIVILEGE_LEVEL_ADMIN 0x04 #endif #define IPMI_1_5 1 #define IPMI_2_0 0 /* Scanning */ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, long usec_timeout, nutscan_snmp_t * sec); nutscan_device_t * nutscan_scan_usb(); nutscan_device_t * nutscan_scan_xml_http(long usec_timeout); nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, long usec_timeout); nutscan_device_t * nutscan_scan_avahi(long usec_timeout); nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec); nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_list); /* Display functions */ void nutscan_display_ups_conf(nutscan_device_t * device); void nutscan_display_parsable(nutscan_device_t * device); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif nut-2.7.4/tools/nut-scanner/nutscan-device.h0000644000175000017500000000455212640473702015744 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-device.h \brief definition of a container describing a NUT discovered device \author Frederic Bohe */ #ifndef SCAN_DEVICE #define SCAN_DEVICE #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /** * \brief Device type string getter * * \param type Device type * * \return Type string */ #define nutscan_device_type_string(type) \ (assert(0 < (type) && (type) < TYPE_END), nutscan_device_type_strings[type - 1]) typedef enum nutscan_device_type { TYPE_NONE=0, TYPE_USB, TYPE_SNMP, TYPE_XML, TYPE_NUT, TYPE_IPMI, TYPE_AVAHI, TYPE_EATON_SERIAL, TYPE_END } nutscan_device_type_t; /** Device type -> string mapping */ extern const char * nutscan_device_type_strings[TYPE_END - 1]; typedef struct nutscan_options { char * option; char * value; struct nutscan_options* next; } nutscan_options_t; typedef struct nutscan_device { nutscan_device_type_t type; char * driver; char * port; nutscan_options_t * opt; struct nutscan_device * prev; struct nutscan_device * next; } nutscan_device_t; nutscan_device_t * nutscan_new_device(); void nutscan_free_device(nutscan_device_t * device); void nutscan_add_option_to_device(nutscan_device_t * device,char * option, char * value); nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); /** * \brief Rewind device list * * \param device Device list item * * \return Device list head */ nutscan_device_t * nutscan_rewind_device(nutscan_device_t * device); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif nut-2.7.4/tools/nut-scanner/nutscan-serial.c0000644000175000017500000001215612640473702015756 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-serial.c \brief helper functions to get serial devices name \author Frederic Bohe \author Arnaud Quette */ #include "nutscan-serial.h" #include #include #include #include "nut_platform.h" #ifdef WIN32 /* Windows: all serial port names start with "COM" */ #define SERIAL_PORT_PREFIX "COM" #else /* Unix: all serial port names start with "/dev/tty" */ #define SERIAL_PORT_PREFIX "/dev/tty" #endif #define ERR_OUT_OF_BOUND "Serial port range out of bound (must be 0 to 9 or a to z depending on your system)\n" typedef struct { char * name; char auto_start_port; char auto_stop_port; } device_portname_t; device_portname_t device_portname[] = { #ifdef NUT_PLATFORM_HPUX /* the first number seems to be a card instance, the second number seems to be a port number */ { "/dev/tty0p%c", '0', '9' }, { "/dev/tty1p%c", '0', '9' }, /* osf/1 and Digital UNIX style */ { "/dev/tty0%c", '0', '9' }, #endif #ifdef NUT_PLATFORM_SOLARIS { "/dev/tty%c", 'a', 'z' }, #endif #ifdef NUT_PLATFORM_AIX { "/dev/tty%c", '0', '9' }, #endif #ifdef NUT_PLATFORM_LINUX { "/dev/ttyS%c", '0', '9' }, { "/dev/ttyUSB%c", '0', '9' }, #endif #ifdef NUT_PLATFORM_MS_WINDOWS { "COM%c", '1', '9'}, #endif /* SGI IRIX */ /* { "/dev/ttyd%i", "=" }, */ /* { "/dev/ttyf%i", "=" }, */ /* FIXME: Mac OS X has no serial port, but maybe ttyUSB? */ { NULL, 0 } }; /* Return 1 if port_name is a full path name to a serial port, * as per SERIAL_PORT_PREFIX */ static int is_serial_port_path(const char * port_name) { if (!strncmp(port_name, SERIAL_PORT_PREFIX, strlen(SERIAL_PORT_PREFIX))) { return 1; } return 0; } /* Add "port" to "list" */ static char ** add_port(char ** list, char * port) { char ** res; int count = 0; if(list == NULL) { count = 0; } else { while(list[count] != NULL) { count++; } } /*+1 to get the number of port from the index nb_ports*/ /*+1 for the terminal NULL */ res = realloc(list,(count+1+1)*sizeof(char*)); if( res == NULL ) { return NULL; } res[count] = strdup(port); res[count+1] = NULL; return res; } /* Return a list of serial ports name, in 'ports_list', according to the OS, * the provided 'ports_range', and the number of available ports */ char ** nutscan_get_serial_ports_list(const char *ports_range) { char start_port = 0; char stop_port = 0; char current_port = 0; char * list_sep_ptr = NULL; char ** ports_list = NULL; char str_tmp[128]; char * tok; device_portname_t *cur_device = NULL; char * saveptr = NULL; char * range; int flag_auto = 0; /* 1) check ports_list */ if ((ports_range == NULL) || (!strncmp(ports_range, "auto", 4))) { flag_auto = 1; } else { range = strdup(ports_range); /* we have a list: * - single element: X (digit) or port name (COM1, /dev/ttyS0, ...) * - range list: X-Y * - multiple elements (coma separated): /dev/ttyS0,/dev/ttyUSB0 */ if ( (list_sep_ptr = strchr(range, '-')) != NULL ) { tok = strtok_r(range,"-",&saveptr); if( tok[1] != 0 ) { fprintf(stderr,ERR_OUT_OF_BOUND); free(range); return NULL; } start_port = tok[0]; tok = strtok_r(NULL,"-",&saveptr); if( tok != NULL ) { if( tok[1] != 0 ) { fprintf(stderr,ERR_OUT_OF_BOUND); free(range); return NULL; } stop_port = tok[0]; } else { stop_port = start_port; } } else if ( ((list_sep_ptr = strchr(ports_range, ',')) != NULL ) && (is_serial_port_path(ports_range)) ) { tok = strtok_r(range,",",&saveptr); while( tok != NULL ) { ports_list = add_port(ports_list,tok); tok = strtok_r(NULL,",",&saveptr); } } else { /* we have been provided a single port name */ /* it's a full device name */ if( ports_range[1] != 0 ) { ports_list = add_port(ports_list,range); } /* it's device number */ else { start_port = stop_port = ports_range[0]; } } free(range); } if( start_port == 0 && !flag_auto) { return ports_list; } for (cur_device=device_portname;cur_device->name!= NULL;cur_device++) { if( flag_auto ) { start_port = cur_device->auto_start_port; stop_port = cur_device->auto_stop_port; } for( current_port=start_port; current_port <= stop_port; current_port++){ snprintf(str_tmp, sizeof(str_tmp),cur_device->name, current_port); ports_list = add_port(ports_list,str_tmp); } } return ports_list; } nut-2.7.4/tools/nut-scanner/README0000644000175000017500000000511412640473702013536 00000000000000NUT device discovery ==================== Introduction ------------ linkman:nut-scanner[8] is available to discover supported NUT devices (USB, SNMP, Eaton XML/HTTP and IPMI) and NUT servers (using Avahi or the classic connection method). This tool actually use a library, called *libnutscan*, to perform actual processing. Client access library ~~~~~~~~~~~~~~~~~~~~~ The nutscan library can be linked into other programs to give access to NUT discovery. Both static and shared versions are provided. linkman:nut-scanner[8] is provided as an example of how to use the nutscan functions. Here is a simple example that scans for USB devices, and use its own iteration function to display results: #include #include #include /* Only enable USB scan */ #define HAVE_USB_H #include "nut-scan.h" int main() { nutscan_options_t * opt; nutscan_device_t *device; if ((device = nutscan_scan_usb()) == NULL) { printf("No device found\n"); exit(EXIT_FAILURE); } /* Rewind the list */ while(device->prev != NULL) { device = device->prev; } /* Print results */ do { printf("USB device found\n\tdriver: \"%s\"\n\tport: \"%s\"\n", device->driver, device->port); /* process options (serial number, bus, ...) */ opt = &(device->opt); do { if( opt->option != NULL ) { printf("\t%s",opt->option); if( opt->value != NULL ) { printf(": \"%s\"", opt->value); } printf("\n"); } opt = opt->next; } while( opt != NULL ); device = device->next; } while( device != NULL ); exit(EXIT_SUCCESS); } This library file and the associated header files are not installed by default. You must `./configure --with-dev` to enable building and installing these files. The libraries can then be built and installed with `make` and `make install` as usual. This must be done before building other (non-NUT) programs which depend on them. For more information, refer to the linkman:nutscan[3], manual page and the various link:man/index.html#devscan[nutscan_*(3)] functions documentation referenced in the same file. Configuration helpers ~~~~~~~~~~~~~~~~~~~~~ NUT provides helper scripts to ease the configuration step of your program, by detecting the right compilation and link flags. For more information, refer to a <>. Python ------ Python support for NUT discovery features is not yet available. Perl ---- Perl support for NUT discovery features is not yet available. Java ---- Java support for NUT discovery features is not yet available. nut-2.7.4/tools/nut-scanner/nutscan-device.c0000644000175000017500000000745712640473702015746 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-device.c \brief manipulation of a container describing a NUT device \author Frederic Bohe */ #include "nutscan-device.h" #include #include #include const char * nutscan_device_type_strings[TYPE_END - 1] = { "USB", "SNMP", "XML", "NUT", "IPMI", "Avahi", "serial", }; nutscan_device_t * nutscan_new_device() { nutscan_device_t * device; device = malloc(sizeof(nutscan_device_t)); if( device==NULL) { return NULL; } memset(device,0,sizeof(nutscan_device_t)); return device; } static void deep_free_device(nutscan_device_t * device) { nutscan_options_t * current; if(device==NULL) { return; } if(device->driver) { free(device->driver); } if(device->port) { free(device->port); } while (device->opt != NULL) { current = device->opt; device->opt = current->next; if(current->option != NULL) { free(current->option); } if(current->value != NULL) { free(current->value); } free(current); }; if(device->prev) { device->prev->next = device->next; } if(device->next) { device->next->prev = device->prev; } free(device); } void nutscan_free_device(nutscan_device_t * device) { if(device==NULL) { return; } while(device->prev != NULL) { deep_free_device(device->prev); } while(device->next != NULL) { deep_free_device(device->next); } deep_free_device(device); } void nutscan_add_option_to_device(nutscan_device_t * device, char * option, char * value) { nutscan_options_t **opt; /* search for last entry */ opt = &device->opt; while (NULL != *opt) opt = &(*opt)->next; *opt = (nutscan_options_t *)malloc(sizeof(nutscan_options_t)); // TBD: A gracefull way to propagate memory failure would be nice assert(NULL != *opt); memset(*opt, 0, sizeof(nutscan_options_t)); if( option != NULL ) { (*opt)->option = strdup(option); } else { (*opt)->option = NULL; } if( value != NULL ) { (*opt)->value = strdup(value); } else { (*opt)->value = NULL; } } nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second) { nutscan_device_t * dev1=NULL; nutscan_device_t * dev2=NULL; /* Get end of first device */ if( first != NULL) { dev1 = first; while(dev1->next != NULL) { dev1 = dev1->next; } } else { if( second == NULL ) { return NULL; } /* return end of second */ dev2 = second; while(dev2->next != NULL) { dev2 = dev2->next; } return dev2; } /* Get start of second */ if( second != NULL ) { dev2 = second; while(dev2->prev != NULL) { dev2 = dev2->prev; } } else { /* return end of first */ dev1 = first; while(dev1->next != NULL) { dev1 = dev1->next; } return dev1; } /* join both */ dev1->next = dev2; dev2->prev = dev1; /* return end of both */ while(dev2->next != NULL) { dev2 = dev2->next; } return dev2; } nutscan_device_t * nutscan_rewind_device(nutscan_device_t * device) { if (NULL == device) return NULL; while (NULL != device->prev) device = device->prev; return device; } nut-2.7.4/tools/nut-scanner/Makefile.in0000644000175000017500000014445312667762001014737 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_LIBLTDL_TRUE@bin_PROGRAMS = nut-scanner$(EXEEXT) @WITH_SSL_TRUE@am__append_1 = $(LIBSSL_CFLAGS) @WITH_SSL_TRUE@am__append_2 = $(LIBSSL_LIBS) @WITH_USB_TRUE@am__append_3 = $(LIBUSB_CFLAGS) @WITH_SNMP_TRUE@am__append_4 = $(LIBNETSNMP_CFLAGS) @WITH_NEON_TRUE@am__append_5 = $(LIBNEON_CFLAGS) @WITH_AVAHI_TRUE@am__append_6 = $(LIBAVAHI_CFLAGS) @WITH_IPMI_TRUE@am__append_7 = $(LIBIPMI_CFLAGS) @WITH_DEV_FALSE@am__append_8 = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h subdir = tools/nut-scanner DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp $(am__dist_noinst_HEADERS_DIST) \ $(am__include_HEADERS_DIST) README ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = @WITH_SSL_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) libnutscan_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am__dirstamp = $(am__leading_dot)dirstamp am_libnutscan_la_OBJECTS = libnutscan_la-scan_nut.lo \ libnutscan_la-scan_ipmi.lo libnutscan_la-nutscan-device.lo \ libnutscan_la-nutscan-ip.lo libnutscan_la-nutscan-display.lo \ libnutscan_la-nutscan-init.lo libnutscan_la-scan_usb.lo \ libnutscan_la-scan_snmp.lo libnutscan_la-scan_xml_http.lo \ libnutscan_la-scan_avahi.lo libnutscan_la-scan_eaton_serial.lo \ libnutscan_la-nutscan-serial.lo \ ../../drivers/libnutscan_la-serial.lo \ ../../drivers/libnutscan_la-bcmxcp_ser.lo \ ../../common/libnutscan_la-common.lo libnutscan_la_OBJECTS = $(am_libnutscan_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libnutscan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libnutscan_la_CFLAGS) \ $(CFLAGS) $(libnutscan_la_LDFLAGS) $(LDFLAGS) -o $@ @WITH_LIBLTDL_TRUE@am_libnutscan_la_rpath = -rpath $(libdir) PROGRAMS = $(bin_PROGRAMS) am_nut_scanner_OBJECTS = nut_scanner-nut-scanner.$(OBJEXT) nut_scanner_OBJECTS = $(am_nut_scanner_OBJECTS) nut_scanner_DEPENDENCIES = libnutscan.la ../../common/libcommon.la nut_scanner_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(nut_scanner_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libnutscan_la_SOURCES) $(nut_scanner_SOURCES) DIST_SOURCES = $(libnutscan_la_SOURCES) $(nut_scanner_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__dist_noinst_HEADERS_DIST = nutscan-usb.h nutscan-snmp.h nut-scan.h \ nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h am__include_HEADERS_DIST = nut-scan.h nutscan-device.h nutscan-ip.h \ nutscan-init.h HEADERS = $(dist_noinst_HEADERS) $(include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ BUILT_SOURCES = nutscan-usb.h nutscan-snmp.h @WITH_LIBLTDL_TRUE@lib_LTLIBRARIES = libnutscan.la libnutscan_la_SOURCES = scan_nut.c scan_ipmi.c \ nutscan-device.c nutscan-ip.c nutscan-display.c \ nutscan-init.c scan_usb.c scan_snmp.c scan_xml_http.c \ scan_avahi.c scan_eaton_serial.c nutscan-serial.c \ ../../drivers/serial.c \ ../../drivers/bcmxcp_ser.c \ ../../common/common.c libnutscan_la_LIBADD = $(NETLIBS) $(LIBLTDL_LIBS) $(am__append_2) libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include \ $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers $(am__append_1) \ $(am__append_3) $(am__append_4) $(am__append_5) \ $(am__append_6) $(am__append_7) nut_scanner_SOURCES = nut-scanner.c nut_scanner_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include nut_scanner_LDADD = libnutscan.la ../../common/libcommon.la dist_noinst_HEADERS = nutscan-usb.h nutscan-snmp.h $(am__append_8) @WITH_DEV_TRUE@include_HEADERS = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h CLEANFILES = nutscan-usb.h nutscan-snmp.h all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/nut-scanner/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tools/nut-scanner/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } ../../drivers/$(am__dirstamp): @$(MKDIR_P) ../../drivers @: > ../../drivers/$(am__dirstamp) ../../drivers/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../../drivers/$(DEPDIR) @: > ../../drivers/$(DEPDIR)/$(am__dirstamp) ../../drivers/libnutscan_la-serial.lo: ../../drivers/$(am__dirstamp) \ ../../drivers/$(DEPDIR)/$(am__dirstamp) ../../drivers/libnutscan_la-bcmxcp_ser.lo: \ ../../drivers/$(am__dirstamp) \ ../../drivers/$(DEPDIR)/$(am__dirstamp) ../../common/$(am__dirstamp): @$(MKDIR_P) ../../common @: > ../../common/$(am__dirstamp) ../../common/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../../common/$(DEPDIR) @: > ../../common/$(DEPDIR)/$(am__dirstamp) ../../common/libnutscan_la-common.lo: ../../common/$(am__dirstamp) \ ../../common/$(DEPDIR)/$(am__dirstamp) libnutscan.la: $(libnutscan_la_OBJECTS) $(libnutscan_la_DEPENDENCIES) $(EXTRA_libnutscan_la_DEPENDENCIES) $(AM_V_CCLD)$(libnutscan_la_LINK) $(am_libnutscan_la_rpath) $(libnutscan_la_OBJECTS) $(libnutscan_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list nut-scanner$(EXEEXT): $(nut_scanner_OBJECTS) $(nut_scanner_DEPENDENCIES) $(EXTRA_nut_scanner_DEPENDENCIES) @rm -f nut-scanner$(EXEEXT) $(AM_V_CCLD)$(nut_scanner_LINK) $(nut_scanner_OBJECTS) $(nut_scanner_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f ../../common/*.$(OBJEXT) -rm -f ../../common/*.lo -rm -f ../../drivers/*.$(OBJEXT) -rm -f ../../drivers/*.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@../../common/$(DEPDIR)/libnutscan_la-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../../drivers/$(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../../drivers/$(DEPDIR)/libnutscan_la-serial.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-device.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-display.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-init.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-ip.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-serial.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_avahi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_ipmi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_nut.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_snmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_usb.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_xml_http.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut_scanner-nut-scanner.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libnutscan_la-scan_nut.lo: scan_nut.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_nut.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_nut.Tpo -c -o libnutscan_la-scan_nut.lo `test -f 'scan_nut.c' || echo '$(srcdir)/'`scan_nut.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_nut.Tpo $(DEPDIR)/libnutscan_la-scan_nut.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_nut.c' object='libnutscan_la-scan_nut.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_nut.lo `test -f 'scan_nut.c' || echo '$(srcdir)/'`scan_nut.c libnutscan_la-scan_ipmi.lo: scan_ipmi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_ipmi.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_ipmi.Tpo -c -o libnutscan_la-scan_ipmi.lo `test -f 'scan_ipmi.c' || echo '$(srcdir)/'`scan_ipmi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_ipmi.Tpo $(DEPDIR)/libnutscan_la-scan_ipmi.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_ipmi.c' object='libnutscan_la-scan_ipmi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_ipmi.lo `test -f 'scan_ipmi.c' || echo '$(srcdir)/'`scan_ipmi.c libnutscan_la-nutscan-device.lo: nutscan-device.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-device.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-device.Tpo -c -o libnutscan_la-nutscan-device.lo `test -f 'nutscan-device.c' || echo '$(srcdir)/'`nutscan-device.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-device.Tpo $(DEPDIR)/libnutscan_la-nutscan-device.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-device.c' object='libnutscan_la-nutscan-device.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-device.lo `test -f 'nutscan-device.c' || echo '$(srcdir)/'`nutscan-device.c libnutscan_la-nutscan-ip.lo: nutscan-ip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-ip.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-ip.Tpo -c -o libnutscan_la-nutscan-ip.lo `test -f 'nutscan-ip.c' || echo '$(srcdir)/'`nutscan-ip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-ip.Tpo $(DEPDIR)/libnutscan_la-nutscan-ip.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-ip.c' object='libnutscan_la-nutscan-ip.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-ip.lo `test -f 'nutscan-ip.c' || echo '$(srcdir)/'`nutscan-ip.c libnutscan_la-nutscan-display.lo: nutscan-display.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-display.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-display.Tpo -c -o libnutscan_la-nutscan-display.lo `test -f 'nutscan-display.c' || echo '$(srcdir)/'`nutscan-display.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-display.Tpo $(DEPDIR)/libnutscan_la-nutscan-display.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-display.c' object='libnutscan_la-nutscan-display.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-display.lo `test -f 'nutscan-display.c' || echo '$(srcdir)/'`nutscan-display.c libnutscan_la-nutscan-init.lo: nutscan-init.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-init.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-init.Tpo -c -o libnutscan_la-nutscan-init.lo `test -f 'nutscan-init.c' || echo '$(srcdir)/'`nutscan-init.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-init.Tpo $(DEPDIR)/libnutscan_la-nutscan-init.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-init.c' object='libnutscan_la-nutscan-init.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-init.lo `test -f 'nutscan-init.c' || echo '$(srcdir)/'`nutscan-init.c libnutscan_la-scan_usb.lo: scan_usb.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_usb.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_usb.Tpo -c -o libnutscan_la-scan_usb.lo `test -f 'scan_usb.c' || echo '$(srcdir)/'`scan_usb.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_usb.Tpo $(DEPDIR)/libnutscan_la-scan_usb.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_usb.c' object='libnutscan_la-scan_usb.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_usb.lo `test -f 'scan_usb.c' || echo '$(srcdir)/'`scan_usb.c libnutscan_la-scan_snmp.lo: scan_snmp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_snmp.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_snmp.Tpo -c -o libnutscan_la-scan_snmp.lo `test -f 'scan_snmp.c' || echo '$(srcdir)/'`scan_snmp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_snmp.Tpo $(DEPDIR)/libnutscan_la-scan_snmp.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_snmp.c' object='libnutscan_la-scan_snmp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_snmp.lo `test -f 'scan_snmp.c' || echo '$(srcdir)/'`scan_snmp.c libnutscan_la-scan_xml_http.lo: scan_xml_http.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_xml_http.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_xml_http.Tpo -c -o libnutscan_la-scan_xml_http.lo `test -f 'scan_xml_http.c' || echo '$(srcdir)/'`scan_xml_http.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_xml_http.Tpo $(DEPDIR)/libnutscan_la-scan_xml_http.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_xml_http.c' object='libnutscan_la-scan_xml_http.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_xml_http.lo `test -f 'scan_xml_http.c' || echo '$(srcdir)/'`scan_xml_http.c libnutscan_la-scan_avahi.lo: scan_avahi.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_avahi.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_avahi.Tpo -c -o libnutscan_la-scan_avahi.lo `test -f 'scan_avahi.c' || echo '$(srcdir)/'`scan_avahi.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_avahi.Tpo $(DEPDIR)/libnutscan_la-scan_avahi.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_avahi.c' object='libnutscan_la-scan_avahi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_avahi.lo `test -f 'scan_avahi.c' || echo '$(srcdir)/'`scan_avahi.c libnutscan_la-scan_eaton_serial.lo: scan_eaton_serial.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_eaton_serial.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_eaton_serial.Tpo -c -o libnutscan_la-scan_eaton_serial.lo `test -f 'scan_eaton_serial.c' || echo '$(srcdir)/'`scan_eaton_serial.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_eaton_serial.Tpo $(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_eaton_serial.c' object='libnutscan_la-scan_eaton_serial.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_eaton_serial.lo `test -f 'scan_eaton_serial.c' || echo '$(srcdir)/'`scan_eaton_serial.c libnutscan_la-nutscan-serial.lo: nutscan-serial.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-serial.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-serial.Tpo -c -o libnutscan_la-nutscan-serial.lo `test -f 'nutscan-serial.c' || echo '$(srcdir)/'`nutscan-serial.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-serial.Tpo $(DEPDIR)/libnutscan_la-nutscan-serial.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-serial.c' object='libnutscan_la-nutscan-serial.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-serial.lo `test -f 'nutscan-serial.c' || echo '$(srcdir)/'`nutscan-serial.c ../../drivers/libnutscan_la-serial.lo: ../../drivers/serial.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT ../../drivers/libnutscan_la-serial.lo -MD -MP -MF ../../drivers/$(DEPDIR)/libnutscan_la-serial.Tpo -c -o ../../drivers/libnutscan_la-serial.lo `test -f '../../drivers/serial.c' || echo '$(srcdir)/'`../../drivers/serial.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../drivers/$(DEPDIR)/libnutscan_la-serial.Tpo ../../drivers/$(DEPDIR)/libnutscan_la-serial.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../drivers/serial.c' object='../../drivers/libnutscan_la-serial.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o ../../drivers/libnutscan_la-serial.lo `test -f '../../drivers/serial.c' || echo '$(srcdir)/'`../../drivers/serial.c ../../drivers/libnutscan_la-bcmxcp_ser.lo: ../../drivers/bcmxcp_ser.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT ../../drivers/libnutscan_la-bcmxcp_ser.lo -MD -MP -MF ../../drivers/$(DEPDIR)/libnutscan_la-bcmxcp_ser.Tpo -c -o ../../drivers/libnutscan_la-bcmxcp_ser.lo `test -f '../../drivers/bcmxcp_ser.c' || echo '$(srcdir)/'`../../drivers/bcmxcp_ser.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../drivers/$(DEPDIR)/libnutscan_la-bcmxcp_ser.Tpo ../../drivers/$(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../drivers/bcmxcp_ser.c' object='../../drivers/libnutscan_la-bcmxcp_ser.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o ../../drivers/libnutscan_la-bcmxcp_ser.lo `test -f '../../drivers/bcmxcp_ser.c' || echo '$(srcdir)/'`../../drivers/bcmxcp_ser.c ../../common/libnutscan_la-common.lo: ../../common/common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT ../../common/libnutscan_la-common.lo -MD -MP -MF ../../common/$(DEPDIR)/libnutscan_la-common.Tpo -c -o ../../common/libnutscan_la-common.lo `test -f '../../common/common.c' || echo '$(srcdir)/'`../../common/common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ../../common/$(DEPDIR)/libnutscan_la-common.Tpo ../../common/$(DEPDIR)/libnutscan_la-common.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='../../common/common.c' object='../../common/libnutscan_la-common.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o ../../common/libnutscan_la-common.lo `test -f '../../common/common.c' || echo '$(srcdir)/'`../../common/common.c nut_scanner-nut-scanner.o: nut-scanner.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -MT nut_scanner-nut-scanner.o -MD -MP -MF $(DEPDIR)/nut_scanner-nut-scanner.Tpo -c -o nut_scanner-nut-scanner.o `test -f 'nut-scanner.c' || echo '$(srcdir)/'`nut-scanner.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nut_scanner-nut-scanner.Tpo $(DEPDIR)/nut_scanner-nut-scanner.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nut-scanner.c' object='nut_scanner-nut-scanner.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -c -o nut_scanner-nut-scanner.o `test -f 'nut-scanner.c' || echo '$(srcdir)/'`nut-scanner.c nut_scanner-nut-scanner.obj: nut-scanner.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -MT nut_scanner-nut-scanner.obj -MD -MP -MF $(DEPDIR)/nut_scanner-nut-scanner.Tpo -c -o nut_scanner-nut-scanner.obj `if test -f 'nut-scanner.c'; then $(CYGPATH_W) 'nut-scanner.c'; else $(CYGPATH_W) '$(srcdir)/nut-scanner.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nut_scanner-nut-scanner.Tpo $(DEPDIR)/nut_scanner-nut-scanner.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nut-scanner.c' object='nut_scanner-nut-scanner.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -c -o nut_scanner-nut-scanner.obj `if test -f 'nut-scanner.c'; then $(CYGPATH_W) 'nut-scanner.c'; else $(CYGPATH_W) '$(srcdir)/nut-scanner.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf ../../common/.libs ../../common/_libs -rm -rf ../../drivers/.libs ../../drivers/_libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f ../../common/$(DEPDIR)/$(am__dirstamp) -rm -f ../../common/$(am__dirstamp) -rm -f ../../drivers/$(DEPDIR)/$(am__dirstamp) -rm -f ../../drivers/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool mostlyclean-am distclean: distclean-am -rm -rf ../../common/$(DEPDIR) ../../drivers/$(DEPDIR) ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ../../common/$(DEPDIR) ../../drivers/$(DEPDIR) ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ uninstall-libLTLIBRARIES .MAKE: all check install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-includeHEADERS uninstall-libLTLIBRARIES nutscan-usb.h nutscan-snmp.h: cd ..; $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/tools/nut-scanner/nutscan-ip.c0000644000175000017500000002037112640473702015105 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-ip.c \brief iterator for IPv4 or IPv6 addresses \author Frederic Bohe */ #include "nutscan-ip.h" #include #include "common.h" #include #include #include static void increment_IPv6(struct in6_addr * addr) { int i; for( i=15 ; i>= 0 ; i--) { addr->s6_addr[i]++; if( addr->s6_addr[i] != 0) { break; } } } static void invert_IPv6(struct in6_addr * addr1, struct in6_addr * addr2) { struct in6_addr addr; memcpy(addr.s6_addr,addr1->s6_addr,sizeof(addr.s6_addr)); memcpy(addr1->s6_addr,addr2->s6_addr,sizeof(addr.s6_addr)); memcpy(addr2->s6_addr,addr.s6_addr,sizeof(addr.s6_addr)); } static int ntop( struct in_addr * ip, char * host, size_t host_size) { struct sockaddr_in in; memset(&in,0,sizeof(struct sockaddr_in)); in.sin_addr = *ip; in.sin_family = AF_INET; return getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), host,host_size,NULL,0,NI_NUMERICHOST); } static int ntop6( struct in6_addr * ip, char * host, size_t host_size) { struct sockaddr_in6 in6; memset(&in6,0,sizeof(struct sockaddr_in6)); memcpy( &in6.sin6_addr, ip, sizeof(struct in6_addr) ); in6.sin6_family = AF_INET6; return getnameinfo((struct sockaddr *)&in6, sizeof(struct sockaddr_in6), host,host_size,NULL,0,NI_NUMERICHOST); } /* Return the first ip or NULL if error */ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const char * stopIP) { int addr; int i; struct addrinfo hints; struct addrinfo *res; struct sockaddr_in * s_in; struct sockaddr_in6 * s_in6; char host[SMALLBUF]; if( startIP == NULL ) { return NULL; } if(stopIP == NULL ) { stopIP = startIP; } memset(&hints,0,sizeof(struct addrinfo)); hints.ai_family = AF_INET; ip->type = IPv4; /* Detecting IPv4 vs IPv6 */ if(getaddrinfo(startIP,NULL,&hints,&res) != 0) { /*Try IPv6 detection */ ip->type = IPv6; hints.ai_family = AF_INET6; if(getaddrinfo(startIP,NULL,&hints,&res) != 0) { fprintf(stderr,"Invalid address : %s\n",startIP); return NULL; } s_in6 = (struct sockaddr_in6 *)res->ai_addr; memcpy(&ip->start6,&s_in6->sin6_addr,sizeof(struct in6_addr)); freeaddrinfo(res); } else { s_in = (struct sockaddr_in *)res->ai_addr; ip->start = s_in->sin_addr; freeaddrinfo(res); } /* Compute stop IP */ if( ip->type == IPv4 ) { hints.ai_family = AF_INET; if(getaddrinfo(stopIP,NULL,&hints,&res) != 0) { fprintf(stderr,"Invalid address : %s\n",stopIP); return NULL; } s_in = (struct sockaddr_in *)res->ai_addr; ip->stop = s_in->sin_addr; freeaddrinfo(res); } else { hints.ai_family = AF_INET6; if(getaddrinfo(stopIP,NULL,&hints,&res) != 0) { fprintf(stderr,"Invalid address : %s\n",stopIP); return NULL; } s_in6 = (struct sockaddr_in6 *)res->ai_addr; memcpy(&ip->stop6,&s_in6->sin6_addr,sizeof(struct in6_addr)); freeaddrinfo(res); } /* Make sure start IP is lesser than stop IP */ if( ip->type == IPv4 ) { if( ntohl(ip->start.s_addr) > ntohl(ip->stop.s_addr) ) { addr = ip->start.s_addr; ip->start.s_addr = ip->stop.s_addr; ip->stop.s_addr = addr; } if( ntop(&ip->start, host, sizeof(host)) != 0 ) { return NULL; } return strdup(host); } else { /* IPv6 */ for( i=0; i<16; i++ ) { if( ip->start6.s6_addr[i] !=ip->stop6.s6_addr[i] ) { if(ip->start6.s6_addr[i]>ip->stop6.s6_addr[i]){ invert_IPv6(&ip->start6,&ip->stop6); } break; } } if( ntop6(&ip->start6, host, sizeof(host)) != 0 ) { return NULL; } return strdup(host); } } /* return the next IP return NULL if there is no more IP */ char * nutscan_ip_iter_inc(nutscan_ip_iter_t * ip) { char host[SMALLBUF]; if( ip->type == IPv4 ) { /* Check if this is the last address to scan */ if(ip->start.s_addr == ip->stop.s_addr) { return NULL; } /* increment the address (need to pass address in host byte order, then pass back in network byte order */ ip->start.s_addr = htonl((ntohl(ip->start.s_addr)+1)); if( ntop(&ip->start, host, sizeof(host)) != 0 ) { return NULL; } return strdup(host); } else { /* Check if this is the last address to scan */ if( memcmp(&ip->start6.s6_addr, &ip->stop6.s6_addr, sizeof(ip->start6.s6_addr)) == 0 ) { return NULL; } increment_IPv6(&ip->start6); if( ntop6(&ip->start6, host, sizeof(host)) != 0 ) { return NULL; } return strdup(host); } } int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) { char * cidr_tok; char * first_ip; char * mask; char * saveptr = NULL; nutscan_ip_iter_t ip; int mask_val; int mask_byte; unsigned long mask_bit; char host[SMALLBUF]; struct addrinfo hints; struct addrinfo *res; struct sockaddr_in * s_in; struct sockaddr_in6 * s_in6; *start_ip = NULL; *stop_ip = NULL; cidr_tok = strdup(cidr); first_ip = strdup(strtok_r(cidr_tok,"/",&saveptr)); if( first_ip == NULL) { return 0; } mask = strtok_r(NULL,"/",&saveptr); if( mask == NULL ) { return 0; } free(cidr_tok); mask_val = atoi(mask); /* Detecting IPv4 vs IPv6 */ memset(&hints,0,sizeof(struct addrinfo)); hints.ai_family = AF_INET; ip.type = IPv4; /* Detecting IPv4 vs IPv6 */ if(getaddrinfo(first_ip,NULL,&hints,&res) != 0) { /*Try IPv6 detection */ ip.type = IPv6; hints.ai_family = AF_INET6; int ret; if((ret=getaddrinfo(first_ip,NULL,&hints,&res)) != 0) { free(first_ip); return 0; } s_in6 = (struct sockaddr_in6 *)res->ai_addr; memcpy(&ip.start6,&s_in6->sin6_addr,sizeof(struct in6_addr)); freeaddrinfo(res); } else { s_in = (struct sockaddr_in *)res->ai_addr; ip.start = s_in->sin_addr; freeaddrinfo(res); } if( ip.type == IPv4 ) { if( mask_val > 0 ) { mask_val --; mask_bit = 0x80000000; mask_bit >>= mask_val; mask_bit--; } else { mask_bit = 0xffffffff; } ip.stop.s_addr = htonl(ntohl(ip.start.s_addr)|mask_bit); ip.start.s_addr = htonl(ntohl(ip.start.s_addr)&(~mask_bit)); if( ntop(&ip.start, host, sizeof(host)) != 0 ) { *start_ip = NULL; *stop_ip = NULL; return 0; } *start_ip = strdup(host); if( ntop(&ip.stop, host, sizeof(host)) != 0 ) { free(*start_ip); *start_ip = NULL; *stop_ip = NULL; return 0; } *stop_ip = strdup(host); free(first_ip); return 1; } else { if(getaddrinfo(first_ip,NULL,&hints,&res) != 0) { return 0; } s_in6 = (struct sockaddr_in6 *)res->ai_addr; memcpy(&ip.stop6,&s_in6->sin6_addr,sizeof(struct in6_addr)); freeaddrinfo(res); mask_byte = mask_val / 8; if( mask_byte < 16 ) { memset( &(ip.stop6.s6_addr[mask_byte+1]), 0xFF, 15 - mask_byte); memset( &(ip.start6.s6_addr[mask_byte+1]), 0x00, 15 - mask_byte); mask_bit = (0x100 >> mask_val%8)-1; ip.stop6.s6_addr[mask_byte] |= mask_bit; ip.start6.s6_addr[mask_byte] &= (~mask_bit); } if( ntop6(&ip.start6, host, sizeof(host)) != 0 ) { *start_ip = NULL; *stop_ip = NULL; return 0; } *start_ip = strdup(host); if( ntop6(&ip.stop6, host, sizeof(host)) != 0 ) { free(*start_ip); *start_ip = NULL; *stop_ip = NULL; return 0; } *stop_ip = strdup(host); } free(first_ip); return 1; } nut-2.7.4/tools/nut-scanner/scan_avahi.c0000644000175000017500000003561312667537407015141 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file scan_avahi.c \brief detect NUT through Avahi mDNS / DNS-SD services \author Frederic Bohe */ #include "common.h" #include "nut-scan.h" #ifdef WITH_AVAHI #include #include #include #include "timehead.h" #include #include #include #include #include #include /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; static AvahiClient* (*nut_avahi_service_browser_get_client)(AvahiServiceBrowser *); static int (*nut_avahi_simple_poll_loop)(AvahiSimplePoll *s); static void (*nut_avahi_client_free)(AvahiClient *client); static int (*nut_avahi_client_errno)(AvahiClient*); static void (*nut_avahi_free)(void *p); static void (*nut_avahi_simple_poll_quit)(AvahiSimplePoll *s); static AvahiClient* (*nut_avahi_client_new)( const AvahiPoll *poll_api, AvahiClientFlags flags, AvahiClientCallback callback, void *userdata, int *error); static void (*nut_avahi_simple_poll_free)(AvahiSimplePoll *s); static AvahiServiceResolver * (*nut_avahi_service_resolver_new)( AvahiClient *client, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiProtocol aprotocol, AvahiLookupFlags flags, AvahiServiceResolverCallback callback, void *userdata); static const char * (*nut_avahi_strerror)(int error); static AvahiClient* (*nut_avahi_service_resolver_get_client)(AvahiServiceResolver *); static AvahiServiceBrowser* (*nut_avahi_service_browser_new)( AvahiClient *client, AvahiIfIndex interface, AvahiProtocol protocol, const char *type, const char *domain, AvahiLookupFlags flags, AvahiServiceBrowserCallback callback, void *userdata); static int (*nut_avahi_service_resolver_free)(AvahiServiceResolver *r); static AvahiSimplePoll *(*nut_avahi_simple_poll_new)(void); static char* (*nut_avahi_string_list_to_string)(AvahiStringList *l); static int (*nut_avahi_service_browser_free)(AvahiServiceBrowser *); static char * (*nut_avahi_address_snprint)(char *ret_s, size_t length, const AvahiAddress *a); static const AvahiPoll* (*nut_avahi_simple_poll_get)(AvahiSimplePoll *s); /* return 0 on error */ int nutscan_load_avahi_library(const char *libname_path) { if( dl_handle != NULL ) { /* if previous init failed */ if( dl_handle == (void *)1 ) { return 0; } /* init has already been done */ return 1; } if (libname_path == NULL) { fprintf(stderr, "AVAHI client library not found. AVAHI search disabled.\n"); return 0; } if( lt_dlinit() != 0 ) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_avahi_service_browser_get_client) = lt_dlsym(dl_handle, "avahi_service_browser_get_client"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_simple_poll_loop) = lt_dlsym(dl_handle, "avahi_simple_poll_loop"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_client_free) = lt_dlsym(dl_handle, "avahi_client_free"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_client_errno) = lt_dlsym(dl_handle, "avahi_client_errno"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_free) = lt_dlsym(dl_handle, "avahi_free"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_simple_poll_quit) = lt_dlsym(dl_handle, "avahi_simple_poll_quit"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_client_new) = lt_dlsym(dl_handle, "avahi_client_new"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_simple_poll_free) = lt_dlsym(dl_handle, "avahi_simple_poll_free"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_service_resolver_new) = lt_dlsym(dl_handle, "avahi_service_resolver_new"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_strerror) = lt_dlsym(dl_handle, "avahi_strerror"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_service_resolver_get_client) = lt_dlsym(dl_handle, "avahi_service_resolver_get_client"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_service_browser_new) = lt_dlsym(dl_handle, "avahi_service_browser_new"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_service_resolver_free) = lt_dlsym(dl_handle, "avahi_service_resolver_free"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_simple_poll_new) = lt_dlsym(dl_handle, "avahi_simple_poll_new"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_string_list_to_string) = lt_dlsym(dl_handle, "avahi_string_list_to_string"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_service_browser_free) = lt_dlsym(dl_handle, "avahi_service_browser_free"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_address_snprint) = lt_dlsym(dl_handle, "avahi_address_snprint"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_avahi_simple_poll_get) = lt_dlsym(dl_handle, "avahi_simple_poll_get"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: fprintf(stderr, "Cannot load AVAHI library (%s) : %s. AVAHI search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } /* end of dynamic link library stuff */ static AvahiSimplePoll *simple_poll = NULL; static nutscan_device_t * dev_ret = NULL; static long avahi_usec_timeout = 0; static void update_device(const char * host_name,const char *ip, uint16_t port,char * text, int proto) { nutscan_device_t * dev = NULL; char * t = NULL; char * t_saveptr = NULL; char * phrase = NULL; char * phrase_saveptr = NULL; char * word = NULL; char * value = NULL; char * device = NULL; char * device_saveptr = NULL; int device_found = 0; char buf[6]; int buf_size; if( text == NULL ) { return; } t = strdup(text); phrase = strtok_r(t,"\"",&t_saveptr); while(phrase != NULL ) { word = strtok_r(phrase,"=",&phrase_saveptr); if( word == NULL ) { phrase = strtok_r(NULL,"\"",&t_saveptr); continue; } value = strtok_r(NULL,"=",&phrase_saveptr); if( value == NULL ) { phrase = strtok_r(NULL,"\"",&t_saveptr); continue; } if( strcmp(word,"device_list") != 0 ) { phrase = strtok_r(NULL,"\"",&t_saveptr); continue; } device = strtok_r(value,";",&device_saveptr); while( device != NULL ) { device_found = 1; dev = nutscan_new_device(); dev->type = TYPE_NUT; dev->driver = strdup("nutclient"); if( proto == AVAHI_PROTO_INET) { nutscan_add_option_to_device(dev,"desc","IPv4"); } if( proto == AVAHI_PROTO_INET6 ) { nutscan_add_option_to_device(dev,"desc","IPv6"); } if( port != PORT) { /* +5+1+1+1 is for : - port number (max 65535 so 5 characters), - '@' and ':' characters - terminating 0 */ buf_size = strlen(device)+strlen(host_name)+ 5+1+1+1; dev->port=malloc(buf_size); if(dev->port) { snprintf(dev->port,buf_size,"%s@%s:%u", device,host_name,port); } } else { /*+1+1 is for '@' character and terminating 0 */ buf_size = strlen(device)+strlen(host_name)+1+1; dev->port=malloc(buf_size); if(dev->port) { snprintf(dev->port,buf_size,"%s@%s", device,host_name); } } if( dev->port ) { dev_ret = nutscan_add_device_to_device(dev_ret,dev); } else { nutscan_free_device(dev); } device = strtok_r(NULL,";",&device_saveptr); }; phrase = strtok_r(NULL,"\"",&t_saveptr); }; free(t); /* If no device published in avahi data, try to get the device by connecting directly to upsd */ if( !device_found) { snprintf(buf,sizeof(buf),"%u",port); dev = nutscan_scan_nut(ip,ip,buf,avahi_usec_timeout); if(dev) { dev_ret = nutscan_add_device_to_device(dev_ret,dev); } /* add an upsd entry without associated device */ else { dev = nutscan_new_device(); dev->type = TYPE_NUT; dev->driver = strdup("nutclient"); if( proto == AVAHI_PROTO_INET) { nutscan_add_option_to_device(dev,"desc","IPv4"); } if( proto == AVAHI_PROTO_INET6 ) { nutscan_add_option_to_device(dev,"desc","IPv6"); } if( port != PORT) { /*+1+1 is for ':' character and terminating 0 */ /*buf is the string containing the port number*/ buf_size = strlen(host_name)+strlen(buf)+1+1; dev->port=malloc(buf_size); if(dev->port) { snprintf(dev->port,buf_size,"%s:%s", host_name,buf); } } else { dev->port=strdup(host_name); } if( dev->port ) { dev_ret = nutscan_add_device_to_device(dev_ret,dev); } else { nutscan_free_device(dev); } } } } static void resolve_callback( AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, AVAHI_GCC_UNUSED void* userdata) { assert(r); /* Called whenever a service has been resolved successfully or timed out */ switch (event) { case AVAHI_RESOLVER_FAILURE: fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, (*nut_avahi_strerror)((*nut_avahi_client_errno)((*nut_avahi_service_resolver_get_client)(r)))); break; case AVAHI_RESOLVER_FOUND: { char a[AVAHI_ADDRESS_STR_MAX], *t; /* fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n", name, type, domain); */ (*nut_avahi_address_snprint)(a, sizeof(a), address); t = (*nut_avahi_string_list_to_string)(txt); /* fprintf(stderr, "\t%s:%u (%s)\n" "\tTXT=%s\n" "\tcookie is %u\n" "\tis_local: %i\n" "\tour_own: %i\n" "\twide_area: %i\n" "\tmulticast: %i\n" "\tcached: %i\n", host_name, port, a, t, avahi_string_list_get_service_cookie(txt), !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN), !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); */ update_device(host_name,a,port,t,address->proto); (*nut_avahi_free)(t); } } (*nut_avahi_service_resolver_free)(r); } static void browse_callback( AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) { AvahiClient *c = userdata; assert(b); /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ switch (event) { case AVAHI_BROWSER_FAILURE: fprintf(stderr, "(Browser) %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)((*nut_avahi_service_browser_get_client)(b)))); (*nut_avahi_simple_poll_quit)(simple_poll); return; case AVAHI_BROWSER_NEW: /* fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); */ /* We ignore the returned resolver object. In the callback function we free it. If the server is terminated before the callback function is called the server will free the resolver for us. */ if (!((*nut_avahi_service_resolver_new)(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) fprintf(stderr, "Failed to resolve service '%s': %s\n", name, (*nut_avahi_strerror)((*nut_avahi_client_errno)(c))); break; case AVAHI_BROWSER_REMOVE: fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); break; case AVAHI_BROWSER_ALL_FOR_NOW: (*nut_avahi_simple_poll_quit)(simple_poll); case AVAHI_BROWSER_CACHE_EXHAUSTED: /* fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); */ break; } } static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { assert(c); /* Called whenever the client or server state changes */ if (state == AVAHI_CLIENT_FAILURE) { fprintf(stderr, "Server connection failure: %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)(c))); (*nut_avahi_simple_poll_quit)(simple_poll); } } nutscan_device_t * nutscan_scan_avahi(long usec_timeout) { /* Example service publication * $ avahi-publish -s nut _upsd._tcp 3493 txtvers=1 protovers=1.0.0 device_list="dev1;dev2" */ AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; int error; if( !nutscan_avail_avahi ) { return NULL; } avahi_usec_timeout = usec_timeout; /* Allocate main loop object */ if (!(simple_poll = (*nut_avahi_simple_poll_new)())) { fprintf(stderr, "Failed to create simple poll object.\n"); goto fail; } /* Allocate a new client */ client = (*nut_avahi_client_new)((*nut_avahi_simple_poll_get)(simple_poll), 0, client_callback, NULL, &error); /* Check wether creating the client object succeeded */ if (!client) { fprintf(stderr, "Failed to create client: %s\n", (*nut_avahi_strerror)(error)); goto fail; } /* Create the service browser */ if (!(sb = (*nut_avahi_service_browser_new)(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_upsd._tcp", NULL, 0, browse_callback, client))) { fprintf(stderr, "Failed to create service browser: %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)(client))); goto fail; } /* Run the main loop */ (*nut_avahi_simple_poll_loop)(simple_poll); fail: /* Cleanup things */ if (sb) (*nut_avahi_service_browser_free)(sb); if (client) (*nut_avahi_client_free)(client); if (simple_poll) (*nut_avahi_simple_poll_free)(simple_poll); return nutscan_rewind_device(dev_ret); } #else /* WITH_AVAHI */ /* stub function */ nutscan_device_t * nutscan_scan_avahi(long usec_timeout) { return NULL; } #endif /* WITH_AVAHI */ nut-2.7.4/tools/nut-scanner/nutscan-usb.h0000644000175000017500000001076712670015112015271 00000000000000/* nutscan-usb * Copyright (C) 2011 - Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DEVSCAN_USB_H #define DEVSCAN_USB_H #include #include "nut_stdint.h" /* for uint16_t */ typedef struct { uint16_t vendorID; uint16_t productID; char* driver_name; } usb_device_id_t; /* USB IDs device table */ static usb_device_id_t usb_device_table[] = { { 0x0001, 0x0000, "nutdrv_atcl_usb" }, { 0x03f0, 0x0001, "usbhid-ups" }, { 0x03f0, 0x1f01, "bcmxcp_usb" }, { 0x03f0, 0x1f02, "bcmxcp_usb" }, { 0x03f0, 0x1f06, "usbhid-ups" }, { 0x03f0, 0x1f08, "usbhid-ups" }, { 0x03f0, 0x1f09, "usbhid-ups" }, { 0x03f0, 0x1f0a, "usbhid-ups" }, { 0x03f0, 0x1fe0, "usbhid-ups" }, { 0x03f0, 0x1fe1, "usbhid-ups" }, { 0x03f0, 0x1fe2, "usbhid-ups" }, { 0x03f0, 0x1fe3, "usbhid-ups" }, { 0x03f0, 0x1fe5, "usbhid-ups" }, { 0x03f0, 0x1fe6, "usbhid-ups" }, { 0x03f0, 0x1fe7, "usbhid-ups" }, { 0x03f0, 0x1fe8, "usbhid-ups" }, { 0x0463, 0x0001, "usbhid-ups" }, { 0x0463, 0xffff, "usbhid-ups" }, { 0x047c, 0xffff, "usbhid-ups" }, { 0x0483, 0x0035, "nutdrv_qx" }, { 0x04b4, 0x5500, "riello_usb" }, { 0x04d8, 0xd004, "usbhid-ups" }, { 0x04d8, 0xd005, "usbhid-ups" }, { 0x050d, 0x0375, "usbhid-ups" }, { 0x050d, 0x0551, "usbhid-ups" }, { 0x050d, 0x0750, "usbhid-ups" }, { 0x050d, 0x0751, "usbhid-ups" }, { 0x050d, 0x0900, "usbhid-ups" }, { 0x050d, 0x0910, "usbhid-ups" }, { 0x050d, 0x0912, "usbhid-ups" }, { 0x050d, 0x0980, "usbhid-ups" }, { 0x050d, 0x0f51, "usbhid-ups" }, { 0x050d, 0x1100, "usbhid-ups" }, { 0x051d, 0x0000, "usbhid-ups" }, { 0x051d, 0x0002, "usbhid-ups" }, { 0x051d, 0x0003, "usbhid-ups" }, { 0x0592, 0x0002, "bcmxcp_usb" }, { 0x0592, 0x0004, "usbhid-ups" }, { 0x05b8, 0x0000, "blazer_usb" }, { 0x0665, 0x5161, "blazer_usb" }, { 0x06da, 0x0002, "bcmxcp_usb" }, { 0x06da, 0x0003, "blazer_usb" }, { 0x06da, 0x0004, "blazer_usb" }, { 0x06da, 0x0005, "blazer_usb" }, { 0x06da, 0x0201, "blazer_usb" }, { 0x06da, 0x0601, "blazer_usb" }, { 0x06da, 0xffff, "usbhid-ups" }, { 0x075d, 0x0300, "usbhid-ups" }, { 0x0764, 0x0005, "usbhid-ups" }, { 0x0764, 0x0501, "usbhid-ups" }, { 0x0764, 0x0601, "usbhid-ups" }, { 0x0925, 0x1234, "richcomm_usb" }, { 0x09ae, 0x0001, "tripplite_usb" }, { 0x09ae, 0x1003, "usbhid-ups" }, { 0x09ae, 0x1007, "usbhid-ups" }, { 0x09ae, 0x1008, "usbhid-ups" }, { 0x09ae, 0x1009, "usbhid-ups" }, { 0x09ae, 0x1010, "usbhid-ups" }, { 0x09ae, 0x2005, "usbhid-ups" }, { 0x09ae, 0x2007, "usbhid-ups" }, { 0x09ae, 0x2008, "usbhid-ups" }, { 0x09ae, 0x2009, "usbhid-ups" }, { 0x09ae, 0x2010, "usbhid-ups" }, { 0x09ae, 0x2011, "usbhid-ups" }, { 0x09ae, 0x2012, "usbhid-ups" }, { 0x09ae, 0x2013, "usbhid-ups" }, { 0x09ae, 0x2014, "usbhid-ups" }, { 0x09ae, 0x3008, "usbhid-ups" }, { 0x09ae, 0x3009, "usbhid-ups" }, { 0x09ae, 0x3010, "usbhid-ups" }, { 0x09ae, 0x3011, "usbhid-ups" }, { 0x09ae, 0x3012, "usbhid-ups" }, { 0x09ae, 0x3013, "usbhid-ups" }, { 0x09ae, 0x3014, "usbhid-ups" }, { 0x09ae, 0x3015, "usbhid-ups" }, { 0x09ae, 0x3016, "usbhid-ups" }, { 0x09ae, 0x4001, "usbhid-ups" }, { 0x09ae, 0x4002, "usbhid-ups" }, { 0x09ae, 0x4003, "usbhid-ups" }, { 0x09ae, 0x4004, "usbhid-ups" }, { 0x09ae, 0x4005, "usbhid-ups" }, { 0x09ae, 0x4006, "usbhid-ups" }, { 0x09ae, 0x4007, "usbhid-ups" }, { 0x09ae, 0x4008, "usbhid-ups" }, { 0x0d9f, 0x0001, "usbhid-ups" }, { 0x0d9f, 0x0004, "usbhid-ups" }, { 0x0d9f, 0x00a2, "usbhid-ups" }, { 0x0d9f, 0x00a3, "usbhid-ups" }, { 0x0d9f, 0x00a4, "usbhid-ups" }, { 0x0d9f, 0x00a5, "usbhid-ups" }, { 0x0d9f, 0x00a6, "usbhid-ups" }, { 0x0f03, 0x0001, "blazer_usb" }, { 0x10af, 0x0001, "usbhid-ups" }, { 0x10af, 0x0004, "usbhid-ups" }, { 0x10af, 0x0008, "usbhid-ups" }, { 0x14f0, 0x00c9, "blazer_usb" }, { 0x2b2d, 0xffff, "usbhid-ups" }, { 0xffff, 0x0000, "blazer_usb" }, /* Terminating entry */ { -1, -1, NULL } }; #endif /* DEVSCAN_USB_H */ nut-2.7.4/tools/nut-scanner/scan_usb.c0000644000175000017500000001561112667537407014636 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file scan_usb.c \brief detect NUT supported USB devices \author Frederic Bohe */ #include "common.h" #include "nut-scan.h" #ifdef WITH_USB #include "upsclient.h" #include "nutscan-usb.h" #include #include #include /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; static int (*nut_usb_close)(usb_dev_handle *dev); static int (*nut_usb_find_busses)(void); static char * (*nut_usb_strerror)(void); static void (*nut_usb_init)(void); static int (*nut_usb_get_string_simple)(usb_dev_handle *dev, int index, char *buf, size_t buflen); static struct usb_bus * (*nut_usb_busses); static usb_dev_handle * (*nut_usb_open)(struct usb_device *dev); static int (*nut_usb_find_devices)(void); /* return 0 on error */ int nutscan_load_usb_library(const char *libname_path) { if( dl_handle != NULL ) { /* if previous init failed */ if( dl_handle == (void *)1 ) { return 0; } /* init has already been done */ return 1; } if (libname_path == NULL) { fprintf(stderr, "USB library not found. USB search disabled.\n"); return 0; } if( lt_dlinit() != 0 ) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_usb_close) = lt_dlsym(dl_handle, "usb_close"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_find_busses) = lt_dlsym(dl_handle, "usb_find_busses"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_strerror) = lt_dlsym(dl_handle, "usb_strerror"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_init) = lt_dlsym(dl_handle, "usb_init"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle, "usb_get_string_simple"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_usb_open) = lt_dlsym(dl_handle, "usb_open"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle,"usb_find_devices"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: fprintf(stderr, "Cannot load USB library (%s) : %s. USB search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } /* end of dynamic link library stuff */ static char* is_usb_device_supported(usb_device_id_t *usb_device_id_list, int dev_VendorID, int dev_ProductID) { usb_device_id_t *usbdev; for (usbdev=usb_device_id_list; usbdev->driver_name != NULL; usbdev++) { if ( (usbdev->vendorID == dev_VendorID) && (usbdev->productID == dev_ProductID) ) { return usbdev->driver_name; } } return NULL; } /* return NULL if error */ nutscan_device_t * nutscan_scan_usb() { int ret; char string[256]; char *driver_name = NULL; char *serialnumber = NULL; char *device_name = NULL; char *vendor_name = NULL; struct usb_device *dev; struct usb_bus *bus; usb_dev_handle *udev; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; if( !nutscan_avail_usb ) { return NULL; } /* libusb base init */ (*nut_usb_init)(); (*nut_usb_find_busses)(); (*nut_usb_find_devices)(); for (bus = (*nut_usb_busses); bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if ((driver_name = is_usb_device_supported(usb_device_table, dev->descriptor.idVendor, dev->descriptor.idProduct)) != NULL) { /* open the device */ udev = (*nut_usb_open)(dev); if (!udev) { fprintf(stderr,"Failed to open device, \ skipping. (%s)\n", (*nut_usb_strerror)()); continue; } /* get serial number */ if (dev->descriptor.iSerialNumber) { ret = (*nut_usb_get_string_simple)(udev, dev->descriptor.iSerialNumber, string, sizeof(string)); if (ret > 0) { serialnumber = strdup(str_rtrim(string, ' ')); } } /* get product name */ if (dev->descriptor.iProduct) { ret = (*nut_usb_get_string_simple)(udev, dev->descriptor.iProduct, string, sizeof(string)); if (ret > 0) { device_name = strdup(str_rtrim(string, ' ')); } } /* get vendor name */ if (dev->descriptor.iManufacturer) { ret = (*nut_usb_get_string_simple)(udev, dev->descriptor.iManufacturer, string, sizeof(string)); if (ret > 0) { vendor_name = strdup(str_rtrim(string, ' ')); } } nut_dev = nutscan_new_device(); if(nut_dev == NULL) { fprintf(stderr,"Memory allocation \ error\n"); nutscan_free_device(current_nut_dev); free(serialnumber); free(device_name); free(vendor_name); return NULL; } nut_dev->type = TYPE_USB; if(driver_name) { nut_dev->driver = strdup(driver_name); } nut_dev->port = strdup("auto"); sprintf(string,"%04X",dev->descriptor.idVendor); nutscan_add_option_to_device(nut_dev,"vendorid", string); sprintf(string,"%04X", dev->descriptor.idProduct); nutscan_add_option_to_device(nut_dev,"productid", string); if(device_name) { nutscan_add_option_to_device(nut_dev, "product", device_name); free(device_name); } if(serialnumber) { nutscan_add_option_to_device(nut_dev, "serial", serialnumber); free(serialnumber); } if(vendor_name) { nutscan_add_option_to_device(nut_dev, "vendor", vendor_name); free(vendor_name); } nutscan_add_option_to_device(nut_dev,"bus", bus->dirname); current_nut_dev = nutscan_add_device_to_device( current_nut_dev, nut_dev); memset (string, 0, sizeof(string)); (*nut_usb_close)(udev); } } } return nutscan_rewind_device(current_nut_dev); } #else /* WITH_USB */ nutscan_device_t * nutscan_scan_usb() { return NULL; } #endif /* WITH_USB */ nut-2.7.4/tools/nut-scanner/scan_eaton_serial.c0000644000175000017500000002660512640473702016503 00000000000000/* * Copyright (C) 2012 - EATON * * 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 */ /*! \file scan_eaton_serial.c \brief detect Eaton serial XCP, SHUT and Q1 devices \author Arnaud Quette */ #include "common.h" /* Need this on AIX when using xlc to get alloca */ #ifdef _AIX #pragma alloca #endif /* _AIX */ #include #include #include #include #include #include #include "nut-scan.h" #include "serial.h" #include "bcmxcp_io.h" #include "bcmxcp.h" #include "nutscan-serial.h" #ifdef HAVE_PTHREAD #include #endif /* SHUT header */ #define SHUT_SYNC 0x16 #define MAX_TRY 4 /* BCMXCP header */ extern unsigned char AUT[4]; extern struct pw_baud_rate { int rate; int name; } pw_baud_rates[]; /* Local list of found devices */ static nutscan_device_t * dev_ret = NULL; /* Remap some functions to avoid undesired behavior (drivers/main.c) */ char *getval(const char *var) { return NULL; } #ifdef HAVE_PTHREAD static pthread_mutex_t dev_mutex; #endif /* Drivers name */ #define SHUT_DRIVER_NAME "mge-shut" #define XCP_DRIVER_NAME "bcmxcp" #define Q1_DRIVER_NAME "blazer_ser" /* Fake driver main, for using serial functions, needed for bcmxcp_ser.c */ char *device_path; int upsfd; int exit_flag = 0; int do_lock_port; /* Functions extracted from drivers/bcmxcp.c, to avoid pulling too many things * lightweight function to calculate the 8-bit * two's complement checksum of buf, using XCP data length (including header) * the result must be 0 for the sequence data to be valid */ int checksum_test(const unsigned char *buf) { unsigned char checksum = 0; int i, length; /* buf[2] is the length of the XCP frame ; add 5 for the header */ length = (int)(buf[2]) + 5; for (i = 0; i < length; i++) { checksum += buf[i]; } /* Compute the 8-bit, Two's Complement checksum now and return it */ checksum = ((0x100 - checksum) & 0xFF); return (checksum == 0); } unsigned char calc_checksum(const unsigned char *buf) { unsigned char c; int i; c = 0; for(i = 0; i < 2 + buf[1]; i++) c -= buf[i]; return c; } /******************************************************************************* * SHUT functions (MGE legacy, but Eaton path forward) ******************************************************************************/ /* Light version of of drivers/libshut.c->shut_synchronise() * return 1 if OK, 0 otherwise */ int shut_synchronise(int upsfd) { int try; u_char reply = '\0'; /* Sync with the UPS according to notification */ for (try = 0; try < MAX_TRY; try++) { if ((ser_send_char(upsfd, SHUT_SYNC)) == -1) { continue; } ser_get_char(upsfd, &reply, 1, 0); if (reply == SHUT_SYNC) { return 1; } } return 0; } /* SHUT scan: * send SYNC token (0x16) and receive the SYNC token back * FIXME: maybe try to get device descriptor?! */ nutscan_device_t * nutscan_scan_eaton_serial_shut(const char* port_name) { nutscan_device_t * dev = NULL; int devfd = -1; if ( (devfd = ser_open_nf(port_name)) != -1 ) { /* set RTS to off and DTR to on to allow correct behavior * with UPS using PnP feature */ if (ser_set_dtr(devfd, 1) != -1) { ser_set_rts(devfd, 0); ser_set_speed_nf(devfd, port_name, B2400); if (shut_synchronise(devfd)) { /* Communication established successfully! */ dev = nutscan_new_device(); dev->type = TYPE_EATON_SERIAL; dev->driver = strdup(SHUT_DRIVER_NAME); dev->port = strdup(port_name); #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif dev_ret = nutscan_add_device_to_device(dev_ret, dev); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif } } /* Close the device */ ser_close(devfd, NULL); } return dev; } /******************************************************************************* * XCP functions (Eaton Powerware legacy) ******************************************************************************/ /* XCP scan: * baudrate nego (...) * Send ESC to take it out of menu * Wait 90ms * Send auth command (AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5};) * Wait 500ms (or less?) * Send PW_SET_REQ_ONLY_MODE command (0xA0) and wait for response * [Get ID Block (PW_ID_BLOCK_REQ) (0x31)] */ nutscan_device_t * nutscan_scan_eaton_serial_xcp(const char* port_name) { nutscan_device_t * dev = NULL; int i, ret, devfd = -1; unsigned char answer[256]; unsigned char sbuf[128]; memset(sbuf, 0, 128); if ( (devfd = ser_open_nf(port_name)) != -1 ) { #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif upsfd = devfd; #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif for (i=0; (pw_baud_rates[i].rate != 0) && (dev == NULL); i++) { memset(answer, 0, 256); if (ser_set_speed_nf(devfd, port_name, pw_baud_rates[i].rate) == -1) break; ret = ser_send_char(devfd, 0x1d); /* send ESC to take it out of menu */ if (ret <= 0) break; usleep(90000); send_write_command(AUT, 4); usleep(500000); /* Discovery with Baud Hunting (XCP protocol spec. §4.1.2) * sending PW_SET_REQ_ONLY_MODE should be enough, since * the unit should send back Identification block */ sbuf[0] = PW_COMMAND_START_BYTE; sbuf[1] = (unsigned char)1; sbuf[2] = PW_SET_REQ_ONLY_MODE; sbuf[3] = calc_checksum(sbuf); ret = ser_send_buf_pace(devfd, 1000, sbuf, 4); /* Read PW_COMMAND_START_BYTE byte */ ret = ser_get_char(devfd, answer, 1, 0); #if 0 /* FIXME: seems not needed, but requires testing with more devices! */ if (ret <= 0) { usleep(250000); /* 500000? */ memset(answer, 0, 256); ret = command_sequence(&id_command, 1, answer); } #endif if ( (ret > 0) && (answer[0] == PW_COMMAND_START_BYTE) ) { dev = nutscan_new_device(); dev->type = TYPE_EATON_SERIAL; dev->driver = strdup(XCP_DRIVER_NAME); dev->port = strdup(port_name); #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif dev_ret = nutscan_add_device_to_device(dev_ret, dev); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif break; } usleep(100000); } /* Close the device */ ser_close(devfd, NULL); } return dev; } /******************************************************************************* * Q1 functions (Phoenixtec/Centralion/Santak, still Eaton path forward) ******************************************************************************/ #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ #define MAXTRIES 3 /* Q1 scan: * - open the serial port and set the speed to 2400 baud * - simply try to get Q1 (status) string * - check its size and first char. which should be '(' */ nutscan_device_t * nutscan_scan_eaton_serial_q1(const char* port_name) { nutscan_device_t * dev = NULL; struct termios tio; int ret = 0, retry; int devfd = -1; char buf[128]; if ( (devfd = ser_open_nf(port_name)) != -1 ) { if (ser_set_speed_nf(devfd, port_name, B2400) != -1) { if (!tcgetattr(devfd, &tio)) { /* Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = '\r'; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (!tcsetattr(devfd, TCSANOW, &tio)) { /* Set the default (normal) cablepower */ ser_set_dtr(devfd, 1); ser_set_rts(devfd, 0); /* Allow some time to settle for the cablepower */ usleep(100000); /* Only try pure 'Q1', not older ones like 'D' or 'QS' * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] */ for (retry = 1; retry <= MAXTRIES; retry++) { /* simplified code */ ser_flush_io(devfd); if ( (ret = ser_send(devfd, "Q1\r")) > 0) { /* Get Q1 reply */ if ( (ret = ser_get_buf(devfd, buf, sizeof(buf), SER_WAIT_SEC, 0)) > 0) { /* Check answer */ /* should at least (and most) be 46 chars */ if (ret >= 46) { if (buf[0] == '(') { dev = nutscan_new_device(); dev->type = TYPE_EATON_SERIAL; dev->driver = strdup(Q1_DRIVER_NAME); dev->port = strdup(port_name); #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif dev_ret = nutscan_add_device_to_device(dev_ret, dev); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif break; } } } } } } } } /* Close the device */ ser_close(devfd, NULL); } return dev; } static void * nutscan_scan_eaton_serial_device(void * port_arg) { nutscan_device_t * dev = NULL; char* port_name = (char*) port_arg; /* Try SHUT first */ if ( (dev = nutscan_scan_eaton_serial_shut(port_name)) == NULL) { usleep(100000); /* Else, try XCP */ if ( (dev = nutscan_scan_eaton_serial_xcp(port_name)) == NULL) { /* Else, try Q1 */ usleep(100000); dev = nutscan_scan_eaton_serial_q1(port_name); } /* Else try UTalk? */ } return dev; } nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) { struct sigaction oldact; int change_action_handler = 0; char *current_port_name = NULL; char **serial_ports_list; int current_port_nb; int i; #ifdef HAVE_PTHREAD pthread_t thread; pthread_t * thread_array = NULL; int thread_count = 0; pthread_mutex_init(&dev_mutex,NULL); #endif /* 1) Get ports_list */ serial_ports_list = nutscan_get_serial_ports_list(ports_range); if( serial_ports_list == NULL ) { return NULL; } /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if( sigaction(SIGPIPE, NULL, &oldact) == 0 ) { if( oldact.sa_handler == SIG_DFL ) { change_action_handler = 1; signal(SIGPIPE,SIG_IGN); } } /* port(s) iterator */ current_port_nb = 0; while(serial_ports_list[current_port_nb] != NULL) { current_port_name = serial_ports_list[current_port_nb]; #ifdef HAVE_PTHREAD if (pthread_create(&thread, NULL, nutscan_scan_eaton_serial_device, (void*)current_port_name) == 0){ thread_count++; thread_array = realloc(thread_array, thread_count*sizeof(pthread_t)); thread_array[thread_count-1] = thread; } #else nutscan_scan_eaton_serial_device(current_port_name); #endif current_port_nb++; } #ifdef HAVE_PTHREAD for ( i = 0; i < thread_count ; i++) { pthread_join(thread_array[i],NULL); } pthread_mutex_destroy(&dev_mutex); free(thread_array); #endif if(change_action_handler) { signal(SIGPIPE,SIG_DFL); } /* free everything... */ i=0; while(serial_ports_list[i] != NULL) { free(serial_ports_list[i]); i++; } free( serial_ports_list); return nutscan_rewind_device(dev_ret); } nut-2.7.4/tools/nut-scanner/scan_ipmi.c0000644000175000017500000005101312667537407014777 00000000000000/* * Copyright (C) * 2011 - 2012 Arnaud Quette * * 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 */ /*! \file scan_ipmi.c \brief detect NUT supported Power Supply Units \author Arnaud Quette */ #include "common.h" #include "nut-scan.h" #ifdef WITH_IPMI #include "upsclient.h" #include #include #include #include #define NUT_IPMI_DRV_NAME "nut-ipmipsu" /* IPMI defines */ /* 5 seconds for establishing an IPMI connection */ #define IPMI_SESSION_TIMEOUT_LENGTH_DEFAULT 5000 #define IPMI_RETRANSMISSION_TIMEOUT_LENGTH_DEFAULT 250 /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; #ifdef HAVE_FREEIPMI_11X_12X /* Functions symbols remapping */ #define IPMI_FRU_CLOSE_DEVICE_ID "ipmi_fru_close_device_id" #define IPMI_FRU_CTX_DESTROY "ipmi_fru_ctx_destroy" #define IPMI_FRU_CTX_CREATE "ipmi_fru_ctx_create" #define IPMI_FRU_CTX_SET_FLAGS "ipmi_fru_ctx_set_flags" #define IPMI_FRU_OPEN_DEVICE_ID "ipmi_fru_open_device_id" #define IPMI_FRU_CTX_ERRORMSG "ipmi_fru_ctx_errormsg" #define IPMI_FRU_READ_DATA_AREA "ipmi_fru_read_data_area" #define IPMI_FRU_PARSE_NEXT "ipmi_fru_next" typedef ipmi_fru_ctx_t ipmi_fru_parse_ctx_t; typedef ipmi_sdr_ctx_t ipmi_sdr_cache_ctx_t; /* Functions remapping */ static void (*nut_ipmi_sdr_ctx_destroy) (ipmi_sdr_ctx_t ctx); #else /* HAVE_FREEIPMI_11X_12X */ #define IPMI_FRU_AREA_SIZE_MAX IPMI_FRU_PARSE_AREA_SIZE_MAX #define IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS #define IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION /* Functions symbols remapping */ #define IPMI_FRU_CLOSE_DEVICE_ID "ipmi_fru_parse_close_device_id" #define IPMI_FRU_CTX_DESTROY "ipmi_fru_parse_ctx_destroy" #define IPMI_FRU_CTX_CREATE "ipmi_fru_parse_ctx_create" #define IPMI_FRU_CTX_SET_FLAGS "ipmi_fru_parse_ctx_set_flags" #define IPMI_FRU_OPEN_DEVICE_ID "ipmi_fru_parse_open_device_id" #define IPMI_FRU_CTX_ERRORMSG "ipmi_fru_parse_ctx_errormsg" #define IPMI_FRU_READ_DATA_AREA "ipmi_fru_parse_read_data_area" #define IPMI_FRU_PARSE_NEXT "ipmi_fru_parse_next" /* Functions remapping */ static void (*nut_ipmi_sdr_cache_ctx_destroy) (ipmi_sdr_cache_ctx_t ctx); static void (*nut_ipmi_sdr_parse_ctx_destroy) (ipmi_sdr_parse_ctx_t ctx); #endif /* HAVE_FREEIPMI_11X_12X */ static int (*nut_ipmi_fru_close_device_id) (ipmi_fru_parse_ctx_t ctx); static void (*nut_ipmi_fru_ctx_destroy) (ipmi_fru_parse_ctx_t ctx); static ipmi_fru_parse_ctx_t (*nut_ipmi_fru_ctx_create) (ipmi_ctx_t ipmi_ctx); static int (*nut_ipmi_fru_ctx_set_flags) (ipmi_fru_parse_ctx_t ctx, unsigned int flags); static int (*nut_ipmi_fru_open_device_id) (ipmi_fru_parse_ctx_t ctx, uint8_t fru_device_id); static char * (*nut_ipmi_fru_ctx_errormsg) (ipmi_fru_parse_ctx_t ctx); static int (*nut_ipmi_fru_read_data_area) (ipmi_fru_parse_ctx_t ctx, unsigned int *area_type, unsigned int *area_length, void *areabuf, unsigned int areabuflen); static int (*nut_ipmi_fru_next) (ipmi_fru_parse_ctx_t ctx); static ipmi_ctx_t (*nut_ipmi_ctx_create) (void); static int (*nut_ipmi_ctx_find_inband) (ipmi_ctx_t ctx, ipmi_driver_type_t *driver_type, int disable_auto_probe, uint16_t driver_address, uint8_t register_spacing, const char *driver_device, unsigned int workaround_flags, unsigned int flags); static int (*nut_ipmi_ctx_open_outofband) (ipmi_ctx_t ctx, const char *hostname, const char *username, const char *password, uint8_t authentication_type, uint8_t privilege_level, unsigned int session_timeout, unsigned int retransmission_timeout, unsigned int workaround_flags, unsigned int flags); static int (*nut_ipmi_ctx_errnum) (ipmi_ctx_t ctx); static char * (*nut_ipmi_ctx_errormsg) (ipmi_ctx_t ctx); static int (*nut_ipmi_ctx_close) (ipmi_ctx_t ctx); static void (*nut_ipmi_ctx_destroy) (ipmi_ctx_t ctx); /* Internal functions */ static nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t * sec); /* Return 0 on error */ int nutscan_load_ipmi_library(const char *libname_path) { if( dl_handle != NULL ) { /* if previous init failed */ if( dl_handle == (void *)1 ) { return 0; } /* init has already been done */ return 1; } if (libname_path == NULL) { fprintf(stderr, "IPMI library not found. IPMI search disabled.\n"); return 0; } if( lt_dlinit() != 0 ) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } /* Clear any existing error */ lt_dlerror(); *(void **) (&nut_ipmi_fru_close_device_id) = lt_dlsym(dl_handle, IPMI_FRU_CLOSE_DEVICE_ID); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_fru_ctx_destroy) = lt_dlsym(dl_handle, IPMI_FRU_CTX_DESTROY); if ((dl_error = lt_dlerror()) != NULL) { goto err; } #ifdef HAVE_FREEIPMI_11X_12X *(void **) (&nut_ipmi_sdr_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_sdr_ctx_destroy"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } #else /* HAVE_FREEIPMI_11X_12X */ *(void **) (&nut_ipmi_sdr_cache_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_sdr_cache_ctx_destroy"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_sdr_parse_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_sdr_parse_ctx_destroy"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } #endif /* HAVE_FREEIPMI_11X_12X */ *(void **) (&nut_ipmi_fru_ctx_create) = lt_dlsym(dl_handle, IPMI_FRU_CTX_CREATE); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_fru_ctx_set_flags) = lt_dlsym(dl_handle, IPMI_FRU_CTX_SET_FLAGS); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_fru_open_device_id) = lt_dlsym(dl_handle, IPMI_FRU_OPEN_DEVICE_ID); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_fru_ctx_errormsg) = lt_dlsym(dl_handle, IPMI_FRU_CTX_ERRORMSG); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_fru_read_data_area) = lt_dlsym(dl_handle, IPMI_FRU_READ_DATA_AREA); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_fru_next) = lt_dlsym(dl_handle, IPMI_FRU_PARSE_NEXT); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_create) = lt_dlsym(dl_handle, "ipmi_ctx_create"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_find_inband) = lt_dlsym(dl_handle, "ipmi_ctx_find_inband"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_open_outofband) = lt_dlsym(dl_handle, "ipmi_ctx_open_outofband"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_errnum) = lt_dlsym(dl_handle, "ipmi_ctx_errnum"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_errormsg) = lt_dlsym(dl_handle, "ipmi_ctx_errormsg"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_close) = lt_dlsym(dl_handle, "ipmi_ctx_close"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_ctx_destroy"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: fprintf(stderr, "Cannot load IPMI library (%s) : %s. IPMI search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } /* end of dynamic link library stuff */ /* Cleanup IPMI contexts */ #ifdef HAVE_FREEIPMI_11X_12X static void nut_freeipmi_cleanup(ipmi_fru_parse_ctx_t fru_parse_ctx, ipmi_sdr_ctx_t sdr_ctx) #else /* HAVE_FREEIPMI_11X_12X */ static void nut_freeipmi_cleanup(ipmi_fru_parse_ctx_t fru_parse_ctx, ipmi_sdr_cache_ctx_t sdr_cache_ctx, ipmi_sdr_parse_ctx_t sdr_parse_ctx) #endif /* HAVE_FREEIPMI_11X_12X */ { if (fru_parse_ctx) { (*nut_ipmi_fru_close_device_id) (fru_parse_ctx); (*nut_ipmi_fru_ctx_destroy) (fru_parse_ctx); } #ifdef HAVE_FREEIPMI_11X_12X if (sdr_ctx) { (*nut_ipmi_sdr_ctx_destroy) (sdr_ctx); } #else /* HAVE_FREEIPMI_11X_12X */ if (sdr_cache_ctx) { (*nut_ipmi_sdr_cache_ctx_destroy) (sdr_cache_ctx); } if (sdr_parse_ctx) { (*nut_ipmi_sdr_parse_ctx_destroy) (sdr_parse_ctx); } #endif /* HAVE_FREEIPMI_11X_12X */ } /* Return 1 if supported, 0 otherwise */ int is_ipmi_device_supported(ipmi_ctx_t ipmi_ctx, int ipmi_id) { int ret = -1; unsigned int area_type = 0; unsigned int area_length = 0; uint8_t areabuf[IPMI_FRU_AREA_SIZE_MAX+1]; ipmi_fru_parse_ctx_t fru_parse_ctx = NULL; #ifdef HAVE_FREEIPMI_11X_12X ipmi_sdr_ctx_t sdr_ctx = NULL; #else /* HAVE_FREEIPMI_11X_12X */ ipmi_sdr_cache_ctx_t sdr_cache_ctx = NULL; ipmi_sdr_parse_ctx_t sdr_parse_ctx = NULL; #endif /* HAVE_FREEIPMI_11X_12X */ /* Parse FRU information */ if (!(fru_parse_ctx = (*nut_ipmi_fru_ctx_create) (ipmi_ctx))) { fprintf(stderr, "Error with %s(): %s\n", IPMI_FRU_CTX_CREATE, (*nut_ipmi_ctx_errormsg)(ipmi_ctx)); return 0; } /* lots of motherboards calculate checksums incorrectly */ if ((*nut_ipmi_fru_ctx_set_flags) (fru_parse_ctx, IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) { #ifdef HAVE_FREEIPMI_11X_12X nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); #else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); #endif /* HAVE_FREEIPMI_11X_12X */ return 0; } if ((*nut_ipmi_fru_open_device_id) (fru_parse_ctx, ipmi_id) < 0) { #ifdef HAVE_FREEIPMI_11X_12X nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); #else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); #endif /* HAVE_FREEIPMI_11X_12X */ return 0; } do { /* clear fields */ area_type = 0; area_length = 0; memset (areabuf, '\0', IPMI_FRU_AREA_SIZE_MAX + 1); /* parse FRU buffer */ if ((*nut_ipmi_fru_read_data_area) (fru_parse_ctx, &area_type, &area_length, areabuf, IPMI_FRU_AREA_SIZE_MAX) < 0) { #ifdef HAVE_FREEIPMI_11X_12X nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); #else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); #endif /* HAVE_FREEIPMI_11X_12X */ return 0; } if (area_length) { if (area_type == IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION) { /* Found a POWER_SUPPLY record */ #ifdef HAVE_FREEIPMI_11X_12X nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); #else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); #endif /* HAVE_FREEIPMI_11X_12X */ return 1; } } } while ((ret = (*nut_ipmi_fru_next) (fru_parse_ctx)) == 1); /* No need for further errors checking */ #ifdef HAVE_FREEIPMI_11X_12X nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); #else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); #endif /* HAVE_FREEIPMI_11X_12X */ return 0; } /* Check for IPMI support on a specific (local or remote) system * Return NULL on error, or a valid nutscan_device_t otherwise */ nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t * ipmi_sec) { ipmi_ctx_t ipmi_ctx = NULL; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; int ret = -1; int ipmi_id = 0; char port_id[64]; if( !nutscan_avail_ipmi ) { return NULL; } /* Initialize the FreeIPMI library. */ if (!(ipmi_ctx = (*nut_ipmi_ctx_create) ())) { /* we have to force cleanup, since exit handler is not yet installed */ fprintf(stderr, "ipmi_ctx_create\n"); return NULL; } /* Are we scanning locally, or over the network? */ if (IPaddr == NULL) { /* FIXME: we need root right to access local IPMI! if (!ipmi_is_root ()) { fprintf(stderr, "IPMI scan: %s\n", ipmi_ctx_strerror (IPMI_ERR_PERMISSION)); } */ if ((ret = (*nut_ipmi_ctx_find_inband) (ipmi_ctx, NULL, 0, /* don't disable auto-probe */ 0, 0, NULL, 0, /* workaround flags, none by default */ 0 /* flags */ )) < 0) { fprintf(stderr, "ipmi_ctx_find_inband: %s\n", (*nut_ipmi_ctx_errormsg) (ipmi_ctx)); return NULL; } if (!ret) { /* No local IPMI device detected */ return NULL; } } else { #if 0 if (ipmi_sec->ipmi_version == IPMI_2_0) { /* FIXME: need processing?! * int parse_kg (void *out, unsigned int outlen, const char *in) * if ((rv = parse_kg (common_cmd_args_config->k_g, IPMI_MAX_K_G_LENGTH + 1, data->string)) < 0) * { * fprintf (stderr, "Config File Error: k_g input formatted incorrectly\n"); * exit (EXIT_FAILURE); * }*/ if ((ret = (*nut_ipmi_ctx_open_outofband_2_0) (ipmi_ctx, IPaddr, ipmi_sec->username, ipmi_sec->password, ipmi_sec->K_g_BMC_key, ??? (ipmi_sec->K_g_BMC_key) ? config->k_g_len : 0, ipmi_sec->privilege_level, ipmi_sec->cipher_suite_id, IPMI_SESSION_TIMEOUT_LENGTH_DEFAULT, IPMI_RETRANSMISSION_TIMEOUT_LENGTH_DEFAULT, ipmi_dev->workaround_flags, flags) < 0) { IPMI_MONITORING_DEBUG (("ipmi_ctx_open_outofband_2_0: %s", ipmi_ctx_errormsg (c->ipmi_ctx))); if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_USERNAME_INVALID) c->errnum = IPMI_MONITORING_ERR_USERNAME_INVALID; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PASSWORD_INVALID) c->errnum = IPMI_MONITORING_ERR_PASSWORD_INVALID; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_INSUFFICIENT) c->errnum = IPMI_MONITORING_ERR_PRIVILEGE_LEVEL_INSUFFICIENT; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_CANNOT_BE_OBTAINED) c->errnum = IPMI_MONITORING_ERR_PRIVILEGEL_LEVEL_CANNOT_BE_OBTAINED; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_K_G_INVALID) c->errnum = IPMI_MONITORING_ERR_K_G_INVALID; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_CIPHER_SUITE_ID_UNAVAILABLE) c->errnum = IPMI_MONITORING_ERR_CIPHER_SUITE_ID_UNAVAILABLE; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PASSWORD_VERIFICATION_TIMEOUT) c->errnum = IPMI_MONITORING_ERR_PASSWORD_VERIFICATION_TIMEOUT; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_IPMI_2_0_UNAVAILABLE) c->errnum = IPMI_MONITORING_ERR_IPMI_2_0_UNAVAILABLE; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_CONNECTION_TIMEOUT) c->errnum = IPMI_MONITORING_ERR_CONNECTION_TIMEOUT; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_SESSION_TIMEOUT) c->errnum = IPMI_MONITORING_ERR_SESSION_TIMEOUT; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE || ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_IPMI_ERROR) c->errnum = IPMI_MONITORING_ERR_IPMI_ERROR; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_BMC_BUSY) c->errnum = IPMI_MONITORING_ERR_BMC_BUSY; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_OUT_OF_MEMORY) c->errnum = IPMI_MONITORING_ERR_OUT_OF_MEMORY; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_HOSTNAME_INVALID) c->errnum = IPMI_MONITORING_ERR_HOSTNAME_INVALID; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PARAMETERS) c->errnum = IPMI_MONITORING_ERR_PARAMETERS; else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_SYSTEM_ERROR) c->errnum = IPMI_MONITORING_ERR_SYSTEM_ERROR; else c->errnum = IPMI_MONITORING_ERR_INTERNAL_ERROR; return (-1); } } else { /* Not IPMI 2.0 */ #endif /* 0 */ /* Fall back to IPMI 1.5 */ if ((ret = (*nut_ipmi_ctx_open_outofband) (ipmi_ctx, IPaddr, ipmi_sec->username, ipmi_sec->password, ipmi_sec->authentication_type, ipmi_sec->privilege_level, IPMI_SESSION_TIMEOUT_LENGTH_DEFAULT, IPMI_RETRANSMISSION_TIMEOUT_LENGTH_DEFAULT, ipmi_sec->workaround_flags, IPMI_FLAGS_DEFAULT )) < 0) { /* No IPMI device detected on this host! if ((*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_USERNAME_INVALID || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PASSWORD_INVALID || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_INSUFFICIENT || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_CANNOT_BE_OBTAINED || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_AUTHENTICATION_TYPE_UNAVAILABLE || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PASSWORD_VERIFICATION_TIMEOUT || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_HOSTNAME_INVALID || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_CONNECTION_TIMEOUT) { */ /* FIXME: don't log timeout errors */ fprintf(stderr, "nut_ipmi_ctx_open_outofband: %s\n", (*nut_ipmi_ctx_errormsg) (ipmi_ctx)); return NULL; /*}*/ } } /* Loop through all possible components */ for (ipmi_id = 0 ; ipmi_id <= IPMI_FRU_DEVICE_ID_MAX ; ipmi_id++) { if (is_ipmi_device_supported(ipmi_ctx, ipmi_id)) { if ( (nut_dev = nutscan_new_device()) == NULL ) { fprintf(stderr,"Memory allocation error\n"); nutscan_free_device(current_nut_dev); break; } /* Fill the device structure (sufficient with driver and port) */ nut_dev->type = TYPE_IPMI; nut_dev->driver = strdup(NUT_IPMI_DRV_NAME); if (IPaddr == NULL) { sprintf(port_id, "id%x", ipmi_id); } else { /* FIXME: also check against "localhost" and its IPv{4,6} */ sprintf(port_id, "id%x@%s", ipmi_id, IPaddr); } nut_dev->port = strdup(port_id); /* FIXME: also dump device.serial? * using drivers/libfreeipmi_get_board_info() */ current_nut_dev = nutscan_add_device_to_device( current_nut_dev, nut_dev); memset (port_id, 0, sizeof(port_id)); } } /* Final cleanup */ if (ipmi_ctx) { (*nut_ipmi_ctx_close) (ipmi_ctx); (*nut_ipmi_ctx_destroy) (ipmi_ctx); } return current_nut_dev; } /* General IPMI scan entry point: scan 1 to n devices, local or remote, * for IPMI support * Return NULL on error, or a valid nutscan_device_t otherwise */ nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip, nutscan_ipmi_t * sec) { nutscan_ip_iter_t ip; char * ip_str = NULL; nutscan_ipmi_t * tmp_sec; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; if( !nutscan_avail_ipmi ) { return NULL; } /* Are we scanning locally, or through the network? */ if (start_ip == NULL) { /* Local PSU scan */ current_nut_dev = nutscan_scan_ipmi_device(NULL, NULL); } else { ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); while(ip_str != NULL) { tmp_sec = malloc(sizeof(nutscan_ipmi_t)); memcpy(tmp_sec, sec, sizeof(nutscan_ipmi_t)); if ((current_nut_dev = nutscan_scan_ipmi_device(ip_str, tmp_sec)) != NULL) { /* Store the positive result */ current_nut_dev = nutscan_add_device_to_device(current_nut_dev, nut_dev); } /* Prepare the next iteration */ ip_str = nutscan_ip_iter_inc(&ip); }; } return nutscan_rewind_device(current_nut_dev); } #else /* WITH_IPMI */ /* stub function */ nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec) { return NULL; } #endif /* WITH_IPMI */ nut-2.7.4/tools/nut-scanner/nutscan-ip.h0000644000175000017500000000307712640473702015116 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-ip.h \brief iterator for IPv4 or IPv6 addresses \author Frederic Bohe */ #ifndef SCAN_IP #define SCAN_IP #include #include #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif enum network_type { IPv4, IPv6 }; typedef struct nutscan_ip_iter { enum network_type type; struct in_addr start; struct in_addr stop; struct in6_addr start6; struct in6_addr stop6; } nutscan_ip_iter_t; char * nutscan_ip_iter_init(nutscan_ip_iter_t *, const char * startIP, const char * stopIP); char * nutscan_ip_iter_inc(nutscan_ip_iter_t *); int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif nut-2.7.4/tools/nut-scanner/nut-scanner.c0000644000175000017500000004200612667537407015274 00000000000000/* * Copyright (C) 2011 - 2012 Arnaud Quette * * 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 */ /*! \file nut-scanner.c \brief a tool to detect NUT supported devices \author Arnaud Quette */ #include #include #include #include "common.h" #include "nut_version.h" #include #include #ifdef HAVE_PTHREAD #include #endif #include "nut-scan.h" #define DEFAULT_TIMEOUT 5 #define ERR_BAD_OPTION (-1) const char optstring[] = "?ht:s:e:E:c:l:u:W:X:w:x:p:b:B:d:D:CUSMOAm:NPqIVa"; #ifdef HAVE_GETOPT_LONG const struct option longopts[] = {{ "timeout",required_argument,NULL,'t' }, { "start_ip",required_argument,NULL,'s' }, { "end_ip",required_argument,NULL,'e' }, { "eaton_serial",required_argument,NULL,'E' }, { "mask_cidr",required_argument,NULL,'m' }, { "community",required_argument,NULL,'c' }, { "secLevel",required_argument,NULL,'l' }, { "secName",required_argument,NULL,'u' }, { "authPassword",required_argument,NULL,'W' }, { "privPassword",required_argument,NULL,'X' }, { "authProtocol",required_argument,NULL,'w' }, { "privProtocol",required_argument,NULL,'x' }, { "username",required_argument,NULL,'b' }, { "password",required_argument,NULL,'B' }, { "authType",required_argument,NULL,'d' }, { "cipher_suite_id",required_argument,NULL,'D' }, { "port",required_argument,NULL,'p' }, { "complete_scan",no_argument,NULL,'C' }, { "usb_scan",no_argument,NULL,'U' }, { "snmp_scan",no_argument,NULL,'S' }, { "xml_scan",no_argument,NULL,'M' }, { "oldnut_scan",no_argument,NULL,'O' }, { "avahi_scan",no_argument,NULL,'A' }, { "ipmi_scan",no_argument,NULL,'I' }, { "disp_nut_conf",no_argument,NULL,'N' }, { "disp_parsable",no_argument,NULL,'P' }, { "quiet",no_argument,NULL,'q' }, { "help",no_argument,NULL,'h' }, { "version",no_argument,NULL,'V' }, { "available",no_argument,NULL,'a' }, {NULL,0,NULL,0}}; #else #define getopt_long(a,b,c,d,e) getopt(a,b,c) #endif /* HAVE_GETOPT_LONG */ static nutscan_device_t *dev[TYPE_END]; static long timeout = DEFAULT_TIMEOUT*1000*1000; /* in usec */ static char * start_ip = NULL; static char * end_ip = NULL; static char * port = NULL; static char * serial_ports = NULL; #ifdef HAVE_PTHREAD static pthread_t thread[TYPE_END]; static void * run_usb(void * arg) { dev[TYPE_USB] = nutscan_scan_usb(); return NULL; } static void * run_snmp(void * arg) { nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip,end_ip,timeout,sec); return NULL; } static void * run_xml(void * arg) { dev[TYPE_XML] = nutscan_scan_xml_http(timeout); return NULL; } static void * run_nut_old(void * arg) { dev[TYPE_NUT] = nutscan_scan_nut(start_ip,end_ip,port,timeout); return NULL; } static void * run_avahi(void * arg) { dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout); return NULL; } static void * run_ipmi(void * arg) { nutscan_ipmi_t * sec = (nutscan_ipmi_t *)arg; dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip,end_ip,sec); return NULL; } static void * run_eaton_serial(void * arg) { dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial (serial_ports); return NULL; } #endif /* HAVE_PTHREAD */ int printq(int quiet,const char *fmt, ...) { va_list ap; int ret; if(quiet) { return 0; } va_start(ap, fmt); ret = vprintf(fmt, ap); va_end(ap); return ret; } int main(int argc, char *argv[]) { nutscan_snmp_t snmp_sec; nutscan_ipmi_t ipmi_sec; int opt_ret; char * cidr = NULL; int allow_all = 0; int allow_usb = 0; int allow_snmp = 0; int allow_xml = 0; int allow_oldnut = 0; int allow_avahi = 0; int allow_ipmi = 0; int allow_eaton_serial = 0; /* MUST be requested explicitely! */ int quiet = 0; void (*display_func)(nutscan_device_t * device); int ret_code = EXIT_SUCCESS; memset(&snmp_sec, 0, sizeof(snmp_sec)); memset(&ipmi_sec, 0, sizeof(ipmi_sec)); /* Set the default values for IPMI */ ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD5; ipmi_sec.ipmi_version = IPMI_1_5; /* default to IPMI 1.5, if not otherwise specified */ ipmi_sec.cipher_suite_id = 3; /* default to HMAC-SHA1; HMAC-SHA1-96; AES-CBC-128 */ ipmi_sec.privilege_level = IPMI_PRIVILEGE_LEVEL_ADMIN; /* should be sufficient */ nutscan_init(); display_func = nutscan_display_ups_conf; while((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL))!=-1) { switch(opt_ret) { case 't': timeout = atol(optarg)*1000*1000; /*in usec*/ if( timeout == 0 ) { fprintf(stderr,"Illegal timeout value, using default %ds\n", DEFAULT_TIMEOUT); timeout = DEFAULT_TIMEOUT*1000*1000; } break; case 's': start_ip = strdup(optarg); end_ip = start_ip; break; case 'e': end_ip = strdup(optarg); break; case 'E': serial_ports = strdup(optarg); allow_eaton_serial = 1; break; case 'm': cidr = strdup(optarg); break; case 'c': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.community = strdup(optarg); break; case 'l': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.secLevel = strdup(optarg); break; case 'u': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.secName = strdup(optarg); break; case 'W': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.authPassword = strdup(optarg); break; case 'X': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.privPassword = strdup(optarg); break; case 'w': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.authProtocol = strdup(optarg); break; case 'x': if(!nutscan_avail_snmp) { goto display_help; } snmp_sec.privProtocol = strdup(optarg); break; case 'S': if(!nutscan_avail_snmp) { goto display_help; } allow_snmp = 1; break; case 'b': if(!nutscan_avail_ipmi) { goto display_help; } ipmi_sec.username = strdup(optarg); break; case 'B': if(!nutscan_avail_ipmi) { goto display_help; } ipmi_sec.password = strdup(optarg); break; case 'd': if(!nutscan_avail_ipmi) { goto display_help; } if (!strcmp(optarg, "NONE")) { ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_NONE; } else if (!strcmp(optarg, "STRAIGHT_PASSWORD_KEY")) { ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY; } else if (!strcmp(optarg, "MD2")) { ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD2; } else if (!strcmp(optarg, "MD5")) { ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD5; } else { fprintf(stderr,"Unknown authentication type (%s). Defaulting to MD5\n", optarg); } break; case 'D': if(!nutscan_avail_ipmi) { goto display_help; } ipmi_sec.cipher_suite_id = atoi(optarg); /* Force IPMI 2.0! */ ipmi_sec.ipmi_version = IPMI_2_0; break; case 'p': port = strdup(optarg); break; case 'C': allow_all = 1; break; case 'U': if(!nutscan_avail_usb) { goto display_help; } allow_usb = 1; break; case 'M': if(!nutscan_avail_xml_http) { goto display_help; } allow_xml = 1; break; case 'O': allow_oldnut = 1; break; case 'A': if(!nutscan_avail_avahi) { goto display_help; } allow_avahi = 1; break; case 'I': if(!nutscan_avail_ipmi) { goto display_help; } allow_ipmi = 1; break; case 'N': display_func = nutscan_display_ups_conf; break; case 'P': display_func = nutscan_display_parsable; break; case 'q': quiet = 1; break; case 'V': printf("Network UPS Tools - %s\n", NUT_VERSION_MACRO); exit(EXIT_SUCCESS); case 'a': printf("OLDNUT\n"); if(nutscan_avail_usb) { printf("USB\n"); } if(nutscan_avail_snmp) { printf("SNMP\n"); } if(nutscan_avail_xml_http) { printf("XML\n"); } if(nutscan_avail_avahi) { printf("AVAHI\n"); } if(nutscan_avail_ipmi) { printf("IPMI\n"); } printf("EATON_SERIAL\n"); exit(EXIT_SUCCESS); case '?': ret_code = ERR_BAD_OPTION; case 'h': default: display_help: puts("nut-scanner : detecting available power devices.\n"); puts("OPTIONS:"); printf(" -C, --complete_scan: Scan all available devices (default).\n"); if( nutscan_avail_usb ) { printf(" -U, --usb_scan: Scan USB devices.\n"); } if( nutscan_avail_snmp ) { printf(" -S, --snmp_scan: Scan SNMP devices.\n"); } if( nutscan_avail_xml_http ) { printf(" -M, --xml_scan: Scan XML/HTTP devices.\n"); } printf(" -O, --oldnut_scan: Scan NUT devices (old method).\n"); if( nutscan_avail_avahi ) { printf(" -A, --avahi_scan: Scan NUT devices (avahi method).\n"); } if( nutscan_avail_ipmi ) { printf(" -I, --ipmi_scan: Scan IPMI devices.\n"); } printf(" -E, --eaton_serial : Scan serial Eaton devices (XCP, SHUT and Q1).\n"); printf("\nNetwork specific options:\n"); printf(" -t, --timeout : network operation timeout (default %d).\n",DEFAULT_TIMEOUT); printf(" -s, --start_ip : First IP address to scan.\n"); printf(" -e, --end_ip : Last IP address to scan.\n"); printf(" -m, --mask_cidr : Give a range of IP using CIDR notation.\n"); if( nutscan_avail_snmp ) { printf("\nSNMP v1 specific options:\n"); printf(" -c, --community : Set SNMP v1 community name (default = public)\n"); printf("\nSNMP v3 specific options:\n"); printf(" -l, --secLevel : Set the securityLevel used for SNMPv3 messages (allowed values: noAuthNoPriv,authNoPriv,authPriv)\n"); printf(" -u, --secName : Set the securityName used for authenticated SNMPv3 messages (mandatory if you set secLevel. No default)\n"); printf(" -w, --authProtocol : Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5)\n"); printf(" -W, --authPassword : Set the authentication pass phrase used for authenticated SNMPv3 messages (mandatory if you set secLevel to authNoPriv or authPriv)\n"); printf(" -x, --privProtocol : Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES)\n"); printf(" -X, --privPassword : Set the privacy pass phrase used for encrypted SNMPv3 messages (mandatory if you set secLevel to authPriv)\n"); } if( nutscan_avail_ipmi ) { printf("\nIPMI over LAN specific options:\n"); printf(" -b, --username : Set the username used for authenticating IPMI over LAN connections (mandatory for IPMI over LAN. No default)\n"); /* Specify the username to use when authenticating with the remote host. If not specified, a null (i.e. anonymous) username is assumed. The user must have * at least ADMIN privileges in order for this tool to operate fully. */ printf(" -B, --password : Specify the password to use when authenticationg with the remote host (mandatory for IPMI over LAN. No default)\n"); /* Specify the password to use when authenticationg with the remote host. If not specified, a null password is assumed. Maximum password length is 16 for IPMI * 1.5 and 20 for IPMI 2.0. */ printf(" -d, --authType : Specify the IPMI 1.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, MD2, and MD5) with the remote host (default=MD5)\n"); printf(" -D, --cipher_suite_id : Specify the IPMI 2.0 cipher suite ID to use, for authentication, integrity, and confidentiality (default=3)\n"); } printf("\nNUT specific options:\n"); printf(" -p, --port : Port number of remote NUT upsd\n"); printf("\ndisplay specific options:\n"); printf(" -N, --disp_nut_conf: Display result in the ups.conf format\n"); printf(" -P, --disp_parsable: Display result in a parsable format\n"); printf("\nMiscellaneous options:\n"); printf(" -V, --version: Display NUT version\n"); printf(" -a, --available: Display available bus that can be scanned\n"); printf(" -q, --quiet: Display only scan result. No information on currently scanned bus is displayed.\n"); return ret_code; } } if( cidr ) { nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); } if( !allow_usb && !allow_snmp && !allow_xml && !allow_oldnut && !allow_avahi && !allow_ipmi && !allow_eaton_serial) { allow_all = 1; } if( allow_all ) { allow_usb = 1; allow_snmp = 1; allow_xml = 1; allow_oldnut = 1; allow_avahi = 1; allow_ipmi = 1; /* BEWARE: allow_all does not include allow_eaton_serial! */ } if( allow_usb && nutscan_avail_usb ) { printq(quiet,"Scanning USB bus.\n"); #ifdef HAVE_PTHREAD if(pthread_create(&thread[TYPE_USB],NULL,run_usb,NULL)) { nutscan_avail_usb = 0; } #else dev[TYPE_USB] = nutscan_scan_usb(); #endif /* HAVE_PTHREAD */ } if( allow_snmp && nutscan_avail_snmp ) { if( start_ip == NULL ) { printq(quiet,"No start IP, skipping SNMP\n"); nutscan_avail_snmp = 0; } else { printq(quiet,"Scanning SNMP bus.\n"); #ifdef HAVE_PTHREAD if( pthread_create(&thread[TYPE_SNMP],NULL,run_snmp,&snmp_sec)) { nutscan_avail_snmp = 0; } #else dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip,end_ip,timeout,&snmp_sec); #endif /* HAVE_PTHREAD */ } } if( allow_xml && nutscan_avail_xml_http) { printq(quiet,"Scanning XML/HTTP bus.\n"); #ifdef HAVE_PTHREAD if(pthread_create(&thread[TYPE_XML],NULL,run_xml,NULL)) { nutscan_avail_xml_http = 0; } #else dev[TYPE_XML] = nutscan_scan_xml_http(timeout); #endif /* HAVE_PTHREAD */ } if( allow_oldnut && nutscan_avail_nut) { if( start_ip == NULL ) { printq(quiet,"No start IP, skipping NUT bus (old connect method)\n"); nutscan_avail_nut = 0; } else { printq(quiet,"Scanning NUT bus (old connect method).\n"); #ifdef HAVE_PTHREAD if(pthread_create(&thread[TYPE_NUT],NULL,run_nut_old,NULL)) { nutscan_avail_nut = 0; } #else dev[TYPE_NUT] = nutscan_scan_nut(start_ip,end_ip,port,timeout); #endif /* HAVE_PTHREAD */ } } if( allow_avahi && nutscan_avail_avahi) { printq(quiet,"Scanning NUT bus (avahi method).\n"); #ifdef HAVE_PTHREAD if(pthread_create(&thread[TYPE_AVAHI],NULL,run_avahi,NULL)) { nutscan_avail_avahi = 0; } #else dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout); #endif /* HAVE_PTHREAD */ } if( allow_ipmi && nutscan_avail_ipmi) { printq(quiet,"Scanning IPMI bus.\n"); #ifdef HAVE_PTHREAD if(pthread_create(&thread[TYPE_IPMI],NULL,run_ipmi,&ipmi_sec)) { nutscan_avail_ipmi = 0; } #else dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip,end_ip,&ipmi_sec); #endif /* HAVE_PTHREAD */ } /* Eaton serial scan */ if (allow_eaton_serial) { printq(quiet,"Scanning serial bus for Eaton devices.\n"); #ifdef HAVE_PTHREAD pthread_create(&thread[TYPE_EATON_SERIAL], NULL, run_eaton_serial, serial_ports); /* FIXME: check return code */ #else dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial (serial_ports); #endif /* HAVE_PTHREAD */ } #ifdef HAVE_PTHREAD if( allow_usb && nutscan_avail_usb && thread[TYPE_USB]) { pthread_join(thread[TYPE_USB], NULL); } if( allow_snmp && nutscan_avail_snmp && thread[TYPE_SNMP]) { pthread_join(thread[TYPE_SNMP], NULL); } if( allow_xml && nutscan_avail_xml_http && thread[TYPE_XML]) { pthread_join(thread[TYPE_XML], NULL); } if( allow_oldnut && nutscan_avail_nut && thread[TYPE_NUT]) { pthread_join(thread[TYPE_NUT], NULL); } if( allow_avahi && nutscan_avail_avahi && thread[TYPE_AVAHI]) { pthread_join(thread[TYPE_AVAHI], NULL); } if( allow_ipmi && nutscan_avail_ipmi && thread[TYPE_IPMI]) { pthread_join(thread[TYPE_IPMI], NULL); } if (allow_eaton_serial && thread[TYPE_EATON_SERIAL]) { pthread_join(thread[TYPE_EATON_SERIAL], NULL); } #endif /* HAVE_PTHREAD */ display_func(dev[TYPE_USB]); nutscan_free_device(dev[TYPE_USB]); display_func(dev[TYPE_SNMP]); nutscan_free_device(dev[TYPE_SNMP]); display_func(dev[TYPE_XML]); nutscan_free_device(dev[TYPE_XML]); display_func(dev[TYPE_NUT]); nutscan_free_device(dev[TYPE_NUT]); display_func(dev[TYPE_AVAHI]); nutscan_free_device(dev[TYPE_AVAHI]); display_func(dev[TYPE_IPMI]); nutscan_free_device(dev[TYPE_IPMI]); display_func(dev[TYPE_EATON_SERIAL]); nutscan_free_device(dev[TYPE_EATON_SERIAL]); nutscan_free(); return EXIT_SUCCESS; } nut-2.7.4/tools/nut-scanner/nutscan-display.c0000644000175000017500000000515412640473702016144 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-display.c \brief format and display scanned devices \author Frederic Bohe */ #include "common.h" #include #include "nutscan-device.h" char * nutscan_device_type_string[TYPE_END]= { "NONE", "USB", "SNMP", "XML", "NUT", "IPMI", "AVAHI", "EATON_SERIAL" }; void nutscan_display_ups_conf(nutscan_device_t * device) { nutscan_device_t * current_dev = device; nutscan_options_t * opt; static int nutdev_num = 1; if(device==NULL) { return; } /* Find start of the list */ while(current_dev->prev != NULL) { current_dev = current_dev->prev; } /* Display each devices */ do { printf("[nutdev%i]\n\tdriver = \"%s\"\n\tport = \"%s\"\n", nutdev_num, current_dev->driver, current_dev->port); opt = current_dev->opt; while (NULL != opt) { if( opt->option != NULL ) { printf("\t%s",opt->option); if( opt->value != NULL ) { printf(" = \"%s\"", opt->value); } printf("\n"); } opt = opt->next; } nutdev_num++; current_dev = current_dev->next; } while( current_dev != NULL ); } void nutscan_display_parsable(nutscan_device_t * device) { nutscan_device_t * current_dev = device; nutscan_options_t * opt; if(device==NULL) { return; } /* Find start of the list */ while(current_dev->prev != NULL) { current_dev = current_dev->prev; } /* Display each devices */ do { printf("%s:driver=\"%s\",port=\"%s\"", nutscan_device_type_string[current_dev->type], current_dev->driver, current_dev->port); opt = current_dev->opt; while (NULL != opt) { if( opt->option != NULL ) { printf(",%s",opt->option); if( opt->value != NULL ) { printf("=\"%s\"", opt->value); } } opt = opt->next; } printf("\n"); current_dev = current_dev->next; } while( current_dev != NULL ); } nut-2.7.4/tools/nut-scanner/scan_nut.c0000644000175000017500000001605312667537407014654 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file scan_nut.c \brief detect remote NUT services \author Frederic Bohe */ #include "common.h" #include "upsclient.h" #include "nut-scan.h" #ifdef HAVE_PTHREAD #include #endif #include /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; static int (*nut_upscli_splitaddr)(const char *buf,char **hostname, int *port); static int (*nut_upscli_tryconnect)(UPSCONN_t *ups, const char *host, int port, int flags,struct timeval * timeout); static int (*nut_upscli_list_start)(UPSCONN_t *ups, unsigned int numq, const char **query); static int (*nut_upscli_list_next)(UPSCONN_t *ups, unsigned int numq, const char **query,unsigned int *numa, char ***answer); static int (*nut_upscli_disconnect)(UPSCONN_t *ups); static nutscan_device_t * dev_ret = NULL; #ifdef HAVE_PTHREAD static pthread_mutex_t dev_mutex; #endif struct scan_nut_arg { char * hostname; long timeout; }; /* return 0 on error */ int nutscan_load_upsclient_library(const char *libname_path) { if( dl_handle != NULL ) { /* if previous init failed */ if( dl_handle == (void *)1 ) { return 0; } /* init has already been done */ return 1; } if (libname_path == NULL) { fprintf(stderr, "NUT client library not found. NUT search disabled.\n"); return 0; } if( lt_dlinit() != 0 ) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_upscli_splitaddr) = lt_dlsym(dl_handle, "upscli_splitaddr"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_upscli_tryconnect) = lt_dlsym(dl_handle, "upscli_tryconnect"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_upscli_list_start) = lt_dlsym(dl_handle, "upscli_list_start"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_upscli_list_next) = lt_dlsym(dl_handle, "upscli_list_next"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_upscli_disconnect) = lt_dlsym(dl_handle, "upscli_disconnect"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: fprintf(stderr, "Cannot load NUT library (%s) : %s. NUT search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } /* FIXME: SSL support */ static void * list_nut_devices(void * arg) { struct scan_nut_arg * nut_arg = (struct scan_nut_arg*)arg; char *target_hostname = nut_arg->hostname; struct timeval tv; int port; unsigned int numq, numa; const char *query[4]; char **answer; char *hostname = NULL; UPSCONN_t *ups = malloc(sizeof(*ups)); nutscan_device_t * dev = NULL; int buf_size; tv.tv_sec = nut_arg->timeout / (1000*1000); tv.tv_usec = nut_arg->timeout % (1000*1000); query[0] = "UPS"; numq = 1; if ((*nut_upscli_splitaddr)(target_hostname, &hostname, &port) != 0) { free(target_hostname); free(nut_arg); free(ups); return NULL; } if ((*nut_upscli_tryconnect)(ups, hostname, port,UPSCLI_CONN_TRYSSL,&tv) < 0) { free(target_hostname); free(nut_arg); free(ups); return NULL; } if((*nut_upscli_list_start)(ups, numq, query) < 0) { (*nut_upscli_disconnect)(ups); free(target_hostname); free(nut_arg); free(ups); return NULL; } while ((*nut_upscli_list_next)(ups,numq, query, &numa, &answer) == 1) { /* UPS */ if (numa < 3) { (*nut_upscli_disconnect)(ups); free(target_hostname); free(nut_arg); free(ups); return NULL; } /* FIXME: check for duplication by getting driver.port and device.serial * for comparison with other busses results */ /* FIXME: * - also print answer[2] if != "Unavailable"? * - for upsmon.conf or ups.conf (using dummy-ups)? */ if (numa >= 3) { dev = nutscan_new_device(); dev->type = TYPE_NUT; dev->driver = strdup("nutclient"); /* +1+1 is for '@' character and terminating 0 */ buf_size = strlen(answer[1])+strlen(hostname)+1+1; dev->port = malloc(buf_size); if( dev->port ) { snprintf(dev->port,buf_size,"%s@%s",answer[1], hostname); #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif dev_ret = nutscan_add_device_to_device(dev_ret,dev); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif } } } (*nut_upscli_disconnect)(ups); free(target_hostname); free(nut_arg); free(ups); return NULL; } nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, const char* port,long usec_timeout) { nutscan_ip_iter_t ip; char * ip_str = NULL; char * ip_dest = NULL; char buf[SMALLBUF]; struct sigaction oldact; int change_action_handler = 0; int i; struct scan_nut_arg *nut_arg; #ifdef HAVE_PTHREAD pthread_t thread; pthread_t * thread_array = NULL; int thread_count = 0; pthread_mutex_init(&dev_mutex,NULL); #endif if( !nutscan_avail_nut ) { return NULL; } /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if( sigaction(SIGPIPE, NULL, &oldact) == 0 ) { if( oldact.sa_handler == SIG_DFL ) { change_action_handler = 1; signal(SIGPIPE,SIG_IGN); } } ip_str = nutscan_ip_iter_init(&ip,startIP,stopIP); while( ip_str != NULL ) { if( port ) { if( ip.type == IPv4 ) { snprintf(buf,sizeof(buf),"%s:%s",ip_str,port); } else { snprintf(buf,sizeof(buf),"[%s]:%s",ip_str,port); } ip_dest = strdup(buf); } else { ip_dest = strdup(ip_str); } if((nut_arg = malloc(sizeof(struct scan_nut_arg))) == NULL ) { free(ip_dest); break; } nut_arg->timeout = usec_timeout; nut_arg->hostname = ip_dest; #ifdef HAVE_PTHREAD if (pthread_create(&thread,NULL,list_nut_devices,(void*)nut_arg)==0){ thread_count++; thread_array = realloc(thread_array, thread_count*sizeof(pthread_t)); thread_array[thread_count-1] = thread; } #else list_nut_devices(nut_arg); #endif free(ip_str); ip_str = nutscan_ip_iter_inc(&ip); } #ifdef HAVE_PTHREAD for ( i=0; i < thread_count ; i++) { pthread_join(thread_array[i],NULL); } pthread_mutex_destroy(&dev_mutex); free(thread_array); #endif if(change_action_handler) { signal(SIGPIPE,SIG_DFL); } return nutscan_rewind_device(dev_ret); } nut-2.7.4/tools/nut-scanner/nutscan-snmp.h0000644000175000017500000000505412670015111015445 00000000000000/* nutscan-snmp * Copyright (C) 2011 - Frederic Bohe * Copyright (C) 2016 - Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DEVSCAN_SNMP_H #define DEVSCAN_SNMP_H typedef struct { char * oid; char * mib; char * sysoid; } snmp_device_id_t; /* SNMP IDs device table */ static snmp_device_id_t snmp_device_table[] = { { "", "apc_ats", ".1.3.6.1.4.1.318.1.3.11"}, { ".1.3.6.1.4.1.534.10.2.1.2.0", "eaton_ats", ".1.3.6.1.4.1.705.1"}, { ".1.3.6.1.4.1.13742.1.1.12.0", "raritan", ".1.3.6.1.4.1.13742"}, { "", "xppc", ".1.3.6.1.4.1.935"}, { "1.3.6.1.4.1.534.1.1.2.0", "pw", ".1.3.6.1.4.1.534.1"}, { "1.3.6.1.4.1.534.1.1.2.0", "pxgx_ups", ".1.3.6.1.4.1.534.2.12"}, { "1.3.6.1.2.1.33.1.1.1.0", "ietf", ".1.3.6.1.2.1.33"}, { "", "ietf", ".1.3.6.1.4.1.850.1"}, { ".1.3.6.1.4.1.232.165.3.1.1.0", "cpqpower", ".1.3.6.1.4.1.232.165.3"}, { ".1.3.6.1.4.1.17373.3.1.1.0", "aphel_genesisII", ".1.3.6.1.4.1.17373"}, { ".1.3.6.1.4.1.534.6.6.6.1.1.12.0", "aphel_revelation", ".1.3.6.1.4.1.534.6.6.6"}, { ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0", "eaton_epdu", ".1.3.6.1.4.1.534.6.6.7"}, { ".1.3.6.1.4.1.20677.1", "pulizzi_switched1", ".1.3.6.1.4.1.20677.1"}, { ".1.3.6.1.4.1.20677.1", "pulizzi_switched2", ".1.3.6.1.4.1.20677.2"}, { ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0", "cyberpower", ".1.3.6.1.4.1.3808"}, { ".1.3.6.1.4.1.705.1.1.1.0", "mge", ".1.3.6.1.4.1.705.1"}, { "", "delta_ups", ".1.3.6.1.4.1.2254.2.4"}, { "", "huawei", ".1.3.6.1.4.1.8072.3.2.10"}, { ".1.3.6.1.4.1.4555.1.1.1.1.1.1.0", "netvision", ".1.3.6.1.4.1.4555.1.1.1"}, { ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", "apcc", NULL}, { ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", "baytech", NULL}, { ".1.3.6.1.4.1.2947.1.1.2.0", "bestpower", NULL}, /* Terminating entry */ { NULL, NULL, NULL} }; #endif /* DEVSCAN_SNMP_H */ nut-2.7.4/tools/nut-scanner/scan_xml_http.c0000644000175000017500000001500112667537407015675 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file scan_xml_http.c \brief detect NUT supported XML HTTP devices \author Frederic Bohe */ #include "common.h" #include "nut-scan.h" #ifdef WITH_NEON #include #include #include #include #include #include #include #include #include #include #include /* dynamic link library stuff */ static char * libname = "libneon"; static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; static void (*nut_ne_xml_push_handler)(ne_xml_parser *p, ne_xml_startelm_cb *startelm, ne_xml_cdata_cb *cdata, ne_xml_endelm_cb *endelm, void *userdata); static void (*nut_ne_xml_destroy)(ne_xml_parser *p); static ne_xml_parser * (*nut_ne_xml_create)(void); static int (*nut_ne_xml_parse)(ne_xml_parser *p, const char *block, size_t len); /* return 0 on error */ int nutscan_load_neon_library(const char *libname_path) { if( dl_handle != NULL ) { /* if previous init failed */ if( dl_handle == (void *)1 ) { return 0; } /* init has already been done */ return 1; } if (libname_path == NULL) { fprintf(stderr, "Neon library not found. XML search disabled.\n"); return 0; } if( lt_dlinit() != 0 ) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; } lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_ne_xml_push_handler) = lt_dlsym(dl_handle, "ne_xml_push_handler"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ne_xml_destroy) = lt_dlsym(dl_handle,"ne_xml_destroy"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ne_xml_create) = lt_dlsym(dl_handle,"ne_xml_create"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ne_xml_parse) = lt_dlsym(dl_handle,"ne_xml_parse"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: fprintf(stderr, "Cannot load XML library (%s) : %s. XML search disabled.\n", libname, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; } static int startelm_cb(void *userdata, int parent, const char *nspace, const char *name, const char **atts) { nutscan_device_t * dev = (nutscan_device_t *)userdata; char buf[SMALLBUF]; int i = 0; while( atts[i] != NULL ) { if(strcmp(atts[i],"type") == 0) { snprintf(buf,sizeof(buf),"%s",atts[i+1]); nutscan_add_option_to_device(dev,"desc",buf); return 0; } i=i+2; } return 0; } nutscan_device_t * nutscan_scan_xml_http(long usec_timeout) { char *scanMsg = ""; int port = 4679; int peerSocket; int sockopt_on = 1; struct sockaddr_in sockAddress; socklen_t sockAddressLength = sizeof(sockAddress); memset(&sockAddress, 0, sizeof(sockAddress)); fd_set fds; struct timeval timeout; int ret; char buf[SMALLBUF]; char string[SMALLBUF]; ssize_t recv_size; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; if( !nutscan_avail_xml_http ) { return NULL; } if((peerSocket = socket(AF_INET, SOCK_DGRAM, 0)) != -1) { /* Initialize socket */ sockAddress.sin_family = AF_INET; sockAddress.sin_addr.s_addr = INADDR_BROADCAST; sockAddress.sin_port = htons(port); setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, &sockopt_on, sizeof(sockopt_on)); /* Send scan request */ if(sendto(peerSocket, scanMsg, strlen(scanMsg), 0, (struct sockaddr *)&sockAddress, sockAddressLength) <= 0) { fprintf(stderr,"Error sending Eaton \n"); } else { FD_ZERO(&fds); FD_SET(peerSocket,&fds); timeout.tv_sec = usec_timeout / 1000000; timeout.tv_usec = usec_timeout % 1000000; while ((ret=select(peerSocket+1,&fds,NULL,NULL, &timeout) )) { timeout.tv_sec = usec_timeout / 1000000; timeout.tv_usec = usec_timeout % 1000000; if( ret == -1 ) { fprintf(stderr, "Error waiting on \ socket: %d\n",errno); break; } sockAddressLength = sizeof(struct sockaddr_in); recv_size = recvfrom(peerSocket,buf, sizeof(buf),0, (struct sockaddr *)&sockAddress, &sockAddressLength); if(recv_size==-1) { fprintf(stderr, "Error reading \ socket: %d\n",errno); continue; } if( getnameinfo( (struct sockaddr *)&sockAddress, sizeof(struct sockaddr_in),string, sizeof(string),NULL,0, NI_NUMERICHOST) != 0) { fprintf(stderr, "Error converting IP address \ : %d\n",errno); continue; } nut_dev = nutscan_new_device(); if(nut_dev == NULL) { fprintf(stderr,"Memory allocation \ error\n"); return NULL; } nut_dev->type = TYPE_XML; /* Try to read device type */ ne_xml_parser *parser = (*nut_ne_xml_create)(); (*nut_ne_xml_push_handler)(parser, startelm_cb, NULL, NULL, nut_dev); (*nut_ne_xml_parse)(parser, buf, recv_size); (*nut_ne_xml_destroy)(parser); nut_dev->driver = strdup("netxml-ups"); sprintf(buf,"http://%s",string); nut_dev->port = strdup(buf); current_nut_dev = nutscan_add_device_to_device( current_nut_dev,nut_dev); } } } else { fprintf(stderr,"Error creating socket\n"); } return nutscan_rewind_device(current_nut_dev); } #else /* WITH_NEON */ nutscan_device_t * nutscan_scan_xml_http(long usec_timeout) { return NULL; } #endif /* WITH_NEON */ nut-2.7.4/tools/nut-scanner/Makefile.am0000644000175000017500000000322412640473702014712 00000000000000BUILT_SOURCES = nutscan-usb.h nutscan-snmp.h nutscan-usb.h nutscan-snmp.h: cd ..; $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps # Only build nut-scanner, and its library, if libltdl was found (required!) if WITH_LIBLTDL bin_PROGRAMS = nut-scanner lib_LTLIBRARIES = libnutscan.la endif libnutscan_la_SOURCES = scan_nut.c scan_ipmi.c \ nutscan-device.c nutscan-ip.c nutscan-display.c \ nutscan-init.c scan_usb.c scan_snmp.c scan_xml_http.c \ scan_avahi.c scan_eaton_serial.c nutscan-serial.c \ ../../drivers/serial.c \ ../../drivers/bcmxcp_ser.c \ ../../common/common.c libnutscan_la_LIBADD = $(NETLIBS) $(LIBLTDL_LIBS) libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers nut_scanner_SOURCES = nut-scanner.c nut_scanner_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include nut_scanner_LDADD = libnutscan.la ../../common/libcommon.la if WITH_SSL libnutscan_la_CFLAGS += $(LIBSSL_CFLAGS) libnutscan_la_LIBADD += $(LIBSSL_LIBS) endif if WITH_USB libnutscan_la_CFLAGS += $(LIBUSB_CFLAGS) endif if WITH_SNMP libnutscan_la_CFLAGS += $(LIBNETSNMP_CFLAGS) endif if WITH_NEON libnutscan_la_CFLAGS += $(LIBNEON_CFLAGS) endif if WITH_AVAHI libnutscan_la_CFLAGS += $(LIBAVAHI_CFLAGS) endif if WITH_IPMI libnutscan_la_CFLAGS += $(LIBIPMI_CFLAGS) endif dist_noinst_HEADERS = nutscan-usb.h nutscan-snmp.h if WITH_DEV include_HEADERS = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h else dist_noinst_HEADERS += nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h endif CLEANFILES = nutscan-usb.h nutscan-snmp.h nut-2.7.4/tools/nut-scanner/nutscan-serial.h0000644000175000017500000000176612640473702015770 00000000000000/* * Copyright (C) 2011 - EATON * * 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 */ /*! \file nutscan-serial.h \brief helper functions to get serial devices name \author Frederic Bohe */ #ifndef SCAN_SERIAL #define SCAN_SERIAL char ** nutscan_get_serial_ports_list(const char *ports_range); #endif nut-2.7.4/tools/Makefile.am0000644000175000017500000000563212667537461012476 00000000000000# TODO: remove redundancies! # XXX this does not work with Automake!!! # # In fact the very concept is entirely antithetical to Automake. # # SUBDIRS are explicitly a listing of all the directories that make # must recurse into BEFORE processing the current directory. # # These python scripts must be moved into a sub-directory, and _only_ # executed IF they need to be, and all the nut-scanner sources need # to be moved out of a sub-directory into this directory. # # Anyway, for the time being, we force build in ./ before nut-scanner, # to have nutscan-{usb,snmp}.h built before going into the nut-scanner # sub-directory SUBDIRS = . nut-scanner EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh \ gitlog2changelog.py nut-snmpinfo.py driver-list-format.sh all: nut-scanner-deps # XXX these rules are all bogus! They cause un-named target files to # always be rebuilt! None of that is ever the right way to use make, # and especially not Automake. Explicit filenames and their exact # dependencies need to be properly listed. nut-scanner-deps: @if python -c 1; then \ echo "Regenerating the SNMP helper files."; \ $(top_srcdir)/tools/nut-snmpinfo.py; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ echo "Skipping the SNMP helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi @if perl -e 1; then \ echo "Regenerating the USB helper files."; \ $(top_srcdir)/tools/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ echo "Skipping the USB helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi # call the USB info script upon "make dist", and if Perl is present # call the SNMP info script upon "make dist", and if Python is present # and call both for building nut-scanner # Also ensure that data/driver.list is well formatted dist-hook: @if python -c 1; then \ echo "Regenerating the SNMP helper files."; \ $(distdir)/nut-snmpinfo.py; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ echo "Skipping the SNMP helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi @if perl -e 1; then \ echo "Regenerating the USB helper files."; \ $(distdir)/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ echo "Skipping the USB helper files regeneration."; \ echo "----------------------------------------------------------------------"; \ fi @$(distdir)/driver-list-format.sh; .PHONY: nut-scanner-deps nut-scanner-snmp-deps nut-scanner-usb-deps nut-2.7.4/config.sub0000755000175000017500000010577512640476420011262 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-09-11' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: nut-2.7.4/server/0000755000175000017500000000000012670024742010645 500000000000000nut-2.7.4/server/netuser.c0000644000175000017500000000743012640443572012425 00000000000000/* netuser.c - LOGIN/LOGOUT/USERNAME/PASSWORD/MASTER handlers for upsd Copyright (C) 2003 Russell Kroll 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 "common.h" #include "upsd.h" #include "sstate.h" #include "state.h" #include "neterr.h" #include "user.h" /* for user_checkaction */ #include "netuser.h" /* LOGIN */ void net_login(nut_ctype_t *client, int numarg, const char **arg) { upstype_t *ups; if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } if (client->loginups != NULL) { upslogx(LOG_INFO, "Client %s@%s tried to login twice", client->username, client->addr); send_err(client, NUT_ERR_ALREADY_LOGGED_IN); return; } /* make sure we got a valid UPS name */ ups = get_ups_ptr(arg[0]); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } /* make sure this is a valid user */ if (!user_checkaction(client->username, client->password, "LOGIN")) { send_err(client, NUT_ERR_ACCESS_DENIED); return; } ups->numlogins++; client->loginups = xstrdup(ups->name); upslogx(LOG_INFO, "User %s@%s logged into UPS [%s]%s", client->username, client->addr, client->loginups, client->ssl ? " (SSL)" : ""); sendback(client, "OK\n"); } void net_logout(nut_ctype_t *client, int numarg, const char **arg) { if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } if (client->loginups != NULL) { upslogx(LOG_INFO, "User %s@%s logged out from UPS [%s]%s", client->username, client->addr, client->loginups, client->ssl ? " (SSL)" : ""); } sendback(client, "OK Goodbye\n"); client->last_heard = 0; } /* MASTER */ void net_master(nut_ctype_t *client, int numarg, const char **arg) { upstype_t *ups; if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } ups = get_ups_ptr(arg[0]); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } /* make sure this user is allowed to do MASTER */ if (!user_checkaction(client->username, client->password, "MASTER")) { send_err(client, NUT_ERR_ACCESS_DENIED); return; } /* this is just an access level check */ sendback(client, "OK MASTER-GRANTED\n"); } /* USERNAME */ void net_username(nut_ctype_t *client, int numarg, const char **arg) { if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } if (client->username != NULL) { upslogx(LOG_INFO, "Client %s@%s tried to set a username twice", client->username, client->addr); send_err(client, NUT_ERR_ALREADY_SET_USERNAME); return; } client->username = xstrdup(arg[0]); sendback(client, "OK\n"); } /* PASSWORD */ void net_password(nut_ctype_t *client, int numarg, const char **arg) { if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } if (client->password != NULL) { if (client->username) upslogx(LOG_INFO, "Client %s@%s tried to set a password twice", client->username, client->addr); else upslogx(LOG_INFO, "Client on %s tried to set a password twice", client->addr); send_err(client, NUT_ERR_ALREADY_SET_PASSWORD); return; } client->password = xstrdup(arg[0]); sendback(client, "OK\n"); } nut-2.7.4/server/netinstcmd.h0000644000175000017500000000032512640444140013101 00000000000000#ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void net_instcmd(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/netset.h0000644000175000017500000000032112640444140012227 00000000000000#ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void net_set(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/netcmds.h0000644000175000017500000000343112640444140012367 00000000000000/* netcmds.h - upsd support structure details Copyright (C) 2001 Russell Kroll 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 "nut_ctype.h" #include "netssl.h" #include "netget.h" #include "netset.h" #include "netlist.h" #include "netmisc.h" #include "netuser.h" #include "netinstcmd.h" #define FLAG_USER 0x0001 /* username and password must be set */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif struct { const char *name; void (*func)(nut_ctype_t *client, int numargs, const char **arg); int flags; } netcmds[] = { { "VER", net_ver, 0 }, { "NETVER", net_netver, 0 }, { "HELP", net_help, 0 }, { "STARTTLS", net_starttls, 0 }, { "GET", net_get, 0 }, { "LIST", net_list, 0 }, { "USERNAME", net_username, 0 }, { "PASSWORD", net_password, 0 }, { "LOGIN", net_login, FLAG_USER }, { "LOGOUT", net_logout, 0 }, { "MASTER", net_master, FLAG_USER }, { "FSD", net_fsd, FLAG_USER }, { "SET", net_set, FLAG_USER }, { "INSTCMD", net_instcmd, FLAG_USER }, { NULL, (void(*)())(NULL), 0 } }; #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/netlist.h0000644000175000017500000000032212640444140012410 00000000000000#ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void net_list(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/upsd.h0000644000175000017500000000462112640473702011715 00000000000000/* upsd.h - support structures and other minor details Copyright (C) 1999 Russell Kroll 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 */ /* * Much of the content from here was also useful to the * drivers, so has been moved into include/shared-tables.h * instead of being within the daemon specific include file * */ #ifndef UPSD_H_SEEN #define UPSD_H_SEEN #include "attribute.h" #include "common.h" #include #include #include #include "timehead.h" #include #include "parseconf.h" #include "nut_ctype.h" #include "upstype.h" #define NUT_NET_ANSWER_MAX SMALLBUF #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /* prototypes from upsd.c */ upstype_t *get_ups_ptr(const char *upsname); int ups_available(const upstype_t *ups, nut_ctype_t *client); void listen_add(const char *addr, const char *port); void kick_login_clients(const char *upsname); int sendback(nut_ctype_t *client, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int send_err(nut_ctype_t *client, const char *errtype); void server_load(void); void server_free(void); void check_perms(const char *fn); /* declarations from upsd.c */ extern int maxage, maxconn; extern char *statepath, *datapath; extern upstype_t *firstups; extern nut_ctype_t *firstclient; /* map commands onto signals */ #define SIGCMD_STOP SIGTERM #define SIGCMD_RELOAD SIGHUP /* awkward way to make a string out of a numeric constant */ #define string_const_aux(x) #x #define string_const(x) string_const_aux(x) #ifdef SHUT_RDWR #define shutdown_how SHUT_RDWR #else #define shutdown_how 2 #endif #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif /* UPSD_H_SEEN */ nut-2.7.4/server/conf.h0000644000175000017500000000272112640444140011660 00000000000000/* conf.h - supporting elements of conf parsing functions for upsd Copyright (C) 2001 Russell Kroll 2008 Arjen de Korte 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 */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /* read upsd.conf */ void load_upsdconf(int reloading); /* add valid UPSes from ups.conf to the internal structures */ void upsconf_add(int reloading); /* flush existing config, then reread everything */ void conf_reload(void); typedef struct ups_s { char *upsname; char *driver; char *port; char *desc; struct ups_s *next; } ups_t; /* used for really clean shutdowns */ void delete_acls(void); void delete_access(void); extern int num_ups; #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/stype.h0000644000175000017500000000235612640473702012111 00000000000000/* stype.h - server data definitions for upsd Copyright (C) 2007 Arjen de Korte This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef STYPE_H_SEEN #define STYPE_H_SEEN 1 #include #ifndef NI_MAXHOST #define NI_MAXHOST 1025 #endif #ifndef NI_MAXSERV #define NI_MAXSERV 32 #endif #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif typedef struct stype_s { char *addr; char *port; int sock_fd; struct stype_s *next; } stype_t; #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif /* STYPE_H_SEEN */ nut-2.7.4/server/desc.c0000644000175000017500000000577612640443572011671 00000000000000/* desc.c - variable/command description handling for upsd Copyright (C) 2003 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "common.h" #include "parseconf.h" #include "desc.h" extern const char *datapath; typedef struct dlist_s { char *name; char *desc; struct dlist_s *next; } dlist_t; static dlist_t *cmd_list = NULL, *var_list = NULL; static void list_free(dlist_t *ptr) { dlist_t *next; while (ptr) { next = ptr->next; free(ptr->name); free(ptr->desc); free(ptr); ptr = next; } } static const char *list_get(const dlist_t *list, const char *name) { const dlist_t *temp; for (temp = list; temp != NULL; temp = temp->next) { if (!strcasecmp(temp->name, name)) { return temp->desc; } } return NULL; } static void desc_add(dlist_t **list, const char *name, const char *desc) { dlist_t *temp; for (temp = *list; temp != NULL; temp = temp->next) { if (!strcasecmp(temp->name, name)) { break; } } if (temp == NULL) { temp = xcalloc(1, sizeof(*temp)); temp->name = xstrdup(name); temp->next = *list; *list = temp; } free(temp->desc); temp->desc = xstrdup(desc); } static void desc_file_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf (cmdvartab): %s", errmsg); } /* interface */ void desc_load(void) { char fn[SMALLBUF]; PCONF_CTX_t ctx; snprintf(fn, sizeof(fn), "%s/cmdvartab", datapath); pconf_init(&ctx, desc_file_err); /* this file is not required */ if (!pconf_file_begin(&ctx, fn)) { upslogx(LOG_INFO, "%s not found - disabling descriptions", fn); pconf_finish(&ctx); return; } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); continue; } if (ctx.numargs < 3) { continue; } if (!strcmp(ctx.arglist[0], "CMDDESC")) { desc_add(&cmd_list, ctx.arglist[1], ctx.arglist[2]); continue; } if (!strcmp(ctx.arglist[0], "VARDESC")) { desc_add(&var_list, ctx.arglist[1], ctx.arglist[2]); continue; } /* unknown */ } pconf_finish(&ctx); } void desc_free(void) { list_free(cmd_list); list_free(var_list); cmd_list = var_list = NULL; } const char *desc_get_cmd(const char *name) { return list_get(cmd_list, name); } const char *desc_get_var(const char *name) { return list_get(var_list, name); } nut-2.7.4/server/Makefile.in0000644000175000017500000006016712667762001012647 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: server VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_WRAP_TRUE@am__append_1 = $(LIBWRAP_CFLAGS) @WITH_SSL_TRUE@am__append_2 = $(LIBSSL_CFLAGS) @WITH_WRAP_TRUE@am__append_3 = $(LIBWRAP_LIBS) @WITH_SSL_TRUE@am__append_4 = $(LIBSSL_LIBS) sbin_PROGRAMS = upsd$(EXEEXT) EXTRA_PROGRAMS = sockdebug$(EXEEXT) subdir = server DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(sbindir)" PROGRAMS = $(sbin_PROGRAMS) am_sockdebug_OBJECTS = sockdebug.$(OBJEXT) sockdebug_OBJECTS = $(am_sockdebug_OBJECTS) sockdebug_LDADD = $(LDADD) am__DEPENDENCIES_1 = @WITH_WRAP_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @WITH_SSL_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) sockdebug_DEPENDENCIES = ../common/libcommon.la \ ../common/libparseconf.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_upsd_OBJECTS = upsd.$(OBJEXT) user.$(OBJEXT) conf.$(OBJEXT) \ netssl.$(OBJEXT) sstate.$(OBJEXT) desc.$(OBJEXT) \ netget.$(OBJEXT) netmisc.$(OBJEXT) netlist.$(OBJEXT) \ netuser.$(OBJEXT) netset.$(OBJEXT) netinstcmd.$(OBJEXT) upsd_OBJECTS = $(am_upsd_OBJECTS) upsd_LDADD = $(LDADD) upsd_DEPENDENCIES = ../common/libcommon.la ../common/libparseconf.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_3) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(sockdebug_SOURCES) $(upsd_SOURCES) DIST_SOURCES = $(sockdebug_SOURCES) $(upsd_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include $(am__append_1) $(am__append_2) LDADD = ../common/libcommon.la ../common/libparseconf.la $(NETLIBS) \ $(am__append_3) $(am__append_4) upsd_SOURCES = upsd.c user.c conf.c netssl.c sstate.c desc.c \ netget.c netmisc.c netlist.c netuser.c netset.c netinstcmd.c \ conf.h nut_ctype.h desc.h netcmds.h neterr.h netget.h netinstcmd.h \ netlist.h netmisc.h netset.h netuser.h netssl.h sstate.h stype.h upsd.h \ upstype.h user-data.h user.h sockdebug_SOURCES = sockdebug.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu server/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu server/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list sockdebug$(EXEEXT): $(sockdebug_OBJECTS) $(sockdebug_DEPENDENCIES) $(EXTRA_sockdebug_DEPENDENCIES) @rm -f sockdebug$(EXEEXT) $(AM_V_CCLD)$(LINK) $(sockdebug_OBJECTS) $(sockdebug_LDADD) $(LIBS) upsd$(EXEEXT): $(upsd_OBJECTS) $(upsd_DEPENDENCIES) $(EXTRA_upsd_DEPENDENCIES) @rm -f upsd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsd_OBJECTS) $(upsd_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/desc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netget.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netinstcmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netlist.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netmisc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netset.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netuser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockdebug.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sstate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-sbinPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-sbinPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-sbinPROGRAMS cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-sbinPROGRAMS install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/server/conf.c0000644000175000017500000002422712640473702011666 00000000000000/* conf.c - configuration handlers for upsd Copyright (C) 2001 Russell Kroll 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 "upsd.h" #include "conf.h" #include "upsconf.h" #include "sstate.h" #include "user.h" #include "netssl.h" ups_t *upstable = NULL; int num_ups = 0; /* add another UPS for monitoring from ups.conf */ static void ups_create(const char *fn, const char *name, const char *desc) { upstype_t *temp; for (temp = firstups; temp != NULL; temp = temp->next) { if (!strcasecmp(temp->name, name)) { upslogx(LOG_ERR, "UPS name [%s] is already in use!", name); return; } } /* grab some memory and add the info */ temp = xcalloc(1, sizeof(*temp)); temp->fn = xstrdup(fn); temp->name = xstrdup(name); if (desc) { temp->desc = xstrdup(desc); } temp->stale = 1; temp->retain = 1; temp->sock_fd = sstate_connect(temp); /* preload this to the current time to avoid false staleness */ time(&temp->last_heard); temp->next = firstups; firstups = temp; num_ups++; } /* change the configuration of an existing UPS (used during reloads) */ static void ups_update(const char *fn, const char *name, const char *desc) { upstype_t *temp; temp = get_ups_ptr(name); if (!temp) { upslogx(LOG_ERR, "UPS %s disappeared during reload", name); return; } /* paranoia */ if (!temp->fn) { upslogx(LOG_ERR, "UPS %s had a NULL filename!", name); /* let's give it something quick to use later */ temp->fn = xstrdup(""); } /* when the filename changes, force a reconnect */ if (strcmp(temp->fn, fn) != 0) { upslogx(LOG_NOTICE, "Redefined UPS [%s]", name); /* release all data */ sstate_infofree(temp); sstate_cmdfree(temp); pconf_finish(&temp->sock_ctx); close(temp->sock_fd); temp->sock_fd = -1; temp->dumpdone = 0; /* now redefine the filename and wrap up */ free(temp->fn); temp->fn = xstrdup(fn); } /* update the description */ free(temp->desc); if (desc) temp->desc = xstrdup(desc); else temp->desc = NULL; /* always set this on reload */ temp->retain = 1; } /* return 1 if usable, 0 if not */ static int parse_upsd_conf_args(int numargs, char **arg) { /* everything below here uses up through arg[1] */ if (numargs < 2) return 0; /* MAXAGE */ if (!strcmp(arg[0], "MAXAGE")) { maxage = atoi(arg[1]); return 1; } /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { maxconn = atoi(arg[1]); return 1; } /* STATEPATH */ if (!strcmp(arg[0], "STATEPATH")) { free(statepath); statepath = xstrdup(arg[1]); return 1; } /* DATAPATH */ if (!strcmp(arg[0], "DATAPATH")) { free(datapath); datapath = xstrdup(arg[1]); return 1; } #ifdef WITH_OPENSSL /* CERTFILE */ if (!strcmp(arg[0], "CERTFILE")) { free(certfile); certfile = xstrdup(arg[1]); return 1; } #elif (defined WITH_NSS) /* WITH_OPENSSL */ /* CERTPATH */ if (!strcmp(arg[0], "CERTPATH")) { free(certfile); certfile = xstrdup(arg[1]); return 1; } #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION /* CERTREQUEST (0 | 1 | 2) */ if (!strcmp(arg[0], "CERTREQUEST")) { certrequest = atoi(arg[1]); return 1; } #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ #endif /* WITH_OPENSSL | WITH_NSS */ /* ACCEPT [...] */ if (!strcmp(arg[0], "ACCEPT")) { upslogx(LOG_WARNING, "ACCEPT in upsd.conf is no longer supported - switch to LISTEN"); return 1; } /* REJECT [...] */ if (!strcmp(arg[0], "REJECT")) { upslogx(LOG_WARNING, "REJECT in upsd.conf is no longer supported - switch to LISTEN"); return 1; } /* LISTEN
[] */ if (!strcmp(arg[0], "LISTEN")) { if (numargs < 3) listen_add(arg[1], string_const(PORT)); else listen_add(arg[1], arg[2]); return 1; } /* everything below here uses up through arg[2] */ if (numargs < 3) return 0; /* ACL */ if (!strcmp(arg[0], "ACL")) { upslogx(LOG_WARNING, "ACL in upsd.conf is no longer supported - switch to LISTEN"); return 1; } #ifdef WITH_NSS /* CERTIDENT */ if (!strcmp(arg[0], "CERTIDENT")) { free(certname); certname = xstrdup(arg[1]); free(certpasswd); certpasswd = xstrdup(arg[2]); return 1; } #endif /* WITH_NSS */ /* not recognized */ return 0; } /* called for fatal errors in parseconf like malloc failures */ static void upsd_conf_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf (upsd.conf): %s", errmsg); } void load_upsdconf(int reloading) { char fn[SMALLBUF]; PCONF_CTX_t ctx; snprintf(fn, sizeof(fn), "%s/upsd.conf", confpath()); check_perms(fn); pconf_init(&ctx, upsd_conf_err); if (!pconf_file_begin(&ctx, fn)) { pconf_finish(&ctx); if (!reloading) fatalx(EXIT_FAILURE, "%s", ctx.errmsg); upslogx(LOG_ERR, "Reload failed: %s", ctx.errmsg); return; } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); continue; } if (ctx.numargs < 1) continue; if (!parse_upsd_conf_args(ctx.numargs, ctx.arglist)) { unsigned int i; char errmsg[SMALLBUF]; snprintf(errmsg, sizeof(errmsg), "upsd.conf: invalid directive"); for (i = 0; i < ctx.numargs; i++) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); upslogx(LOG_WARNING, "%s", errmsg); } } pconf_finish(&ctx); } /* callback during parsing of ups.conf */ void do_upsconf_args(char *upsname, char *var, char *val) { ups_t *temp; /* no "global" stuff for us */ if (!upsname) { return; } /* check if UPS is already listed */ for (temp = upstable; temp != NULL; temp = temp->next) { if (!strcmp(temp->upsname, upsname)) { break; } } /* if not listed, create a new entry and prepend it to the list */ if (temp == NULL) { temp = xcalloc(1, sizeof(*temp)); temp->upsname = xstrdup(upsname); temp->next = upstable; upstable = temp; } if (!strcmp(var, "driver")) { free(temp->driver); temp->driver = xstrdup(val); } else if (!strcmp(var, "port")) { free(temp->port); temp->port = xstrdup(val); } else if (!strcmp(var, "desc")) { free(temp->desc); temp->desc = xstrdup(val); } } /* add valid UPSes from ups.conf to the internal structures */ void upsconf_add(int reloading) { ups_t *tmp = upstable, *next; char statefn[SMALLBUF]; if (!tmp) { upslogx(LOG_WARNING, "Warning: no UPS definitions in ups.conf"); return; } while (tmp) { /* save for later, since we delete as we go along */ next = tmp->next; /* this should always be set, but better safe than sorry */ if (!tmp->upsname) { tmp = tmp->next; continue; } /* don't accept an entry that's missing items */ if ((!tmp->driver) || (!tmp->port)) { upslogx(LOG_WARNING, "Warning: ignoring incomplete configuration for UPS [%s]\n", tmp->upsname); } else { snprintf(statefn, sizeof(statefn), "%s-%s", tmp->driver, tmp->upsname); /* if a UPS exists, update it, else add it as new */ if ((reloading) && (get_ups_ptr(tmp->upsname) != NULL)) ups_update(statefn, tmp->upsname, tmp->desc); else ups_create(statefn, tmp->upsname, tmp->desc); } /* free tmp's resources */ free(tmp->driver); free(tmp->port); free(tmp->desc); free(tmp->upsname); free(tmp); tmp = next; } /* upstable should be completely gone by this point */ upstable = NULL; } /* remove a UPS from the linked list */ static void delete_ups(upstype_t *target) { upstype_t *ptr, *last; if (!target) return; ptr = last = firstups; while (ptr) { if (ptr == target) { upslogx(LOG_NOTICE, "Deleting UPS [%s]", target->name); /* make sure nobody stays logged into this thing */ kick_login_clients(target->name); /* about to delete the first ups? */ if (ptr == last) firstups = ptr->next; else last->next = ptr->next; if (ptr->sock_fd != -1) close(ptr->sock_fd); /* release memory */ sstate_infofree(ptr); sstate_cmdfree(ptr); pconf_finish(&ptr->sock_ctx); free(ptr->fn); free(ptr->name); free(ptr->desc); free(ptr); return; } last = ptr; ptr = ptr->next; } /* shouldn't happen */ upslogx(LOG_ERR, "delete_ups: UPS not found"); } /* see if we can open a file */ static int check_file(const char *fn) { char chkfn[SMALLBUF]; FILE *f; snprintf(chkfn, sizeof(chkfn), "%s/%s", confpath(), fn); f = fopen(chkfn, "r"); if (!f) { upslog_with_errno(LOG_ERR, "Reload failed: can't open %s", chkfn); return 0; /* failed */ } fclose(f); return 1; /* OK */ } /* called after SIGHUP */ void conf_reload(void) { upstype_t *upstmp, *upsnext; upslogx(LOG_INFO, "SIGHUP: reloading configuration"); /* see if we can access upsd.conf before blowing away the config */ if (!check_file("upsd.conf")) return; /* reset retain flags on all known UPS entries */ upstmp = firstups; while (upstmp) { upstmp->retain = 0; upstmp = upstmp->next; } /* reload from ups.conf */ read_upsconf(); upsconf_add(1); /* 1 = reloading */ /* now reread upsd.conf */ load_upsdconf(1); /* 1 = reloading */ /* now delete all UPS entries that didn't get reloaded */ upstmp = firstups; while (upstmp) { /* upstmp may be deleted during this pass */ upsnext = upstmp->next; if (upstmp->retain == 0) delete_ups(upstmp); upstmp = upsnext; } /* did they actually delete the last UPS? */ if (firstups == NULL) upslogx(LOG_WARNING, "Warning: no UPSes currently defined!"); /* and also make sure upsd.users can be read... */ if (!check_file("upsd.users")) return; /* delete all users */ user_flush(); /* and finally reread from upsd.users */ user_load(); } nut-2.7.4/server/upstype.h0000644000175000017500000000312112640473702012445 00000000000000/* upstype.h - internal UPS tracking structure details Copyright (C) 2003 Russell Kroll 2008 Arjen de Korte This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef UPSTYPE_H_SEEN #define UPSTYPE_H_SEEN 1 #include "parseconf.h" #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /* structure for the linked list of each UPS that we track */ typedef struct upstype_s { char *name; char *fn; char *desc; int sock_fd; int stale; int dumpdone; int data_ok; time_t last_heard; time_t last_ping; time_t last_connfail; PCONF_CTX_t sock_ctx; struct st_tree_s *inforoot; struct cmdlist_s *cmdlist; int numlogins; int fsd; /* forced shutdown in effect? */ int retain; struct upstype_s *next; } upstype_t; extern upstype_t *firstups; #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif /* UPSTYPE_H_SEEN */ nut-2.7.4/server/upsd.c0000644000175000017500000005236312640473702011716 00000000000000/* upsd.c - watches ups state files and answers queries Copyright (C) 1999 Russell Kroll 2008 Arjen de Korte 2011 - 2012 Arnaud Quette 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 "upsd.h" #include "upstype.h" #include "conf.h" #include "netcmds.h" #include "upsconf.h" #include #include #include #include #include "user.h" #include "nut_ctype.h" #include "stype.h" #include "netssl.h" #include "sstate.h" #include "desc.h" #include "neterr.h" #ifdef HAVE_WRAP #include int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* HAVE_WRAP */ /* externally-visible settings and pointers */ upstype_t *firstups = NULL; /* default 15 seconds before data is marked stale */ int maxage = 15; /* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ int maxconn = 0; /* preloaded to STATEPATH in main, can be overridden via upsd.conf */ char *statepath = NULL; /* preloaded to DATADIR in main, can be overridden via upsd.conf */ char *datapath = NULL; /* everything else */ const char *progname; nut_ctype_t *firstclient = NULL; /* static nut_ctype_t *lastclient = NULL; */ /* default is to listen on all local interfaces */ static stype_t *firstaddr = NULL; static int opt_af = AF_UNSPEC; typedef enum { DRIVER = 1, CLIENT, SERVER } handler_type_t; typedef struct { handler_type_t type; void *data; } handler_t; /* pollfd */ static struct pollfd *fds = NULL; static handler_t *handler = NULL; /* pid file */ static char pidfn[SMALLBUF]; /* set by signal handlers */ static int reload_flag = 0, exit_flag = 0; static const char *inet_ntopW (struct sockaddr_storage *s) { static char str[40]; switch (s->ss_family) { case AF_INET: return inet_ntop (AF_INET, &(((struct sockaddr_in *)s)->sin_addr), str, 16); case AF_INET6: return inet_ntop (AF_INET6, &(((struct sockaddr_in6 *)s)->sin6_addr), str, 40); default: errno = EAFNOSUPPORT; return NULL; } } /* return a pointer to the named ups if possible */ upstype_t *get_ups_ptr(const char *name) { upstype_t *tmp; if (!name) { return NULL; } for (tmp = firstups; tmp; tmp = tmp->next) { if (!strcasecmp(tmp->name, name)) { return tmp; } } return NULL; } /* mark the data stale if this is new, otherwise cleanup any remaining junk */ static void ups_data_stale(upstype_t *ups) { /* don't complain again if it's already known to be stale */ if (ups->stale == 1) { return; } ups->stale = 1; upslogx(LOG_NOTICE, "Data for UPS [%s] is stale - check driver", ups->name); } /* mark the data ok if this is new, otherwise do nothing */ static void ups_data_ok(upstype_t *ups) { if (ups->stale == 0) { return; } ups->stale = 0; upslogx(LOG_NOTICE, "UPS [%s] data is no longer stale", ups->name); } /* add another listening address */ void listen_add(const char *addr, const char *port) { stype_t *server; /* don't change listening addresses on reload */ if (reload_flag) { return; } /* grab some memory and add the info */ server = xcalloc(1, sizeof(*server)); server->addr = xstrdup(addr); server->port = xstrdup(port); server->sock_fd = -1; server->next = firstaddr; firstaddr = server; upsdebugx(3, "listen_add: added %s:%s", server->addr, server->port); } /* create a listening socket for tcp connections */ static void setuptcp(stype_t *server) { struct addrinfo hints, *res, *ai; int v = 0, one = 1; upsdebugx(3, "setuptcp: try to bind to %s port %s", server->addr, server->port); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = opt_af; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if ((v = getaddrinfo(server->addr, server->port, &hints, &res)) != 0) { if (v == EAI_SYSTEM) { fatal_with_errno(EXIT_FAILURE, "getaddrinfo"); } fatalx(EXIT_FAILURE, "getaddrinfo: %s", gai_strerror(v)); } for (ai = res; ai; ai = ai->ai_next) { int sock_fd; if ((sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) { upsdebug_with_errno(3, "setuptcp: socket"); continue; } if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) != 0) { fatal_with_errno(EXIT_FAILURE, "setuptcp: setsockopt"); } if (bind(sock_fd, ai->ai_addr, ai->ai_addrlen) < 0) { upsdebug_with_errno(3, "setuptcp: bind"); close(sock_fd); continue; } if ((v = fcntl(sock_fd, F_GETFL, 0)) == -1) { fatal_with_errno(EXIT_FAILURE, "setuptcp: fcntl(get)"); } if (fcntl(sock_fd, F_SETFL, v | O_NDELAY) == -1) { fatal_with_errno(EXIT_FAILURE, "setuptcp: fcntl(set)"); } if (listen(sock_fd, 16) < 0) { upsdebug_with_errno(3, "setuptcp: listen"); close(sock_fd); continue; } server->sock_fd = sock_fd; break; } freeaddrinfo(res); /* leave up to the caller, server_load(), to fail silently if there is * no other valid LISTEN interface */ if (server->sock_fd < 0) { upslogx(LOG_ERR, "not listening on %s port %s", server->addr, server->port); } else { upslogx(LOG_INFO, "listening on %s port %s", server->addr, server->port); } return; } /* decrement the login counter for this ups */ static void declogins(const char *upsname) { upstype_t *ups; ups = get_ups_ptr(upsname); if (!ups) { upslogx(LOG_INFO, "Tried to decrement invalid ups name (%s)", upsname); return; } ups->numlogins--; if (ups->numlogins < 0) { upslogx(LOG_ERR, "Programming error: UPS [%s] has numlogins=%d", ups->name, ups->numlogins); } } /* disconnect a client connection and free all related memory */ static void client_disconnect(nut_ctype_t *client) { if (!client) { return; } upsdebugx(2, "Disconnect from %s", client->addr); shutdown(client->sock_fd, 2); close(client->sock_fd); if (client->loginups) { declogins(client->loginups); } ssl_finish(client); pconf_finish(&client->ctx); if (client->prev) { client->prev->next = client->next; } else { /* deleting first entry */ firstclient = client->next; } if (client->next) { client->next->prev = client->prev; } else { /* deleting last entry */ /* lastclient = client->prev; */ } free(client->addr); free(client->loginups); free(client->password); free(client->username); free(client); return; } /* send the buffer of length to host */ int sendback(nut_ctype_t *client, const char *fmt, ...) { int res, len; char ans[NUT_NET_ANSWER_MAX+1]; va_list ap; if (!client) { return 0; } va_start(ap, fmt); vsnprintf(ans, sizeof(ans), fmt, ap); va_end(ap); len = strlen(ans); #ifdef WITH_SSL if (client->ssl) { res = ssl_write(client, ans, len); } else #endif /* WITH_SSL */ { res = write(client->sock_fd, ans, len); } upsdebugx(2, "write: [destfd=%d] [len=%d] [%s]", client->sock_fd, len, str_rtrim(ans, '\n')); if (len != res) { upslog_with_errno(LOG_NOTICE, "write() failed for %s", client->addr); client->last_heard = 0; return 0; /* failed */ } return 1; /* OK */ } /* just a simple wrapper for now */ int send_err(nut_ctype_t *client, const char *errtype) { if (!client) { return -1; } upsdebugx(4, "Sending error [%s] to client %s", errtype, client->addr); return sendback(client, "ERR %s\n", errtype); } /* disconnect anyone logged into this UPS */ void kick_login_clients(const char *upsname) { nut_ctype_t *client, *cnext; for (client = firstclient; client; client = cnext) { cnext = client->next; /* if it's not logged in, don't check it */ if (!client->loginups) { continue; } if (!strcmp(client->loginups, upsname)) { upslogx(LOG_INFO, "Kicking client %s (was on UPS [%s])\n", client->addr, upsname); client_disconnect(client); } } } /* make sure a UPS is sane - connected, with fresh data */ int ups_available(const upstype_t *ups, nut_ctype_t *client) { if (ups->sock_fd < 0) { send_err(client, NUT_ERR_DRIVER_NOT_CONNECTED); return 0; } if (ups->stale) { send_err(client, NUT_ERR_DATA_STALE); return 0; } /* must be OK */ return 1; } /* check flags and access for an incoming command from the network */ static void check_command(int cmdnum, nut_ctype_t *client, int numarg, const char **arg) { if (netcmds[cmdnum].flags & FLAG_USER) { #ifdef HAVE_WRAP struct request_info req; #endif /* HAVE_WRAP */ if (!client->username) { send_err(client, NUT_ERR_USERNAME_REQUIRED); return; } if (!client->password) { send_err(client, NUT_ERR_PASSWORD_REQUIRED); return; } #ifdef HAVE_WRAP request_init(&req, RQ_DAEMON, progname, RQ_FILE, client->sock_fd, RQ_USER, client->username, 0); fromhost(&req); if (!hosts_access(&req)) { /* tcp-wrappers says access should be denied */ send_err(client, NUT_ERR_ACCESS_DENIED); return; } #endif /* HAVE_WRAP */ } /* looks good - call the command */ netcmds[cmdnum].func(client, numarg - 1, &arg[1]); } /* parse requests from the network */ static void parse_net(nut_ctype_t *client) { int i; /* shouldn't happen */ if (client->ctx.numargs < 1) { send_err(client, NUT_ERR_UNKNOWN_COMMAND); return; } for (i = 0; netcmds[i].name; i++) { if (!strcasecmp(netcmds[i].name, client->ctx.arglist[0])) { check_command(i, client, client->ctx.numargs, (const char **) client->ctx.arglist); return; } } /* fallthrough = not matched by any entry in netcmds */ send_err(client, NUT_ERR_UNKNOWN_COMMAND); } /* answer incoming tcp connections */ static void client_connect(stype_t *server) { struct sockaddr_storage csock; #if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) int clen; #else socklen_t clen; #endif int fd; nut_ctype_t *client; clen = sizeof(csock); fd = accept(server->sock_fd, (struct sockaddr *) &csock, &clen); if (fd < 0) { return; } client = xcalloc(1, sizeof(*client)); client->sock_fd = fd; time(&client->last_heard); client->addr = xstrdup(inet_ntopW(&csock)); pconf_init(&client->ctx, NULL); if (firstclient) { firstclient->prev = client; client->next = firstclient; } firstclient = client; /* if (lastclient) { client->prev = lastclient; lastclient->next = client; } lastclient = client; */ upsdebugx(2, "Connect from %s", client->addr); } /* read tcp messages and handle them */ static void client_readline(nut_ctype_t *client) { char buf[SMALLBUF]; int i, ret; #ifdef WITH_SSL if (client->ssl) { ret = ssl_read(client, buf, sizeof(buf)); } else #endif /* WITH_SSL */ { ret = read(client->sock_fd, buf, sizeof(buf)); } if (ret < 0) { upsdebug_with_errno(2, "Disconnect %s (read failure)", client->addr); client_disconnect(client); return; } if (ret == 0) { upsdebugx(2, "Disconnect %s (no data available)", client->addr); client_disconnect(client); return; } /* fragment handling code */ for (i = 0; i < ret; i++) { /* add to the receive queue one by one */ switch (pconf_char(&client->ctx, buf[i])) { case 1: time(&client->last_heard); /* command received */ parse_net(client); continue; case 0: continue; /* haven't gotten a line yet */ default: /* parse error */ upslogx(LOG_NOTICE, "Parse error on sock: %s", client->ctx.errmsg); return; } } return; } void server_load(void) { stype_t *server; /* default behaviour if no LISTEN addres has been specified */ if (!firstaddr) { if (opt_af != AF_INET) { listen_add("::1", string_const(PORT)); } if (opt_af != AF_INET6) { listen_add("127.0.0.1", string_const(PORT)); } } for (server = firstaddr; server; server = server->next) { setuptcp(server); } /* check if we have at least 1 valid LISTEN interface */ if (firstaddr->sock_fd < 0) { fatalx(EXIT_FAILURE, "no listening interface available"); } } void server_free(void) { stype_t *server, *snext; /* cleanup server fds */ for (server = firstaddr; server; server = snext) { snext = server->next; if (server->sock_fd != -1) { close(server->sock_fd); } free(server->addr); free(server->port); free(server); } firstaddr = NULL; } static void client_free(void) { nut_ctype_t *client, *cnext; /* cleanup client fds */ for (client = firstclient; client; client = cnext) { cnext = client->next; client_disconnect(client); } } void driver_free(void) { upstype_t *ups, *unext; for (ups = firstups; ups; ups = unext) { unext = ups->next; if (ups->sock_fd != -1) { close(ups->sock_fd); } sstate_infofree(ups); sstate_cmdfree(ups); pconf_finish(&ups->sock_ctx); free(ups->fn); free(ups->name); free(ups->desc); free(ups); } } static void upsd_cleanup(void) { if (strlen(pidfn) > 0) { unlink(pidfn); } /* dump everything */ user_flush(); desc_free(); server_free(); client_free(); driver_free(); free(statepath); free(datapath); free(certfile); free(certname); free(certpasswd); free(fds); free(handler); } void poll_reload(void) { int ret; ret = sysconf(_SC_OPEN_MAX); if (ret < maxconn) { fatalx(EXIT_FAILURE, "Your system limits the maximum number of connections to %d\n" "but you requested %d. The server won't start until this\n" "problem is resolved.\n", ret, maxconn); } fds = xrealloc(fds, maxconn * sizeof(*fds)); handler = xrealloc(handler, maxconn * sizeof(*handler)); } /* service requests and check on new data */ static void mainloop(void) { int i, ret, nfds = 0; upstype_t *ups; nut_ctype_t *client, *cnext; stype_t *server; time_t now; time(&now); if (reload_flag) { conf_reload(); poll_reload(); reload_flag = 0; } /* scan through driver sockets */ for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { /* see if we need to (re)connect to the socket */ if (ups->sock_fd < 0) { ups->sock_fd = sstate_connect(ups); continue; } /* throw some warnings if it's not feeding us data any more */ if (sstate_dead(ups, maxage)) { ups_data_stale(ups); } else { ups_data_ok(ups); } fds[nfds].fd = ups->sock_fd; fds[nfds].events = POLLIN; handler[nfds].type = DRIVER; handler[nfds].data = ups; nfds++; } /* scan through client sockets */ for (client = firstclient; client; client = cnext) { cnext = client->next; if (difftime(now, client->last_heard) > 60) { /* shed clients after 1 minute of inactivity */ client_disconnect(client); continue; } if (nfds >= maxconn) { /* ignore clients that we are unable to handle */ continue; } fds[nfds].fd = client->sock_fd; fds[nfds].events = POLLIN; handler[nfds].type = CLIENT; handler[nfds].data = client; nfds++; } /* scan through server sockets */ for (server = firstaddr; server && (nfds < maxconn); server = server->next) { if (server->sock_fd < 0) { continue; } fds[nfds].fd = server->sock_fd; fds[nfds].events = POLLIN; handler[nfds].type = SERVER; handler[nfds].data = server; nfds++; } upsdebugx(2, "%s: polling %d filedescriptors", __func__, nfds); ret = poll(fds, nfds, 2000); if (ret == 0) { upsdebugx(2, "%s: no data available", __func__); return; } if (ret < 0) { upslog_with_errno(LOG_ERR, "%s", __func__); return; } for (i = 0; i < nfds; i++) { if (fds[i].revents & (POLLHUP|POLLERR|POLLNVAL)) { switch(handler[i].type) { case DRIVER: sstate_disconnect((upstype_t *)handler[i].data); break; case CLIENT: client_disconnect((nut_ctype_t *)handler[i].data); break; case SERVER: upsdebugx(2, "%s: server disconnected", __func__); break; default: upsdebugx(2, "%s: disconnected", __func__); break; } continue; } if (fds[i].revents & POLLIN) { switch(handler[i].type) { case DRIVER: sstate_readline((upstype_t *)handler[i].data); break; case CLIENT: client_readline((nut_ctype_t *)handler[i].data); break; case SERVER: client_connect((stype_t *)handler[i].data); break; default: upsdebugx(2, "%s: has data available", __func__); break; } continue; } } } static void help(const char *progname) { printf("Network server for UPS data.\n\n"); printf("usage: %s [OPTIONS]\n", progname); printf("\n"); printf(" -c send via signal to background process\n"); printf(" commands:\n"); printf(" - reload: reread configuration files\n"); printf(" - stop: stop process and exit\n"); printf(" -D raise debugging level\n"); printf(" -h display this help\n"); printf(" -r chroots to \n"); printf(" -q raise log level threshold\n"); printf(" -u switch to (if started as root)\n"); printf(" -V display the version of this software\n"); printf(" -4 IPv4 only\n"); printf(" -6 IPv6 only\n"); exit(EXIT_SUCCESS); } static void set_reload_flag(int sig) { reload_flag = 1; } static void set_exit_flag(int sig) { exit_flag = sig; } static void setup_signals(void) { struct sigaction sa; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGHUP); sa.sa_flags = 0; /* basic signal setup to ignore SIGPIPE */ sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); /* handle shutdown signals */ sa.sa_handler = set_exit_flag; sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* handle reloading */ sa.sa_handler = set_reload_flag; sigaction(SIGHUP, &sa, NULL); } void check_perms(const char *fn) { int ret; struct stat st; ret = stat(fn, &st); if (ret != 0) { fatal_with_errno(EXIT_FAILURE, "stat %s", fn); } /* include the x bit here in case we check a directory */ if (st.st_mode & (S_IROTH | S_IXOTH)) { upslogx(LOG_WARNING, "%s is world readable", fn); } } int main(int argc, char **argv) { int i, cmd = 0; char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; progname = xbasename(argv[0]); /* yes, xstrdup - the conf handlers call free on this later */ statepath = xstrdup(dflt_statepath()); datapath = xstrdup(DATADIR); /* set up some things for later */ snprintf(pidfn, sizeof(pidfn), "%s/%s.pid", altpidpath(), progname); printf("Network UPS Tools %s %s\n", progname, UPS_VERSION); while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:D")) != -1) { switch (i) { case 'h': help(progname); break; case 'p': case 'i': fatalx(EXIT_FAILURE, "Specifying a listening addresses with '-i
' and '-p '\n" "is deprecated. Use 'LISTEN
[]' in 'upsd.conf' instead.\n" "See 'man 8 upsd.conf' for more information."); case 'q': nut_log_level++; break; case 'r': chroot_path = optarg; break; case 'u': user = optarg; break; case 'V': /* do nothing - we already printed the banner */ exit(EXIT_SUCCESS); case 'c': if (!strncmp(optarg, "reload", strlen(optarg))) cmd = SIGCMD_RELOAD; if (!strncmp(optarg, "stop", strlen(optarg))) cmd = SIGCMD_STOP; /* bad command given */ if (cmd == 0) help(progname); break; case 'D': nut_debug_level++; break; case '4': opt_af = AF_INET; break; case '6': opt_af = AF_INET6; break; default: help(progname); break; } } if (cmd) { sendsignalfn(pidfn, cmd); exit(EXIT_SUCCESS); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ if (sendsignalfn(pidfn, 0) == 0) { printf("Fatal error: A previous upsd instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); } argc -= optind; argv += optind; if (argc != 0) { help(progname); } atexit(upsd_cleanup); setup_signals(); open_syslog(progname); /* send logging to the syslog pre-background for later use */ syslogbit_set(); /* do this here, since getpwnam() might not work in the chroot */ new_uid = get_user_pwent(user); if (chroot_path) { chroot_start(chroot_path); } /* default to system limit (may be overridden in upsd.conf */ maxconn = sysconf(_SC_OPEN_MAX); /* handle upsd.conf */ load_upsdconf(0); /* 0 = initial */ /* start server */ server_load(); become_user(new_uid); if (chdir(statepath)) { fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", statepath); } /* check statepath perms */ check_perms(statepath); /* handle ups.conf */ read_upsconf(); upsconf_add(0); /* 0 = initial */ poll_reload(); if (num_ups == 0) { fatalx(EXIT_FAILURE, "Fatal error: at least one UPS must be defined in ups.conf"); } /* try to bring in the var/cmd descriptions */ desc_load(); /* handle upsd.users */ user_load(); if (!nut_debug_level) { background(); writepid(pidfn); } else { memset(pidfn, 0, sizeof(pidfn)); } /* initialize SSL (keyfile must be readable by nut user) */ ssl_init(); while (!exit_flag) { mainloop(); } ssl_cleanup(); upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); return EXIT_SUCCESS; } nut-2.7.4/server/netmisc.h0000644000175000017500000000063012640444140012372 00000000000000#ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void net_ver(nut_ctype_t *client, int numarg, const char **arg); void net_netver(nut_ctype_t *client, int numarg, const char **arg); void net_help(nut_ctype_t *client, int numarg, const char **arg); void net_fsd(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/netinstcmd.c0000644000175000017500000000532712640443572013113 00000000000000/* netinstcmd.c - INSTCMD handler for upsd Copyright (C) 2003 Russell Kroll 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 "common.h" #include "upsd.h" #include "sstate.h" #include "state.h" #include "user.h" /* for user_checkinstcmd */ #include "neterr.h" #include "netinstcmd.h" static void send_instcmd(nut_ctype_t *client, const char *upsname, const char *cmdname, const char *value) { int found; upstype_t *ups; const cmdlist_t *ctmp; char sockcmd[SMALLBUF], esc[SMALLBUF]; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; ctmp = sstate_getcmdlist(ups); found = 0; while (ctmp) { if (!strcasecmp(ctmp->name, cmdname)) { found = 1; break; } ctmp = ctmp->next; } if (!found) { send_err(client, NUT_ERR_CMD_NOT_SUPPORTED); return; } /* see if this user is allowed to do this command */ if (!user_checkinstcmd(client->username, client->password, cmdname)) { send_err(client, NUT_ERR_ACCESS_DENIED); return; } /* see if the user has also passed a value for this command */ if (value != NULL) { upslogx(LOG_INFO, "Instant command: %s@%s did %s with value \"%s\" on %s", client->username, client->addr, cmdname, value, ups->name); snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s %s\n", cmdname, pconf_encode(value, esc, sizeof(esc))); } else { upslogx(LOG_INFO, "Instant command: %s@%s did %s on %s", client->username, client->addr, cmdname, ups->name); snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s\n", cmdname); } if (!sstate_sendline(ups, sockcmd)) { upslogx(LOG_INFO, "Set command send failed"); send_err(client, NUT_ERR_INSTCMD_FAILED); return; } /* FIXME: need to retrieve the cookie number */ sendback(client, "OK\n"); } void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) { if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* INSTCMD []*/ send_instcmd(client, arg[0], arg[1], (numarg == 3)?arg[2]:NULL); return; } nut-2.7.4/server/user-data.h0000644000175000017500000000226712640444140012625 00000000000000/* user-data.h - structures for user.c Copyright (C) 2001 Russell Kroll 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 */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif typedef struct { char *cmd; void *next; } instcmdlist_t; typedef struct { char *action; void *next; } actionlist_t; typedef struct { char *username; char *password; instcmdlist_t *firstcmd; actionlist_t *firstaction; void *next; } ulist_t; #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/sockdebug.c0000644000175000017500000000653412640443572012712 00000000000000/* sockdebug.c - Network UPS Tools driver-server socket debugger Copyright (C) 2003 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "common.h" #include "parseconf.h" PCONF_CTX_t sock_ctx; static void sock_arg(int numarg, char **arg) { int i; printf("numarg=%d : ", numarg); for (i = 0; i < numarg; i++) printf("[%s] ", arg[i]); printf("\n"); } static int socket_connect(const char *sockfn) { int ret, fd; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", sockfn); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); exit(EXIT_FAILURE); } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); if (ret < 0) { perror("connect"); exit(EXIT_FAILURE); } #if 0 ret = fcntl(fd, F_GETFL, 0); if (ret < 0) { perror("fcntl(get)"); exit(EXIT_FAILURE); } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); if (ret < 0) { perror("fcntl(set)"); exit(EXIT_FAILURE); } #endif return fd; } static void read_sock(int fd) { int i, ret; char buf[SMALLBUF]; ret = read(fd, buf, sizeof(buf)); if (ret == 0) { fprintf(stderr, "read on socket returned 0\n"); exit(EXIT_FAILURE); } if (ret < 0) { perror("read sockfd"); exit(EXIT_FAILURE); } for (i = 0; i < ret; i++) { switch (pconf_char(&sock_ctx, buf[i])) { case 1: sock_arg(sock_ctx.numargs, sock_ctx.arglist); break; case -1: printf("Parse error: [%s]\n", sock_ctx.errmsg); break; } } } int main(int argc, char **argv) { const char *prog = xbasename(argv[0]); int ret, sockfd; if (argc != 2) { fprintf(stderr, "usage: %s \n", prog); fprintf(stderr, " %s /var/state/ups/apcsmart-ttyS1.newsock\n", argv[0]); exit(EXIT_SUCCESS); } sockfd = socket_connect(argv[1]); printf("connected: fd %d\n", sockfd); pconf_init(&sock_ctx, NULL); for (;;) { struct timeval tv; fd_set rfds; int maxfd; tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(fileno(stdin), &rfds); FD_SET(sockfd, &rfds); /* paranoia */ maxfd = (sockfd > fileno(stdin)) ? sockfd : fileno(stdin); ret = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (FD_ISSET(sockfd, &rfds)) read_sock(sockfd); if (FD_ISSET(fileno(stdin), &rfds)) { char buf[SMALLBUF]; fgets(buf, sizeof(buf), stdin); ret = write(sockfd, buf, strlen(buf)); if ((ret < 0) || (ret != (int) strlen(buf))) { perror("write to socket"); exit(EXIT_FAILURE); } } } /* NOTREACHED */ exit(EXIT_FAILURE); } nut-2.7.4/server/netssl.c0000644000175000017500000003552612640473702012255 00000000000000/* netssl.c - Interface to OpenSSL for upsd Copyright (C) 2002 Russell Kroll 2008 Arjen de Korte based on the original implementation: Copyright (C) 2002 Technorama Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "upsd.h" #include "neterr.h" #include "netssl.h" #ifdef WITH_NSS #include #include #include #include #include #include #include #endif /* WITH_NSS */ char *certfile = NULL; char *certname = NULL; char *certpasswd = NULL; #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION int certrequest = 0; #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ static int ssl_initialized = 0; #ifndef WITH_SSL /* stubs for non-ssl compiles */ void net_starttls(nut_ctype_t *client, int numarg, const char **arg) { send_err(client, NUT_ERR_FEATURE_NOT_SUPPORTED); return; } int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) { upslogx(LOG_ERR, "ssl_write called but SSL wasn't compiled in"); return -1; } int ssl_read(nut_ctype_t *client, char *buf, size_t buflen) { upslogx(LOG_ERR, "ssl_read called but SSL wasn't compiled in"); return -1; } void ssl_init(void) { ssl_initialized = 0; /* keep gcc quiet */ } void ssl_finish(nut_ctype_t *client) { if (client->ssl) { upslogx(LOG_ERR, "ssl_finish found active SSL connection but SSL wasn't compiled in"); } } void ssl_cleanup(void) { } #else #ifdef WITH_OPENSSL static SSL_CTX *ssl_ctx = NULL; static void ssl_debug(void) { int e; char errmsg[SMALLBUF]; while ((e = ERR_get_error()) != 0) { ERR_error_string_n(e, errmsg, sizeof(errmsg)); upsdebugx(1, "ssl_debug: %s", errmsg); } } static int ssl_error(SSL *ssl, int ret) { int e; e = SSL_get_error(ssl, ret); switch (e) { case SSL_ERROR_WANT_READ: upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_WANT_READ", ret); break; case SSL_ERROR_WANT_WRITE: upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_WANT_WRITE", ret); break; case SSL_ERROR_SYSCALL: if (ret == 0 && ERR_peek_error() == 0) { upsdebugx(1, "ssl_error() EOF from client"); } else { upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_SYSCALL", ret); } break; default: upsdebugx(1, "ssl_error() ret=%d SSL_ERROR %d", ret, e); ssl_debug(); } return -1; } #elif defined(WITH_NSS) /* WITH_OPENSSL */ static CERTCertificate *cert; static SECKEYPrivateKey *privKey; static char *nss_password_callback(PK11SlotInfo *slot, PRBool retry, void *arg) { if (retry) { /* Force not inted to retrieve password many times. */ return NULL; } upslogx(LOG_INFO, "Intend to retrieve password for %s / %s: password %sconfigured", PK11_GetSlotName(slot), PK11_GetTokenName(slot), certpasswd?"":"not "); return certpasswd ? PL_strdup(certpasswd) : NULL; } static void nss_error(const char* text) { char buffer[SMALLBUF]; PRInt32 length = PR_GetErrorText(buffer); if (length > 0 && length < SMALLBUF) { upsdebugx(1, "nss_error %ld in %s : %s", (long)PR_GetError(), text, buffer); }else{ upsdebugx(1, "nss_error %ld in %s", (long)PR_GetError(), text); } } static int ssl_error(PRFileDesc *ssl, int ret) { char buffer[256]; PRInt32 length; PRErrorCode e; e = PR_GetError(); length = PR_GetErrorText(buffer); if (length > 0 && length < 256) { upsdebugx(1, "ssl_error() ret=%d %*s", e, length, buffer); } else { upsdebugx(1, "ssl_error() ret=%d", e); } return -1; } static SECStatus AuthCertificate(CERTCertDBHandle *arg, PRFileDesc *fd, PRBool checksig, PRBool isServer) { nut_ctype_t *client = (nut_ctype_t *)SSL_RevealPinArg(fd); SECStatus status = SSL_AuthCertificate(arg, fd, checksig, isServer); upslogx(LOG_INFO, "Intend to authenticate client %s : %s.", client?client->addr:"(unnamed)", status==SECSuccess?"SUCCESS":"FAILED"); return status; } static SECStatus BadCertHandler(nut_ctype_t *arg, PRFileDesc *fd) { upslogx(LOG_WARNING, "Certificate validation failed for %s", (arg&&arg->addr)?arg->addr:""); #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION /* BadCertHandler is called when the NSS certificate validation is failed. * If the certificate verification (user conf) is mandatory, reject authentication * else accept it. */ return certrequest==NETSSL_CERTREQ_REQUIRE?SECFailure:SECSuccess; #else /* WITH_CLIENT_CERTIFICATE_VALIDATION */ /* Always accept clients. */ return SECSuccess; #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ } static void HandshakeCallback(PRFileDesc *fd, nut_ctype_t *client_data) { upslogx(LOG_INFO, "SSL handshake done successfully with client %s", client_data->addr); } #endif /* WITH_OPENSSL | WITH_NSS */ void net_starttls(nut_ctype_t *client, int numarg, const char **arg) { #ifdef WITH_OPENSSL int ret; #elif defined(WITH_NSS) /* WITH_OPENSSL */ SECStatus status; PRFileDesc *socket; #endif /* WITH_OPENSSL | WITH_NSS */ if (client->ssl) { send_err(client, NUT_ERR_ALREADY_SSL_MODE); return; } client->ssl_connected = 0; if ((!certfile) || (!ssl_initialized)) { send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); return; } #ifdef WITH_OPENSSL if (!ssl_ctx) { #elif defined(WITH_NSS) /* WITH_OPENSSL */ if (!NSS_IsInitialized()) { #endif /* WITH_OPENSSL | WITH_NSS */ send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); ssl_initialized = 0; return; } if (!sendback(client, "OK STARTTLS\n")) { return; } #ifdef WITH_OPENSSL client->ssl = SSL_new(ssl_ctx); if (!client->ssl) { upslog_with_errno(LOG_ERR, "SSL_new failed\n"); ssl_debug(); return; } if (SSL_set_fd(client->ssl, client->sock_fd) != 1) { upslog_with_errno(LOG_ERR, "SSL_set_fd failed\n"); ssl_debug(); return; } ret = SSL_accept(client->ssl); switch (ret) { case 1: client->ssl_connected = 1; upsdebugx(3, "SSL connected"); break; case 0: upslog_with_errno(LOG_ERR, "SSL_accept do not accept handshake."); ssl_error(client->ssl, ret); break; case -1: upslog_with_errno(LOG_ERR, "Unknown return value from SSL_accept"); ssl_error(client->ssl, ret); break; } #elif defined(WITH_NSS) /* WITH_OPENSSL */ socket = PR_ImportTCPSocket(client->sock_fd); if (socket == NULL){ upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / PR_ImportTCPSocket"); return; } client->ssl = SSL_ImportFD(NULL, socket); if (client->ssl == NULL){ upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_ImportFD"); return; } if (SSL_SetPKCS11PinArg(client->ssl, client) == -1){ upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_SetPKCS11PinArg"); return; } /* Note cast to SSLAuthCertificate to prevent warning due to * bad function prototype in NSS. */ status = SSL_AuthCertificateHook(client->ssl, (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB()); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_AuthCertificateHook"); return; } status = SSL_BadCertHook(client->ssl, (SSLBadCertHandler)BadCertHandler, client); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_BadCertHook"); return; } status = SSL_HandshakeCallback(client->ssl, (SSLHandshakeCallback)HandshakeCallback, client); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_HandshakeCallback"); return; } status = SSL_ConfigSecureServer(client->ssl, cert, privKey, NSS_FindCertKEAType(cert)); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_ConfigSecureServer"); return; } status = SSL_ResetHandshake(client->ssl, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not inialize SSL connection"); nss_error("net_starttls / SSL_ResetHandshake"); return; } /* Note: this call can generate memory leaks not resolvable * by any release function. * Probably SSL session key object allocation. */ status = SSL_ForceHandshake(client->ssl); if (status != SECSuccess) { PRErrorCode code = PR_GetError(); if (code==SSL_ERROR_NO_CERTIFICATE) { upslogx(LOG_WARNING, "Client %s do not provide certificate.", client->addr); } else { nss_error("net_starttls / SSL_ForceHandshake"); /* TODO : Close the connection. */ return; } } client->ssl_connected = 1; #endif /* WITH_OPENSSL | WITH_NSS */ } void ssl_init(void) { #ifdef WITH_NSS SECStatus status; #elif defined(WITH_OPENSSL) #if OPENSSL_VERSION_NUMBER >= 0x10000000L const SSL_METHOD *ssl_method; #else SSL_METHOD *ssl_method; #endif #endif /* WITH_NSS|WITH_OPENSSL */ if (!certfile) { return; } check_perms(certfile); #ifdef WITH_OPENSSL SSL_load_error_strings(); SSL_library_init(); if ((ssl_method = TLSv1_server_method()) == NULL) { ssl_debug(); fatalx(EXIT_FAILURE, "TLSv1_server_method failed"); } if ((ssl_ctx = SSL_CTX_new(ssl_method)) == NULL) { ssl_debug(); fatalx(EXIT_FAILURE, "SSL_CTX_new failed"); } if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) { ssl_debug(); fatalx(EXIT_FAILURE, "SSL_CTX_use_certificate_chain_file(%s) failed", certfile); } if (SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != 1) { ssl_debug(); fatalx(EXIT_FAILURE, "SSL_CTX_use_PrivateKey_file(%s) failed", certfile); } if (SSL_CTX_check_private_key(ssl_ctx) != 1) { ssl_debug(); fatalx(EXIT_FAILURE, "SSL_CTX_check_private_key(%s) failed", certfile); } if (SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:@STRENGTH") != 1) { ssl_debug(); fatalx(EXIT_FAILURE, "SSL_CTX_set_cipher_list failed"); } SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); ssl_initialized = 1; #elif defined(WITH_NSS) /* WITH_OPENSSL */ if (!certname || certname[0]==0 ) { upslogx(LOG_ERR, "The SSL certificate name is not specified."); return; } PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); PK11_SetPasswordFunc(nss_password_callback); if (certfile) /* Note: this call can generate memory leaks not resolvable * by any release function. * Probably NSS key module object allocation and * probably NSS key db object allocation too. */ status = NSS_Init(certfile); else status = NSS_NoDB_Init(NULL); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not initialize SSL context"); nss_error("upscli_init / NSS_[NoDB]_Init"); return; } status = NSS_SetDomesticPolicy(); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not initialize SSL policy"); nss_error("upscli_init / NSS_SetDomesticPolicy"); return; } /* Default server cache config */ status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not initialize SSL server cache"); nss_error("upscli_init / SSL_ConfigServerSessionIDCache"); return; } status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable SSLv3"); nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)"); return; } status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable TLSv1"); nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); return; } #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION if (certrequest < NETSSL_CERTREQ_NO && certrequest > NETSSL_CERTREQ_REQUEST) { upslogx(LOG_ERR, "Invalid certificate requirement"); return; } if (certrequest == NETSSL_CERTREQ_REQUEST || certrequest == NETSSL_CERTREQ_REQUIRE ) { status = SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable certificate request"); nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE)"); return; } } if (certrequest == NETSSL_CERTREQ_REQUIRE ) { status = SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable certificate requirement"); nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE)"); return; } } #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ cert = PK11_FindCertFromNickname(certname, NULL); if(cert==NULL) { upslogx(LOG_ERR, "Can not find server certificate"); nss_error("upscli_init / PK11_FindCertFromNickname"); return; } privKey = PK11_FindKeyByAnyCert(cert, NULL); if(privKey==NULL){ upslogx(LOG_ERR, "Can not find private key associate to server certificate"); nss_error("upscli_init / PK11_FindKeyByAnyCert"); return; } ssl_initialized = 1; #else /* WITH_OPENSSL | WITH_NSS */ upslogx(LOG_ERR, "ssl_init called but SSL wasn't compiled in"); #endif /* WITH_OPENSSL | WITH_NSS */ } int ssl_read(nut_ctype_t *client, char *buf, size_t buflen) { int ret; if (!client->ssl_connected) { return -1; } #ifdef WITH_OPENSSL ret = SSL_read(client->ssl, buf, buflen); #elif defined(WITH_NSS) /* WITH_OPENSSL */ ret = PR_Read(client->ssl, buf, buflen); #endif /* WITH_OPENSSL | WITH_NSS */ if (ret < 1) { ssl_error(client->ssl, ret); return -1; } return ret; } int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) { int ret; if (!client->ssl_connected) { return -1; } #ifdef WITH_OPENSSL ret = SSL_write(client->ssl, buf, buflen); #elif defined(WITH_NSS) /* WITH_OPENSSL */ ret = PR_Write(client->ssl, buf, buflen); #endif /* WITH_OPENSSL | WITH_NSS */ upsdebugx(5, "ssl_write ret=%d", ret); return ret; } void ssl_finish(nut_ctype_t *client) { if (client->ssl) { #ifdef WITH_OPENSSL SSL_free(client->ssl); #elif defined(WITH_NSS) PR_Shutdown(client->ssl, PR_SHUTDOWN_BOTH); PR_Close(client->ssl); #endif /* WITH_OPENSSL | WITH_NSS */ client->ssl_connected = 0; client->ssl = NULL; } } void ssl_cleanup(void) { #ifdef WITH_OPENSSL if (ssl_ctx) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } #elif defined(WITH_NSS) /* WITH_OPENSSL */ CERT_DestroyCertificate(cert); SECKEY_DestroyPrivateKey(privKey); NSS_Shutdown(); PR_Cleanup(); /* Called to release memory arena used by NSS/NSPR. * Prevent to show all PL_ArenaAllocate mem alloc as leaks. * https://developer.mozilla.org/en/NSS_Memory_allocation */ PL_ArenaFinish(); #endif /* WITH_OPENSSL | WITH_NSS */ ssl_initialized = 0; } #endif /* WITH_SSL */ nut-2.7.4/server/sstate.c0000644000175000017500000002101712640473702012236 00000000000000/* sstate.c - Network UPS Tools server-side state management Copyright (C) 2003 Russell Kroll 2008 Arjen de Korte 2012 Arnaud Quette 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 "common.h" #include "timehead.h" #include "sstate.h" #include "upstype.h" #include #include #include #include #include #include #include static int parse_args(upstype_t *ups, int numargs, char **arg) { if (numargs < 1) return 0; if (!strcasecmp(arg[0], "PONG")) { upsdebugx(3, "Got PONG from UPS [%s]", ups->name); return 1; } if (!strcasecmp(arg[0], "DUMPDONE")) { upsdebugx(3, "UPS [%s]: dump is done", ups->name); ups->dumpdone = 1; return 1; } if (!strcasecmp(arg[0], "DATASTALE")) { ups->data_ok = 0; return 1; } if (!strcasecmp(arg[0], "DATAOK")) { ups->data_ok = 1; return 1; } if (numargs < 2) return 0; /* FIXME: all these should return their state_...() value! */ /* ADDCMD */ if (!strcasecmp(arg[0], "ADDCMD")) { state_addcmd(&ups->cmdlist, arg[1]); return 1; } /* DELCMD */ if (!strcasecmp(arg[0], "DELCMD")) { state_delcmd(&ups->cmdlist, arg[1]); return 1; } /* DELINFO */ if (!strcasecmp(arg[0], "DELINFO")) { state_delinfo(&ups->inforoot, arg[1]); return 1; } if (numargs < 3) return 0; /* SETFLAGS ... */ if (!strcasecmp(arg[0], "SETFLAGS")) { state_setflags(ups->inforoot, arg[1], numargs - 2, &arg[2]); return 1; } /* SETINFO */ if (!strcasecmp(arg[0], "SETINFO")) { state_setinfo(&ups->inforoot, arg[1], arg[2]); return 1; } /* ADDENUM */ if (!strcasecmp(arg[0], "ADDENUM")) { state_addenum(ups->inforoot, arg[1], arg[2]); return 1; } /* ADDRANGE */ if (!strcasecmp(arg[0], "ADDRANGE")) { state_addrange(ups->inforoot, arg[1], atoi(arg[2]), atoi(arg[3])); return 1; } /* DELENUM */ if (!strcasecmp(arg[0], "DELENUM")) { state_delenum(ups->inforoot, arg[1], arg[2]); return 1; } /* DELRANGE */ if (!strcasecmp(arg[0], "DELRANGE")) { state_delrange(ups->inforoot, arg[1], atoi(arg[2]), atoi(arg[3])); return 1; } /* SETAUX */ if (!strcasecmp(arg[0], "SETAUX")) { state_setaux(ups->inforoot, arg[1], arg[2]); return 1; } return 0; } /* nothing fancy - just make the driver say something back to us */ static void sendping(upstype_t *ups) { int ret; const char *cmd = "PING\n"; if ((!ups) || (ups->sock_fd < 0)) { return; } upsdebugx(3, "Pinging UPS [%s]", ups->name); ret = write(ups->sock_fd, cmd, strlen(cmd)); if (ret != (int)strlen(cmd)) { upslog_with_errno(LOG_NOTICE, "Send ping to UPS [%s] failed", ups->name); sstate_disconnect(ups); return; } time(&ups->last_ping); } /* interface */ int sstate_connect(upstype_t *ups) { int ret, fd; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", ups->fn); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", ups->name); return -1; } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); if (ret < 0) { time_t now; close(fd); /* rate-limit complaints - don't spam the syslog */ time(&now); if (difftime(now, ups->last_connfail) < SS_CONNFAIL_INT) return -1; ups->last_connfail = now; upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s] (%s)", ups->name, ups->fn); return -1; } ret = fcntl(fd, F_GETFL, 0); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", ups->name); close(fd); return -1; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", ups->name); close(fd); return -1; } /* get a dump started so we have a fresh set of data */ ret = write(fd, dumpcmd, strlen(dumpcmd)); if (ret != (int)strlen(dumpcmd)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", ups->name); close(fd); return -1; } pconf_init(&ups->sock_ctx, NULL); ups->dumpdone = 0; ups->stale = 0; /* now is the last time we heard something from the driver */ time(&ups->last_heard); /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ state_setinfo(&ups->inforoot, "ups.status", "WAIT"); upslogx(LOG_INFO, "Connected to UPS [%s]: %s", ups->name, ups->fn); return fd; } void sstate_disconnect(upstype_t *ups) { if ((!ups) || (ups->sock_fd < 0)) { return; } sstate_infofree(ups); sstate_cmdfree(ups); pconf_finish(&ups->sock_ctx); close(ups->sock_fd); ups->sock_fd = -1; } void sstate_readline(upstype_t *ups) { int i, ret; char buf[SMALLBUF]; if ((!ups) || (ups->sock_fd < 0)) { return; } ret = read(ups->sock_fd, buf, sizeof(buf)); if (ret < 0) { switch(errno) { case EINTR: case EAGAIN: return; default: upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", ups->name); sstate_disconnect(ups); return; } } for (i = 0; i < ret; i++) { switch (pconf_char(&ups->sock_ctx, buf[i])) { case 1: /* set the 'last heard' time to now for later staleness checks */ if (parse_args(ups, ups->sock_ctx.numargs, ups->sock_ctx.arglist)) { time(&ups->last_heard); } continue; case 0: continue; /* haven't gotten a line yet */ default: /* parse error */ upslogx(LOG_NOTICE, "Parse error on sock: %s", ups->sock_ctx.errmsg); return; } } } const char *sstate_getinfo(const upstype_t *ups, const char *var) { return state_getinfo(ups->inforoot, var); } int sstate_getflags(const upstype_t *ups, const char *var) { return state_getflags(ups->inforoot, var); } int sstate_getaux(const upstype_t *ups, const char *var) { return state_getaux(ups->inforoot, var); } const enum_t *sstate_getenumlist(const upstype_t *ups, const char *var) { return state_getenumlist(ups->inforoot, var); } const range_t *sstate_getrangelist(const upstype_t *ups, const char *var) { return state_getrangelist(ups->inforoot, var); } const cmdlist_t *sstate_getcmdlist(const upstype_t *ups) { return ups->cmdlist; } int sstate_dead(upstype_t *ups, int maxage) { time_t now; double elapsed; /* an unconnected ups is always dead */ if (ups->sock_fd < 0) { upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", ups->name); return 1; /* dead */ } time(&now); /* ignore DATAOK/DATASTALE unless the dump is done */ if ((ups->dumpdone) && (!ups->data_ok)) { upsdebugx(3, "sstate_dead: driver for UPS [%s] says data is stale", ups->name); return 1; /* dead */ } elapsed = difftime(now, ups->last_heard); /* somewhere beyond a third of the maximum time - prod it to make it talk */ if ((elapsed > (maxage / 3)) && (difftime(now, ups->last_ping) > (maxage / 3))) sendping(ups); if (elapsed > maxage) { upsdebugx(3, "sstate_dead: didn't hear from driver for UPS [%s] for %g seconds (max %d)", ups->name, elapsed, maxage); return 1; /* dead */ } return 0; } /* release all info(tree) data used by */ void sstate_infofree(upstype_t *ups) { state_infofree(ups->inforoot); ups->inforoot = NULL; } void sstate_cmdfree(upstype_t *ups) { state_cmdfree(ups->cmdlist); ups->cmdlist = NULL; } int sstate_sendline(upstype_t *ups, const char *buf) { int ret; if ((!ups) ||(ups->sock_fd < 0)) { return 0; /* failed */ } ret = write(ups->sock_fd, buf, strlen(buf)); if (ret == (int)strlen(buf)) { return 1; } upslog_with_errno(LOG_NOTICE, "Send to UPS [%s] failed", ups->name); sstate_disconnect(ups); return 0; /* failed */ } const st_tree_t *sstate_getnode(const upstype_t *ups, const char *varname) { return state_tree_find(ups->inforoot, varname); } nut-2.7.4/server/netset.c0000644000175000017500000000701512640443572012241 00000000000000/* netset.c - SET handler for upsd Copyright (C) 2003 Russell Kroll 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 "common.h" #include "upsd.h" #include "sstate.h" #include "state.h" #include "user.h" /* for user_checkaction */ #include "neterr.h" #include "netset.h" static void set_var(nut_ctype_t *client, const char *upsname, const char *var, const char *newval) { upstype_t *ups; const char *val; const enum_t *etmp; const range_t *rtmp; char cmd[SMALLBUF], esc[SMALLBUF]; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; /* make sure this user is allowed to do SET */ if (!user_checkaction(client->username, client->password, "SET")) { send_err(client, NUT_ERR_ACCESS_DENIED); return; } val = sstate_getinfo(ups, var); if (!val) { send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); return; } /* make sure this variable is writable (RW) */ if ((sstate_getflags(ups, var) & ST_FLAG_RW) == 0) { send_err(client, NUT_ERR_READONLY); return; } /* see if the new value is allowed for this variable */ if (sstate_getflags(ups, var) & ST_FLAG_STRING) { int aux; aux = sstate_getaux(ups, var); /* check for insanity from the driver */ if (aux < 1) { upslogx(LOG_WARNING, "UPS [%s]: auxdata for %s is invalid", ups->name, var); send_err(client, NUT_ERR_SET_FAILED); return; } if (aux < (int) strlen(newval)) { send_err(client, NUT_ERR_TOO_LONG); return; } } /* see if it's enumerated */ etmp = sstate_getenumlist(ups, var); if (etmp) { int found = 0; while (etmp) { if (!strcmp(etmp->val, newval)) { found = 1; break; } etmp = etmp->next; } if (!found) { send_err(client, NUT_ERR_INVALID_VALUE); return; } } /* or if it's within a range */ rtmp = sstate_getrangelist(ups, var); if (rtmp) { int found = 0; int inewval = atoi(newval); while (rtmp) { if ((inewval >= rtmp->min) && (inewval <= rtmp->max)) { found = 1; break; } rtmp = rtmp->next; } if (!found) { send_err(client, NUT_ERR_INVALID_VALUE); return; } } /* must be OK now */ upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s", client->username, client->addr, var, ups->name, newval); snprintf(cmd, sizeof(cmd), "SET %s \"%s\"\n", var, pconf_encode(newval, esc, sizeof(esc))); if (!sstate_sendline(ups, cmd)) { upslogx(LOG_INFO, "Set command send failed"); send_err(client, NUT_ERR_SET_FAILED); return; } sendback(client, "OK\n"); } void net_set(nut_ctype_t *client, int numarg, const char **arg) { if (numarg < 4) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* SET VAR UPS VARNAME VALUE */ if (!strcasecmp(arg[0], "VAR")) { set_var(client, arg[1], arg[2], arg[3]); return; } send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } nut-2.7.4/server/netuser.h0000644000175000017500000000074712640444140012426 00000000000000#ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void net_login(nut_ctype_t *client, int numarg, const char **arg); void net_logout(nut_ctype_t *client, int numarg, const char **arg); void net_master(nut_ctype_t *client, int numarg, const char **arg); void net_username(nut_ctype_t *client, int numarg, const char **arg); void net_password(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/sstate.h0000644000175000017500000000442212640473702012244 00000000000000/* sstate.h - Network UPS Tools server-side state management Copyright (C) 2003 Russell Kroll 2008 Arjen de Korte 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SSTATE_H_SEEN #define SSTATE_H_SEEN #include "state.h" #include "upstype.h" #define SS_CONNFAIL_INT 300 /* complain about a dead driver every 5 mins */ #define SS_MAX_READ 256 /* don't let drivers tie us up in read() */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif int sstate_connect(upstype_t *ups); void sstate_disconnect(upstype_t *ups); void sstate_readline(upstype_t *ups); const char *sstate_getinfo(const upstype_t *ups, const char *var); int sstate_getflags(const upstype_t *ups, const char *var); int sstate_getaux(const upstype_t *ups, const char *var); const enum_t *sstate_getenumlist(const upstype_t *ups, const char *var); const range_t *sstate_getrangelist(const upstype_t *ups, const char *var); const cmdlist_t *sstate_getcmdlist(const upstype_t *ups); void sstate_makeinfolist(const upstype_t *ups, char *buf, size_t bufsize); void sstate_makerwlist(const upstype_t *ups, char *buf, size_t bufsize); void sstate_makeinstcmdlist_t(const upstype_t *ups, char *buf, size_t bufsize); int sstate_dead(upstype_t *ups, int maxage); void sstate_infofree(upstype_t *ups); void sstate_cmdfree(upstype_t *ups); int sstate_sendline(upstype_t *ups, const char *buf); const st_tree_t *sstate_getnode(const upstype_t *ups, const char *varname); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif /* SSTATE_H_SEEN */ nut-2.7.4/server/user.h0000644000175000017500000000236612640444140011716 00000000000000/* user.c - supporting elements of user handling functions for upsd Copyright (C) 2001 Russell Kroll 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 */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void user_load(void); int user_checkinstcmd(const char *un, const char *pw, const char *cmd); int user_checkaction(const char *un, const char *pw, const char *action); void user_flush(void); /* cheat - we don't want the full upsd.h included here */ void check_perms(const char *fn); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/user.c0000644000175000017500000002157712640473702011724 00000000000000/* user.c - user handling functions for upsd Copyright (C) 2001 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "common.h" #include "parseconf.h" #include "user.h" #include "user-data.h" ulist_t *users = NULL; static ulist_t *curr_user; /* create a new user entry */ static void user_add(const char *un) { ulist_t *tmp, *last = NULL; if (!un) { return; } for (tmp = users; tmp != NULL; tmp = tmp->next) { last = tmp; if (!strcmp(tmp->username, un)) { fprintf(stderr, "Ignoring duplicate user %s\n", un); return; } } tmp = xcalloc(1, sizeof(*tmp)); tmp->username = xstrdup(un); if (last) { last->next = tmp; } else { users = tmp; } /* remember who we're working on */ curr_user = tmp; } /* set password */ static void user_password(const char *pw) { if (!curr_user) { upslogx(LOG_WARNING, "Ignoring password definition outside " "user section"); return; } if (!pw) { return; } if (curr_user->password) { fprintf(stderr, "Ignoring duplicate password for %s\n", curr_user->username); return; } curr_user->password = xstrdup(pw); } /* attach allowed instcmds to user */ static void user_add_instcmd(const char *cmd) { instcmdlist_t *tmp, *last = NULL; if (!curr_user) { upslogx(LOG_WARNING, "Ignoring instcmd definition outside " "user section"); return; } if (!cmd) { return; } for (tmp = curr_user->firstcmd; tmp != NULL; tmp = tmp->next) { last = tmp; /* ignore duplicates */ if (!strcasecmp(tmp->cmd, cmd)) { return; } } upsdebugx(2, "user_add_instcmd: adding '%s' for %s", cmd, curr_user->username); tmp = xcalloc(1, sizeof(*tmp)); tmp->cmd = xstrdup(cmd); if (last) { last->next = tmp; } else { curr_user->firstcmd = tmp; } } static actionlist_t *addaction(actionlist_t *base, const char *action) { actionlist_t *tmp, *last = NULL; if (!action) { return base; } for (tmp = base; tmp != NULL; tmp = tmp->next) { last = tmp; } tmp = xcalloc(1, sizeof(*tmp)); tmp->action = xstrdup(action); if (last) { last->next = tmp; return base; } return tmp; } /* attach allowed actions to user */ static void user_add_action(const char *act) { if (!curr_user) { upslogx(LOG_WARNING, "Ignoring action definition outside " "user section"); return; } upsdebugx(2, "user_add_action: adding '%s' for %s", act, curr_user->username); curr_user->firstaction = addaction(curr_user->firstaction, act); } static void flushcmd(instcmdlist_t *ptr) { if (!ptr) { return; } flushcmd(ptr->next); free(ptr->cmd); free(ptr); } static void flushaction(actionlist_t *ptr) { if (!ptr) { return; } flushaction(ptr->next); free(ptr->action); free(ptr); } static void flushuser(ulist_t *ptr) { if (!ptr) { return; } flushuser(ptr->next); flushcmd(ptr->firstcmd); flushaction(ptr->firstaction); free(ptr->username); free(ptr->password); free(ptr); } /* flush all user attributes - used during reload */ void user_flush(void) { flushuser(users); users = NULL; } static int user_matchinstcmd(ulist_t *user, const char * cmd) { instcmdlist_t *tmp; for (tmp = user->firstcmd; tmp != NULL; tmp = tmp->next) { if (!strcasecmp(tmp->cmd, cmd)) { return 1; /* good */ } if (!strcasecmp(tmp->cmd, "all")) { return 1; /* good */ } } return 0; /* fail */ } int user_checkinstcmd(const char *un, const char *pw, const char *cmd) { ulist_t *tmp; if ((!un) || (!pw) || (!cmd)) { return 0; /* failed */ } for (tmp = users; tmp != NULL; tmp = tmp->next) { /* let's be paranoid before we call strcmp */ if ((!tmp->username) || (!tmp->password)) { continue; } if (strcmp(tmp->username, un)) { continue; } if (strcmp(tmp->password, pw)) { /* password mismatch */ return 0; /* fail */ } if (!user_matchinstcmd(tmp, cmd)) { return 0; /* fail */ } /* passed all checks */ return 1; /* good */ } /* username not found */ return 0; /* fail */ } static int user_matchaction(ulist_t *user, const char *action) { actionlist_t *tmp; for (tmp = user->firstaction; tmp != NULL; tmp = tmp->next) { if (!strcasecmp(tmp->action, action)) { return 1; /* good */ } } return 0; /* fail */ } int user_checkaction(const char *un, const char *pw, const char *action) { ulist_t *tmp; if ((!un) || (!pw) || (!action)) return 0; /* failed */ for (tmp = users; tmp != NULL; tmp = tmp->next) { /* let's be paranoid before we call strcmp */ if ((!tmp->username) || (!tmp->password)) { continue; } if (strcmp(tmp->username, un)) { continue; } if (strcmp(tmp->password, pw)) { upsdebugx(2, "user_checkaction: password mismatch"); return 0; /* fail */ } if (!user_matchaction(tmp, action)) { upsdebugx(2, "user_matchaction: failed"); return 0; /* fail */ } /* passed all checks */ return 1; /* good */ } /* username not found */ return 0; /* fail */ } /* handle "upsmon master" and "upsmon slave" for nicer configurations */ static void set_upsmon_type(char *type) { /* master: login, master, fsd */ if (!strcasecmp(type, "master")) { user_add_action("login"); user_add_action("master"); user_add_action("fsd"); return; } /* slave: just login */ if (!strcasecmp(type, "slave")) { user_add_action("login"); return; } upslogx(LOG_WARNING, "Unknown upsmon type %s", type); } /* actually do something with the variable + value pairs */ static void parse_var(char *var, char *val) { if (!strcasecmp(var, "password")) { user_password(val); return; } if (!strcasecmp(var, "instcmds")) { user_add_instcmd(val); return; } if (!strcasecmp(var, "actions")) { user_add_action(val); return; } if (!strcasecmp(var, "allowfrom")) { upslogx(LOG_WARNING, "allowfrom in upsd.users is no longer used"); return; } /* someone did 'upsmon = type' - allow it anyway */ if (!strcasecmp(var, "upsmon")) { set_upsmon_type(val); return; } upslogx(LOG_NOTICE, "Unrecognized user setting %s", var); } /* parse first var+val pair, then flip through remaining vals */ static void parse_rest(char *var, char *fval, char **arg, int next, int left) { int i; /* no globals supported yet, so there's no sense in continuing */ if (!curr_user) { return; } parse_var(var, fval); if (left == 0) { return; } for (i = 0; i < left; i++) { parse_var(var, arg[next + i]); } } static void user_parse_arg(int numargs, char **arg) { char *ep; if ((numargs == 0) || (!arg)) { return; } /* ignore old file format */ if (!strcasecmp(arg[0], "user")) { return; } /* handle 'foo=bar' (compressed form) */ ep = strchr(arg[0], '='); if (ep) { *ep = '\0'; /* parse first var/val, plus subsequent values (if any) */ /* 0 1 2 ... */ /* foo=bar ... */ parse_rest(arg[0], ep+1, arg, 1, numargs - 1); return; } /* look for section headers - [username] */ if ((arg[0][0] == '[') && (arg[0][strlen(arg[0])-1] == ']')) { arg[0][strlen(arg[0])-1] = '\0'; user_add(&arg[0][1]); return; } if (numargs < 2) { return; } if (!strcasecmp(arg[0], "upsmon")) { set_upsmon_type(arg[1]); } /* everything after here needs arg[1] and arg[2] */ if (numargs < 3) { return; } /* handle 'foo = bar' (split form) */ if (!strcmp(arg[1], "=")) { /* 0 1 2 3 4 ... */ /* foo = bar ... */ /* parse first var/val, plus subsequent values (if any) */ parse_rest(arg[0], arg[2], arg, 3, numargs - 3); return; } /* ... unhandled ... */ } /* called for fatal errors in parseconf like malloc failures */ static void upsd_user_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf(upsd.users): %s", errmsg); } void user_load(void) { char fn[SMALLBUF]; PCONF_CTX_t ctx; curr_user = NULL; snprintf(fn, sizeof(fn), "%s/upsd.users", confpath()); check_perms(fn); pconf_init(&ctx, upsd_user_err); if (!pconf_file_begin(&ctx, fn)) { pconf_finish(&ctx); upslogx(LOG_WARNING, "%s", ctx.errmsg); return; } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); continue; } user_parse_arg(ctx.numargs, ctx.arglist); } pconf_finish(&ctx); } nut-2.7.4/server/desc.h0000644000175000017500000000042512640444140011650 00000000000000 #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void desc_load(void); void desc_free(void); const char *desc_get_cmd(const char *name); const char *desc_get_var(const char *name); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/server/nut_ctype.h0000644000175000017500000000306112640473702012751 00000000000000/* nut_ctype.h - client data definitions for upsd Copyright (C) 2002 Russell Kroll 2008 Arjen de Korte 2011 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NUT_CTYPE_H_SEEN #define NUT_CTYPE_H_SEEN 1 /* Mozilla NSS */ #ifdef WITH_NSS #include #include #endif /* OpenSSL */ #ifdef WITH_OPENSSL #include #include #endif #include "parseconf.h" /* client structure */ typedef struct nut_ctype_s { char *addr; int sock_fd; time_t last_heard; char *loginups; char *password; char *username; #ifdef WITH_OPENSSL SSL *ssl; #elif defined(WITH_NSS) PRFileDesc *ssl; #else void *ssl; #endif int ssl_connected; PCONF_CTX_t ctx; /* doubly linked list */ struct nut_ctype_s *prev; struct nut_ctype_s *next; } nut_ctype_t; #endif /* NUT_CTYPE_H_SEEN */ nut-2.7.4/server/netssl.h0000644000175000017500000000341212640444140012241 00000000000000/* netssl.h - ssl support prototypes for upsd Copyright (C) 2002 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NETSSL_H_SEEN #define NETSSL_H_SEEN 1 #include "nut_ctype.h" #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif extern char *certfile; extern char *certname; extern char *certpasswd; #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION extern int certrequest; #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ /* List possible values for certrequested */ /* No request */ #define NETSSL_CERTREQ_NO 0 /* Requested (cnx failed if no certificate) */ #define NETSSL_CERTREQ_REQUEST 1 /* Required (cnx failed if no certificate or invalid CA chain) */ #define NETSSL_CERTREQ_REQUIRE 2 void ssl_init(void); void ssl_finish(nut_ctype_t *client); void ssl_cleanup(void); int ssl_read(nut_ctype_t *client, char *buf, size_t buflen); int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen); void net_starttls(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #endif /* NETSSL_H_SEEN */ nut-2.7.4/server/Makefile.am0000644000175000017500000000161312640473702012623 00000000000000# Network UPS Tools: server # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include if WITH_WRAP AM_CFLAGS += $(LIBWRAP_CFLAGS) endif if WITH_SSL AM_CFLAGS += $(LIBSSL_CFLAGS) endif LDADD = ../common/libcommon.la ../common/libparseconf.la $(NETLIBS) if WITH_WRAP LDADD += $(LIBWRAP_LIBS) endif if WITH_SSL LDADD += $(LIBSSL_LIBS) endif sbin_PROGRAMS = upsd EXTRA_PROGRAMS = sockdebug upsd_SOURCES = upsd.c user.c conf.c netssl.c sstate.c desc.c \ netget.c netmisc.c netlist.c netuser.c netset.c netinstcmd.c \ conf.h nut_ctype.h desc.h netcmds.h neterr.h netget.h netinstcmd.h \ netlist.h netmisc.h netset.h netuser.h netssl.h sstate.h stype.h upsd.h \ upstype.h user-data.h user.h sockdebug_SOURCES = sockdebug.c nut-2.7.4/server/neterr.h0000644000175000017500000000300212640443572012233 00000000000000/* network error definitions for consistency */ #define NUT_ERR_ACCESS_DENIED "ACCESS-DENIED" #define NUT_ERR_UNKNOWN_UPS "UNKNOWN-UPS" #define NUT_ERR_VAR_NOT_SUPPORTED "VAR-NOT-SUPPORTED" #define NUT_ERR_CMD_NOT_SUPPORTED "CMD-NOT-SUPPORTED" #define NUT_ERR_INVALID_ARGUMENT "INVALID-ARGUMENT" #define NUT_ERR_INSTCMD_FAILED "INSTCMD-FAILED" #define NUT_ERR_SET_FAILED "SET-FAILED" #define NUT_ERR_READONLY "READONLY" #define NUT_ERR_TOO_LONG "TOO-LONG" #define NUT_ERR_FEATURE_NOT_SUPPORTED "FEATURE-NOT-SUPPORTED" #define NUT_ERR_FEATURE_NOT_CONFIGURED "FEATURE-NOT-CONFIGURED" #define NUT_ERR_ALREADY_SSL_MODE "ALREADY-SSL-MODE" /* errors which are only used by top-level upsd functions */ #define NUT_ERR_DRIVER_NOT_CONNECTED "DRIVER-NOT-CONNECTED" #define NUT_ERR_DATA_STALE "DATA-STALE" #define NUT_ERR_ALREADY_LOGGED_IN "ALREADY-LOGGED-IN" #define NUT_ERR_INVALID_PASSWORD "INVALID-PASSWORD" #define NUT_ERR_ALREADY_SET_PASSWORD "ALREADY-SET-PASSWORD" #define NUT_ERR_INVALID_USERNAME "INVALID-USERNAME" #define NUT_ERR_ALREADY_SET_USERNAME "ALREADY-SET-USERNAME" #define NUT_ERR_USERNAME_REQUIRED "USERNAME-REQUIRED" #define NUT_ERR_PASSWORD_REQUIRED "PASSWORD-REQUIRED" #define NUT_ERR_UNKNOWN_COMMAND "UNKNOWN-COMMAND" /* errors which are only used with the old functions */ #define NUT_ERR_VAR_UNKNOWN "VAR-UNKNOWN" #define NUT_ERR_UNKNOWN_TYPE "UNKNOWN-TYPE" #define NUT_ERR_UNKNOWN_INSTCMD "UNKNOWN-INSTCMD" #define NUT_ERR_MISSING_ARGUMENT "MISSING-ARGUMENT" #define NUT_ERR_INVALID_VALUE "INVALID-VALUE" nut-2.7.4/server/netlist.c0000644000175000017500000001557512640473702012431 00000000000000/* netlist.c - LIST handlers for upsd Copyright (C) 2003 Russell Kroll 2012 Arnaud Quette 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 "common.h" #include "upsd.h" #include "sstate.h" #include "state.h" #include "neterr.h" #include "netlist.h" extern upstype_t *firstups; /* for list_ups */ extern nut_ctype_t *firstclient; /* for list_clients */ static int tree_dump(st_tree_t *node, nut_ctype_t *client, const char *ups, int rw, int fsd) { int ret; if (!node) return 1; /* not an error */ if (node->left) { ret = tree_dump(node->left, client, ups, rw, fsd); if (!ret) return 0; /* write failed in child */ } if (rw) { /* only send this back if it's been flagged RW */ if (node->flags & ST_FLAG_RW) { ret = sendback(client, "RW %s %s \"%s\"\n", ups, node->var, node->val); } else { ret = 1; /* dummy */ } } else { /* normal variable list only */ /* status is always a special case */ if ((fsd == 1) && (!strcasecmp(node->var, "ups.status"))) { ret = sendback(client, "VAR %s %s \"FSD %s\"\n", ups, node->var, node->val); } else { ret = sendback(client, "VAR %s %s \"%s\"\n", ups, node->var, node->val); } } if (ret != 1) return 0; if (node->right) return tree_dump(node->right, client, ups, rw, fsd); return 1; } static void list_rw(nut_ctype_t *client, const char *upsname) { const upstype_t *ups; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; if (!sendback(client, "BEGIN LIST RW %s\n", upsname)) return; if (!tree_dump(ups->inforoot, client, upsname, 1, ups->fsd)) return; sendback(client, "END LIST RW %s\n", upsname); } static void list_var(nut_ctype_t *client, const char *upsname) { const upstype_t *ups; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; if (!sendback(client, "BEGIN LIST VAR %s\n", upsname)) return; if (!tree_dump(ups->inforoot, client, upsname, 0, ups->fsd)) return; sendback(client, "END LIST VAR %s\n", upsname); } static void list_cmd(nut_ctype_t *client, const char *upsname) { const upstype_t *ups; cmdlist_t *ctmp; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; if (!sendback(client, "BEGIN LIST CMD %s\n", upsname)) return; for (ctmp = ups->cmdlist; ctmp != NULL; ctmp = ctmp->next) { if (!sendback(client, "CMD %s %s\n", upsname, ctmp->name)) return; } sendback(client, "END LIST CMD %s\n", upsname); } static void list_enum(nut_ctype_t *client, const char *upsname, const char *var) { const upstype_t *ups; const st_tree_t *node; const enum_t *etmp; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; node = sstate_getnode(ups, var); if (!node) { send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); return; } if (!sendback(client, "BEGIN LIST ENUM %s %s\n", upsname, var)) return; for (etmp = node->enum_list; etmp != NULL; etmp = etmp->next) { if (!sendback(client, "ENUM %s %s \"%s\"\n", upsname, var, etmp->val)) return; } sendback(client, "END LIST ENUM %s %s\n", upsname, var); } static void list_range(nut_ctype_t *client, const char *upsname, const char *var) { const upstype_t *ups; const st_tree_t *node; const range_t *rtmp; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; node = sstate_getnode(ups, var); if (!node) { send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); return; } if (!sendback(client, "BEGIN LIST RANGE %s %s\n", upsname, var)) return; for (rtmp = node->range_list; rtmp != NULL; rtmp = rtmp->next) { if (!sendback(client, "RANGE %s %s \"%i\" \"%i\"\n", upsname, var, rtmp->min, rtmp->max)) return; } sendback(client, "END LIST RANGE %s %s\n", upsname, var); } static void list_ups(nut_ctype_t *client) { upstype_t *utmp; char esc[SMALLBUF]; if (!sendback(client, "BEGIN LIST UPS\n")) return; utmp = firstups; while (utmp) { int ret; if (utmp->desc) { pconf_encode(utmp->desc, esc, sizeof(esc)); ret = sendback(client, "UPS %s \"%s\"\n", utmp->name, esc); } else { ret = sendback(client, "UPS %s \"Description unavailable\"\n", utmp->name); } if (!ret) return; utmp = utmp->next; } sendback(client, "END LIST UPS\n"); } static void list_clients(nut_ctype_t *client, const char *upsname) { const upstype_t *ups; nut_ctype_t *c, *cnext; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!sendback(client, "BEGIN LIST CLIENT %s\n", upsname)) return; if (firstclient) { int ret; /* show connected clients */ for (c = firstclient; c; c = cnext) { if (c->loginups && (!ups || !strcasecmp(c->loginups, ups->name))) { ret = sendback(client, "CLIENT %s %s\n", c->loginups, c->addr); if (!ret) return; } cnext = c->next; } } sendback(client, "END LIST CLIENT %s\n", upsname); } void net_list(nut_ctype_t *client, int numarg, const char **arg) { if (numarg < 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* LIST UPS */ if (!strcasecmp(arg[0], "UPS")) { list_ups(client); return; } if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* LIST VAR UPS */ if (!strcasecmp(arg[0], "VAR")) { list_var(client, arg[1]); return; } /* LIST RW UPS */ if (!strcasecmp(arg[0], "RW")) { list_rw(client, arg[1]); return; } /* LIST CMD UPS */ if (!strcasecmp(arg[0], "CMD")) { list_cmd(client, arg[1]); return; } /* LIST CLIENT UPS */ if (!strcasecmp(arg[0], "CLIENT")) { list_clients(client, arg[1]); return; } if (numarg < 3) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* LIST ENUM UPS VARNAME */ if (!strcasecmp(arg[0], "ENUM")) { list_enum(client, arg[1], arg[2]); return; } /* LIST RANGE UPS VARNAME */ if (!strcasecmp(arg[0], "RANGE")) { list_range(client, arg[1], arg[2]); return; } send_err(client, NUT_ERR_INVALID_ARGUMENT); } nut-2.7.4/server/netmisc.c0000644000175000017500000000446612640443572012410 00000000000000/* netmisc.c - miscellaneous network handlers for upsd (VER, HELP, FSD) Copyright (C) 2003 Russell Kroll 2012 Arnaud Quette 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 "common.h" #include "upsd.h" #include "sstate.h" #include "state.h" #include "user.h" /* for user_checkaction */ #include "neterr.h" #include "netmisc.h" void net_ver(nut_ctype_t *client, int numarg, const char **arg) { if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } sendback(client, "Network UPS Tools upsd %s - http://www.networkupstools.org/\n", UPS_VERSION); } void net_netver(nut_ctype_t *client, int numarg, const char **arg) { if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } sendback(client, "%s\n", NUT_NETVERSION); } void net_help(nut_ctype_t *client, int numarg, const char **arg) { if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } sendback(client, "Commands: HELP VER GET LIST SET INSTCMD LOGIN LOGOUT" " USERNAME PASSWORD STARTTLS\n"); } void net_fsd(nut_ctype_t *client, int numarg, const char **arg) { upstype_t *ups; if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } ups = get_ups_ptr(arg[0]); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } /* make sure this user is allowed to do FSD */ if (!user_checkaction(client->username, client->password, "FSD")) { send_err(client, NUT_ERR_ACCESS_DENIED); return; } upslogx(LOG_INFO, "Client %s@%s set FSD on UPS [%s]", client->username, client->addr, ups->name); ups->fsd = 1; sendback(client, "OK FSD-SET\n"); } nut-2.7.4/server/netget.c0000644000175000017500000001311312667537407012233 00000000000000/* netget.c - GET handlers for upsd Copyright (C) 2003 Russell Kroll 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 "common.h" #include "upsd.h" #include "sstate.h" #include "state.h" #include "desc.h" #include "neterr.h" #include "netget.h" static void get_numlogins(nut_ctype_t *client, const char *upsname) { const upstype_t *ups; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; sendback(client, "NUMLOGINS %s %d\n", upsname, ups->numlogins); } static void get_upsdesc(nut_ctype_t *client, const char *upsname) { const upstype_t *ups; char esc[SMALLBUF]; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (ups->desc) { pconf_encode(ups->desc, esc, sizeof(esc)); sendback(client, "UPSDESC %s \"%s\"\n", upsname, esc); } else { sendback(client, "UPSDESC %s \"Unavailable\"\n", upsname); } } static void get_desc(nut_ctype_t *client, const char *upsname, const char *var) { const upstype_t *ups; const char *desc; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; desc = desc_get_var(var); if (desc) sendback(client, "DESC %s %s \"%s\"\n", upsname, var, desc); else sendback(client, "DESC %s %s \"Description unavailable\"\n", upsname, var); } static void get_cmddesc(nut_ctype_t *client, const char *upsname, const char *cmd) { const upstype_t *ups; const char *desc; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; desc = desc_get_cmd(cmd); if (desc) sendback(client, "CMDDESC %s %s \"%s\"\n", upsname, cmd, desc); else sendback(client, "CMDDESC %s %s \"Description unavailable\"\n", upsname, cmd); } static void get_type(nut_ctype_t *client, const char *upsname, const char *var) { char buf[SMALLBUF]; const upstype_t *ups; const st_tree_t *node; ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; node = sstate_getnode(ups, var); if (!node) { send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); return; } snprintf(buf, sizeof(buf), "TYPE %s %s", upsname, var); if (node->flags & ST_FLAG_RW) snprintfcat(buf, sizeof(buf), " RW"); if (node->enum_list) { sendback(client, "%s ENUM\n", buf); return; } if (node->range_list) { sendback(client, "%s RANGE\n", buf); return; } if (node->flags & ST_FLAG_STRING) { sendback(client, "%s STRING:%d\n", buf, node->aux); return; } /* Any variable that is not string | range | enum is just a simple * numeric value */ sendback(client, "TYPE %s %s NUMBER\n", upsname, var); } static void get_var_server(nut_ctype_t *client, const char *upsname, const char *var) { if (!strcasecmp(var, "server.info")) { sendback(client, "VAR %s server.info " "\"Network UPS Tools upsd %s - " "http://www.networkupstools.org/\"\n", upsname, UPS_VERSION); return; } if (!strcasecmp(var, "server.version")) { sendback(client, "VAR %s server.version \"%s\"\n", upsname, UPS_VERSION); return; } send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); } static void get_var(nut_ctype_t *client, const char *upsname, const char *var) { const upstype_t *ups; const char *val; /* ignore upsname for server.* variables */ if (!strncasecmp(var, "server.", 7)) { get_var_server(client, upsname, var); return; } ups = get_ups_ptr(upsname); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); return; } if (!ups_available(ups, client)) return; val = sstate_getinfo(ups, var); if (!val) { send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); return; } /* handle special case for status */ if ((!strcasecmp(var, "ups.status")) && (ups->fsd)) sendback(client, "VAR %s %s \"FSD %s\"\n", upsname, var, val); else sendback(client, "VAR %s %s \"%s\"\n", upsname, var, val); } void net_get(nut_ctype_t *client, int numarg, const char **arg) { if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* GET NUMLOGINS UPS */ if (!strcasecmp(arg[0], "NUMLOGINS")) { get_numlogins(client, arg[1]); return; } /* GET UPSDESC UPS */ if (!strcasecmp(arg[0], "UPSDESC")) { get_upsdesc(client, arg[1]); return; } if (numarg < 3) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* GET VAR UPS VARNAME */ if (!strcasecmp(arg[0], "VAR")) { get_var(client, arg[1], arg[2]); return; } /* GET TYPE UPS VARNAME */ if (!strcasecmp(arg[0], "TYPE")) { get_type(client, arg[1], arg[2]); return; } /* GET DESC UPS VARNAME */ if (!strcasecmp(arg[0], "DESC")) { get_desc(client, arg[1], arg[2]); return; } /* GET CMDDESC UPS CMDNAME */ if (!strcasecmp(arg[0], "CMDDESC")) { get_cmddesc(client, arg[1], arg[2]); return; } send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } nut-2.7.4/server/netget.h0000644000175000017500000000032112640444140012213 00000000000000#ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif void net_get(nut_ctype_t *client, int numarg, const char **arg); #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/aclocal.m40000644000175000017500000015710512667761775011154 00000000000000# generated automatically by aclocal 1.14.1 -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # longlong.m4 serial 17 dnl Copyright (C) 1999-2007, 2009-2014 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Paul Eggert. # Define HAVE_LONG_LONG_INT if 'long long int' works. # This fixes a bug in Autoconf 2.61, and can be faster # than what's in Autoconf 2.62 through 2.68. # Note: If the type 'long long int' exists but is only 32 bits large # (as on some very old compilers), HAVE_LONG_LONG_INT will not be # defined. In this case you can treat 'long long int' like 'long int'. AC_DEFUN([AC_TYPE_LONG_LONG_INT], [ AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], [ac_cv_type_long_long_int=yes if test "x${ac_cv_prog_cc_c99-no}" = xno; then ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int if test $ac_cv_type_long_long_int = yes; then dnl Catch a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. dnl If cross compiling, assume the bug is not important, since dnl nobody cross compiles for this platform as far as we know. AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[@%:@include @%:@ifndef LLONG_MAX @%:@ define HALF \ (1LL << (sizeof (long long int) * CHAR_BIT - 2)) @%:@ define LLONG_MAX (HALF - 1 + HALF) @%:@endif]], [[long long int n = 1; int i; for (i = 0; ; i++) { long long int m = n << i; if (m >> i != n) return 1; if (LLONG_MAX / 2 < m) break; } return 0;]])], [], [ac_cv_type_long_long_int=no], [:]) fi fi]) if test $ac_cv_type_long_long_int = yes; then AC_DEFINE([HAVE_LONG_LONG_INT], [1], [Define to 1 if the system has the type 'long long int'.]) fi ]) # Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. # This fixes a bug in Autoconf 2.61, and can be faster # than what's in Autoconf 2.62 through 2.68. # Note: If the type 'unsigned long long int' exists but is only 32 bits # large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT # will not be defined. In this case you can treat 'unsigned long long int' # like 'unsigned long int'. AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], [ AC_CACHE_CHECK([for unsigned long long int], [ac_cv_type_unsigned_long_long_int], [ac_cv_type_unsigned_long_long_int=yes if test "x${ac_cv_prog_cc_c99-no}" = xno; then AC_LINK_IFELSE( [_AC_TYPE_LONG_LONG_SNIPPET], [], [ac_cv_type_unsigned_long_long_int=no]) fi]) if test $ac_cv_type_unsigned_long_long_int = yes; then AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], [Define to 1 if the system has the type 'unsigned long long int'.]) fi ]) # Expands to a C program that can be used to test for simultaneous support # of 'long long' and 'unsigned long long'. We don't want to say that # 'long long' is available if 'unsigned long long' is not, or vice versa, # because too many programs rely on the symmetry between signed and unsigned # integer types (excluding 'bool'). AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], [ AC_LANG_PROGRAM( [[/* For now, do not test the preprocessor; as of 2007 there are too many implementations with broken preprocessors. Perhaps this can be revisited in 2012. In the meantime, code should not expect #if to work with literals wider than 32 bits. */ /* Test literals. */ long long int ll = 9223372036854775807ll; long long int nll = -9223372036854775807LL; unsigned long long int ull = 18446744073709551615ULL; /* Test constant expressions. */ typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) ? 1 : -1)]; typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 ? 1 : -1)]; int i = 63;]], [[/* Test availability of runtime routines for shift and division. */ long long int llmax = 9223372036854775807ll; unsigned long long int ullmax = 18446744073709551615ull; return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) | (llmax / ll) | (llmax % ll) | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) | (ullmax / ull) | (ullmax % ull));]]) ]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) # # Copyright © 2004 Scott James Remnant . # # 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 exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])# PKG_CHECK_MODULES # PKG_INSTALLDIR(DIRECTORY) # ------------------------- # Substitutes the variable pkgconfigdir as the location where a module # should install pkg-config .pc files. By default the directory is # $libdir/pkgconfig, but the default can be changed by passing # DIRECTORY. The user can override through the --with-pkgconfigdir # parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ]) dnl PKG_INSTALLDIR # PKG_NOARCH_INSTALLDIR(DIRECTORY) # ------------------------- # Substitutes the variable noarch_pkgconfigdir as the location where a # module should install arch-independent pkg-config .pc files. By # default the directory is $datadir/pkgconfig, but the default can be # changed by passing DIRECTORY. The user can override through the # --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ]) dnl PKG_NOARCH_INSTALLDIR # PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------- # Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])# PKG_CHECK_VAR # Copyright (C) 2002-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.14' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.14.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.14.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering # Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAINTAINER_MODE([DEFAULT-MODE]) # ---------------------------------- # Control maintainer-specific portions of Makefiles. # Default is to disable them, unless 'enable' is passed literally. # For symmetry, 'disable' may be passed as well. Anyway, the user # can override the default with the --enable/--disable switch. AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]), [enable], [m4_define([am_maintainer_other], [disable])], [disable], [m4_define([am_maintainer_other], [enable])], [m4_define([am_maintainer_other], [enable]) m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) dnl maintainer-mode's default is 'disable' unless 'enable' is passed AC_ARG_ENABLE([maintainer-mode], [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], am_maintainer_other[ make rules and dependencies not useful (and sometimes confusing) to the casual installer])], [USE_MAINTAINER_MODE=$enableval], [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) AC_MSG_RESULT([$USE_MAINTAINER_MODE]) AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) MAINT=$MAINTAINER_MODE_TRUE AC_SUBST([MAINT])dnl ] ) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2013 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/ax_compare_version.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/nut_arg_with.m4]) m4_include([m4/nut_check_asciidoc.m4]) m4_include([m4/nut_check_libavahi.m4]) m4_include([m4/nut_check_libfreeipmi.m4]) m4_include([m4/nut_check_libgd.m4]) m4_include([m4/nut_check_libltdl.m4]) m4_include([m4/nut_check_libneon.m4]) m4_include([m4/nut_check_libnetsnmp.m4]) m4_include([m4/nut_check_libnss.m4]) m4_include([m4/nut_check_libopenssl.m4]) m4_include([m4/nut_check_libpowerman.m4]) m4_include([m4/nut_check_libusb.m4]) m4_include([m4/nut_check_libwrap.m4]) m4_include([m4/nut_check_os.m4]) m4_include([m4/nut_report_feature.m4]) m4_include([m4/nut_type_socklen_t.m4]) nut-2.7.4/test-driver0000755000175000017500000001027712640476421011466 00000000000000#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2013-07-13.22; # UTC # Copyright (C) 2011-2013 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then estatus=1 fi case $estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: nut-2.7.4/INSTALL.nut0000644000175000017500000002351212640473702011121 00000000000000Installation instructions ========================= This chapter describe the various methods for installing Network UPS Tools. Whenever it is possible, prefer <>. Packagers have done an excellent and hard work at improving NUT integration into their system. [[Installing_source]] Installing from source ---------------------- These are the essential steps for compiling and installing this software. The NUT linkdoc:packager-guide[Packager Guide], which presents the best practices for installing and integrating NUT, is also a good reading. [NOTE] .Keep in mind that... ================================================================================ - the paths shown below are the default values you get by just calling configure by itself. If you have used --prefix or similar, things will be different. Also, if you didn't install this program from source yourself, the paths will probably have a number of differences. - by default, your system probably won't find the man pages, since they install to /usr/local/ups/man. You can fix this by editing your MANPATH, or just do this: man -M /usr/local/ups/man - if your favorite system offers up to date binary packages, you should always prefer these over a source installation. Along with the known advantages of such systems for installation, upgrade and removal, there are many integration issues that have been addressed. ================================================================================ Prepare your system ~~~~~~~~~~~~~~~~~~~~ System User creation ^^^^^^^^^^^^^^^^^^^^ Create at least one system user and a group for running this software. You might call them "ups" and "nut". The exact names aren't important as long as you are consistent. The process for doing this varies from one system to the next, and explaining how to add users is beyond the scope of this document. For the purposes of this document, the user name and group name will be 'ups' and 'nut' respectively. Be sure the new user is a member of the new group! If you forget to do this, you will have problems later on when you try to start upsd. Build and install ~~~~~~~~~~~~~~~~~ [[Configuration]] Configuration ^^^^^^^^^^^^^ Configure the source tree for your system. Add the '--with-user' and '--with-group' switch to set the user name and group that you created above. ./configure --with-user=ups --with-group=nut If you need any other switches for configure, add them here. For example: * to build and install USB drivers, add '--with-usb' (note that you need to install libusb development package or files). * to build and install SNMP drivers, add '--with-snmp' (note that you need to install libsnmp development package or files). * to build and install CGI scripts, add '--with-cgi'. See <> from the User Manual, docs/configure.txt or './configure --help' for all the available options. If you alter paths with additional switches, be sure to use those new paths while reading the rest of the steps. Reference: <> from the User Manual. Build the programs ^^^^^^^^^^^^^^^^^^ make This will build the NUT client and server programs and the selected drivers. It will also build any other features that were selected during <> step above. Installation ^^^^^^^^^^^^ [NOTE] ===================================================================== you should now gain privileges for installing software if necessary: su ===================================================================== Install the files to a system level directory: make install This will install the compiled programs and man pages, as well as some data files required by NUT. Any optional features selected during configuration will also be installed. This will also install sample versions of the NUT configuration files. Sample files are installed with names like ups.conf.sample so they will not overwrite any existing real config files you may have created. If you are packaging this software, then you will probably want to use the DESTDIR variable to redirect the build into another place, i.e.: make DESTDIR=/tmp/package install make DESTDIR=/tmp/package install-conf [[StatePath]] State path creation ^^^^^^^^^^^^^^^^^^^ Create the state path directory for the driver(s) and server to use for storing UPS status data and other auxiliary files, and make it group-writable by the group of the system user you created. mkdir -p /var/state/ups chmod 0770 /var/state/ups chown root:nut /var/state/ups [[Ownership]] Ownership and permissions ^^^^^^^^^^^^^^^^^^^^^^^^^ Set ownership data and permissions on your serial or USB ports that go to your UPS hardware. Be sure to limit access to just the user you created earlier. These examples assume the second serial port (ttyS1) on a typical Slackware system. On FreeBSD, that would be cuaa1. Serial ports vary greatly, so yours may be called something else. chmod 0660 /dev/ttyS1 chown root:nut /dev/ttyS1 //////////////////////////////////////////////////////////////////////////////// FIXME: TBR //////////////////////////////////////////////////////////////////////////////// The setup for USB ports is slightly more complicated. Device files for USB devices, such as /proc/bus/usb/002/001, are usually created "on the fly" when a device is plugged in, and disappear when the device is disconnected. Moreover, the names of these device files can change randomly. To set up the correct permissions for the USB device, you may need to set up (operating system dependent) hotplugging scripts. Sample scripts and information are provided in the scripts/hotplug and scripts/udev directories. For most users, the hotplugging scripts will be installed automatically by "make install". (If you want to try if a driver works without setting up hotplugging, you can add the "-u root" option to upsd, upsmon, and drivers; this should allow you to follow the below instructions. However, don't forget to set up the correct permissions later!). NOTE: if you are using something like udev or devd, make sure these permissions stay set across a reboot. If they revert to the old values, your drivers may fail to start. You are now ready to configure NUT, and start testing and using it. You can jump directly to the <>. [[Installing_packages]] Installing from packages ------------------------ This chapter describes the specific installation steps when using binary packages that exist on various major systems. [[Debian]] Debian, Ubuntu and other derivatives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NOTE: NUT is packaged and well maintained in these systems. The official Debian packager is part of the NUT Team. Using your preferred method (apt-get, aptitude, Synaptic, ...), install the 'nut' package, and optionally the following: - 'nut-cgi', if you need the CGI (HTML) option, - 'nut-snmp', if you need the snmp-ups driver, - 'nut-xml', for the netxml-ups driver, - 'nut-powerman-pdu', to control the PowerMan daemon (PDU management) - 'nut-dev', if you need the development files. //////////////////////////////////////////////////////////////////////////////// - nut-client //////////////////////////////////////////////////////////////////////////////// Configuration files are located in /etc/nut. linkman:nut.conf[5] must be edited to be able to invoke /etc/init.d/nut NOTE: Ubuntu users can access the APT URL installation by clicking on link:apt://nut[this link]. [[Mandriva]] Mandriva ~~~~~~~~ NOTE: NUT is packaged and well maintained in these systems. The official Mandriva packager is part of the NUT Team. Using your preferred method (urpmi, RPMdrake, ...), install one of the two below packages: - 'nut-server' if you have a 'standalone' or 'netserver' installation, - 'nut' if you have a 'netclient' installation. Optionally, you can also install the following: - 'nut-cgi', if you need the CGI (HTML) option, - 'nut-devel', if you need the development files. [[SUSE]] SUSE / openSUSE ~~~~~~~~~~~~~~~ NOTE: NUT is packaged and well maintained in these systems. The official SUSE packager is part of the NUT Team. Install the 'nut-classic' package, and optionally the following: - 'nut-drivers-net', if you need the snmp-ups or the netxml-ups drivers, - 'nut-cgi', if you need the CGI (HTML) option, - 'nut-devel', if you need the development files, NOTE: SUSE and openSUSE users can use the link:http://software.opensuse.org/search?baseproject=ALL&p=1&q=nut[one-click install method] to install NUT. [[RedHat]] Red Hat, Fedora and CentOS ~~~~~~~~~~~~~~~~~~~~~~~~~~ NOTE: NUT is packaged and well maintained in these systems. The official Red Hat packager is part of the NUT Team. Using your preferred method (yum, Add/Remove Software, ...), install one of the two below packages: - 'nut' if you have a 'standalone' or 'netserver' installation, - 'nut-client' if you have a 'netclient' installation. Optionally, you can also install the following: - 'nut-cgi', if you need the CGI (HTML) option, - 'nut-xml', if you need the netxml-ups driver, - 'nut-devel', if you need the development files. [[FreeBSD]] FreeBSD ~~~~~~~ You can either install NUT as a binary package or as a port. Binary package ^^^^^^^^^^^^^^ To install the main component, use the following command: # pkg_add -r nut Port ^^^^ The port is located under /usr/ports/sysutils/nut. To install it, use the following command: # cd /usr/ports/sysutils/nut/ && make install clean You have to define WITH_NUT_CGI to build the optional CGI scripts. Optionally, you can also install the following ports: - sysutils/nut-snmp, for the SNMP driver, - sysutils/nut-usb, for the USB drivers, - sysutils/nut-libupsclient, for the upsclient library. You are now ready to configure NUT, and start testing and using it. You can jump directly to the <>. nut-2.7.4/conf/0000755000175000017500000000000012670024740010262 500000000000000nut-2.7.4/conf/upssched.conf.sample.in0000644000175000017500000000746112640443572014571 00000000000000# Network UPS Tools - upssched.conf sample file # # ============================================================================ # # CMDSCRIPT # # This script gets called to invoke commands for timers that trigger. # It is given a single argument - the in your # AT ... START-TIMER defines. # # *** This must be defined *before* the first AT line. Otherwise the # program will complain and exit without doing anything. # # A shell script with a big case..esac construct should work nicely for this. # An example has been provided to help you get started. CMDSCRIPT @BINDIR@/upssched-cmd # ============================================================================ # # PIPEFN # # This sets the file name of the FIFO that will pass communications between # processes to start and stop timers. This should be set to some path where # normal users can't create the file, due to the possibility of symlinking # and other evil. # # Note: if you are running Solaris or similar, the permissions that # upssched sets on this file *are not enough* to keep you safe. If # your OS ignores the permissions on a FIFO, then you MUST put this in # a protected directory! # # Note 2: by default, upsmon will run upssched as whatever user you have # defined with RUN_AS_USER in upsmon.conf. Make sure that user can # create files and write to files in the path you use for PIPEFN and # LOCKFN. # # My recommendation: create a special directory for upssched, make it # owned by your upsmon user, then use it for both. # # This is commented out by default to make you visit this file and think # about how your system works before potentially opening a hole. # # PIPEFN @STATEPATH@/upssched/upssched.pipe # ============================================================================ # # LOCKFN # # REQUIRED. This was added after version 1.2.1. # # upssched needs to be able to create this filename in order to avoid # a race condition when two events are dispatched from upsmon at nearly # the same time. This file will only exist briefly. It must not be # created by any other process. # # You should put this in the same directory as PIPEFN. # # LOCKFN @STATEPATH@/upssched/upssched.lock # ============================================================================ # # AT # # Define a handler for a specific event on UPS . # # can be the special value * to apply this handler to every # possible value of . # # Run the command via your CMDSCRIPT when it happens. # # Note that any AT that matches both the and the # for the current event will be used. # ============================================================================ # # Possible AT commands # # - START-TIMER # # Start a timer called that will trigger after # seconds, calling your CMDSCRIPT with as the first # argument. # # Example: # Start a timer that'll execute when any UPS (*) has been gone 10 seconds # # AT COMMBAD * START-TIMER upsgone 10 # ----------------------------------------------------------------------- # # - CANCEL-TIMER [cmd] # # Cancel a running timer called , if possible. If the timer # has passed then pass the optional argument to CMDSCRIPT. # # Example: # If a specific UPS (myups@localhost) comes back online, then stop the # timer before it triggers # # AT COMMOK myups@localhost CANCEL-TIMER upsgone # ----------------------------------------------------------------------- # # - EXECUTE # # Immediately pass as an argument to CMDSCRIPT. # # Example: # If any UPS (*) reverts to utility power, then execute # 'ups-back-on-line' via CMDSCRIPT. # # AT ONLINE * EXECUTE ups-back-on-line nut-2.7.4/conf/upsmon.conf.sample.in0000644000175000017500000003573412640473702014276 00000000000000# Network UPS Tools: example upsmon configuration # # This file contains passwords, so keep it secure. # -------------------------------------------------------------------------- # RUN_AS_USER # # By default, upsmon splits into two processes. One stays as root and # waits to run the SHUTDOWNCMD. The other one switches to another userid # and does everything else. # # The default nonprivileged user is set at compile-time with # 'configure --with-user=...'. # # You can override it with '-u ' when starting upsmon, or just # define it here for convenience. # # Note: if you plan to use the reload feature, this file (upsmon.conf) # must be readable by this user! Since it contains passwords, DO NOT # make it world-readable. Also, do not make it writable by the upsmon # user, since it creates an opportunity for an attack by changing the # SHUTDOWNCMD to something malicious. # # For best results, you should create a new normal user like "nutmon", # and make it a member of a "nut" group or similar. Then specify it # here and grant read access to the upsmon.conf for that group. # # This user should not have write access to upsmon.conf. # # RUN_AS_USER @RUN_AS_USER@ # -------------------------------------------------------------------------- # MONITOR ("master"|"slave") # # List systems you want to monitor. Not all of these may supply power # to the system running upsmon, but if you want to watch it, it has to # be in this section. # # You must have at least one of these declared. # # is a UPS identifier in the form @[:] # like ups@localhost, su700@mybox, etc. # # Examples: # # - "su700@mybox" means a UPS called "su700" on a system called "mybox" # # - "fenton@bigbox:5678" is a UPS called "fenton" on a system called # "bigbox" which runs upsd on port "5678". # # The UPS names like "su700" and "fenton" are set in your ups.conf # in [brackets] which identify a section for a particular driver. # # If the ups.conf on host "doghouse" has a section called "snoopy", the # identifier for it would be "snoopy@doghouse". # # is an integer - the number of power supplies that this UPS # feeds on this system. Most computers only have one power supply, so this # is normally set to 1. You need a pretty big or special box to have any # other value here. # # You can also set this to 0 for a system that doesn't supply any power, # but you still want to monitor. Use this when you want to hear about # changes for a given UPS without shutting down when it goes critical, # unless is 0. # # and must match an entry in that system's # upsd.users. If your username is "monmaster" and your password is # "blah", the upsd.users would look like this: # # [monmaster] # password = blah # upsmon master (or slave) # # "master" means this system will shutdown last, allowing the slaves # time to shutdown first. # # "slave" means this system shuts down immediately when power goes critical. # # Examples: # # MONITOR myups@bigserver 1 monmaster blah master # MONITOR su700@server.example.com 1 upsmon secretpass slave # MONITOR myups@localhost 1 upsmon pass master (or slave) # -------------------------------------------------------------------------- # MINSUPPLIES # # Give the number of power supplies that must be receiving power to keep # this system running. Most systems have one power supply, so you would # put "1" in this field. # # Large/expensive server type systems usually have more, and can run with # a few missing. The HP NetServer LH4 can run with 2 out of 4, for example, # so you'd set that to 2. The idea is to keep the box running as long # as possible, right? # # Obviously you have to put the redundant supplies on different UPS circuits # for this to make sense! See big-servers.txt in the docs subdirectory # for more information and ideas on how to use this feature. MINSUPPLIES 1 # -------------------------------------------------------------------------- # SHUTDOWNCMD "" # # upsmon runs this command when the system needs to be brought down. # # This should work just about everywhere ... if it doesn't, well, change it. SHUTDOWNCMD "/sbin/shutdown -h +0" # -------------------------------------------------------------------------- # NOTIFYCMD # # upsmon calls this to send messages when things happen # # This command is called with the full text of the message as one argument. # The environment string NOTIFYTYPE will contain the type string of # whatever caused this event to happen. # # Note that this is only called for NOTIFY events that have EXEC set with # NOTIFYFLAG. See NOTIFYFLAG below for more details. # # Making this some sort of shell script might not be a bad idea. For more # information and ideas, see docs/scheduling.txt # # Example: # NOTIFYCMD @BINDIR@/notifyme # -------------------------------------------------------------------------- # POLLFREQ # # Polling frequency for normal activities, measured in seconds. # # Adjust this to keep upsmon from flooding your network, but don't make # it too high or it may miss certain short-lived power events. POLLFREQ 5 # -------------------------------------------------------------------------- # POLLFREQALERT # # Polling frequency in seconds while UPS on battery. # # You can make this number lower than POLLFREQ, which will make updates # faster when any UPS is running on battery. This is a good way to tune # network load if you have a lot of these things running. # # The default is 5 seconds for both this and POLLFREQ. POLLFREQALERT 5 # -------------------------------------------------------------------------- # HOSTSYNC - How long upsmon will wait before giving up on another upsmon # # The master upsmon process uses this number when waiting for slaves to # disconnect once it has set the forced shutdown (FSD) flag. If they # don't disconnect after this many seconds, it goes on without them. # # Similarly, upsmon slave processes wait up to this interval for the # master upsmon to set FSD when a UPS they are monitoring goes critical - # that is, on battery and low battery. If the master doesn't do its job, # the slaves will shut down anyway to avoid damage to the file systems. # # This "wait for FSD" is done to avoid races where the status changes # to critical and back between polls by the master. HOSTSYNC 15 # -------------------------------------------------------------------------- # DEADTIME - Interval to wait before declaring a stale ups "dead" # # upsmon requires a UPS to provide status information every few seconds # (see POLLFREQ and POLLFREQALERT) to keep things updated. If the status # fetch fails, the UPS is marked stale. If it stays stale for more than # DEADTIME seconds, the UPS is marked dead. # # A dead UPS that was last known to be on battery is assumed to have gone # to a low battery condition. This may force a shutdown if it is providing # a critical amount of power to your system. # # Note: DEADTIME should be a multiple of POLLFREQ and POLLFREQALERT. # Otherwise you'll have "dead" UPSes simply because upsmon isn't polling # them quickly enough. Rule of thumb: take the larger of the two # POLLFREQ values, and multiply by 3. DEADTIME 15 # -------------------------------------------------------------------------- # POWERDOWNFLAG - Flag file for forcing UPS shutdown on the master system # # upsmon will create a file with this name in master mode when it's time # to shut down the load. You should check for this file's existence in # your shutdown scripts and run 'upsdrvctl shutdown' if it exists. # # See the config-notes.txt file in the docs subdirectory for more information. # Refer to the section: # [[UPS_shutdown]] "Configuring automatic shutdowns for low battery events" # or refer to the online version. POWERDOWNFLAG /etc/killpower # -------------------------------------------------------------------------- # NOTIFYMSG - change messages sent by upsmon when certain events occur # # You can change the default messages to something else if you like. # # NOTIFYMSG "message" # # NOTIFYMSG ONLINE "UPS %s on line power" # NOTIFYMSG ONBATT "UPS %s on battery" # NOTIFYMSG LOWBATT "UPS %s battery is low" # NOTIFYMSG FSD "UPS %s: forced shutdown in progress" # NOTIFYMSG COMMOK "Communications with UPS %s established" # NOTIFYMSG COMMBAD "Communications with UPS %s lost" # NOTIFYMSG SHUTDOWN "Auto logout and shutdown proceeding" # NOTIFYMSG REPLBATT "UPS %s battery needs to be replaced" # NOTIFYMSG NOCOMM "UPS %s is unavailable" # NOTIFYMSG NOPARENT "upsmon parent process died - shutdown impossible" # # Note that %s is replaced with the identifier of the UPS in question. # # Possible values for : # # ONLINE : UPS is back online # ONBATT : UPS is on battery # LOWBATT : UPS has a low battery (if also on battery, it's "critical") # FSD : UPS is being shutdown by the master (FSD = "Forced Shutdown") # COMMOK : Communications established with the UPS # COMMBAD : Communications lost to the UPS # SHUTDOWN : The system is being shutdown # REPLBATT : The UPS battery is bad and needs to be replaced # NOCOMM : A UPS is unavailable (can't be contacted for monitoring) # NOPARENT : The process that shuts down the system has died (shutdown impossible) # -------------------------------------------------------------------------- # NOTIFYFLAG - change behavior of upsmon when NOTIFY events occur # # By default, upsmon sends walls (global messages to all logged in users) # and writes to the syslog when things happen. You can change this. # # NOTIFYFLAG [+][+] ... # # NOTIFYFLAG ONLINE SYSLOG+WALL # NOTIFYFLAG ONBATT SYSLOG+WALL # NOTIFYFLAG LOWBATT SYSLOG+WALL # NOTIFYFLAG FSD SYSLOG+WALL # NOTIFYFLAG COMMOK SYSLOG+WALL # NOTIFYFLAG COMMBAD SYSLOG+WALL # NOTIFYFLAG SHUTDOWN SYSLOG+WALL # NOTIFYFLAG REPLBATT SYSLOG+WALL # NOTIFYFLAG NOCOMM SYSLOG+WALL # NOTIFYFLAG NOPARENT SYSLOG+WALL # # Possible values for the flags: # # SYSLOG - Write the message in the syslog # WALL - Write the message to all users on the system # EXEC - Execute NOTIFYCMD (see above) with the message # IGNORE - Don't do anything # # If you use IGNORE, don't use any other flags on the same line. # -------------------------------------------------------------------------- # RBWARNTIME - replace battery warning time in seconds # # upsmon will normally warn you about a battery that needs to be replaced # every 43200 seconds, which is 12 hours. It does this by triggering a # NOTIFY_REPLBATT which is then handled by the usual notify structure # you've defined above. # # If this number is not to your liking, override it here. RBWARNTIME 43200 # -------------------------------------------------------------------------- # NOCOMMWARNTIME - no communications warning time in seconds # # upsmon will let you know through the usual notify system if it can't # talk to any of the UPS entries that are defined in this file. It will # trigger a NOTIFY_NOCOMM by default every 300 seconds unless you # change the interval with this directive. NOCOMMWARNTIME 300 # -------------------------------------------------------------------------- # FINALDELAY - last sleep interval before shutting down the system # # On a master, upsmon will wait this long after sending the NOTIFY_SHUTDOWN # before executing your SHUTDOWNCMD. If you need to do something in between # those events, increase this number. Remember, at this point your UPS is # almost depleted, so don't make this too high. # # Alternatively, you can set this very low so you don't wait around when # it's time to shut down. Some UPSes don't give much warning for low # battery and will require a value of 0 here for a safe shutdown. # # Note: If FINALDELAY on the slave is greater than HOSTSYNC on the master, # the master will give up waiting for the slave to disconnect. FINALDELAY 5 # -------------------------------------------------------------------------- # CERTPATH - path to certificates (database directory or directory with CA's) # # When compiled with SSL support, you can enter the certificate path here. # # With NSS: # Certificates are stored in a dedicated database (splitted in 3 files). # Specify the path of the database directory. # # CERTPATH @CONFPATH@/cert/upsmon # # With OpenSSL: # Directory containing CA certificates in PEM format, used to verify # the server certificate presented by the upsd server. The files each # contain one CA certificate. The files are looked up by the CA subject # name hash value, which must hence be available. # # CERTPATH /usr/ssl/certs # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. # -------------------------------------------------------------------------- # CERTIDENT - self certificate name and database password # CERTIDENT # # When compiled with SSL support with NSS, you can specify the certificate # name to retrieve from database to authenticate itself and the password # required to access certificate related private key. # # CERTIDENT "my nut monitor" "MyPasSw0rD" # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. # -------------------------------------------------------------------------- # CERTHOST - security properties for an host # CERTHOST # # When compiled with SSL support with NSS, you can specify security directive # for each server you can contact. # Each entry maps server name with the expected certificate name and flags # indicating if the server certificate is verified and if the connection # must be secure. # # CERTHOST localhost "My nut server" 1 1 # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. # -------------------------------------------------------------------------- # CERTVERIFY - make upsmon verify all connections with certificates # CERTVERIFY 1 # # When compiled with SSL support, make upsmon verify all connections with # certificates. # Without this, there is no guarantee that the upsd is the right host. # Enabling this greatly reduces the risk of man in the middle attacks. # This effectively forces the use of SSL, so don't use this unless # all of your upsd hosts are ready for SSL and have their certificates # in order. # When compiled with NSS support of SSL, can be overriden for host # specified with a CERTHOST directive. # -------------------------------------------------------------------------- # FORCESSL - force upsmon to use SSL # FORCESSL 1 # # When compiled with SSL, specify that a secured connection must be used # to communicate with upsd. # If you don't use 'CERTVERIFY 1', then this will at least make sure # that nobody can sniff your sessions without a large effort. Setting # this will make upsmon drop connections if the remote upsd doesn't # support SSL, so don't use it unless all of them have it running. # When compiled with NSS support of SSL, can be overriden for host # specified with a CERTHOST directive. nut-2.7.4/conf/hosts.conf.sample0000644000175000017500000000206012640443572013474 00000000000000# Network UPS Tools: example hosts.conf # # This file is used to control the CGI programs. If you have not # installed them, you may safely ignore or delete this file. # # ----------------------------------------------------------------------- # # upsstats will use the list of MONITOR entries when displaying the # default template (upsstats.html). The "FOREACHUPS" directive in the # template will use this file to find systems running upsd. # # upsstats and upsimage also use this file to determine if a host may be # monitored. This keeps evil people from using your system to annoy # others with unintended queries. # # upsset presents a list of systems that may be viewed and controlled # using this file. # # ----------------------------------------------------------------------- # # Usage: list systems running upsd that you want to monitor # # MONITOR "" # # Examples: # # MONITOR myups@localhost "Local UPS" # MONITOR su2200@10.64.1.1 "Finance department" # MONITOR matrix@shs-server.example.edu "Sierra High School data room #1" nut-2.7.4/conf/upsstats.html.sample0000644000175000017500000000764312640443572014255 00000000000000 @TEMPC@ @UPSSTATSPATH upsstats.cgi@ @UPSIMAGEPATH upsimage.cgi@ @REFRESH@ Network UPS Tools upsstats @VERSION@ : UPS Status @REFRESH@
@FOREACHUPS@ @IFSUPP input.L2-L3.voltage@ @IFBETWEEN input.transfer.low input.transfer.high input.L1-L2.voltage@ @IFBETWEEN input.transfer.low input.transfer.high input.L2-L3.voltage@ @IFBETWEEN input.transfer.low input.transfer.high input.L3-L1.voltage@ @ENDFOR@
Network UPS Tools upsstats @VERSION@
@DATE %a %b %d %X %Z %Y@
System Model Status Battery Input (VAC) Output (VAC) Load (%) UPS
Temp
Battery
Runtime
Data
Tree
@HOSTLINK@ @VAR ups.model@ @STATUS@ @IFSUPP battery.charge@ @VAR battery.charge@ % @ENDIF@ @ELSE@ @IFSUPP input.L2-N.voltage@ @IFBETWEEN input.transfer.low input.transfer.high input.L1-N.voltage@ @IFBETWEEN input.transfer.low input.transfer.high input.L2-N.voltage@ @IFBETWEEN input.transfer.low input.transfer.high input.L3-N.voltage@ @ELSE@ @IFBETWEEN input.transfer.low input.transfer.high input.voltage@ @ELSE@ @ENDIF@ @IFSUPP input.L2-L3.voltage@ @VAR input.L1-L2.voltage@ @VAR input.L2-L3.voltage@ @VAR input.L3-L1.voltage@ @ELSE@ @IFSUPP input.L2-N.voltage@ @VAR input.L1-N.voltage@ @VAR input.L2-N.voltage@ @VAR input.L3-N.voltage@ @ELSE@ @IFSUPP input.voltage@ @VAR input.voltage@ @ENDIF@ @IFSUPP output.L2-L3.voltage@ @VAR output.L1-L2.voltage@ @VAR output.L2-L3.voltage@ @VAR output.L3-L1.voltage@ @ELSE@ @IFSUPP output.L2-N.voltage@ @VAR output.L1-N.voltage@ @VAR output.L2-N.voltage@ @VAR output.L3-N.voltage@ @ELSE@ @IFSUPP output.voltage@ @VAR output.voltage@ @ENDIF@ @IFSUPP output.L2.power.percent@ @VAR output.L1.power.percent@ @VAR output.L2.power.percent@ @VAR output.L3.power.percent@ @ELSE@ @IFSUPP output.L2.realpower.percent@ @VAR output.L1.realpower.percent@ @VAR output.L2.realpower.percent@ @VAR output.L3.realpower.percent@ @ELSE@ @IFSUPP ups.load@ @VAR ups.load@ % @ENDIF@ @IFSUPP ups.temperature@ @UPSTEMP@ @DEGREES@ @ELSE@ @IFSUPP battery.temperature@ @BATTTEMP@ @DEGREES@ @ENDIF@ @IFSUPP battery.runtime@ @RUNTIME@ @ENDIF@ @TREELINK@

Valid CSS! Valid HTML 4.0 Transitional
nut-2.7.4/conf/upsd.conf.sample0000644000175000017500000001074212640473702013313 00000000000000# Network UPS Tools: example upsd configuration file # # This file contains access control data, you should keep it secure. # # It should only be readable by the user that upsd becomes. See the FAQ. # # Each entry below provides usage and default value. # ======================================================================= # MAXAGE # MAXAGE 15 # # This defaults to 15 seconds. After a UPS driver has stopped updating # the data for this many seconds, upsd marks it stale and stops making # that information available to clients. After all, the only thing worse # than no data is bad data. # # You should only use this if your driver has difficulties keeping # the data fresh within the normal 15 second interval. Watch the syslog # for notifications from upsd about staleness. # ======================================================================= # STATEPATH # STATEPATH /var/run/nut # # Tell upsd to look for the driver state sockets in 'path' rather # than the default that was compiled into the program. # ======================================================================= # LISTEN
[] # LISTEN 127.0.0.1 3493 # LISTEN ::1 3493 # # This defaults to the localhost listening addresses and port 3493. # In case of IP v4 or v6 disabled kernel, only the available one will be used. # # You may specify each interface you want upsd to listen on for connections, # optionally with a port number. # # You may need this if you have multiple interfaces on your machine and # you don't want upsd to listen to all interfaces (for instance on a # firewall, you may not want to listen to the external interface). # # This will only be read at startup of upsd. If you make changes here, # you'll need to restart upsd, reload will have no effect. # ======================================================================= # MAXCONN # MAXCONN 1024 # # This defaults to maximum number allowed on your system. Each UPS, each # LISTEN address and each client count as one connection. If the server # runs out of connections, it will no longer accept new incoming client # connections. Only set this if you know exactly what you're doing. # ======================================================================= # CERTFILE # CERTFILE /usr/local/ups/etc/upsd.pem # # When compiled with SSL support with OpenSSL backend, # you can enter the certificate file here. # The certificates must be in PEM format and must be sorted starting with # the subject's certificate (server certificate), followed by intermediate # CA certificates (if applicable_ and the highest level (root) CA. It should # end with the server key. See 'docs/security.txt' or the Security chapter of # NUT user manual for more information on the SSL support in NUT. # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. # ======================================================================= # CERTPATH # CERTPATH /usr/local/ups/etc/cert/upsd # # When compiled with SSL support with NSS backend, # you can enter the certificate path here. # Certificates are stored in a dedicated database (splitted in 3 files). # Specify the path of the database directory. # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. # ======================================================================= # CERTIDENT # CERTIDENT "my nut server" "MyPasSw0rD" # # When compiled with SSL support with NSS backend, # you can specify the certificate name to retrieve from database to # authenticate itself and the password # required to access certificate related private key. # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. # ======================================================================= # CERTREQUEST # CERTREQUEST REQUIRE # # When compiled with SSL support with NSS backend and client certificate # validation (disabled by default, see 'docs/security.txt'), # you can specify if upsd requests or requires client's' certificates. # Possible values are : # - 0 to not request to clients to provide any certificate # - 1 to require to all clients a certificate # - 2 to require to all clients a valid certificate # # See 'docs/security.txt' or the Security chapter of NUT user manual # for more information on the SSL support in NUT. nut-2.7.4/conf/upsstats-single.html.sample0000644000175000017500000001476412640443572015536 00000000000000 @TEMPC@ @UPSSTATSPATH upsstats.cgi@ @UPSIMAGEPATH upsimage.cgi@ @REFRESH@ @HOSTDESC@ : @VAR ups.model@ on @HOST@ @IFSUPP ambient.temperature@ @ELSE@ @IFSUPP ambient.humidity@ @ENDIF@ @IFSUPP ambient.temperature@ @IFSUPP ambient.humidity@ @ELSE@ @IFSUPP ambient.temperature@ @ELSE@ @IFSUPP ambient.humidity@ @ENDIF@
Network UPS Tools upsstats @VERSION@ - @HOSTDESC@ - @VAR ups.model@ on @HOST@
@DATE %a %b %d %X %Z %Y@AmbientAmbientBattery Input Output Load
@IFSUPP battery.runtime@ @ENDIF@ @IFSUPP ups.temperature@ @ENDIF@ @IFSUPP battery.voltage@ @ENDIF@ @ELSE@ @IFSUPP input.L2-N.voltage@ @VAR input.L1-N.voltage@ V
@VAR input.L2-N.voltage@ V
@VAR input.L3-N.voltage@ V
@ELSE@ @IFSUPP input.voltage@ @VAR input.voltage@ V
@ENDIF@ @IFSUPP input.L2.current@ @ELSE@ @IFSUPP input.current@ @ENDIF@ @IFSUPP input.frequency@ @ENDIF@ @ELSE@ @IFSUPP output.L2-N.voltage@ @VAR output.L1-N.voltage@ V
@VAR output.L2-N.voltage@ V
@VAR output.L3-N.voltage@ V
@ELSE@ @IFSUPP output.voltage@ @VAR output.voltage@ V @ENDIF@ @IFSUPP output.L2.current@ @ELSE@ @IFSUPP output.current@ @ENDIF@ @IFSUPP output.frequency@ @ENDIF@
UPS Model: @VAR ups.model@
Status: @STATUS@
Runtime: @RUNTIME@
UPS temp: @UPSTEMP@ @DEGREES@
Battery: @VAR battery.voltage@ V@IFSUPP battery.current@, @VAR battery.current@ A
Input: @IFSUPP input.L2-L3.voltage@ @VAR input.L1-L2.voltage@ V
@VAR input.L2-L3.voltage@ V
@VAR input.L3-L1.voltage@ V
@VAR input.L1.current@ A
@VAR input.L2.current@ A
@VAR input.L3.current@ A
@VAR input.current@ A
@VAR input.frequency@ Hz
Output: @IFSUPP output.L2-L3.voltage@ @VAR output.L1-L2.voltage@ V
@VAR output.L2-L3.voltage@ V
@VAR output.L3-L1.voltage@ V
@VAR output.L1.current@ A
@VAR output.L2.current@ A
@VAR output.L3.current@ A
@VAR output.current@ A
@VAR output.frequency@ Hz
Temperature
@IMG ambient.temperature tempmin=0 tempmax=50 width=90@
Humidity
@IMG ambient.humidity width=90@
Temperature
@IMG ambient.temperature tempmin=0 tempmax=50@
Humidity
@IMG ambient.humidity@
@IFSUPP battery.charge@ @IFSUPP battery.voltage@ @ELSE@ @IFSUPP battery.charge@ @ELSE@ @ENDIF@
Charge
@IMG battery.charge width=90@
Voltage
@IMG battery.voltage width=90@
Charge
@IMG battery.charge@
Voltage
@IMG battery.voltage@
@IFSUPP input.L2-L3.voltage@ @ELSE@ @IFSUPP input.L2-N.voltage@ @ELSE@ @ENDIF@
L1-L2
@IMG input.L1-L2.voltage width=68@
L2-L3
@IMG input.L2-L3.voltage width=68@
L3-L1
@IMG input.L3-L1.voltage width=68@
L1-N
@IMG input.L1-N.voltage width=68@
L2-N
@IMG input.L2-N.voltage width=68@
L3-N
@IMG input.L3-N.voltage width=68@

@IMG input.voltage@
@IFSUPP output.L2-L3.voltage@ @ELSE@ @IFSUPP output.L2-N.voltage@ @ELSE@ @ENDIF@
L1-L2
@IMG output.L1-L2.voltage width=68@
L2-L3
@IMG output.L2-L3.voltage width=68@
L3-L1
@IMG output.L3-L1.voltage width=68@
L1-N
@IMG output.L1-N.voltage width=68@
L2-N
@IMG output.L2-N.voltage width=68@
L3-N
@IMG output.L3-N.voltage width=68@

@IMG output.voltage@
@IFSUPP output.L2.power.percent@ @ELSE@ @IFSUPP output.L2.realpower.percent@ @ELSE@ @ENDIF@
L1
@IMG output.L1.power.percent width=68@
L2
@IMG output.L2.power.percent width=68@
L3
@IMG output.L3.power.percent width=68@
L1
@IMG output.L1.realpower.percent width=68@
L2
@IMG output.L2.realpower.percent width=68@
L3
@IMG output.L3.realpower.percent width=68@

@IMG ups.load@
Valid HTML 4.0 Transitional nut-2.7.4/conf/Makefile.in0000644000175000017500000004462612667762000012267 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: conf VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = conf DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/upsmon.conf.sample.in \ $(srcdir)/upssched.conf.sample.in \ $(am__dist_sysconf_DATA_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = upsmon.conf.sample upssched.conf.sample CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__dist_sysconf_DATA_DIST = upsd.conf.sample upsd.users.sample \ nut.conf.sample ups.conf.sample hosts.conf.sample \ upsset.conf.sample upsstats.html.sample \ upsstats-single.html.sample am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(sysconfdir)" "$(DESTDIR)$(sysconfdir)" DATA = $(dist_sysconf_DATA) $(nodist_sysconf_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ INSTALL_0600 = $(INSTALL) -m 0600 SECFILES = upsd.conf.sample upsd.users.sample PUBFILES = nut.conf.sample ups.conf.sample CGIPUB = hosts.conf.sample upsset.conf.sample upsstats.html.sample \ upsstats-single.html.sample @WITH_CGI_FALSE@CGI_INSTALL = @WITH_CGI_TRUE@CGI_INSTALL = $(CGIPUB) dist_sysconf_DATA = $(SECFILES) $(PUBFILES) $(CGI_INSTALL) nodist_sysconf_DATA = upssched.conf.sample upsmon.conf.sample all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu conf/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu conf/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): upsmon.conf.sample: $(top_builddir)/config.status $(srcdir)/upsmon.conf.sample.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ upssched.conf.sample: $(top_builddir)/config.status $(srcdir)/upssched.conf.sample.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-dist_sysconfDATA: $(dist_sysconf_DATA) @$(NORMAL_INSTALL) @list='$(dist_sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sysconfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sysconfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(sysconfdir)" || exit $$?; \ done uninstall-dist_sysconfDATA: @$(NORMAL_UNINSTALL) @list='$(dist_sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(sysconfdir)'; $(am__uninstall_files_from_dir) install-nodist_sysconfDATA: $(nodist_sysconf_DATA) @$(NORMAL_INSTALL) @list='$(nodist_sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sysconfdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sysconfdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(sysconfdir)" || exit $$?; \ done uninstall-nodist_sysconfDATA: @$(NORMAL_UNINSTALL) @list='$(nodist_sysconf_DATA)'; test -n "$(sysconfdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(sysconfdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(sysconfdir)" "$(DESTDIR)$(sysconfdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-dist_sysconfDATA install-nodist_sysconfDATA install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_sysconfDATA uninstall-nodist_sysconfDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_sysconfDATA install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-nodist_sysconfDATA install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-dist_sysconfDATA \ uninstall-nodist_sysconfDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/conf/nut.conf.sample0000644000175000017500000000300212640443572013137 00000000000000# Network UPS Tools: example nut.conf # ############################################################################## # General section ############################################################################## # The MODE determines which part of the NUT is to be started, and which # configuration files must be modified. # # This file try to standardize the various files being found in the field, like # /etc/default/nut on Debian based systems, /etc/sysconfig/ups on RedHat based # systems, ... Distribution's init script should source this file to see which # component(s) has to be started. # # The values of MODE can be: # - none: NUT is not configured, or use the Integrated Power Management, or use # some external system to startup NUT components. So nothing is to be started. # - standalone: This mode address a local only configuration, with 1 UPS # protecting the local system. This implies to start the 3 NUT layers (driver, # upsd and upsmon) and the matching configuration files. This mode can also # address UPS redundancy. # - netserver: same as for the standalone configuration, but also need # some more network access controls (firewall, tcp-wrappers) and possibly a # specific LISTEN directive in upsd.conf. # Since this MODE is opened to the network, a special care should be applied # to security concerns. # - netclient: this mode only requires upsmon. # # IMPORTANT NOTE: # This file is intended to be sourced by shell scripts. # You MUST NOT use spaces around the equal sign! MODE=none nut-2.7.4/conf/upsd.users.sample0000644000175000017500000000412312640443572013525 00000000000000# Network UPS Tools: Example upsd.users # # This file sets the permissions for upsd - the UPS network daemon. # Users are defined here, are given passwords, and their privileges are # controlled here too. Since this file will contain passwords, keep it # secure, with only enough permissions for upsd to read it. # -------------------------------------------------------------------------- # Each user gets a section. To start a section, put the username in # brackets on a line by itself. To set something for that user, specify # it under that section heading. The username is case-sensitive, so # admin and AdMiN are two different users. # # Possible settings: # # password: The user's password. This is case-sensitive. # # -------------------------------------------------------------------------- # # actions: Let the user do certain things with upsd. # # Valid actions are: # # SET - change the value of certain variables in the UPS # FSD - set the "forced shutdown" flag in the UPS # # -------------------------------------------------------------------------- # # instcmds: Let the user initiate specific instant commands. Use "ALL" # to grant all commands automatically. There are many possible # commands, so use 'upscmd -l' to see what your hardware supports. Here # are a few examples: # # test.panel.start - Start a front panel test # test.battery.start - Start battery test # test.battery.stop - Stop battery test # calibrate.start - Start calibration # calibrate.stop - Stop calibration # # -------------------------------------------------------------------------- # # Example: # # [admin] # password = mypass # actions = SET # instcmds = ALL # # # --- Configuring for a user who can execute tests only # # [testuser] # password = pass # instcmds = test.battery.start # instcmds = test.battery.stop # # --- Configuring for upsmon # # To add a user for your upsmon, use this example: # # [upsmon] # password = pass # upsmon master # or # upsmon slave # # The matching MONITOR line in your upsmon.conf would look like this: # # MONITOR myups@localhost 1 upsmon pass master (or slave) nut-2.7.4/conf/Makefile.am0000644000175000017500000000066412640473702012247 00000000000000# Network UPS Tools: conf INSTALL_0600 = $(INSTALL) -m 0600 SECFILES = upsd.conf.sample upsd.users.sample PUBFILES = nut.conf.sample ups.conf.sample CGIPUB = hosts.conf.sample upsset.conf.sample upsstats.html.sample \ upsstats-single.html.sample if WITH_CGI CGI_INSTALL = $(CGIPUB) else CGI_INSTALL = endif dist_sysconf_DATA = $(SECFILES) $(PUBFILES) $(CGI_INSTALL) nodist_sysconf_DATA = upssched.conf.sample upsmon.conf.sample nut-2.7.4/conf/upsset.conf.sample0000644000175000017500000000261412640443572013664 00000000000000# Network UPS Tools - upsset.conf sample file # # This file is provided to ensure that you do not expose your upsd server # to the world upon installing the CGI programs. Specifically, it keeps # the upsset.cgi program from running until you have assured it that you # have secured your web server's CGI directory. # # By default, your web server will probably let anyone access upsset.cgi # once it is installed. This means that anyone could attempt to crack # upsd logins since they would appear to be coming from your web server, # rather than the outside world, slipping through any ACL/ACCESS definitions. # # For this reason, you *MUST* first secure your CGI programs before # enabling upsset in this configuration file. If you can't do this in # your web server, then you should *not* run this program. # # For Apache, the .htaccess file can be used in the directory with the # programs. You'll need something like this: # # # deny from all # allow from your.network.addresses # # # You will probably have to set "AllowOverride Limit" for this directory in # your server-level configuration file as well. # # If this doesn't make sense, then stop reading and leave this program alone. # # Assuming you have all this done (and it works), then you may uncomment # the line below and start using upsset.cgi through your web browser. # ### ### I_HAVE_SECURED_MY_CGI_DIRECTORY ### nut-2.7.4/conf/ups.conf.sample0000644000175000017500000001101212640473702013136 00000000000000# Network UPS Tools: example ups.conf # # --- SECURITY NOTE --- # # If you use snmp-ups and set a community string in here, you # will have to secure this file to keep other users from obtaining # that string. It needs to be readable by upsdrvctl and any drivers, # and by upsd. # # --- # # This is where you configure all the UPSes that this system will be # monitoring directly. These are usually attached to serial ports, but # USB devices and SNMP devices are also supported. # # This file is used by upsdrvctl to start and stop your driver(s), and # is also used by upsd to determine which drivers to monitor. The # drivers themselves also read this file for configuration directives. # # The general form is: # # [upsname] # driver = # port = # < any other directives here > # # The section header ([upsname]) can be just about anything as long as # it is a single word inside brackets. upsd uses this to uniquely # identify a UPS on this system. # # If you have a UPS called snoopy, your section header would be "[snoopy]". # On a system called "doghouse", the line in your upsmon.conf to monitor # it would look something like this: # # MONITOR snoopy@doghouse 1 upsmonuser mypassword master # # It might look like this if monitoring in slave mode: # # MONITOR snoopy@doghouse 1 upsmonuser mypassword slave # # Configuration directives # ------------------------ # # These directives are common to all drivers that support ups.conf: # # driver: REQUIRED. Specify the program to run to talk to this UPS. # apcsmart, bestups, and sec are some examples. # # port: REQUIRED. The serial port where your UPS is connected. # /dev/ttyS0 is usually the first port on Linux boxes, for example. # # sdorder: optional. When you have multiple UPSes on your system, you # usually need to turn them off in a certain order. upsdrvctl # shuts down all the 0s, then the 1s, 2s, and so on. To exclude # a UPS from the shutdown sequence, set this to -1. # # The default value for this parameter is 0. # # nolock: optional, and not recommended for use in this file. # # If you put nolock in here, the driver will not lock the # serial port every time it starts. This may allow other # processes to seize the port if you start more than one by # mistake. # # This is only intended to be used on systems where locking # absolutely must be disabled for the software to work. # # maxstartdelay: optional. This can be set as a global variable # above your first UPS definition and it can also be # set in a UPS section. This value controls how long # upsdrvctl will wait for the driver to finish starting. # This keeps your system from getting stuck due to a # broken driver or UPS. # # The default is 45 seconds. # # synchronous: optional. The driver work by default in asynchronous # mode (i.e *synchronous=no*). This means that all data # are pushed by the driver on the communication socket to # upsd (Unix socket on Unix, Named pipe on Windows) without # waiting for these data to be actually consumed. With # some HW, such as ePDUs, that can produce a lot of data, # asynchronous mode may cause some congestion, resulting in # the socket to be full, and the driver to appear as not # connected. By enabling the 'synchronous' flag # (value = 'yes'), the driver will wait for data to be # consumed by upsd, prior to publishing more. This can be # enabled either globally or per driver. # # The default is 'no' (i.e. asynchronous mode) for backward # compatibility of the driver behavior. # # Anything else is passed through to the hardware-specific part of # the driver. # # Examples # -------- # # A simple example for a UPS called "powerpal" that uses the blazer_ser # driver on /dev/ttyS0 is: # # [powerpal] # driver = blazer_ser # port = /dev/ttyS0 # desc = "Web server" # # If your UPS driver requires additional settings, you can specify them # here. For example, if it supports a setting of "1234" for the # variable "cable", it would look like this: # # [myups] # driver = mydriver # port = /dev/ttyS1 # cable = 1234 # desc = "Something descriptive" # # To find out if your driver supports any extra settings, start it with # the -h option and/or read the driver's documentation. nut-2.7.4/TODO0000644000175000017500000001072012640443572007752 00000000000000NUT roadmap and ideas for future expansion ------------------------------------------ Here are some ideas that have come up over the years but haven't been implemented yet. This may be a good place to start if you're looking for a rainy day hacking project. Roadmap ~~~~~~~ 2.6 ^^^ This release is focused on the website and documentation rewrite, using the excellent link:http://www.methods.co.nz/asciidoc[AsciiDoc]. 2.8 ^^^ This branch will focus on configuration and user interface improvements. 3.0 ^^^ This major transition will mark the final switch to a complete power device broker. Non-network "upsmon" ~~~~~~~~~~~~~~~~~~~~ Some systems don't want a daemon listening to the network. This can be for security reasons, or perhaps because the system has been squashed down and doesn't have TCP/IP available. For these situations you could run a driver and program that sits on top of the driver socket to do local monitoring. This also makes monitoring extremely easy to automate - you don't need to worry about usernames, passwords or firewalling. Just start a driver and drop this program on top of it. - Parse ups.conf and open the state socket for a driver - Send DUMPALL and enter a select loop - Parse SETINFOs that change ups.status - When you get OB LB, shut down Completely unprivileged upsmon ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ upsmon currently retains root in a forked process so it can call the shutdown command. The only reason it needs root on most systems is that only privileged users can signal init or send a message on /dev/initctl. In the case of systems running sysvinit (Slackware, others?), upsmon could just open /dev/initctl while it has root and then drop it completely. When it's time to shut down, fire a control structure at init across the lingering socket and tell it to enter runlevel 0. This has been shown to work in local tests, but it's not portable. It could only be offered as an option for those systems which run that flavor of init. It also needs to be tested to see what happens to the lingering fd over time, such as when init restarts after an upgrade. For other systems, there is always the possibility of having a suid program which does nothing but prod init into starting a shutdown. Lock down the group access so only upsmon's unprivileged user can access it, and make that your SHUTDOWNCMD. Then it could drop root completely. Chrooted upsmon ~~~~~~~~~~~~~~~ upsmon could run the network monitoring part in a chroot jail if it had a pipe to another process running outside for NOTIFY dispatches. Such a pipe would have to be constructed extremely carefully so an attacker could not compromise it from the jailed process. A state machine with a tightly defined sequence could do this safely. All it has to do is dispatch the UPS name and event type. [start] [type] [length] [stop] Monitor program with interpreted language ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once in awhile, I get requests for a way to shut down based on the UPS temperature, or ambient humidity, or at a certain battery charge level, or any number of things other than an "OB LB" status. It should be obvious that adding a way to monitor all of that in upsmon would bloat upsmon for all those people who really don't need anything like that. A separate program that interprets a list of rules and uses it to monitor the UPS equipment is the way to solve this. If you have a condition that needs to be tested, add a rule. Some of the tools that such a language would need include simple greater-than/less-than testing (if battery.charge < 20), equivalence testing (if ups.model = "SMART-UPS 700"), and some way to set and clear timers. Due to the expected size and limited audience for such a program, it might have to be distributed separately. NOTE: Python may be a good candidate. Sandbox ~~~~~~~ - check to refresh and integrate the https://alioth.debian.org/pm/?group_id=30602[tasks] list and https://alioth.debian.org/tracker/?atid=411545&group_id=30602&func=browse[feature requests] list from Alioth - add "Generic ?Ascii? driver": I've got to think more about that, but the recent solar panel driver, and the powerman internal approach of a generic engine with a scripting interface is a cool idea. Ref http://powerman.svn.sourceforge.net/viewvc/powerman/trunk/etc/apcpdu.dev?revision=969&view=markup - integrate the (future) new powerman LUA engine (maybe/mustbe used for the driver above?) for native PDU support - see how we can help and collaborate with DeviceKit-power nut-2.7.4/docs/0000755000175000017500000000000012670024741010266 500000000000000nut-2.7.4/docs/man/0000755000175000017500000000000012670024741011041 500000000000000nut-2.7.4/docs/man/netxml-ups.80000644000175000017500000001253112640476522013175 00000000000000'\" t .\" Title: netxml-ups .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NETXML\-UPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" netxml-ups \- Driver for Eaton / MGE Network Management Card / Proxy (XML/HTTP Protocol) equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the netxml\-ups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp netxml\-ups support all recent Eaton / MGE models which use a Network Management Card or Proxy (MGE XML/HTTP protocol based)\&. This applies to both Eaton (previously MGE Office Protection Systems) and to MGE UPS SYSTEMS\&. Supported card and proxy models are: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NMC Minislot (Ref 66102, firmware EA or newer), .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SNMP/Web Minislot card (Ref 66244) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NMC Transverse (Ref 66074), .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NMC & Modbus/JBus (Ref 66103), .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Network Management Proxy, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ePDU Monitored (newer version)\&. .RE .sp Older models, such as SNMP card (Ref 66062 and Ref 66045), use the SNMP protocol and should use the \fBsnmp-ups\fR(8) driver with the "mibs=mge" parameter\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBtimeout\fR=\fIvalue\fR .RS 4 The timeout for connecting to and reading from the UPS\&. Defaults to 5 seconds\&. Don\(cqt change this value unless you know exactly what you\(cqre doing\&. .RE .sp This value \fBmust never\fR be higher than half the MAXAGE value specified in \fBupsd.conf\fR(5), otherwise you run the risk that \fBupsd\fR(8) declares the driver stale while it is waiting for a connection to timeout\&. .PP \fBsubscribe\fR .RS 4 Connect to the NMC in subscribed mode\&. This allows to receive notifications and alarms more quickly, beside from the standard polling requests\&. .RE .PP \fBlogin\fR=\fIvalue\fR .RS 4 Set the login value for authenticated mode\&. This feature also needs the \fBpassword\fR argument, and allows value settings in the card\&. This feature is not used yet\&. .RE .PP \fBpassword\fR=\fIvalue\fR .RS 4 Set the password value, needed with the login for authenticated mode\&. This feature is not used yet\&. .RE .PP \fBshutdown_duration\fR=\fIvalue\fR .RS 4 Set the shutdown duration of the operating system, in seconds\&. This represents the amount of time needed by the system to operate a clean shutdown\&. Defaults to 120 seconds\&. .RE .PP \fBshutdown_timer\fR=\fIvalue\fR .RS 4 Set the shutdown timer, in seconds\&. After \fIvalue\fR seconds running on battery, the local system will receive a notification to shutdown\&. Defaults to "none" (disabled)\&. .RE .SH "IMPLEMENTATION" .sp The hostname of the UPS is specified with the "port" value in \fBups\&.conf\fR, i\&.e\&.: .sp .if n \{\ .RS 4 .\} .nf [mgexml] driver = netxml\-ups port = http://netxml\-ups\&.example\&.com:80 .fi .if n \{\ .RE .\} .sp Specifying the method to connect to the UPS (http, https) is mandatory\&. If the port is equal to the default for the method specified (80 for http, 443 for https) it may be omitted\&. .sp In order not to overload older NMCs by polling them too frequently, it is recommended to increase the "pollinterval" (see \fBnutupsdrv\fR(8)) and \fBups.conf\fR(5)) to at least 5 seconds\&. .SH "KNOWN ISSUES" .sp Don\(cqt connect to the UPS through a proxy\&. Although it would be trivial to add support for proxies, this is not recommended and don\(cqt ask for it\&. Not only because it will prevent the driver to make a persistent connection to the UPS, but also it adds an additional failure mode\&. If the proxy goes down (for whatever reason), the driver will no longer be able to reach the UPS\&. .SH "AUTHORS" .sp Arjen de Korte .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upsmon.txt0000644000175000017500000003655212640473702013060 00000000000000UPSMON(8) ========= NAME ---- upsmon - UPS monitor and shutdown controller SYNOPSIS -------- *upsmon* -h *upsmon* -c 'command' *upsmon* [-D] [-K] [-p] [-u 'user'] DESCRIPTION ----------- *upsmon* is the client process that is responsible for the most important part of UPS monitoring--shutting down the system when the power goes out. It can call out to other helper programs for notification purposes during power events. upsmon can monitor multiple systems using a single process. Every UPS that is defined in the linkman:upsmon.conf[5] configuration file is assigned a power value and a type (*slave* or *master*). OPTIONS ------- *-h*:: Display the help message. *-c* 'command':: Send the command 'command' to the existing upsmon process. Valid commands are: *fsd*;; shutdown all master UPSes (use with caution) *stop*;; stop monitoring and exit *reload*;; reread linkman:upsmon.conf[5] configuration file. See "reloading nuances" below if this doesn't work. *-D*:: Raise the debugging level. upsmon will run in the foreground and prints information on stdout about the monitoring process. Use this multiple times for more details. *-K*:: Test for the shutdown flag. If it exists and contains the magic string from upsmon, then upsmon will exit with `EXIT_SUCCESS`. Any other condition will make upsmon exit with `EXIT_FAILURE`. + You can test for a successful exit from `upsmon -K` in your shutdown scripts to know when to call linkman:upsdrvctl[8] to shut down the UPS. *-p*:: Run privileged all the time. Normally upsmon will split into two processes. The majority of the code runs as an unprivileged user, and only a tiny stub runs as root. This switch will disable that mode, and run the old "all root all the time" system. + This is not the recommended mode, and you should not use this unless you have a very good reason. *-u* 'user':: Set the user for the unprivileged monitoring process. This has no effect when using -p. + The default user is set at configure time with 'configure --with-user=...'. Typically this is 'nobody', but other distributions will probably have a specific 'nut' user for this task. If your notification scripts need to run as a specific user, set it here. + You can also set this in the linkman:upsmon.conf[5] file with the RUN_AS_USER directive. UPS DEFINITIONS --------------- In the linkman:upsmon.conf[5], you must specify at least one UPS that will be monitored. Use the MONITOR directive. MONITOR 'system' 'powervalue' 'username' 'password' 'type' The 'system' refers to a linkman:upsd[8] server, in the form +upsname[@hostname[:port]]+. The default hostname is "localhost". Some examples follow: - "su700@mybox" means a UPS called "su700" on a system called "mybox". This is the normal form. - "fenton@bigbox:5678" is a UPS called "fenton" on a system called "bigbox" which runs linkman:upsd[8] on port "5678". The 'powervalue' refers to how many power supplies on this system are being driven this UPS. This is typically set to 1, but see the section on power values below. The 'username' is a section in your linkman:upsd.users[5] file. Whatever password you set in that section must match the 'password' set in this file. The type set in that section must also match the 'type' here-- *master* or *slave*. In general, a master process is one running on the system with the UPS actually plugged into a serial port, and a slave is drawing power from the UPS but can't talk to it directly. See the section on UPS types for more. NOTIFY EVENTS ------------- *upsmon* senses several events as it monitors each UPS. They are called notify events as they can be used to tell the users and admins about the change in status. See the additional NOTIFY-related sections below for information on customizing the delivery of these messages. *ONLINE*:: The UPS is back on line. *ONBATT*:: The UPS is on battery. *LOWBATT*:: The UPS battery is low (as determined by the driver). *FSD*:: The UPS has been commanded into the "forced shutdown" mode. *COMMOK*:: Communication with the UPS has been established. *COMMBAD*:: Communication with the UPS was just lost. *SHUTDOWN*:: The local system is being shut down. *REPLBATT*:: The UPS needs to have its battery replaced. *NOCOMM*:: The UPS can't be contacted for monitoring. NOTIFY COMMAND -------------- In linkman:upsmon.conf[5], you can configure a program called the NOTIFYCMD that will handle events that occur. +NOTIFYCMD+ "'path to program'" +NOTIFYCMD "/usr/local/bin/notifyme"+ Remember to wrap the path in "quotes" if it contains any spaces. The program you run as your NOTIFYCMD can use the environment variables NOTIFYTYPE and UPSNAME to know what has happened and on which UPS. It also receives the notification message (see below) as the first (and only) argument, so you can deliver a preformatted message too. Note that the NOTIFYCMD will only be called for a given event when you set the EXEC flag by using the notify flags, below: NOTIFY FLAGS ------------ By default, all notify events (see above) generate a global message (wall) to all users, plus they are logged via the syslog. You can change this with the NOTIFYFLAG directive in the configuration file: +NOTIFYFLAG+ 'notifytype' 'flags' Examples: - `NOTIFYFLAG ONLINE SYSLOG` - `NOTIFYFLAG ONBATT SYSLOG+WALL` - `NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC` The flags that can be set on a given notify event are: *SYSLOG*:: Write this message to the syslog. *WALL*:: Send this message to all users on the system via *wall*(1). *EXEC*:: Execute the NOTIFYCMD. *IGNORE*:: Don't do anything. If you use this, don't use any of the other flags. You can mix these flags. "SYSLOG+WALL+EXEC" does all three for a given event. NOTIFY MESSAGES --------------- upsmon comes with default messages for each of the NOTIFY events. These can be changed with the NOTIFYMSG directive. +NOTIFYMSG+ 'type' "'message'" Examples: - `NOTIFYMSG ONLINE "UPS %s is getting line power"` - ` NOTIFYMSG ONBATT "Someone pulled the plug on %s"` The first instance of %s is replaced with the identifier of the UPS that generated the event. These messages are used when sending walls to the users directly from upsmon, and are also passed to the NOTIFYCMD. POWER VALUES ------------ The "current overall power value" is the sum of all UPSes that are currently able to supply power to the system hosting upsmon. Any UPS that is either on line or just on battery contributes to this number. If a UPS is critical (on battery and low battery) or has been put into "forced shutdown" mode, it no longer contributes. A "power value" on a MONITOR line in the config file is the number of power supplies that the UPS runs on the current system. +MONITOR+ 'upsname' 'powervalue' 'username' 'password' 'type' Normally, you only have one power supply, so it will be set to 1. +MONITOR myups@myhost 1 username mypassword master+ On a large server with redundant power supplies, the power value for a UPS may be greater than 1. You may also have more than one of them defined. +MONITOR ups-alpha@myhost 2 username mypassword master+ +MONITOR ups-beta@myhost 2 username mypassword master+ You can also set the power value for a UPS to 0 if it does not supply any power to that system. This is generally used when you want to use the upsmon notification features for a UPS even though it's not actually running the system that hosts upsmon. Don't set this to "master" unless you really want to power this UPS off when this instance of upsmon needs to shut down for its own reasons. +MONITOR faraway@anotherbox 0 username mypassword slave+ The "minimum power value" is the number of power supplies that must be receiving power in order to keep the computer running. +MINSUPPLIES+ 'value' Typical PCs only have 1, so most users will leave this at the default. +MINSUPPLIES 1+ If you have a server or similar system with redundant power, then this value will usually be set higher. One that requires three power supplies to be running at all times would simply set it to 3. +MINSUPPLIES 3+ When the current overall power value drops below the minimum power value, upsmon starts the shutdown sequence. This design allows you to lose some of your power supplies in a redundant power environment without bringing down the entire system while still working properly for smaller systems. UPS TYPES --------- *upsmon* and linkman:upsd[8] don't always run on the same system. When they do, any UPSes that are directly attached to the upsmon host should be monitored in "master" mode. This makes upsmon take charge of that equipment, and it will wait for slaves to disconnect before shutting down the local system. This allows the distant systems (monitoring over the network) to shut down cleanly before `upsdrvctl shutdown` runs and turns them all off. When upsmon runs as a slave, it is relying on the distant system to tell it about the state of the UPS. When that UPS goes critical (on battery and low battery), it immediately invokes the local shutdown command. This needs to happen quickly. Once it disconnects from the distant linkman:upsd[8] server, the master upsmon will start its own shutdown process. Your slaves must all shut down before the master turns off the power or filesystem damage may result. upsmon deals with slaves that get wedged, hang, or otherwise fail to disconnect from linkman:upsd[8] in a timely manner with the HOSTSYNC timer. During a shutdown situation, the master upsmon will give up after this interval and it will shut down anyway. This keeps the master from sitting there forever (which would endanger that host) if a slave should break somehow. This defaults to 15 seconds. If your master system is shutting down too quickly, set the FINALDELAY interval to something greater than the default 15 seconds. Don't set this too high, or your UPS battery may run out of power before the master upsmon process shuts down that system. TIMED SHUTDOWNS --------------- For those rare situations where the shutdown process can't be completed between the time that low battery is signalled and the UPS actually powers off the load, use the linkman:upssched[8] helper program. You can use it along with upsmon to schedule a shutdown based on the "on battery" event. upssched can then come back to upsmon to initiate the shutdown once it has run on battery too long. This can be complicated and messy, so stick to the default critical UPS handling if you can. REDUNDANT POWER SUPPLIES ------------------------ If you have more than one power supply for redundant power, you may also have more than one UPS feeding your computer. upsmon can handle this. Be sure to set the UPS power values appropriately and the MINSUPPLIES value high enough so that it keeps running until it really does need to shut down. For example, the HP NetServer LH4 by default has 3 power supplies installed, with one bay empty. It has two power cords, one per side of the box. This means that one power cord powers two power supply bays, and that you can only have two UPSes supplying power. Connect UPS "alpha" to the cord feeding two power supplies, and UPS "beta" to the cord that feeds the third and the empty slot. Define alpha as a powervalue of 2, and beta as a powervalue of 1. Set the MINSUPPLIES to 2. When alpha goes on battery, your current overall power value will stay at 3, as it's still supplying power. However, once it goes critical (on battery and low battery), it will stop contributing to the current overall power value. That means the value will be 1 (beta alone), which is less than 2. That is insufficient to run the system, and upsmon will invoke the shutdown sequence. However, if beta goes critical, subtracting its contribution will take the current overall value from 3 to 2. This is just high enough to satisfy the minimum, so the system will continue running as before. If beta returns later, it will be re-added and the current value will go back to 3. This allows you to swap out UPSes, change a power configuration, or whatever, as long as you maintain the minimum power value at all times. MIXED OPERATIONS ---------------- Besides being able to monitor multiple UPSes, upsmon can also monitor them as different roles. If you have a system with multiple power supplies serviced by separate UPS batteries, it's possible to be a master on one and a slave on the other. This usually happens when you run out of serial ports and need to do the monitoring through another system nearby. This is also complicated, especially when it comes time to power down a UPS that has gone critical but doesn't supply the local system. You can do this with some scripting magic in your notify command script, but it's beyond the scope of this manual. FORCED SHUTDOWNS ---------------- When upsmon is forced to bring down the local system, it sets the "FSD" (forced shutdown) flag on any UPSes that it is running in master mode. This is used to synchronize slaves in the event that a master UPS that is otherwise OK needs to be brought down due to some pressing event on the master. You can manually invoke this mode on the master upsmon by starting another copy with `-c fsd`. This is useful when you want to initiate a shutdown before the critical stage through some external means, such as linkman:upssched[8]. DEAD UPSES ---------- In the event that upsmon can't reach linkman:upsd[8], it declares that UPS "dead" after some interval controlled by DEADTIME in the linkman:upsmon.conf[5]. If this happens while that UPS was last known to be on battery, it is assumed to have gone critical and no longer contributes to the overall power value. upsmon will alert you to a UPS that can't be contacted for monitoring with a "NOCOMM" notifier by default every 300 seconds. This can be changed with the NOCOMMWARNTIME setting. RELOADING NUANCES ----------------- upsmon usually gives up root powers for the process that does most of the work, including handling signals like SIGHUP to reload the configuration file. This means your linkman:upsmon.conf[8] file must be readable by the non-root account that upsmon switches to. If you want reloads to work, upsmon must run as some user that has permissions to read the configuration file. I recommend making a new user just for this purpose, as making the file readable by "nobody" (the default user) would be a bad idea. See the RUN_AS_USER section in linkman:upsmon.conf[8] for more on this topic. Additionally, you can't change the SHUTDOWNCMD or POWERDOWNFLAG definitions with a reload due to the split-process model. If you change those values, you *must* stop upsmon and start it back up. upsmon will warn you in the syslog if you make changes to either of those values during a reload. SIMULATING POWER FAILURES ------------------------- To test a synchronized shutdown without pulling the plug on your UPS(es), you need only set the forced shutdown (FSD) flag on them. You can do this by calling upsmon again to set the flag, i.e.: +upsmon -c fsd+ After that, the master and the slaves will do their usual shutdown sequence as if the battery had gone critical. This is much easier on your UPS equipment, and it beats crawling under a desk to find the plug. FILES ----- linkman:upsmon.conf[5] SEE ALSO -------- Server: ~~~~~~~ linkman:upsd[8] Clients: ~~~~~~~~ linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_add_device_to_device.30000644000175000017500000000544712665610656016714 00000000000000'\" t .\" Title: nutscan_add_device_to_device .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_ADD_DEVICE_T" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_add_device_to_device \- Concatenate two devices structure\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); .fi .SH "DESCRIPTION" .sp The nutscan_device_t contains the following variables: .sp .if n \{\ .RS 4 .\} .nf nutscan_device_type_t type; char * driver; char * port; nutscan_options_t opt; struct nutscan_device * prev; struct nutscan_device * next; .fi .if n \{\ .RE .\} .sp This is a double linked list of device\&. Each device is described by its type, its driver name, its port and any number of optional data\&. .sp The \fBnutscan_add_device_to_device()\fR concatenates \fIfirst\fR and \fIsecond\fR devices to a unique device\&. No new device is created, the two linked list are simply linked to each other\&. So \fIfirst\fR and \fIsecond\fR devices are likely to be modified by this function\&. .SH "RETURN VALUE" .sp The \fBnutscan_add_device_to_device()\fR functions returns a pointer to a device containg both passed devices\&. Note that it\(cqs not a new device, so it is either \fIfirst\fR or \fIsecond\fR which is returned\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3) nut-2.7.4/docs/man/nutscan_scan_xml_http.txt0000644000175000017500000000231212640444140016112 00000000000000NUTSCAN_SCAN_XML_HTTP(3) ======================== NAME ---- nutscan_scan_xml_http - Scan network for XML/HTTP devices. SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_xml_http(long usec_timeout); DESCRIPTION ----------- The *nutscan_scan_xml_http()* function try to detect NUT compatible XML/HTTP devices. It does this by issuing a broadcast message on currently configured network interfaces. It waits up to 'usec_timeout' microseconds for a response from potential devices. You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ The *nutscan_scan_xml_http()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] nut-2.7.4/docs/man/nutscan_free_device.30000644000175000017500000000377312665610655015063 00000000000000'\" t .\" Title: nutscan_free_device .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_FREE_DEVICE" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_free_device \- Free a nutscan_device_t structure created by nutscan_new_device\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf void nutscan_free_device(nutscan_device_t * device); .fi .SH "DESCRIPTION" .sp The \fBnutscan_free_device()\fR function free a nutscan_device_type_t structure\&. Doing so, it free the whole linked list, not only the given device\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3) nut-2.7.4/docs/man/upscli_list_next.30000644000175000017500000000635112665610635014451 00000000000000'\" t .\" Title: upscli_list_next .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_LIST_NEXT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_list_next \- retrieve list items from a UPS .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_list_next(UPSCONN_t *ups, unsigned int numq, const char **query, unsigned int *numa, char ***answer) .fi .SH "DESCRIPTION" .sp The \fBupscli_list_next()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure, and the pointer \fIquery\fR to an array of \fInumq\fR query elements\&. It performs a read from the network and expects to find either another list item or the end of a list\&. .sp You must call \fBupscli_list_start\fR(3) before calling this function\&. .sp This function will return 1 and set values in \fInuma\fR and \fIanswer\fR if a list item is received\&. If the list is done, it will return 0, and the values in \fInuma\fR and \fIanswer\fR are undefined\&. .sp Calling this function after it returns something other than 1 is undefined\&. .SH "QUERY FORMATTING" .sp You may not change the values of \fInumq\fR or \fIquery\fR between the call to \fBupscli_list_start\fR(3) and the first call to this function\&. You also may not change the values between calls to this function\&. .SH "ANSWER FORMATTING" .sp The contents of \fInuma\fR and \fIanswer\fR work just like a call to \fBupscli_get\fR(3)\&. The values returned by \fBupsd\fR(8) are identical to a single item request, so this is not surprising\&. .SH "ERROR CHECKING" .sp This function checks the response from \fBupsd\fR(8) against your query\&. If the response is not part of the list you have requested, it will return an error code\&. .sp When this happens, \fBupscli_upserror\fR(3) will return UPSCLI_ERR_PROTOCOL\&. .SH "RETURN VALUE" .sp The \fBupscli_list_next()\fR function returns 1 when list data is present, 0 if the list is finished, or \-1 if an error occurs\&. .sp It is possible to have an empty list\&. The function will return 0 for its first call in that case\&. .SH "SEE ALSO" .sp \fBupscli_list_start\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/belkinunv.txt0000644000175000017500000002573212640443572013534 00000000000000BELKINUNV(8) ============ NAME ---- belkinunv - Driver for Belkin "Universal UPS" and compatible NOTE ---- This man page only documents the hardware-specific features of the belkin driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The belkinunv driver is known to work with the Belkin Universal UPS models F6C800-UNV and F6C120-UNV, and is expected to work with other Belkin Universal UPS models. The driver only supports serial communication, not USB. The Trust UPS and older Belkin units are not supported by this driver, and neither are the Belkin Home Office models (F6H500-SER and so forth). However, some Belkin models, such as the Regulator Pro, are supported by the linkman:belkin[8] driver, and the Home Office models are supported using the linkman:genericups[8] driver with `upstype=7`. SOFT SHUTDOWN WORKAROUND ------------------------ One problem with the Belkin Universal UPS is that it cannot enter a soft shutdown (shut down the load until AC power returns) unless the batteries are completely depleted. Thus, one cannot just shut off the UPS after operating system shutdown; it will not come back on when the power comes back on. Therefore, the belkinunv driver should never be used with the *-k* option. Instead, the *-x wait* option is provided as a workaround. When called with the *-x wait* option, *belkinunv* behaves as a standalone program (i.e., it does not fork into the background). It performs one simple task: it connects to the UPS, waits for AC power to return, and then exits with status 0. This is meant to be used in a shutdown script as follows: during a shutdown, after all filesystems have been remounted read-only, and just before the system would normally be halted: check /etc/killpower (or similar) to see if this shutdown was caused by linkman:upsmon[8], and if yes, call *belkinunv -x wait*. If AC power comes back on, *belkinunv* exits, and things should be arranged so that the system reboots in this case. If AC power does not come back on, the UPS will eventually run out of batteries, kill the computer's power supply, and go into soft shutdown mode, which means everything will reboot properly when the power returns. In either case, a deadlock is avoided. In addition, if an optional integer argument is given to the *-x wait* option, this causes *belkinunv* to wait not only for AC power to be present, but also for the battery charge to reach the given level. I use this as part of my startup scripts, to ensure that the batteries are sufficiently charged before the computer continues booting. This should be put very early in the startup script, before any filesystems are mounted read/write, and before any filesystem checks are performed. Several other *-x* options are provided to fine-tune this behavior. See the <<_options,options>> below for detailed descriptions. See the <<_examples,examples>> below for examples of how to use *belkinunv* in shutdown and startup scripts. OPTIONS ------- See also linkman:nutupsdrv[8] for generic options. Never use the *-k* option with this driver; it does not work properly. *-x wait*[='level']:: When this option is used, *belkinunv* does not fork into the background, but behaves as a standalone program. It connects to the UPS and waits until AC power is present. If 'level' is specified, it also waits until the battery charge reaches at least the given level in percent. Then, and only then, *belkinunv* exits. In addition, while *belkinunv* runs in this mode, it displays a status line with information on the UPS status and battery level. This is intended for use in the computer's shutdown and startup scripts, as described under <<_soft_shutdown_workaround,Soft Shutdown Workaround>> above. *-x nohang*:: This option only has an effect when used in conjunction with the *-x wait* option. It causes *belkinunv* to exit if a connection with the UPS cannot be established or is lost, instead of retrying forever, which is the default behavior. The *-x nohang* option should be used in a startup script, to ensure the computer remains bootable even if the UPS has been disconnected during the power failure (for instance, you attached your computer to a generator, carried it to a neighbor's house, or whatever). *-x flash*:: This option only has an effect when used in conjunction with the *-x wait* option. It causes the UPS load to be shut off for a short time ("flashed") just after the AC power has returned and the requested battery level (if any) has been attained. This is useful if slaves are attached to this UPS; the flash will cause all of them to reboot. Note that, due to the design of the Belkin UPS hardware, the load shutdown lasts ca. 1--2 minutes; a shorter flash cannot be performed reliably. Also, the computers will reboot at the scheduled time, on battery power if necessary, even if AC power fails again in the meantime. This should not be a problem, as your startup scripts can catch this situation. *-x silent*:: This option only has an effect when used in conjunction with the *-x wait* option. It suppresses the status line which *belkinunv* would normally print. *-x dumbterm*:: This option only has an effect when used in conjunction with the *-x wait* option. It changes the way in which *belkinunv* prints its status line. Normally, terminal control sequences are used to overwrite the same line with new status information, each time the status is updated. This may not work on all terminals. If the *-x dumbterm* option is given, each status update is written on a new line. VARIABLES --------- *battery.charge*:: *battery.runtime*:: not supported by all hardware. *battery.voltage*:: *battery.voltage.nominal*:: *input.frequency*:: *input.frequency.nominal*:: e.g. 60 for 60Hz *input.sensitivity*:: writable: normal/medium/low *input.transfer.high*:: writable: high transfer voltage point in V *input.transfer.low*:: writable: low transfer voltage point in V *input.voltage*:: *input.voltage.maximum*:: *input.voltage.minimum*:: *input.voltage.nominal*:: *output.frequency*:: *output.voltage*:: *ups.beeper.status*:: writable. Values: enabled/disabled/muted. This variable controls the state of the panel beeper. Enabled means sound when the alarm is present, disabled means never sound, and muted means the sound is temporarily disabled until the alarm would normally stop sounding. In the muted state, the beeper is automatically turned back on at the next event (AC failure, battery test, etc). Also, the beeper can't be turned off during a critical event (low battery). Note that not all UPS models support the "disabled" state. *ups.firmware*:: *ups.load*:: *ups.model*:: *ups.power.nominal*:: e.g. 800 for an 800VA system *ups.status*:: a list of flags; see the <<_status_flags,status flags>> below. *ups.temperature*:: not supported by all hardware. *ups.test.result*:: *ups.delay.restart*:: time to restart (read only) *ups.delay.shutdown*:: time to shutdown (read only). This is always a multiple of 60 seconds. *ups.type*:: ONLINE/OFFLINE/LINEINT. This describes the basic layout of this UPS (for GUI clients which want to draw an animated picture of power flow). An offline UPS has a direct connection from AC input to AC output, and also a connection from AC input to the battery, and from the battery to AC output. An online UPS lacks the direct connection from AC input to AC output, whereas a line interactive UPS lacks the connection from AC input to the battery. COMMANDS -------- *beeper.enable, beeper.disable, beeper.mute*:: Enable, disable or mute the panel beeper. Note that if the beeper is muted, it is automatically turned back on at the next event (AC failure, battery test, etc). Also, the beeper can't be turned muted during a critical event (low battery). *reset.input.minmax*:: Reset the variables *input.voltage.minimum* and *input.voltage.maximum*. *shutdown.reboot*:: Shut down load immediately for about 1--2 minutes. *shutdown.reboot.graceful*:: After 40 second delay, shut down load for about 1--2 minutes. *shutdown.stayoff*:: Shut down load immediately and stay off. The only way it can be turned back on is by manually pressing the front panel button. *test.battery.start, test.battery.stop*:: Start/stop 10 second battery test. *test.failure.start, test.failure.stop*:: Start/stop "deep" battery test. STATUS FLAGS ------------ *OB*:: load is on battery, including during tests *OFF*:: load is off *OL*:: load is online *ACFAIL*:: AC failure. Note that this refers to the AC input, and thus it is not the same as "OB". An AC failure can occur at any time, for instance, during a battery test, or when the UPS load is off. *OVER*:: overload *OVERHEAT*:: overheat *COMMFAULT*:: UPS fault *LB*:: low battery *CHRG*:: charging *DEPLETED*:: the battery is depleted. When the UPS raises this flag, it simultaneously switches off the load. *RB*:: replace battery EXAMPLES -------- Here is an example for how *belkinunv* should be used in a computer's shutdown script. These commands should go in the very last part of the shutdown script, after all file systems have been mounted read-only, and just before the computer halts. Note that *belkinunv* must be installed in a directory which is still readable at that point. # NEAR END OF SHUTDOWN SCRIPT: # if shutdown was caused by UPS, perform Belkin UPS workaround. if [ -f /etc/killpower ] ; then echo "Waiting for AC power, or for UPS batteries to run out..." /usr/bin/belkinunv -x wait /dev/ttyS1 # we get here if the power came back on. Reboot. echo "Power is back. Rebooting..." reboot fi And here is an example of how to use *belkinunv* in the startup script. These commands should go near the beginning of the startup script, before any file systems are mounted read/write, and before any file system integrity checks are done. # NEAR BEGINNING OF STARTUP SCRIPT: # if we are recovering from a power failure, wait for the UPS to # charge to a comfortable level before writing anything to disk if [ -f /etc/killpower ] ; then echo "Waiting for UPS battery charge to reach 60%..." /usr/bin/belkinunv -x wait=60 -x nohang /dev/ttyS1 fi EXIT STATUS ----------- When used normally, *belkinunv* forks into the background and its diagnostics are the same as for all NUT drivers, see linkman:nutupsdrv[8]. When used with the *-x wait* option, the exit status is normally *0*. If the *-x nohang* option has also been specified, an exit status of *1* indicates that communication with the UPS was lost. If the *-x flash* option has been specified, an exit status of *2* indicates that the timed shutdown has failed. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in linkman:ups.conf[5]. SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ - The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - The documentation for the protocol used by this UPS: link:http://www.mscs.dal.ca/~selinger/ups/belkin-universal-ups.html[belkin-universal-ups.html] AUTHOR ------ Peter Selinger nut-2.7.4/docs/man/metasys.txt0000644000175000017500000000270112640443572013213 00000000000000METASYS(8) ========== NAME ---- metasys - Driver for Meta System UPS equipment NOTE ---- This man page only documents the hardware-specific features of the metasys driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The *metasys* driver was written with the "Meta System UPS Protocol Rev.1.12" kindly supplied from Meta System. The driver should support all the common features of the ups models: - HF Line (/2) 1-8 boards - HF Millennium (810, 820) - HF TOP Line (910, 920, 930, 940, 950, 970, 980) - ECO Network (750, 1000, 1050, 1500, 1800, 2000, 2100, 2500, 3000) - ECO (305, 308, 311, 511, 516, 519, 522, SX, SXI) - ally HF (800, 1000, 1250, 1600, 2000, 2500) - Megaline (1250, 2500, 3750, 5000, 6250, 7500, 8750, 10000) CABLING ------- The needed cable is a standard pin-to-pin serial cable with at least pins 2, 3, and 5 (on DB9 connector) connected. EXTRA ARGUMENTS --------------- This driver supports no extra arguments from linkman:ups.conf[5]. BUGS ---- This driver has been tested on Meta System HF Millennium 820 and ally HF 1000 only. Any information about the use of the driver with the other listed UPS are really welcome. AUTHOR ------ Fabio Di Niro SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nut.conf.txt0000644000175000017500000000654512640473702013270 00000000000000NUT.CONF(5) =========== NAME ---- nut.conf - UPS definitions for Network UPS Tools DESCRIPTION ----------- This file attempts to standardize the various files being found in different installations, like /etc/default/nut on Debian based systems and /etc/sysconfig/ups on RedHat based systems. Distribution's init script should source this file in order to determine which components have to be started. Blank lines are ignored. Lines with a hash ('#') character at the 1st position of the line are ignored, too. They can be used to add comments. IMPORTANT NOTE -------------- This file is intended to be sourced by shell scripts. You MUST NOT use spaces around the equal sign! Refer to the EXAMPLE section for illustrations. DIRECTIVES ---------- *MODE*:: Required. Recognized values are 'none', 'standalone', 'netserver' and 'netclient'. Defaults to 'none'. none;; Indicates that NUT should not get started automatically, possibly because it is not configured or that an Integrated Power Management or some external system, is used to startup the NUT components. standalone;; Addresses a local only configuration, with 1 UPS protecting the local system. This implies to start the 3 NUT layers (driver, upsd and upsmon), with the related configuration files. This mode can also address UPS redundancy. netserver;; Like the standalone configuration, but also possibly need one or more specific LISTEN directive(s) in upsd.conf. Since this MODE is open to the network, a special care should be applied to security concerns. netclient;; When only upsmon is required, possibly because there are other hosts that are more closely attached to the UPS, the MODE should be set to netclient. *UPSD_OPTIONS*:: Optional. Set upsd specific options. See linkman:upsd[8] for more details. It is ignored when 'MODE' above indicates that no upsd should be running. *UPSMON_OPTIONS*:: Optional. Set upsmon specific options. See linkman:upsmon[8] for more details. It is ignored when 'MODE' above indicates that no upsmon should be running. *POWEROFF_WAIT*:: Optional. At the end of an emergency system halt, the upsmon master will signal the UPS to switch off. This may fail for a number of reasons. Most notably is the case that mains power returns during the shutdown process. See the section "Power races" in /usr/share/doc/nut/FAQ.txt.gz. The system will wait this long for the UPS to cut power, and then reboot. It should be long enough to exhaust the batteries, in case line power continues to be unavailable. On the other hand, it should not be so long that the system remains offline for an unreasonable amount of time if line power has returned. See sleep(1) for compatible time syntax. If you specify the time in seconds, use the "s" suffix. WARNING: this workaround might be dangerous under some circumstances. Please read http://bugs.debian.org/358696 for more details. EXAMPLE ------- # /etc/nut/nut.conf. See nut.conf(5) MODE=none UPSD_OPTIONS="" UPSMON_OPTIONS="" # POWEROFF_WAIT=15m INTEGRATION ----------- An init script, such as /etc/init.d/nut, is expected to source this file in order to determine which component(s) has to be started. SEE ALSO -------- linkman:ups.conf[5], linkman:upsd.conf[5], linkman:upsd.users[5], linkman:upsmon.conf[5] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_has_device.30000644000175000017500000000003312665610643015226 00000000000000.so libnutclient_devices.3 nut-2.7.4/docs/man/oneac.80000644000175000017500000001176312640476510012151 00000000000000'\" t .\" Title: oneac .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "ONEAC" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" oneac \- Driver for Oneac UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the oneac driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports various Oneac UPS families: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} EG (late 80s, early 90s, plug\-in serial interface card) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ON (early and mid\-90s, plug\-in serial interface card) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} OZ (mid\-90s on, DB\-25 std\&., interface slot) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} OB (early 2000\(cqs on, big cabinet, DB\-25 std\&., interface slot) .RE .sp If your UPS is equipped with the Basic Interface card, use the \fBgenericups\fR(8) driver\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5) file: .PP \fBtesttime\fR=\fInum\fR .RS 4 Change battery test time from the 2 minute default\&. .RE .PP \fBoffdelay\fR=\fInum\fR .RS 4 Change shutdown delay time from 0 second default\&. .RE .SH "INSTANT COMMANDS" .sp This driver supports the following Instant Commands\&. (See \fBupscmd\fR(8)) .SS "All UPS units" .PP \fBshutdown\&.return\fR .RS 4 Turn off the load possibly after a delay and return when power is back\&. .RE .PP \fBshutdown\&.stop\fR .RS 4 Stop a shutdown in progress\&. .RE .PP \fBshutdown\&.reboot\fR .RS 4 Shut down the load briefly while rebooting the UPS\&. .RE .PP \fBtest\&.failure\&.start\fR .RS 4 Starts a 15 second long simulation of an input power failure\&. .RE .PP \fBtest\&.battery\&.start\&.quick\fR .RS 4 Start a "quick" battery test\&. The default time is 2 minutes\&. This time can be set in the \fBups\&.conf\fR file\&. See \fBtestime\fR above\&. .RE .PP \fBtest\&.battery\&.stop\fR .RS 4 Stops a battery test that is in progress\&. .RE .SS "All ON UPS units" .PP \fBreset\&.input\&.minmax\fR .RS 4 Reset the minimum and maximum input line voltage values seen since the last reset or power on\&. .RE .SS "Newer ON UPS units" .PP \fBtest\&.panel\&.start\fR .RS 4 Start testing the UPS panel\&. .RE .PP \fBtest\&.battery\&.start\&.deep\fR .RS 4 Start a "deep" battery test\&. This test runs the UPS until the low battery point and then returns to the AC line\&. .RE .PP \fBreset\&.input\&.minmax\fR .RS 4 Reset the minimum and maximum input line voltage values seen since the last reset or power on\&. .RE .PP \fBbeeper\&.enable\fR .RS 4 Enable UPS beeper/buzzer\&. .RE .PP \fBbeeper\&.disable\fR .RS 4 Disable UPS beeper/buzzer\&. .RE .PP \fBbeeper\&.mute\fR .RS 4 Mutes the UPS beeper/buzzer for the current alarm condition(s)\&. .RE .SH "WRITABLE VARIABLES" .sp See \fBupsrw\fR(8) to see what variables are writable for the UPS\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp If your UPS supports writing battery\&.runtime\&.low, the new set value is to be entered in minutes (up to 99) but the reported value is reported in seconds (set value * 60)\&. .sp .5v .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp If your UPS supports input\&.transfer\&.low and input\&.transfer\&.high, those values are used to create an allowable output range\&. The UPS will do what it can to keep the output voltage value within the defined range (for example: tap change or switch to inverter)\&. .sp .5v .RE .SH "AUTHOR" .sp Bill Elliot , Eric Lawson .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/bestuferrups.80000644000175000017500000000365212640476476013626 00000000000000'\" t .\" Title: bestuferrups .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BESTUFERRUPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" bestuferrups \- Driver for Best Power Micro\-Ferrups .SH "NOTE" .sp This man page only documents the hardware\-specific features of the bestuferrups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp Best Power Micro\-Ferrups ME3100, probably other similar models too\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "AUTHORS" .sp Andreas Wrede, John Stone (bestuferrups) .sp Grant Taylor (bestfort) .sp Russell Kroll (bestups) .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/riello_usb.80000644000175000017500000000375512640476521013227 00000000000000'\" t .\" Title: riello_usb .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "RIELLO_USB" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" riello_usb \- Driver for Riello UPS Protocol UPS equipment via USB .SH "SYNOPSIS" .sp \fBriello_usb\fR \-h .sp \fBriello_usb\fR \-a \fIUPS_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the riello_usb driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp riello_usb supports all recent Riello UPS with USB\&. .sp Older Riello UPS products are not supported\&. .SH "AUTHOR" .sp Massimo Zampieri .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutdrv_qx.80000644000175000017500000011261512667757216013132 00000000000000'\" t .\" Title: nutdrv_qx .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/09/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTDRV_QX" "8" "03/09/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutdrv_qx \- Driver for Q* protocol serial and USB based UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the \fBnutdrv_qx\fR driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The \fBnutdrv_qx\fR driver is known to work with various UPSes from \fIBlazer\fR, \fIEnergy Sistem\fR, \fIFenton Technologies\fR, \fIGeneral Electric\fR, \fIMustek\fR, \fIVoltronic Power\fR (rebranded by many, many \- have I said many? \- others\&.\&. Long story short: if your UPS came with a software called \fIViewpower\fR, chances are high that it works with this driver with one of the \fIvoltronic*\fR protocols or with the \fImecer\fR one) and many others\&. .sp The NUT compatibility table lists all the known supported models\&. Keep in mind, however, that other models not listed there may also be supported, but haven\(cqt been tested\&. .sp All devices with a serial interface and many with a USB interface are supported\&. .SH "EXTRA ARGUMENTS" .sp You may need to override or provide defaults for some values, depending on the make and model of your UPS\&. .sp The following are the ones that most likely will need changing (see \fBups.conf\fR(5)): .PP \fBondelay =\fR \fIvalue\fR .RS 4 Time to wait before switching on the UPS (seconds)\&. This value is truncated to units of 60 seconds\&. .sp Note that a value below 3 minutes, may cause earlier firmware versions to not switch on automatically, so it defaults to 3 minutes (i\&.e\&. 180 seconds)\&. .sp This option provides a default value for \fBups\&.delay\&.start\fR that will then be used by the driver in the automatic shutdown sequence (i\&.e\&. calling the driver with the \fB\-k\fR option, calling \fBupsdrvctl\fR(8) with the \fBshutdown\fR option or when the FSD flag is set and \fBupsmon\fR(8) enters its shutdown sequence): however you can change this value \(oqon the fly\(cq for the actual session, only for the use with instant commands, setting \fBups\&.delay\&.start\fR with \fBupsrw\fR(8)\&. .RE .PP \fBoffdelay =\fR \fIvalue\fR .RS 4 Time to wait before shutting down the UPS (seconds)\&. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds)\&. Defaults to 30 seconds\&. .sp This option provides a default value for \fBups\&.delay\&.shutdown\fR that will then be used by the driver in the automatic shutdown sequence (i\&.e\&. calling the driver with the \fB\-k\fR option, calling \fBupsdrvctl\fR(8) with the \fBshutdown\fR option or when the FSD flag is set and \fBupsmon\fR(8) enters its shutdown sequence): however you can change this value \(oqon the fly\(cq for the actual session, only for the use with instant commands, setting \fBups\&.delay\&.shutdown\fR with \fBupsrw\fR(8)\&. .RE .PP \fBstayoff\fR .RS 4 If you set stayoff in \fBups.conf\fR(5) when FSD arises the UPS will call a \fBshutdown\&.stayoff\fR shutting down after \fBups\&.delay\&.shutdown\fR seconds and won\(cqt return (see KNOWN PROBLEMS), otherwise (standard behaviour) the UPS will call \fBshutdown\&.return\fR shutting down after \fBups\&.delay\&.shutdown\fR seconds and then turn on after \fBups\&.delay\&.start\fR seconds (if mains meanwhile returned)\&. .RE .PP \fBprotocol =\fR \fIstring\fR .RS 4 Skip autodetection of the protocol to use and only use the one specified\&. Supported values: \fIbestups\fR, \fImecer\fR, \fImegatec\fR, \fImegatec/old\fR, \fImustek\fR, \fIq1\fR, \fIvoltronic\fR, \fIvoltronic\-qs\fR, \fIvoltronic\-qs\-hex\fR and \fIzinto\fR\&. .sp Note that if you end up using the \fIq1\fR protocol, you may want to give a try to the \fImecer\fR, \fImegatec\fR and \fIzinto\fR ones setting the \fBnovendor\fR/\fBnorating\fR flags (only one, or both)\&. .RE .PP \fBpollfreq =\fR \fIvalue\fR .RS 4 Set polling frequency, in seconds, to reduce the data flow\&. Between two polling requests the driver will do \(oqquick polls\(cq dealing just with ups\&.status\&. The default value is 30 (in seconds)\&. .RE .sp If your UPS doesn\(cqt report either \fBbattery\&.charge\fR or \fBbattery\&.runtime\fR you may want to add the following ones in order to have guesstimated values: .PP \fBdefault\&.battery\&.voltage\&.high =\fR \fIvalue\fR .RS 4 Maximum battery voltage that is reached after about 12 to 24 hours charging\&. If you want the driver to report a guesstimated \fBbattery\&.charge\fR, you need to specify this (see BATTERY CHARGE)\&. .RE .PP \fBdefault\&.battery\&.voltage\&.low =\fR \fIvalue\fR .RS 4 Minimum battery voltage just before the UPS automatically shuts down\&. If you want the driver to report a guesstimated \fBbattery\&.charge\fR, you need to specify this (see BATTERY CHARGE)\&. .RE .PP \fBdefault\&.battery\&.voltage\&.nominal =\fR \fIvalue\fR, \fBoverride\&.battery\&.voltage\&.nominal =\fR \fIvalue\fR .RS 4 Some devices show a wrong nominal battery voltage (or none at all), so you may need to override or set a default value\&. .RE .PP \fBoverride\&.battery\&.packs =\fR \fIvalue\fR .RS 4 Some devices report a part of the total battery voltage\&. For instance, if \fBbattery\&.voltage\&.nominal\fR is 24 V, but it reports a \fBbattery\&.voltage\fR of around 2 V, the number of \fBbattery\&.packs\fR to correct this reading would be 12\&. The driver will attempt to detect this automatically, but if this fails somehow, you may want to override this value\&. .RE .PP \fBruntimecal =\fR \fIvalue,value,value,value\fR .RS 4 Parameter used in the (optional) runtime estimation\&. This takes two runtimes at different loads\&. Typically, this uses the runtime at full load and the runtime at half load\&. For instance, if your UPS has a rated runtime of 240 seconds at full load and 720 seconds at half load, you would enter .sp .if n \{\ .RS 4 .\} .nf runtimecal = 240,100,720,50 .fi .if n \{\ .RE .\} .sp The first load should always be higher than the second\&. If you have values available for loads other than 100 and 50 % respectively, you can use those too, but keep them spaced apart as far as reasonably possible\&. Just don\(cqt get too close to no load (prediction of runtime depends more on idle load for the battery then)\&. .RE .PP \fBchargetime =\fR \fIvalue\fR .RS 4 The time needed to fully recharge the battery after being fully discharged\&. If not specified, the driver defaults to 43200 seconds (12 hours)\&. Only used if \fBruntimecal\fR is also specified\&. .RE .PP \fBidleload =\fR \fIvalue\fR .RS 4 Minimum battery load used by the driver to estimate the runtime\&. If not specified, the driver defaults to 10%\&. Only used if \fBruntimecal\fR is also specified\&. .RE .SS "BESTUPS, MECER, MEGATAEC, MEGATEC/OLD, MUSTEK, Q1, VOLTRONIC\-QS, VOLTRONIC\-QS\-HEX, ZINTO PROTOCOLS" .PP \fBignoresab\fR .RS 4 Some UPSes incorrectly report the \(oqShutdown Active\(cq bit as always on, consequently making the driver believe the UPS is nearing a shutdown (and, as a result, ups\&.status always contains FSD\&... and you know what this means)\&. Setting this flag will make the driver ignore the \(oqShutdown Active\(cq bit\&. .RE .SS "MECER, MEGATAEC, MEGATEC/OLD, MUSTEK, ZINTO PROTOCOLS" .PP \fBondelay\fR .RS 4 The acceptable range is 0\&.\&.599940 seconds\&. .RE .PP \fBoffdelay\fR .RS 4 The acceptable range is 12\&.\&.600 seconds\&. .RE .PP \fBnorating\fR .RS 4 Some UPSes will lock up if you attempt to read rating information from them\&. Setting this flag will make the driver skip this step\&. .RE .PP \fBnovendor\fR .RS 4 Some UPSes will lock up if you attempt to read vendor information from them\&. Setting this flag will make the driver skip this step\&. .RE .SS "BESTUPS PROTOCOL" .PP \fBondelay\fR .RS 4 The acceptable range is 60\&.\&.599940 seconds\&. .RE .PP \fBoffdelay\fR .RS 4 The acceptable range is 12\&.\&.5940 seconds\&. .RE .PP \fBpins_shutdown_mode =\fR \fIvalue\fR .RS 4 Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power\(cqs EPS\-0059) to \fIvalue\fR [0\&.\&.6]\&. .RE .SS "Q1 PROTOCOL" .PP \fBondelay\fR .RS 4 The acceptable range is 0\&.\&.599940 seconds\&. .RE .PP \fBoffdelay\fR .RS 4 The acceptable range is 12\&.\&.600 seconds\&. .RE .SS "VOLTRONIC\-QS, VOLTRONIC\-QS\-HEX PROTOCOLS" .PP \fBondelay\fR .RS 4 The acceptable range is 60\&.\&.599940 seconds\&. .RE .PP \fBoffdelay\fR .RS 4 The acceptable range is 12\&.\&.540 seconds\&. .RE .SS "VOLTRONIC PROTOCOL" .sp The following options are supported only by the \fIvoltronic\fR protocol\&. Not all of them are available on all the UPSes supported by this protocol\&. .PP \fBondelay\fR .RS 4 The acceptable range is 0\&.\&.599940 seconds\&. .RE .PP \fBoffdelay\fR .RS 4 The acceptable range is 12\&.\&.5940 seconds\&. .RE .PP \fBbattery_number =\fR \fIvalue\fR .RS 4 Set number of batteries that make a pack to \fIvalue\fR [1\&.\&.9]\&. This setting will change the charge and runtime estimation reported by the UPS\&. .RE .PP \fBoutput_phase_angle =\fR \fIvalue\fR .RS 4 Changes output phase angle to the provided value [000, 120, 180, 240]\(de\&. .RE .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBUPS CAPABILITY SETTINGS\fR .RS 4 .PP \fBreset_to_default\fR .RS 4 Reset capability options and their voltage and frequency limits to safe default values\&. (\fBDoable only when the UPS is in Standby Mode\fR) .sp Note that setting this option will reset also \fBups\&.start\&.auto\fR, \fBbattery\&.protection\fR, \fBbattery\&.energysave\fR, \fBups\&.start\&.battery\fR, \fBoutlet\&.0\&.switchable\fR, \fBinput\&.transfer\&.high\fR, \fBinput\&.transfer\&.low\fR, \fBinput\&.frequency\&.high\fR and \fBinput\&.frequency\&.low\fR\&. .RE .sp These UPSes can be fine\-tuned to suit your needs enabling or disabling the following options (the driver should tell you which one the UPS is capable of on startup: the settable ones will be reported either ar \fIenabled\fR or \fIdisabled\fR in the logs): .PP \fBalarm_control =\fR \fIstring\fR .RS 4 Enable or disable alarm (BEEP!) [enabled/disabled]\&. Settable also \(oqon the fly\(cq with \fBbeeper\&.enable\fR and \fBbeeper\&.disable\fR instant commands\&. .RE .PP \fBbypass_alarm =\fR \fIstring\fR .RS 4 Enable or disable alarm (BEEP!) at Bypass Mode [enabled/disabled]\&. .RE .PP \fBbattery_alarm =\fR \fIstring\fR .RS 4 Enable or disable alarm (BEEP!) at Battery Mode [enabled/disabled]\&. .RE .PP \fBbypass_when_off =\fR \fIstring\fR .RS 4 Enable or disable bypass when the UPS is Off [enabled/disabled]\&. If enabled, AC will directly provide power to connected devices when the UPS is off\&. .RE .PP \fBbypass_forbidding =\fR \fIstring\fR .RS 4 Enable or disable Bypass Forbidding [enabled/disabled]\&. If enabled, the UPS will not transfer to bypass mode under any condition\&. .RE .PP \fBconverter_mode =\fR \fIstring\fR .RS 4 Enable or disable Converter Mode [enabled/disabled]\&. When input frequency is within 40 Hz to 70 Hz, the UPS can be set at a constant output frequency, 50 Hz or 60 Hz\&. The UPS will still charge battery under this mode\&. .RE .PP \fBeco_mode =\fR \fIstring\fR .RS 4 Enable or disable ECO Mode [enabled/disabled]\&. When input voltage/frequency are within acceptable range, the UPS will bypass voltage to output for energy saving\&. PFC and INVERTER are still active at this mode\&. Settable also \(oqon the fly\(cq with \fBbypass\&.start\fR and \fBbypass\&.stop\fR instant commands\&. .RE .PP \fBadvanced_eco_mode =\fR \fIstring\fR .RS 4 Enable or disable Advanced ECO Mode [enabled/disabled]\&. When input voltage/frequency are within acceptable range, the UPS will bypass voltage to output for energy saving\&. PFC and INVERTER are off at this mode\&. .RE .PP \fBbattery_open_status_check =\fR \fIstring\fR .RS 4 Enable or disable Battery Open Status Check [enabled/disabled]\&. If enabled, when the UPS is turned on, it will check if the battery is connected or not\&. .RE .PP \fBsite_fault_detection =\fR \fIstring\fR .RS 4 Enable or disable site fault detection [enabled/disabled]\&. If enabled, the UPS will beep when the input neutral and hot wires are reversed\&. .RE .PP \fBconstant_phase_angle =\fR \fIstring\fR .RS 4 Enable or disable Constant Phase Angle Function (output and input phase angles are not equal) [enabled/disabled]\&. .RE .PP \fBlimited_runtime_on_battery =\fR \fIstring\fR .RS 4 Enable or disable limited runtime on battery mode [enabled/disabled]\&. .RE .RE .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBBYPASS MODE VOLTAGE/FREQUENCY LIMITS\fR .RS 4 .sp Variables to fine\-tune voltage and frequency limits for Bypass mode\&. These limits are reset to safe default values by \fBreset_to_default\fR\&. .sp If AC voltage and frequency are within acceptable range, Bypass mode will be used (If the UPS is capable of and it\(cqs enabled)\&. .sp Since these values are device\-specific, if your UPS support them, you will get their settable limits printed in the logs on startup\&. .PP \fBmax_bypass_volt =\fR \fIvalue\fR .RS 4 Maximum voltage for Bypass Mode (V)\&. .RE .PP \fBmin_bypass_volt =\fR \fIvalue\fR .RS 4 Minimum voltage for Bypass Mode (V)\&. .RE .PP \fBmax_bypass_freq =\fR \fIvalue\fR .RS 4 Maximum frequency for Bypass Mode (Hz)\&. .RE .PP \fBmin_bypass_freq =\fR \fIvalue\fR .RS 4 Minimum frequency for Bypass Mode (Hz)\&. .RE .RE .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBOPTIONS SPECIFIC FOR P31 UPSES\fR .RS 4 .sp The following options are available only on P31 UPSes\&. .PP \fBwork_range_type =\fR \fIstring\fR .RS 4 Device grid working range for P31 UPSes [Appliance/UPS]\&. .RE .RE .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBTESTING\fR .RS 4 .sp This protocol comes with a couple of functions that are not enabled by default because of the lack of knowledge of some part of the communication protocol used by these UPSes by your friendly neighborhood developer\&. Since these functions are supposed to be queries to the UPS for some kind of informations, they \fIshould\fR not make your UPS go boom\&. So if you are brave enough to risk your UPS and attached devices\*(Aq life to help the developers, this will be very appreciated\&.\&. \fBDo it at your own risk\fR\&. .PP \fBtesting\fR .RS 4 If invoked the driver will exec also commands that still need testing\&. .RE .RE .SS "SERIAL INTERFACE ONLY" .PP \fBcablepower =\fR \fIstring\fR .RS 4 By default the driver will set DTR and clear RTS (\fInormal\fR)\&. If you find that your UPS isn\(cqt detected or the communication with the UPS is unreliable, you may try if clear DTR and set RTS (\fIreverse\fR), set DTR and RTS (\fIboth\fR) or clear DTR and RTS (\fInone\fR) improves this situation\&. .RE .SS "USB INTERFACE ONLY" .PP \fBport =\fR \fIstring\fR .RS 4 You must set \fIvalue\fR to \fBauto\fR\&. .RE .PP \fBvendorid =\fR \fIregex\fR, \fBproductid =\fR \fIregex\fR, \fBvendor =\fR \fIregex\fR, \fBproduct =\fR \fIregex\fR, \fBserial =\fR \fIregex\fR .RS 4 Select a specific UPS, in case there is more than one connected via USB\&. Each option specifies an extended regular expression (see \fBregex(7)\fR) that must match the UPS\(cqs entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4\-digit hexadecimal code for vendorid and productid\&. Try \fB\-DD\fR for finding out the strings to match\&. .sp Examples: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x vendor="Foo\&.Corporation\&.*" .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x vendorid=051d* (APC) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x product="\&.*(Smart|Back)\-?UPS\&.*" .RE .RE .PP \fBbus =\fR \fIregex\fR .RS 4 Select a UPS on a specific USB bus or group of busses\&. The argument is a regular expression that must match the bus name where the UPS is connected (e\&.g\&. bus="002", bus="00[2\-3]")\&. .RE .PP \fBsubdriver =\fR \fIstring\fR .RS 4 Select a serial\-over\-USB subdriver to use\&. You have a choice between \fBcypress\fR, \fBfabula\fR, \fBfuji\fR, \fBippon\fR, \fBkrauler\fR, \fBphoenix\fR and \fBsgs\fR\&. When using this option, it is mandatory to also specify the \fBvendorid\fR and \fBproductid\fR\&. .RE .PP \fBlangid_fix =\fR \fIvalue\fR .RS 4 Apply the language ID workaround to the \fBkrauler\fR subdriver\&. This is mandatory for some devices to work (LDLC, Dynamix and others)\&. You must provide \fBvalue\fR (0x409 or 0x4095), according to your device entry in NUT hardware compatibility list (HCL)\&. .RE .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBIMPLEMENTATION NOTES\fR .RS 4 .PP \fB\fIfabula\fR\fR\fB subdriver\fR .RS 4 This subdriver, meant to be used with the \fImegatec\fR protocol, does \fBnot\fR support the various \fBtest\&.battery\fR commands\&. Plus, the \fBshutdown\&.return\fR command ignores the values set in \fIups\&.delay\&.start\fR/\fBondelay\fR and makes the UPS turn on the load as soon as power is back\&. .RE .PP \fB\fIfuji\fR\fR\fB subdriver\fR .RS 4 This subdriver, meant to be used with the \fImegatec\fR protocol, does \fBnot\fR support the \fBshutdown\&.stayoff\fR and \fBload\&.off\fR commands\&. Plus, the \fBshutdown\&.return\fR command ignores the values set in \fIups\&.delay\&.start\fR/\fBondelay\fR and makes the UPS turn on the load as soon as power is back\&. .RE .PP \fB\fIkrauler\fR\fR\fB subdriver\fR .RS 4 This subdriver, meant to be used with the \fImegatec\fR protocol, does \fBnot\fR support the shutdown commands, i\&.e\&.: \fBshutdown\&.return\fR, \fBshutdown\&.stayoff\fR and \fBload\&.off\fR\&. .RE .RE .SH "UPS COMMANDS" .sp This driver supports some instant commands (see \fBupscmd\fR(8)): .PP \fBbeeper\&.toggle\fR .RS 4 Toggle the UPS beeper\&. (Not available on some hardware) .RE .PP \fBload\&.on\fR .RS 4 Turn on the load immediately\&. (Not available on some hardware) .RE .PP \fBload\&.off\fR .RS 4 Turn off the load immediately (see KNOWN PROBLEMS)\&. .RE .PP \fBshutdown\&.return\fR .RS 4 Turn off the load and return when power is back\&. Uses the timers defined by \fBups\&.delay\&.start\fR and \fBups\&.delay\&.shutdown\fR\&. .RE .PP \fBshutdown\&.stayoff\fR .RS 4 Turn off the load and remain off (see KNOWN PROBLEMS)\&. Uses the timer defined by \fBups\&.delay\&.shutdown\fR\&. .RE .PP \fBshutdown\&.stop\fR .RS 4 Stop a shutdown in progress\&. .RE .PP \fBtest\&.battery\&.start\&.deep\fR .RS 4 Perform a long battery test\&. (Not available on some hardware) .RE .PP \fBtest\&.battery\&.start\&.quick\fR .RS 4 Perform a quick (10 second) battery test\&. .RE .PP \fBtest\&.battery\&.stop\fR .RS 4 Stop a running battery test\&. (Not available on some hardware) .RE .SS "BESTUPS, MECER, MEGATEC, MEGATEC/OLD, MUSTEK, Q1, ZINTO PROTOCOLS" .PP \fBtest\&.battery\&.start\fR \fIvalue\fR .RS 4 Perform a battery test for the duration of \fIvalue\fR seconds (truncated to 60 seconds) [60\&.\&.5940]\&. .RE .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBVOLTRONIC POWER P98 UNITS (WITH MECER PROTOCOL)\fR .RS 4 .PP \fBtest\&.battery\&.start\fR \fIvalue\fR .RS 4 Perform a battery test for the duration of \fIvalue\fR seconds (truncated to 60 seconds) [12\&.\&.5940]\&. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds)\&. .RE .RE .SS "VOLTRONIC PROTOCOL" .sp The following instant commands are available for the \fIvoltronic\fR protocol\&. Not all of them are available on all the UPSes supported by this protocol\&. .PP \fBbeeper\&.enable\fR .RS 4 Enable the UPS beeper\&. .RE .PP \fBbeeper\&.disable\fR .RS 4 Disable the UPS beeper\&. .RE .PP \fBtest\&.battery\&.start\fR \fIvalue\fR .RS 4 Perform a battery test for the duration of \fIvalue\fR seconds [12\&.\&.5940]\&. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds)\&. .RE .PP \fBoutlet\&.1\&.load\&.off\fR .RS 4 Turn off outlet 1 load immediately\&. .RE .PP \fBoutlet\&.1\&.load\&.on\fR .RS 4 Turn on outlet 1 load immediately\&. .RE .PP \fBoutlet\&.2\&.load\&.off\fR .RS 4 Turn off outlet 2 load immediately\&. .RE .PP \fBoutlet\&.2\&.load\&.on\fR .RS 4 Turn on outlet 2 load immediately\&. .RE .PP \fBoutlet\&.3\&.load\&.off\fR .RS 4 Turn off outlet 3 load immediately\&. .RE .PP \fBoutlet\&.3\&.load\&.on\fR .RS 4 Turn on outlet 3 load immediately\&. .RE .PP \fBoutlet\&.4\&.load\&.off\fR .RS 4 Turn off outlet 4 load immediately\&. .RE .PP \fBoutlet\&.4\&.load\&.on\fR .RS 4 Turn on outlet 4 load immediately\&. .RE .PP \fBbypass\&.start\fR .RS 4 Put the UPS in ECO Mode\&. .RE .PP \fBbypass\&.stop\fR .RS 4 Take the UPS out of ECO Mode\&. .RE .SH "BATTERY CHARGE" .sp Due to popular demand, this driver will report a guesstimated \fBbattery\&.charge\fR and optionally \fBbattery\&.runtime\fR, provided you specified a couple of the EXTRA ARGUMENTS listed above\&. .sp If you specify both \fBbattery\&.voltage\&.high\fR and \fBbattery\&.voltage\&.low\fR in \fBups.conf\fR(5), but don\(cqt enter \fBruntimecal\fR, it will guesstimate the state of charge by looking at the battery voltage alone\&. This is not reliable under load, as this only gives reasonably accurate readings if you disconnect the load, let the battery rest for a couple of minutes and then measure the open cell voltage\&. This just isn\(cqt practical if the power went out and the UPS is providing power for your systems\&. .sp .if n \{\ .RS 4 .\} .nf battery\&.voltage \- battery\&.voltage\&.low battery\&.charge = \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- x 100 % battery\&.voltage\&.high \- battery\&.voltage\&.low .fi .if n \{\ .RE .\} .sp There is a way to get better readings without disconnecting the load but this requires one to keep track on how much (and how fast) current is going in and out of the battery\&. If you specified the \fBruntimecal\fR, the driver will attempt to do this\&. Note however, that this heavily relies on the values you enter and that the UPS must be able to report the load as well\&. There are quite a couple of devices that report 0 % (or any other fixed value) at all times, in which case this obviously doesn\(cqt work\&. .sp The driver also has no way of determining the degradation of the battery capacity over time, so you\(cqll have to deal with this yourself (by adjusting the values in \fBruntimecal\fR)\&. Also note that the driver guesses the initial state of charge based on the battery voltage, so this may be less than 100 %, even when you are certain that they are full\&. There is just no way to reliably measure this between 0 and 100 % full charge\&. .sp This is better than nothing (but not by much)\&. If any of the above calculations is giving you incorrect readings, you are the one that put in the values in \fBups.conf\fR(5), so don\(cqt complain with the author\&. If you need something better, buy a UPS that reports \fBbattery\&.charge\fR and \fBbattery\&.runtime\fR all by itself without the help of a NUT driver\&. .SH "NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS" .sp The \fBnutdrv_qx\fR driver having replaced the megatec ones, some configuration changes may be required by users switching to \fBnutdrv_qx\fR\&. .sp Part of this, the following megatec options, in \fBups.conf\fR(5), have to be changed: .PP \fBbattvolts\fR .RS 4 You need to use \fIdefault\&.battery\&.voltage\&.high\fR and \fIdefault\&.battery\&.voltage\&.low\fR .RE .PP \fBdtr\fR and \fBrts\fR .RS 4 You need to use \fIcablepower\fR .RE .PP \fBignoreoff\fR .RS 4 This parameter can simply be discarded, since it was a wrong understanding of the specification\&. .RE .SH "NOTES FOR THE PREVIOUS USER OF BLAZER DRIVERS" .sp The \fBnutdrv_qx\fR driver having replaced the blazer ones, some configuration changes may be required by users switching to \fBnutdrv_qx\fR\&. .sp Part of this, the following blazer options, in \fBups.conf\fR(5), have to be changed: .PP \fBondelay\fR .RS 4 While the previous blazer drivers expected minutes, the new \fBnutdrv_qx\fR driver wants seconds\&. .RE .sp The following instant command has also been changed: .PP \fBtest\&.battery\&.start\fR \fIvalue\fR .RS 4 While the old blazer drivers expected a \fIvalue\fR in minutes, the \fBnutdrv_qx\fR driver wants a \fIvalue\fR in seconds\&. .RE .SH "NOTES FOR THE PREVIOUS USER OF BESTUPS DRIVER" .sp The \fBnutdrv_qx\fR driver having replaced the bestups one, some configuration changes may be required by users switching to \fBnutdrv_qx\fR\&. .sp Part of this, the following bestups options, in \fBups.conf\fR(5), are no longer supported by this driver: .PP \fBnombattvolt\fR, \fBbattvoltmult\fR .RS 4 See BATTERY CHARGE\&. .RE .PP \fBID\fR .RS 4 Discarded\&. .RE .SH "NOTES FOR THE PREVIOUS USER OF VOLTRONIC DRIVERS" .sp The \fBnutdrv_qx\fR driver having replaced the voltronic ones, some configuration changes may be required by users switching to \fBnutdrv_qx\fR\&. .sp Part of this, the following voltronic options, in \fBups.conf\fR(5), have to be changed: .PP \fBondelay\fR .RS 4 While the previous voltronic drivers expected minutes, the new \fBnutdrv_qx\fR driver wants seconds\&. It no longer defaults to 0 minutes but to 3 minutes (i\&.e\&. 180 seconds) for compatibility with the users switching from the old blazer drivers\&. .RE .PP \fBbattnumb\fR .RS 4 This option has been renamed to \fBbattery_number\fR\&. .RE .sp The following options are no longer supported by this driver, you can now change them more conveniently \(oqon the fly\(cq calling \fBupsrw\fR(8) with the appropriate NUT variable \- provided that your UPS supports them\&. .TS tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp \fBbattpacks\fR T}:T{ .sp → \fBbattery\&.packs\fR .sp Set number of battery packs in parallel [1\&.\&.99]\&. This setting will change the charge and runtime estimation reported by the UPS\&. T} T{ .sp \fBbattlow\fR T}:T{ .sp → \fBbattery\&.voltage\&.low\fR .sp Set minimum battery voltage just before the UPS automatically shuts down\&. This setting will change the charge and runtime estimation reported by the UPS\&. T} T{ .sp \fBauto_reboot\fR T}:T{ .sp → \fBups\&.start\&.auto\fR .sp Enable or disable auto reboot [enabled/disabled]\&. If enabled, the UPS will auto recover when AC power returns\&. T} T{ .sp \fBbattery_protection\fR T}:T{ .sp → \fBbattery\&.protection\fR .sp Enable or disable battery deep discharge protection [enabled/disabled]\&. T} T{ .sp \fBenergy_saving\fR T}:T{ .sp → \fBbattery\&.energysave\fR .sp Enable or disable Green power function [enabled/disabled]\&. If enabled, for energy saving, the UPS will auto off when there is no load\&. T} T{ .sp \fBcold_start\fR T}:T{ .sp → \fBups\&.start\&.battery\fR .sp Enable or disable Cold Start [enabled/disabled]\&. If enabled, the UPS can be turned on also if AC is not connected to the UPS\&. T} T{ .sp \fBoutlet_control\fR T}:T{ .sp → \fBoutlet\&.0\&.switchable\fR .sp Enable or disable programmable outlets control at battery mode [enabled/disabled]\&. If enabled, the UPS will cut off programmable outlets after backup time (set through \fBoutlet\&.\fR{\fB1\fR,\fB2\fR,\fB3\fR,\fB4\fR}\fB\&.delay\&.shutdown\fR) arrives\&. If disabled, the UPS will provide continuous power to programmable outlets until the battery is running out\&. T} T{ .sp \fBmax_eco_volt\fR T}:T{ .sp → \fBinput\&.transfer\&.high\fR .sp Maximum voltage for ECO Mode (V)\&. If AC voltage is within acceptable range, ECO mode will be used (If the UPS is capable of and it\(cqs enabled)\&. T} T{ .sp \fBmin_eco_volt\fR T}:T{ .sp → \fBinput\&.transfer\&.low\fR .sp Minimum voltage for ECO Mode (V)\&. If AC voltage is within acceptable range, ECO mode will be used (If the UPS is capable of and it\(cqs enabled)\&. T} T{ .sp \fBmax_eco_freq\fR T}:T{ .sp → \fBinput\&.frequency\&.high\fR .sp Maximum frequency for ECO Mode (Hz)\&. If AC frequency is within acceptable range, ECO mode will be used (If the UPS is capable of and it\(cqs enabled)\&. T} T{ .sp \fBmin_eco_freq\fR T}:T{ .sp → \fBinput\&.frequency\&.low\fR .sp Minimum frequency for ECO Mode (Hz)\&. If AC frequency is within acceptable range, ECO mode will be used (If the UPS is capable of and it\(cqs enabled)\&. T} T{ .sp \fBoutlet1_delay\fR T}:T{ .sp → \fBoutlet\&.1\&.delay\&.shutdown\fR .sp Delay time before programmable outlet 1 shuts down the load when on battery mode [0\&.\&.59940] (seconds)\&. T} T{ .sp \fBoutlet2_delay\fR T}:T{ .sp → \fBoutlet\&.2\&.delay\&.shutdown\fR .sp Delay time before programmable outlet 2 shuts down the load when on battery mode [0\&.\&.59940] (seconds)\&. T} T{ .sp \fBoutlet3_delay\fR T}:T{ .sp → \fBoutlet\&.3\&.delay\&.shutdown\fR .sp Delay time before programmable outlet 3 shuts down the load when on battery mode [0\&.\&.59940] (seconds)\&. T} T{ .sp \fBoutlet4_delay\fR T}:T{ .sp → \fBoutlet\&.4\&.delay\&.shutdown\fR .sp Delay time before programmable outlet 4 shuts down the load when on battery mode [0\&.\&.59940] (seconds)\&. T} T{ .sp \fBbatt_type\fR T}:T{ .sp → \fBbattery\&.type\fR .sp Battery type (for P31 UPSes only) [Li/Flooded/AGM]\&. T} .TE .sp 1 .SH "KNOWN PROBLEMS" .sp Some UPS commands aren\(cqt supported by all models\&. In most cases, the driver will send a message to the system log when the user tries to execute an unsupported command\&. Unfortunately, some models don\(cqt even provide a way for the driver to check for this, so the unsupported commands will silently fail\&. .sp Both the \fBload\&.off\fR and \fBshutdown\&.stayoff\fR instant commands are meant to turn the load off indefinitely\&. However, some UPS models don\(cqt allow this\&. .sp Some models report a bogus value for the beeper status (will always be \fIenabled\fR or \fIdisabled\fR)\&. So, the \fBbeeper\&.toggle\fR command may appear to have no effect in the status reported by the driver when, in fact, it is working fine\&. .sp The temperature and load value is known to be bogus in some models\&. .SS "VOLTRONIC\-QS UNITS" .sp Both \fBload\&.off\fR and \fBshutdown\&.stayoff\fR instant commands are known to work as expected (i\&.e\&. turn the load off indefinitely) only if mains is present, otherwise, as soon as mains returns the load will be powered\&. .sp After issuing a \fBshutdown\&.return\fR instant command, the UPS won\(cqt wait \fBondelay\fR before powering on the load, provided the following conditions are met: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} if the load has been previously (no matter how long before) powered off through \fBload\&.off\fR/\fBshutdown\&.stayoff\fR\fIand\fR powered on through \fBload\&.on\fR/\fBshutdown\&.stop\fR\fIand\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} if AC wasn\(cqt cut after issuing the \fBload\&.off\fR/\fBshutdown\&.stayoff\fR (i\&.e\&. the UPS didn\(cqt turn itself off) \fIand\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} if there\(cqs a power outage after issuing the \fBshutdown\&.return\fR command .RE .sp In this case, as soon as mains returns the load will be powered\&. .SS "VOLTRONIC\-QS\-HEX UNITS" .sp \fBshutdown\&.return\fR, \fBload\&.off\fR, and \fBshutdown\&.stayoff\fR instant commands are known to work as expected only if mains is present, otherwise, as soon as mains returns the load will be powered\&. .SH "UPS WARNINGS (VOLTRONIC PROTOCOL)" .sp The UPSes supported by \fIvoltronic\fR protocol report warnings through a 64bit flag (bit1bit2\&...bit63bit64) where 1 means that a warning arose, while 0 means no warning\&. Since more than one warning at a time can be signaled, and because of the limited space in the ups\&.alarm variable, if the length of the warnings exceeds that of ups\&.alarms variable, they will be reported as bits\&. If you want to know the explanation of that bit you can either watch the log or see the next table (unlisted bits equal to unknown warnings)\&. .sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .B Table\ \&1.\ \&UPS Warnings for \fIvoltronic\fR UPSes .TS allbox tab(:); rtB ltB. T{ # T}:T{ Corresponding Warning T} .T& rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt rt lt. T{ .sp 1 T}:T{ .sp Battery disconnected T} T{ .sp 2 T}:T{ .sp Neutral not connected T} T{ .sp 3 T}:T{ .sp Site fault T} T{ .sp 4 T}:T{ .sp Phase sequence incorrect T} T{ .sp 5 T}:T{ .sp Phase sequence incorrect in bypass T} T{ .sp 6 T}:T{ .sp Input frequency unstable in bypass T} T{ .sp 7 T}:T{ .sp Battery overcharged T} T{ .sp 8 T}:T{ .sp Low battery T} T{ .sp 9 T}:T{ .sp Overload alarm T} T{ .sp 10 T}:T{ .sp Fan alarm T} T{ .sp 11 T}:T{ .sp EPO enabled T} T{ .sp 12 T}:T{ .sp Unable to turn on UPS T} T{ .sp 13 T}:T{ .sp Over temperature alarm T} T{ .sp 14 T}:T{ .sp Charger alarm T} T{ .sp 15 T}:T{ .sp Remote auto shutdown T} T{ .sp 16 T}:T{ .sp L1 input fuse not working T} T{ .sp 17 T}:T{ .sp L2 input fuse not working T} T{ .sp 18 T}:T{ .sp L3 input fuse not working T} T{ .sp 19 T}:T{ .sp Positive PFC abnormal in L1 T} T{ .sp 20 T}:T{ .sp Negative PFC abnormal in L1 T} T{ .sp 21 T}:T{ .sp Positive PFC abnormal in L2 T} T{ .sp 22 T}:T{ .sp Negative PFC abnormal in L2 T} T{ .sp 23 T}:T{ .sp Positive PFC abnormal in L3 T} T{ .sp 24 T}:T{ .sp Negative PFC abnormal in L3 T} T{ .sp 25 T}:T{ .sp Abnormal in CAN\-bus communication T} T{ .sp 26 T}:T{ .sp Abnormal in synchronous signal circuit T} T{ .sp 27 T}:T{ .sp Abnormal in synchronous pulse signal circuit T} T{ .sp 28 T}:T{ .sp Abnormal in host signal circuit T} T{ .sp 29 T}:T{ .sp Male connector of parallel cable not connected well T} T{ .sp 30 T}:T{ .sp Female connector of parallel cable not connected well T} T{ .sp 31 T}:T{ .sp Parallel cable not connected well T} T{ .sp 32 T}:T{ .sp Battery connection not consistent in parallel systems T} T{ .sp 33 T}:T{ .sp AC connection not consistent in parallel systems T} T{ .sp 34 T}:T{ .sp Bypass connection not consistent in parallel systems T} T{ .sp 35 T}:T{ .sp UPS model types not consistent in parallel systems T} T{ .sp 36 T}:T{ .sp Capacity of UPSs not consistent in parallel systems T} T{ .sp 37 T}:T{ .sp Auto restart setting not consistent in parallel systems T} T{ .sp 38 T}:T{ .sp Battery cell over charge T} T{ .sp 39 T}:T{ .sp Battery protection setting not consistent in parallel systems T} T{ .sp 40 T}:T{ .sp Battery detection setting not consistent in parallel systems T} T{ .sp 41 T}:T{ .sp Bypass not allowed setting not consistent in parallel systems T} T{ .sp 42 T}:T{ .sp Converter setting not consistent in parallel systems T} T{ .sp 43 T}:T{ .sp High loss point for frequency in bypass mode not consistent in parallel systems T} T{ .sp 44 T}:T{ .sp Low loss point for frequency in bypass mode not consistent in parallel systems T} T{ .sp 45 T}:T{ .sp High loss point for voltage in bypass mode not consistent in parallel systems T} T{ .sp 46 T}:T{ .sp Low loss point for voltage in bypass mode not consistent in parallel systems T} T{ .sp 47 T}:T{ .sp High loss point for frequency in AC mode not consistent in parallel systems T} T{ .sp 48 T}:T{ .sp Low loss point for frequency in AC mode not consistent in parallel systems T} T{ .sp 49 T}:T{ .sp High loss point for voltage in AC mode not consistent in parallel systems T} T{ .sp 50 T}:T{ .sp Low loss point for voltage in AC mode not consistent in parallel systems T} T{ .sp 51 T}:T{ .sp Warning for locking in bypass mode after 3 consecutive overloads within 30 min T} T{ .sp 52 T}:T{ .sp Warning for three\-phase AC input current unbalance T} T{ .sp 53 T}:T{ .sp Warning for a three\-phase input current unbalance detected in battery mode T} T{ .sp 54 T}:T{ .sp Warning for Inverter inter\-current unbalance T} T{ .sp 55 T}:T{ .sp Programmable outlets cut off pre\-alarm T} T{ .sp 56 T}:T{ .sp Warning for Battery replace T} T{ .sp 57 T}:T{ .sp Abnormal warning on input phase angle T} T{ .sp 58 T}:T{ .sp Warning!! Cover of maintain switch is open T} T{ .sp 62 T}:T{ .sp EEPROM operation error T} .TE .sp 1 .SH "AUTHORS" .sp Daniele Pezzini , Arnaud Quette , John Stamp , Peter Selinger , Arjen de Korte , Alexander Gordeev .SH "SEE ALSO" .sp \fBblazer_ser\fR(8), \fBblazer_usb\fR(8), \fBnutupsdrv\fR(8), \fBups.conf\fR(5), \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsdrvctl\fR(8), \fBupsmon\fR(8), \fBupsrw\fR(8) .SS "Internet Resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp The NUT HCL: http://www\&.networkupstools\&.org/stable\-hcl\&.html nut-2.7.4/docs/man/libnutclient_general.30000644000175000017500000000467312665610644015257 00000000000000'\" t .\" Title: libnutclient_general .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT_GENERAL" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient_general, nutclient_destroy, strarr_alloc, strarr_free \- General and utility functions in Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf typedef void* NUTCLIENT_t; .fi .sp .nf void nutclient_destroy(NUTCLIENT_t client); .fi .sp .nf typedef char** strarr; .fi .sp .nf strarr strarr_alloc(unsigned short count); void strarr_free(strarr arr); .fi .SH "DESCRIPTION" .sp The \fBnutclient_destroy()\fR function destroys a \fINUTCLIENT_t\fR or derived (like \fINUTCLIENT_TCP_t\fR) connection object, and frees allocated memory\&. .sp The \fBstrarr\fR type represents an array of C strings (array of char pointer)\&. The array must always be terminated by a NULL pointer\&. Pointed strings must be allocated by (x)calloc or (x)strdup\&. .sp The \fBstrarr_alloc()\fR function allocates a \fIstrarr\fR array with the specified number of (non\-initialized) string pointers\&. Another additional pointer set to 0 is added at the end of the array\&. .sp The \fBstrarr_free\fR function frees a \fIstrarr\fR array\&. It also frees all pointed strings\&. .sp \fIdev\fR is the device name\&. .SH "SEE ALSO" .sp \fBlibnutclient\fR(3) nut-2.7.4/docs/man/libnutclient.30000644000175000017500000000525512665610642013555 00000000000000'\" t .\" Title: libnutclient .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient \- Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf Refer to this file for more information\&. .fi .SH "DESCRIPTION" .sp The Network UPS Tools (NUT) \fBnutclient\fR library provides a number of useful functions for programs to use when communicating with \fBupsd\fR(8)\&. It provides high\-level representation of NUT data through client connection, devices, variables and commands\&. Unlike \fBupsclient\fR(3), all low\-level protocol details are hidden\&. .sp State is maintained across calls in an opaque structure called NUTCLIENT_t\&. Callers are expected to create one per client connection\&. These will be provided to most of the \fBnutclient\fR functions\&. The format of this structure is subject to change, and client programs must not reference elements within it directly\&. .sp NUTCLIENT_t represents the common connection information\&. Derived versions exist for each connection type (NUTCLIENT_TCP_t for TCP connection; actually the unique connection type, NUTCLIENT_TCP_t can be passed as NUTCLIENT_t parameter)\&. .sp See the nutclient\&.h header for more information\&. .SH "ERROR HANDLING" .sp There is currently no specific mechanism around error handling\&. .SH "SEE ALSO" .sp \fBlibnutclient_devices\fR(3) \fBlibnutclient_commands\fR(3) \fBlibnutclient_general\fR(3) \fBlibnutclient_misc\fR(3) \fBlibnutclient_tcp\fR(3) \fBlibnutclient_variables\fR(3) nut-2.7.4/docs/man/upscli_strerror.txt0000644000175000017500000000146212640443572014772 00000000000000UPSCLI_STRERROR(3) ================== NAME ---- upscli_strerror - return string describing error condition SYNOPSIS -------- #include char *upscli_strerror(UPSCONN_t *ups); DESCRIPTION ----------- The *upscli_strerror*() function takes the pointer 'ups' to a `UPSCONN_t` state structure and returns a string describing the last error which occurred on this connection. The string is valid until the next call to *upscli_strerror*(). RETURN VALUE ------------ The *upscli_strerror*() function returns a description of the error, or an unknown error message if the error code is not recognized. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/upssched.conf.50000644000175000017500000001227512640476464013632 00000000000000'\" t .\" Title: upssched.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSSCHED\&.CONF" "5" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upssched.conf \- Configuration for upssched timer program .SH "DESCRIPTION" .sp This file controls the operations of \fBupssched\fR(8), the timer\-based helper program for \fBupsmon\fR(8)\&. .SH "CONFIGURATION DIRECTIVES" .PP \fBCMDSCRIPT\fR \fIscriptname\fR .RS 4 Required\&. This must be above any AT lines\&. This script is used to invoke commands when your timers are triggered\&. It receives a single argument which is the name of the timer that caused it to trigger\&. .RE .PP \fBPIPEFN\fR \fIfilename\fR .RS 4 Required\&. This sets the file name of the socket which will be used for interprocess communications\&. This should be in a directory where normal users can\(cqt create the file, due to the possibility of symlinking and other evil\&. .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBCaution\fR .ps -1 .br .sp if you are running Solaris or similar, the permissions that upssched sets on this file \fBare not enough\fR to keep you safe\&. If your OS ignores the permissions on a FIFO, then you MUST put this in a protected directory! .sp .5v .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp by default, \fBupsmon\fR(8) will run upssched as whatever user you have defined with RUN_AS_USER in \fBupsmon.conf\fR(8)\&. Make sure that user can create files and write to files in the path you use for PIPEFN and LOCKFN\&. .sp .5v .RE .sp My recommendation: create a special directory for upssched, make it owned by your upsmon user, then use it for both\&. .sp The stock version of the upssched\&.conf ships with PIPEFN disabled to make you visit this portion of the documentation and think about how your system works before potentially opening a security hole\&. .PP \fBLOCKFN\fR \fIfilename\fR .RS 4 Required\&. upssched attempts to create this file in order to avoid a race condition when two events are dispatched from upsmon at nearly the same time\&. This file will only exist briefly\&. It must not be created by any other process\&. .sp You should put this in the same directory as PIPEFN\&. .RE .PP \fBAT\fR \fInotifytype\fR \fIupsname\fR \fIcommand\fR .RS 4 Define a handler for a specific event \fInotifytype\fR on UPS \fIupsname\fR\&. \fIupsname\fR can be the special value * to apply this handler to every UPS\&. .sp This will perform the command \fIcommand\fR when the \fInotifytype\fR and \fIupsname\fR match the current activity\&. Possible values for \fIcommand\fR are: .PP \fBSTART\-TIMER\fR \fItimername\fR \fIinterval\fR .RS 4 Start a timer of \fIinterval\fR seconds\&. When it triggers, it will pass the argument \fItimername\fR as an argument to your CMDSCRIPT\&. .sp Example: .sp Start a timer that\(cqll execute when any UPS (*) has been gone for 10 seconds .sp .if n \{\ .RS 4 .\} .nf AT COMMBAD * START\-TIMER upsgone 10 .fi .if n \{\ .RE .\} .RE .PP \fBCANCEL\-TIMER\fR \fItimername\fR [\fIcmd\fR] .RS 4 Cancel a running timer called \fItimername\fR, if possible\&. If the timer has passed then pass the optional argument \fIcmd\fR to CMDSCRIPT\&. .sp Example: .sp If a specific UPS (myups@localhost) comes back online, then stop the timer before it triggers .sp .if n \{\ .RS 4 .\} .nf AT COMMOK myups@localhost CANCEL\-TIMER upsgone .fi .if n \{\ .RE .\} .RE .PP \fBEXECUTE\fR \fIcommand\fR .RS 4 Immediately pass \fIcommand\fR as an argument to CMDSCRIPT\&. .sp Example: .sp If any UPS (*) reverts to utility power, then execute ups\-back\-on\-line via CMDSCRIPT\&. .sp .if n \{\ .RS 4 .\} .nf AT ONLINE * EXECUTE ups\-back\-on\-line .fi .if n \{\ .RE .\} .RE .RE .sp Note that any AT that matches both the \fInotifytype\fR and the \fIupsname\fR for the current event will be used\&. .sp For a complete list of \fInotifytype\fR possible values, refer to the section NOTIFY EVENTS in \fBupsmon\fR(8)\&. .SH "SEE ALSO" .sp \fBupssched\fR(8), \fBupsmon\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/netxml-ups.txt0000644000175000017500000000674612640444140013647 00000000000000netxml-ups(8) ============= NAME ---- netxml-ups - Driver for Eaton / MGE Network Management Card / Proxy (XML/HTTP Protocol) equipment NOTE ---- This man page only documents the hardware-specific features of the netxml-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ netxml-ups support all recent Eaton / MGE models which use a Network Management Card or Proxy (MGE XML/HTTP protocol based). This applies to both Eaton (previously MGE Office Protection Systems) and to MGE UPS SYSTEMS. Supported card and proxy models are: * NMC Minislot (Ref 66102, firmware EA or newer), * SNMP/Web Minislot card (Ref 66244) * NMC Transverse (Ref 66074), * NMC & Modbus/JBus (Ref 66103), * Network Management Proxy, * ePDU Monitored (newer version). Older models, such as SNMP card (Ref 66062 and Ref 66045), use the SNMP protocol and should use the linkman:snmp-ups[8] driver with the "mibs=mge" parameter. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *timeout*='value':: The timeout for connecting to and reading from the UPS. Defaults to 5 seconds. Don't change this value unless you know exactly what you're doing. This value *must never* be higher than half the MAXAGE value specified in linkman:upsd.conf[5], otherwise you run the risk that linkman:upsd[8] declares the driver stale while it is waiting for a connection to timeout. *subscribe*:: Connect to the NMC in subscribed mode. This allows to receive notifications and alarms more quickly, beside from the standard polling requests. *login*='value':: Set the login value for authenticated mode. This feature also needs the *password* argument, and allows value settings in the card. This feature is not used yet. *password*='value':: Set the password value, needed with the login for authenticated mode. This feature is not used yet. *shutdown_duration*='value':: Set the shutdown duration of the operating system, in seconds. This represents the amount of time needed by the system to operate a clean shutdown. Defaults to 120 seconds. *shutdown_timer*='value':: Set the shutdown timer, in seconds. After 'value' seconds running on battery, the local system will receive a notification to shutdown. Defaults to "none" (disabled). IMPLEMENTATION -------------- The hostname of the UPS is specified with the "port" value in *ups.conf*, i.e.: [mgexml] driver = netxml-ups port = http://netxml-ups.example.com:80 Specifying the method to connect to the UPS (http, https) is mandatory. If the port is equal to the default for the method specified (80 for http, 443 for https) it may be omitted. In order not to overload older NMCs by polling them too frequently, it is recommended to increase the "pollinterval" (see linkman:nutupsdrv[8]) and linkman:ups.conf[5]) to at least 5 seconds. KNOWN ISSUES ------------ Don't connect to the UPS through a proxy. Although it would be trivial to add support for proxies, this is not recommended and don't ask for it. Not only because it will prevent the driver to make a persistent connection to the UPS, but also it adds an additional failure mode. If the proxy goes down (for whatever reason), the driver will no longer be able to reach the UPS. AUTHORS ------- Arjen de Korte SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/liebert.80000644000175000017500000000464312640476504012514 00000000000000'\" t .\" Title: liebert .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIEBERT" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" liebert \- Driver for Liebert contact\-closure UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the liebert driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports some Liebert UPS equipment with a contact\-closure interface\&. This includes the UPStation GXT2 with their contact\-closure cable\&. The smart mode ("Multilink") cable is not supported by this driver\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "BUGS" .sp This driver does not yet support shutdowns by raising DTR\&. Be aware that shutdowns are not possible with the stock contact\-closure cable\&. You may have to build another cable with DTR connected through to the UPS for it to work\&. .sp There is no way for this driver to detect the hardware or cable\&. It will start up successfully even if no UPS is present\&. This is a fundamental limitation of any contact\-closure driver\&. .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscli_cleanup.30000644000175000017500000000334612665610631014064 00000000000000'\" t .\" Title: upscli_cleanup .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_CLEANUP" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_cleanup \- Clean\-up upsclient module after usage\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_cleanup(); .fi .SH "DESCRIPTION" .sp The \fBupscli_cleanup()\fR function flushes SSL caches and frees memory used internally in upsclient module\&. .SH "RETURN VALUE" .sp The \fBupscli_cleanup()\fR function returns 1 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_init\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutscan.txt0000644000175000017500000000362212640443572013204 00000000000000NUTSCAN(3) ========== NAME ---- nutscan - Network UPS Tools (NUT) device discovery library DESCRIPTION ----------- The Network UPS Tools (NUT) *nutscan* library provides the same discovery related features that are also offered by linkman:nut-scanner[8]. It enables the discovery of supported NUT devices (USB, SNMP, Eaton XML/HTTP and IPMI) and NUT servers (using Avahi, or the classic connection method). DISCOVERY FUNCTIONS ------------------- First, include the required header file: #include Then, to discover new devices, use the appropriate function: - linkman:nutscan_scan_usb[3] for supported USB devices, - linkman:nutscan_scan_snmp[3] for supported SNMP agents, - linkman:nutscan_scan_xml_http[3] for Eaton Network Management Card, - linkman:nutscan_scan_nut[3] for NUT servers (upsd), using the classic method, - linkman:nutscan_scan_avahi[3] for NUT servers (upsd), using the mDNS (Avahi) method, - linkman:nutscan_scan_ipmi[3] for supported IPMI PSU. All of these functions return a list of devices found, using the nutscan_device_t structure. This structure is described in linkman:nutscan_add_device_to_device[3]. Helper functions are also provided to output data using standard formats: - linkman:nutscan_display_parsable[3] for parsable output, - linkman:nutscan_display_ups_conf[3] for ups.conf style. ERROR HANDLING -------------- There is currently no specific mechanism for error handling. SEE ALSO -------- linkman:nut-scanner[8], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_cidr_to_ip[3], http://avahi.org/ nut-2.7.4/docs/man/upsc.80000644000175000017500000001046712640476465012047 00000000000000'\" t .\" Title: upsc .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSC" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsc \- example lightweight UPS client .SH "SYNOPSIS" .sp \fBupsc\fR \-l | \-L [\fIhost\fR] .sp \fBupsc\fR \fIups\fR [\fIvariable\fR] .sp \fBupsc\fR \-c \fIups\fR .SH "DESCRIPTION" .sp \fBupsc\fR is provided as a quick way to poll the status of a UPS server\&. It can be used inside shell scripts and other programs that need UPS data but don\(cqt want to include the full interface\&. .SH "OPTIONS" .PP \fB\-l\fR \fIhost\fR .RS 4 List all UPS names configured at \fIhost\fR, one name per line\&. The hostname defaults to "localhost"\&. You may optionally add a colon and a port number\&. .RE .PP \fB\-L\fR \fIhost\fR .RS 4 As above, list all UPS names configured at \fIhost\fR, including their description provided by the remote upsd(8) from ups\&.conf(5)\&. The hostname defaults to "localhost"\&. You may optionally add a colon and a port number to override the default port\&. .RE .PP \fB\-c\fR \fIups\fR .RS 4 Lists each client connected on \fIups\fR, one name per line\&. .RE .PP \fIups\fR .RS 4 Display the status of that UPS\&. The format for this option is \fIupsname[@hostname[:port]]\fR\&. The default hostname is "localhost"\&. .RE .PP \fIvariable\fR .RS 4 Display the value of this variable only\&. By default, upsc retrieves the list of variables from the server and then displays the value for each\&. This may be useful in shell scripts to save an additional pipe into grep\&. .RE .SH "EXAMPLES" .sp To list all variables on an UPS named "myups" on a host called "mybox", with upsd(8) running on port 1234: .sp .if n \{\ .RS 4 .\} .nf $ upsc myups@mybox:1234 battery\&.charge: 100\&.0 battery\&.voltage: 13\&.9 battery\&.voltage\&.nominal: 13\&.6 \&. \&. \&. .fi .if n \{\ .RE .\} .sp To list the UPSes configured on this system, along with their descriptions: .sp .if n \{\ .RS 4 .\} .nf $ upsc \-L apc: Back\-UPS 500 ppro2: Patriot Pro II .fi .if n \{\ .RE .\} .sp To retrieve the status for all UPSes connected to mybox, using Bourne\-shell syntax: .sp .if n \{\ .RS 4 .\} .nf $ for UPS in `upsc \-l mybox:1234`; do upsc $UPS ups\&.status done .fi .if n \{\ .RE .\} .sp To list clients connected on "myups": .sp .if n \{\ .RS 4 .\} .nf $ upsc \-c myups 127\&.0\&.0\&.1 ::1 192\&.168\&.1\&.2 .fi .if n \{\ .RE .\} .SH "SCRIPTED MODE" .sp If you run this program inside a shell script or similar to get the list of devices and variables, you should only consider using output from stdout, not stderr\&. .SH "DIAGNOSTICS" .sp upsc will either print a list of UPS names, a list of all supported variables and their values on the UPS, or an error message\&. If you receive an error, make sure you have specified a valid UPS on the command line, that \fBupsd\fR(8) is really running on the other host and that no firewalls are blocking you\&. .SH "HISTORY" .sp Earlier versions of this program used the \fIupsfetch\fR library and UDP sockets to talk to upsd\&. This version of upsc uses the new \fIupsclient\fR library, which only talks TCP\&. This is why \fIupsct\fR no longer exists\&. .SH "SEE ALSO" .sp \fBupsd\fR(8) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscli_splitname.30000644000175000017500000000514412665610640014427 00000000000000'\" t .\" Title: upscli_splitname .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_SPLITNAME" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_splitname \- split a UPS definition into its components .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port) .fi .SH "DESCRIPTION" .sp The \fBupscli_splitname()\fR function takes a pointer to the raw UPS definition \fIbuf\fR and returns pointers to dynamically allocated memory in \fIupsname\fR and \fIhostname\fR\&. It also copies the port number into \fIport\fR\&. .SH "FORMATTING" .sp A UPS definition is specified according to this format: .sp .if n \{\ .RS 4 .\} .nf [@[:]] .fi .if n \{\ .RE .\} .sp When the UPS name is not given, this function will print an error to stderr and return \-1 without changing anything\&. .sp Definitions without an explicit port value receive the default value of 3493\&. The default hostname is "localhost"\&. .SH "MEMORY USAGE" .sp You must \fBfree\fR(3) the pointers to \fIupsname\fR and \fIhostname\fR when you are done with them to avoid memory leaks\&. .SH "RETURN VALUE" .sp The \fBupscli_splitname()\fR function returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_splitaddr\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/bestups.txt0000644000175000017500000000762512640443572013225 00000000000000BESTUPS(8) ========== NAME ---- bestups - Driver for Best Power / SOLA (Phoenixtec protocol) UPS equipment NOTE ---- This man page only documents the hardware-specific features of the bestups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ *bestups* was designed to monitor Best Power UPS hardware like the Fortress, Fortress Telecom, Axxium Rackmount and Patriot Pro. It also recognizes and supports SOLA units such as the 325, 520 and 620. In addition, the Best 610 is supported using the `ID' option. Other UPS hardware using the Phoenixtec protocol should also work, but they will generate a warning since their battery information is not known. This driver does not support some older Best/SOLA units. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *nombattvolt=*'num':: Override the battery float voltage which is normally determined by asking the hardware. This is useful if your UPS constantly reports `battery.charge` values just below 100% even when it's completely charged. + If you have this problem, set this to whatever `battery.voltage` reports when the UPS is known to be completely charged with a good battery. + The author's Best Fortress 750 uses `nombattvolt=27.4`. *battvoltmult=*'num':: Multiply the reported battery voltage by this number. Some devices report only a fraction of the total battery voltage. + For example, the SOLA 610 700VA UPS (with a 24V battery) reports the single cell voltage (about 2.27V when fully charged). In this particular case you can set `battvoltmult = 12` in linkman:ups.conf[8] to fix this. *ID=*'string':: Set the Identification response string. This should only be used with hardware that supports the Phoenixtec protocol status inquiry commands, but not the "ID" command, such as the Best/SOLA 610. Format of the ID string is: AAA,BBBB,CCC,DDD,EE.E,FF.F + AAA is the three-character identification for the UPS model. + BBBB is the output power in VA (volt amperes). B is an integer number ranging from 0 to 9. + CCC is the Nominal Input Voltage. C is an integer number ranging from 0 to 9. The unit is Volts AC. + DDD is the Nominal Output Voltage. D is an integer number ranging from 0 to 9. The unit is Volts AC. + EE.E is the Battery Voltage that will cause the UPS to shut itself off. E is an integer number ranging from 0 to 9. Then unit is Volts DC and a decimal point is present. + FF.F or FFF.F is the Battery Voltage at full charge. F is an integer number ranging from 0 to 9. Then unit is Volts DC. Typically, for 700VA, 1KVA and 1.5KVA units, the format is FF.F. For 2KVA and 3KVA units, the format is FFF.F. + Example: a Best 610 1.5KVA unit would use the string "610,1500,120,120,10.0,48.0". BUGS ---- The battery charge percentage value (in `battery.charge`) is derived from the voltage data that the UPS returns, since the UPS doesn't return that value directly. On some hardware, the charge will remain at 100% for a long time and then drops quickly shortly before the battery runs out. You can confirm from the `battery.voltage` readings that this is a problem with the UPS and not this driver. Similarly, the float from the charger in some models forces the battery charge percentage back up to 100% immedately after the UPS goes back on-line, so you can't tell when it is really recharged. Finally, some models give one value for the battery's nominal voltage and yet actually have a nominal voltage slightly below that. This leads to things such as the perpetual 98.7% charge on the author's Fortress 750, even when it's been charging for weeks. You can use `nombattvolt=` in linkman:ups.conf[8] to fix this. AUTHOR ------ Russell Kroll, Jason White SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/ivtscd.txt0000644000175000017500000000122212640443572013017 00000000000000IVTSCD(8) ========= NAME ---- ivtscd - driver for the IVT Solar Controller Device NOTE ---- This man page only documents the hardware-specific features of the *ivtscd* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- This driver allows to access the IVT SCD-series devices. EXTRA ARGUMENTS --------------- This driver does not support any extra argument. AUTHOR ------ Arjen de Korte SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_scan_nut.txt0000644000175000017500000000277012640444140015071 00000000000000NUTSCAN_SCAN_NUT(3) =================== NAME ---- nutscan_scan_nut - Scan network for available NUT services. SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, long usec_timeout); DESCRIPTION ----------- The *nutscan_scan_nut()* function try to detect available NUT services and their associated devices. It issues a NUT request on every IP ranging from 'startIP' to 'stopIP'. 'startIP' is mandatory, 'stopIP' is optional. Those IP may be either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. A specific 'port' number may be passed, or NULL to use the default NUT port. This function waits up to 'usec_timeout' microseconds before considering an IP address does not respond to NUT queries. RETURN VALUE ------------ The *nutscan_scan_nut()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], linkman:nutscan_cidr_to_ip[3] nut-2.7.4/docs/man/nutclient_device_master.30000644000175000017500000000003012665610645015745 00000000000000.so libnutclient_misc.3 nut-2.7.4/docs/man/upscli_fd.30000644000175000017500000000364112665610633013026 00000000000000'\" t .\" Title: upscli_fd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_FD" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_fd \- Get file descriptor for connection .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_fd(UPSCONN_t *ups); .fi .SH "DESCRIPTION" .sp The \fBupscli_fd()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure and returns the value of the file descriptor for that connection, if any\&. .sp This may be useful for determining if the connection to \fBupsd\fR(8) has been lost\&. .SH "RETURN VALUE" .sp The \fBupscli_fd()\fR function returns the file descriptor, which may be any non\-negative number\&. It returns \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_connect\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/upsset.conf.txt0000644000175000017500000000362312640443572014001 00000000000000UPSSET.CONF(5) ============== NAME ---- upsset.conf - Configuration for Network UPS Tools upsset.cgi DESCRIPTION ----------- This file only does one job--it lets you convince linkman:upsset.cgi[8] that your system's CGI directory is secure. The program will not run until this file has been properly defined. SECURITY REQUIREMENTS --------------------- linkman:upsset.cgi[8] allows you to try login name and password combinations. There is no rate limiting, as the program shuts down between every request. Such is the nature of CGI programs. Normally, attackers would not be able to access your linkman:upsd[8] server directly as it would be protected by the LISTEN directives in your linkman:upsd.conf[5] file, tcp-wrappers (if available when NUT was built), and hopefully local firewall settings in your OS. *upsset* runs on your web server, so upsd will see it as a connection from a host on an internal network. It doesn't know that the connection is actually coming from someone on the outside. This is why you must secure it. On Apache, you can use the .htaccess file or put the directives in your httpd.conf. It looks something like this, assuming the .htaccess method: deny from all allow from your.network.addresses You will probably have to set "AllowOverride Limit" for this directory in your server-level configuration file as well. If this doesn't make sense, then stop reading and leave this program alone. It's not something you absolutely need to have anyway. Assuming you have all this done, and it actually works (test it!), then you may add the following directive to this file: I_HAVE_SECURED_MY_CGI_DIRECTORY If you lie to the program and someone beats on your upsd through your web server, don't blame me. SEE ALSO -------- linkman:upsset.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_get.30000644000175000017500000001255612665610634013222 00000000000000'\" t .\" Title: upscli_get .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_GET" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_get \- retrieve data from a UPS .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_get(UPSCONN_t *ups, unsigned int numq, const char **query, unsigned int *numa, char ***answer) .fi .SH "DESCRIPTION" .sp The \fBupscli_get()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure, and the pointer \fIquery\fR to an array of \fInumq\fR query elements\&. It builds a properly\-formatted request from those elements and transmits it to \fBupsd\fR(8)\&. .sp Upon success, the response will be split into separate components\&. A pointer to those components will be returned in \fIanswer\fR\&. The number of usable answer components will be returned in \fInuma\fR\&. .SH "USES" .sp This function implements the "GET" command in the protocol\&. As a result, you can use it to request many different things from the server\&. Some examples are: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} GET NUMLOGINS .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} GET UPSDESC .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} GET VAR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} GET TYPE .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} GET DESC .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} GET CMDDESC .RE .SH "QUERY FORMATTING" .sp To generate a request for GET NUMLOGINS su700, you would populate query and numq as follows: .sp .if n \{\ .RS 4 .\} .nf unsigned int numq; const char *query[2]; .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf query[0] = "NUMLOGINS"; query[1] = "su700"; numq = 2; .fi .if n \{\ .RE .\} .sp All escaping of special characters and quoting of elements with spaces is handled for you inside this function\&. .SH "ANSWER FORMATTING" .sp The raw response from upsd to the above query would be NUMLOGINS su700 1\&. Since this is split up for you, the values work out like this: .sp .if n \{\ .RS 4 .\} .nf unsigned int numa; .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf numa = 3; answer[0] = "NUMLOGINS" answer[1] = "su700" answer[2] = "1" .fi .if n \{\ .RE .\} .sp Notice that the value which you seek typically starts at answer[numq]\&. .SH "ERROR CHECKING" .sp This function will check your query against the response from \fBupsd\fR(8)\&. For example, if you send "VAR" "su700" "ups\&.status", it will expect to see those at the beginning of the response\&. .sp If the results from \fBupsd\fR do not pass this case\-insensitive test against your request, this function will return an error\&. When this happens, \fBupscli_upserror\fR(3) will return \fIUPSCLI_ERR_PROTOCOL\fR\&. .SH "ANSWER ARRAY LIFETIME" .sp The pointers contained within the \fIanswer\fR array are only valid until the next call to a \fIupsclient\fR function which references them\&. If you need to use data from multiple calls, you must copy it somewhere else first\&. .sp The \fIanswer\fR array and its elements may change locations, so you must not rely on previous addresses\&. You must only use the addresses which were returned by the most recent call\&. You also must not attempt to use more than \fInuma\fR elements in \fIanswer\fR\&. Such behavior is undefined, and may yield bogus data or a crash\&. .sp The array will be deleted after calling \fBupscli_disconnect\fR(3)\&. Any access after that point is also undefined\&. .SH "RETURN VALUE" .sp The \fBupscli_get()\fR function returns 0 on success, or \-1 if an error occurs\&. .sp If \fBupsd\fR disconnects, you may need to handle or ignore SIGPIPE in order to prevent your program from terminating the next time that the library writes to the disconnected socket\&. The following code in your initialization function will allow the \fBupscli_get()\fR call to return an error in that case: .sp .if n \{\ .RS 4 .\} .nf #include \&.\&.\&. signal (SIGPIPE, SIG_IGN); \&.\&.\&. .fi .if n \{\ .RE .\} .SH "SEE ALSO" .sp \fBupscli_list_start\fR(3), \fBupscli_list_next\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/upslog.txt0000644000175000017500000000566012640473702013044 00000000000000UPSLOG(8) ========= NAME ---- upslog - UPS status logger SYNOPSIS -------- *upslog -h* *upslog* ['OPTIONS'] DESCRIPTION ----------- *upslog* is a daemon that will poll a UPS at periodic intervals, fetch the variables that interest you, format them, and write them to a file. The default format string includes variables that are supported by many common UPS models. See the description below to make your own. OPTIONS ------- *-h*:: Display the help message. *-f* 'format':: Monitor the UPS using this format string. Be sure to enclose 'format' in quotes so your shell doesn't split it up. Valid escapes within this string are: %%;; Insert a single "%" %TIME format%;; Insert the time with strftime formatting %ETIME%;; Insert the number of seconds, ala time_t. This is now a 10 digit number. %HOST%;; insert the local hostname %UPSHOST%;; insert the host of the UPS being monitored %PID%;; insert the pid of upslog %VAR varname%;; insert the value of variable varname The default format string is: %TIME @Y@m@d @H@M@S% %VAR battery.charge% %VAR input.voltage% %VAR ups.load% [%VAR ups.status%] %VAR ups.temperature% %VAR input.frequency% *-i* 'interval':: Wait this many seconds between polls. This defaults to 30 seconds. + If you require tighter timing, you should write your own logger using the linkman:upsclient[3] library. *-l* 'logfile':: Store the results in this file. + You can use "-" for stdout, but upslog will remain in the foreground. *-s* 'ups':: Monitor this UPS. The format for this option is +upsname[@hostname[:port]]+. The default hostname is "localhost". *-u* 'username':: If started as root, upsmon will *setuid*(2) to the user id associated with 'username' for security. + If 'username' is not defined, it will use the value that was compiled into the program. This defaults to "nobody", which is less than ideal. SERVICE DELAYS -------------- The interval value is merely the number given to *sleep*(3) after running through the format string. Therefore, a query will actually take slightly longer than the interval, depending on the speed of your system. ON-DEMAND LOGGING ----------------- Sending a USR1 signal to a running *upslog* process makes it wake from the current sleep and log immediately. This is useful when triggered from a *upssched* event trigger (e.g. `AT ONBATT` or `AT ONLINE`) to ensure that an entry always exists, even if the power goes away for a period of time shorter than that specified by the `-i` argument. LOG ROTATION ------------ *upslog* writes its PID to `upslog.pid`, and will reopen the log file if you send it a SIGHUP. This allows it to keep running when the log is rotated by an external program. SEE ALSO -------- Server: ~~~~~~~ linkman:upsd[8] Clients: ~~~~~~~~ linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upsmon[8], linkman:upssched[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_cidr_to_ip.txt0000644000175000017500000000177612640443572015407 00000000000000NUTSCAN_CIDR_TO_IP(3) ===================== NAME ---- nutscan_cidr_to_ip - Convert a CIDR IP to a range of IP address. SYNOPSIS -------- #include int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip); DESCRIPTION ----------- The *nutscan_cidr_to_ip()* function converts a range of IP address in the CIDR format given as a string in 'cidr', to two IPs in strings pointed by 'start_ip' and 'stop_ip' which can be used as input paramters in the scanning functions of the libnutscan API. It is the caller's responsability to free 'start_ip' and 'stop_ip' strings. RETURN VALUE ------------ The *nutscan_cidr_to_ip()* function returns 0 if an error occured (invalid 'cidr' address) or 1 if successful. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3] nut-2.7.4/docs/man/clone.txt0000644000175000017500000000717712640443572012642 00000000000000CLONE(8) ======== NAME ---- clone - UPS driver clone NOTE ---- This man page only documents the specific features of the clone driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- This driver, which sits on top of another driver socket, allows users to group clients to a particular outlet of a device and deal with this output as if it was a normal UPS. EXTRA ARGUMENTS --------------- This driver supports the following settings: *load.off*='command':: Recommended. Set the command on the "real" UPS driver that will be used to switch off the outlet. You need both *load.off* and *load.on* in order to power cycle the outlet. Otherwise, shutting down the clients powered by an outlet is a one way street (see <<_important,IMPORTANT>>). *load.on*='command':: Recommended. Set the command on the "real" UPS driver that will be used to switch on the outlet. You need both *load.off* and *load.on* in order to power cycle the outlet. Otherwise, shutting down the clients powered by an outlet is a one way street (see <<_important,IMPORTANT>>). *load.status*='value':: Recommended. Set the variable on the "real" UPS driver that will be used to indicate the outlet status (i.e. on/off). If not specified, the clone driver will attempt to keep track of the outlet status, but this is less reliable. *offdelay*='num':: Set the timer (in seconds) before the outlet is turned off after the shutdown condition (+OB LB+) for this outlet is met or a command to shutdown was issued. Defaults to 120 seconds. *ondelay*='num':: Set the timer (in seconds) for the outlet to switch on in case the power returns after the oulet has been switched off. Defaults to 30 seconds. *mincharge*='value':: Set the remaining battery level when the clone UPS switches to LB (percent). *minruntime*='value':: Set the remaining battery runtime when the clone UPS switches to LB (seconds). IMPLEMENTATION -------------- The port specification in the linkman:ups.conf[5] reference the driver socket that the "real" UPS driver is using. For example: [realups] driver = usbhid-ups port = auto [clone-outlet-1] driver = clone port = usbhid-ups-realups load.on = outlet.1.load.on load.off = outlet.1.load.off load.status = outlet.1.status [...] IMPORTANT --------- Unlike a real UPS, you should *not* configure a upsmon master for this driver. When a upsmon master sees the OB LB flags and tells the upsd server it is OK to initiate the shutdown sequence, the server will latch the FSD status and it will not be possible to restart the systems connected without restarting the upsd server. This will be a problem if the power returns after the clone UPS initiated the shutdown sequence on it's outlet, but returns before the real UPS begins shutting down. The solution is in the clone driver, that will insert the FSD flag if needed without the help of a upsmon master. CAVEATS ------- The clone UPS will follow the status on the real UPS driver. You can only make the clone UPS shutdown earlier than the real UPS driver, not later. If the real UPS driver initiates a shutdown, the clone UPS driver will immediately follow. Be aware that the commands to shutdown/restart an outlet on the real UPS drivers are not affected, so if you tell the real UPS driver to shutdown the outlet of the clone UPS driver, your clients will lose power without warning. AUTHOR ------ Arjen de Korte SEE ALSO -------- linkman:upscmd[1], linkman:upsrw[1], linkman:ups.conf[5], linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/libupsclient-config.txt0000644000175000017500000000201612640443572015465 00000000000000LIBUPSCLIENT-CONFIG(1) ====================== NAME ---- libupsclient-config - script to get information about the installed version of libupsclient SYNOPSIS -------- *libupsclient-config* [--version] [--libs] [--cflags] DESCRIPTION ----------- *libupsclient-config* is a tool that is used to determine the compiler and linker flags that should be used to compile and link programs that use *libupsclient* from the Network UPS Tools project. OPTIONS ------- *libupsclient-config* accepts the following options: *--version*:: Print the currently installed version of *libupsclient* on the standard output. *--libs*:: Print the linker flags that are necessary to link a *libupsclient* program. *--cflags*:: Print the compiler flags that are necessary to compile a *libupsclient* program. AUTHORS ------- This manual page was written by Arnaud Quette . SEE ALSO -------- linkman:upsclient[3] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan.30000644000175000017500000000735112665610647012540 00000000000000'\" t .\" Title: nutscan .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan \- Network UPS Tools (NUT) device discovery library .SH "DESCRIPTION" .sp The Network UPS Tools (NUT) \fBnutscan\fR library provides the same discovery related features that are also offered by \fBnut-scanner\fR(8)\&. .sp It enables the discovery of supported NUT devices (USB, SNMP, Eaton XML/HTTP and IPMI) and NUT servers (using Avahi, or the classic connection method)\&. .SH "DISCOVERY FUNCTIONS" .sp First, include the required header file: .sp .if n \{\ .RS 4 .\} .nf #include .fi .if n \{\ .RE .\} .sp Then, to discover new devices, use the appropriate function: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_scan_usb\fR(3) for supported USB devices, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_scan_snmp\fR(3) for supported SNMP agents, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_scan_xml_http\fR(3) for Eaton Network Management Card, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_scan_nut\fR(3) for NUT servers (upsd), using the classic method, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_scan_avahi\fR(3) for NUT servers (upsd), using the mDNS (Avahi) method, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_scan_ipmi\fR(3) for supported IPMI PSU\&. .RE .sp All of these functions return a list of devices found, using the nutscan_device_t structure\&. This structure is described in \fBnutscan_add_device_to_device\fR(3)\&. .sp Helper functions are also provided to output data using standard formats: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_display_parsable\fR(3) for parsable output, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBnutscan_display_ups_conf\fR(3) for ups\&.conf style\&. .RE .SH "ERROR HANDLING" .sp There is currently no specific mechanism for error handling\&. .SH "SEE ALSO" .sp \fBnut-scanner\fR(8), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_cidr_to_ip\fR(3), http://avahi\&.org/ nut-2.7.4/docs/man/upsd.txt0000644000175000017500000001247612640443572012513 00000000000000UPSD(8) ======= NAME ---- upsd - UPS information server SYNOPSIS -------- *upsd* -h *upsd* ['OPTIONS'] DESCRIPTION ----------- *upsd* is responsible for serving the data from the drivers to the clients. It connects to each driver and maintains a local cache of the current state. Queries from the clients are served from this cache, so delays are minimal. It also conveys administrative messages from the clients back to the drivers, such as starting tests, or setting values. Communication between *upsd* and clients is handled on a TCP port. Configuration details for this port are described in linkman:upsd.conf[8]. This program is essential, and must be running at all times to actually make any use out of the drivers and clients. Controls in the configuration files allow you to limit access to the server, but you should also use a firewall for extra protection. Client processes such as linkman:upsmon[8] trust *upsd* for status information about the UPS hardware, so keep it secure. OPTIONS ------- *-c* 'command':: Send 'command' to the background process as a signal. Valid commands are: *reload*;; reread configuration files *stop*;; stop process and exit *-D*:: Raise the debug level. Use this multiple times for additional details. *-h*:: Display the help text. *-r* 'directory':: upsd will *chroot*(2) to 'directory' shortly after startup and before parsing any configuration files with this option set. You can use this to create a "jail" for greater security. + You must coordinate this with your drivers, as upsd must be able to find the state path within 'directory'. See linkman:upsdrvctl[8] and linkman:nutupsdrv[8]. *-u* 'user':: Switch to user 'user' after startup if started as root. This overrides whatever you may have compiled in with `configure --with-user`. *-V*:: Display the version of the program. RELOADING --------- upsd can reload its configuration files without shutting down the process if you send it a SIGHUP or start it again with `-c reload`. This only works if the background process is able to read those files. If you think that upsd can't reload, check your syslogs for error messages. If it's complaining about not being able to read the files, then you need to adjust your system to make it possible. Either change the permissions on the files, or run upsd as another user that will be able to read them. DO NOT make your upsd.conf or upsd.users world-readable, as those files hold important authentication information. In the wrong hands, it could be used by some evil person to spoof your master upsmon and command your systems to shut down. DIAGNOSTICS ----------- upsd expects the drivers to either update their status regularly or at least answer periodic queries, called pings. If a driver doesn't answer, upsd will declare it "stale" and no more information will be provided to the clients. If upsd complains about staleness when you start it, then either your driver or configuration files are probably broken. Be sure that the driver is actually running, and that the UPS definition in linkman:ups.conf[5] is correct. Also make sure that you start your driver(s) before starting upsd. Data can also be marked stale if the driver can no longer communicate with the UPS. In this case, the driver should also provide diagnostic information in the syslog. If this happens, check the serial or USB cabling, or inspect the network path in the case of a SNMP UPS. ACCESS CONTROL -------------- If the server is build with tcp-wrappers support enabled, it will check if the NUT username is allowed to connect from the client address through the `/etc/hosts.allow` and `/etc/hosts.deny` files. Note that this will only be done for commands that require to be logged into the server. Further details are described in *hosts_access*(5). FILES ----- The general upsd configuration file is linkman:upsd.conf[5]. The administrative functions like SET and INSTCMD for users are defined and controlled in linkman:upsd.users[5]. UPS definitions are found in linkman:ups.conf[5]. ENVIRONMENT VARIABLES --------------------- *NUT_CONFPATH* is the path name of the directory that contains `upsd.conf` and other configuration files. If this variable is not set, *upsd* uses a built-in default, which is often `/usr/local/ups/etc`. *NUT_STATEPATH* is the path name of the directory in which *upsd* keeps state information. If this variable is not set, *upsd* uses a built-in default, which is often `/var/state/ups`. The *STATEPATH* directive in linkman:upsd.conf[5] overrides this variable. SEE ALSO -------- Clients: ~~~~~~~~ linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Drivers: ~~~~~~~~ linkman:nutupsdrv[8], linkman:apcsmart[8], linkman:belkin[8], linkman:belkinunv[8], linkman:bestuferrups[8], linkman:bestups[8], linkman:cyberpower[8], linkman:energizerups[8], linkman:etapro[8], linkman:everups[8], linkman:genericups[8], linkman:isbmex[8], linkman:liebert[8], linkman:masterguard[8], linkman:mge-shut[8], linkman:mge-utalk[8], linkman:oneac[8], linkman:powercom[8], linkman:safenet[8], linkman:snmp-ups[8], linkman:tripplite[8], linkman:tripplitesu[8], linkman:victronups[8], Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_scan_snmp.30000644000175000017500000000770112665610647014600 00000000000000'\" t .\" Title: nutscan_scan_snmp .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_SNMP" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_snmp \- Scan network for SNMP devices\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_snmp(const char * start_ip,const char * stop_ip,long timeout, nutscan_snmp_t * sec); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_snmp()\fR function try to detect NUT compatible SNMP devices\&. It tries SNMP queries on every IP ranging from \fIstart_ip\fR to \fIstop_ip\fR\&. Those IP may be either IPv4 or IPv6 addresses or host names\&. .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .sp This function waits up to \fItimeout\fR microseconds before considering an IP address does not respond to SNMP queries\&. .sp A valid nutscan_snmp_t structure must be passed to this function\&. .sp The nutscan_snmp_t structure contains the following members which must be filled as described below: .sp .if n \{\ .RS 4 .\} .nf char * \*(Aqcommunity\*(Aq; char * \*(AqsecLevel\*(Aq; char * \*(AqsecName\*(Aq; char * \*(AqauthPassword\*(Aq; char * \*(AqprivPassword\*(Aq; char * \*(AqauthProtocol\*(Aq; char * \*(AqprivProtocol\*(Aq; .fi .if n \{\ .RE .\} .sp If \fIcommunity\fR is not NULL, SNMP v1 request are sent using this \fIcommunity\fR\&. .sp If \fIcommunity\fR is NULL and \fIsecLevel\fR is NULL, SNMP v1 is selected and \fIcommunity\fR is set to "public"\&. .sp In the other cases, SNMP v3 is used\&. \fIsecLevel\fR may be one of SNMP_SEC_LEVEL_NOAUTH, SNMP_SEC_LEVEL_AUTHNOPRIV or SNMP_SEC_LEVEL_AUTHPRIV\&. \fIsecName\fR is the security name and must be non NULL\&. .sp If \fIsecLevel\fR is set to SNMP_SEC_LEVEL_AUTHNOPRIV, \fIauthPassword\fR must be non NULL\&. .sp If \fIsecLevel\fR is set to SNMP_SEC_LEVEL_AUTHPRIV, \fIauthPassword\fR and \fIprivPassword\fR must be non NULL\&. .sp If \fIauthProtocol\fR is NULL, MD5 protocol is used\&. Else you can set \fIauthProtocol\fR to either "MD5" or "SHA"\&. .sp If \fIprivProtocol\fR is NULL, DES protocol is used\&. Else you can set \fIprivProtocol\fR to either "AES" or "DES"\&. .sp \fIpeername\fR and \fIhandle\fR are used internally and do not need any initialization\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_snmp()\fR function returns a pointer to a nutscan_device_t structure containing all found devices or NULL if an error occurs or no device is found\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_scan_eaton_serial\fR(3), \fBnutscan_cidr_to_ip\fR(3) nut-2.7.4/docs/man/bestfortress.80000644000175000017500000000434312640476475013617 00000000000000'\" t .\" Title: bestfortress .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BESTFORTRESS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" bestfortress \- Driver for old Best Fortress UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the bestfortress driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports old Best Fortress UPS equipment using a serial connection\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBbaudrate\fR=\fInum\fR .RS 4 Set the speed of the serial connection \- 1200, 2400, 4800 or 9600\&. .RE .PP \fBmax_load\fR=\fIVA\fR .RS 4 Set the full\-scale value of the \fBups\&.load\fR variable\&. .RE .SH "AUTHOR" .sp Holger Dietze , Stuart D\&. Gathman .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "The newer Best Power drivers:" .sp \fBbestups\fR(8), \fBbestuferrups\fR(8), \fBbestfcom\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/bcmxcp_usb.txt0000644000175000017500000000550612640443572013661 00000000000000BCMXCP_USB(8) ============= NAME ---- bcmxcp_usb - Experimental driver for UPSes supporting the BCM/XCP protocol over USB NOTE ---- This man page only documents the hardware-specific features of the bcmxcp_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. This driver is a variant of the serial driver bcmxcp and uses the same core code. SUPPORTED HARDWARE ------------------ This driver should recognize all BCM/XCP-compatible UPSes that are connected via USB. It has been developed and tested on Powerware PW3501 hardware. It also has been tested on PW5110 hardware. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]. *shutdown_delay=*'delay':: The number of seconds that the UPS should wait between receiving the shutdown command and actually shutting off. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- *shutdown_delay =*'120' INSTANT COMMANDS ---------------- This driver supports the following Instant Commands: *shutdown.return*:: Turn off the load and return when power is back. *shutdown.stayoff*:: Turn off the load and remain off. *test.battery.start*:: Start a battery test. TODO LIST --------- *Report UPS alarm status*:: BCM/XCP supports reporting a wide range of UPS alarm conditions. *Report UPS statistics informations*:: BCM/XCP supports reporting of UPS statistics data. EXPERIMENTAL DRIVER ------------------- This driver has been tagged experimental, even if it has been reported to be stable. Thus it is not suitable for production systems and it is not built by default. This is mainly due to the fact that it is a new driver. INSTALLATION ------------ This driver is not built by default. You can build it by using "configure --with-usb=yes". Note that it will also install other USB drivers. You also need to install manually the hotplug files (libhidups and libhid.usermap), generally in etc/hotplug/usb/, to address the permission settings problem. Lastly note that the libhidups file must have execution flag set (ie using chmod +x ...). IMPLEMENTATION -------------- bcmxcp_usb only supports 1 UPS at this time. You can put the "auto" value for port in `ups.conf`, i.e.: [pw3105] driver = bcmxcp_usb port = auto KNOWN ISSUES AND BUGS --------------------- "Got EPERM: Operation not permitted upon driver startup" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You have forgotten to install the hotplug files, as explained in the INSTALLATION section above. Don't forget to restart hotplug so that it applies these changes. AUTHOR ------ Tore Ørpetveit , Wolfgang Ocker SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/apcupsd-ups.txt0000644000175000017500000000604212640473702013772 00000000000000APCUPSD-UPS(8) ============== NAME ---- apcupsd-ups - Driver for apcupsd client access NOTE ---- This man page only documents the specific features of the *apcupsd-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- This driver is a client to *apcupsd*. *apcupsd-ups* acts as an *apcupsd* client, simply forwarding data. This can be useful in cases where both protocols are required in a network, or in case apcupsd has a required UPS access mode missing from NUT. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in linkman:nut.conf[5]: *port*=[:]:: This is the name of a remote host running apcupsd (plus an optional port). For instance: [apcupsd] driver = apcupsd-ups port = localhost desc = "apcupsd client" BACKGROUND ---------- This driver was originally written in one evening to allow interworking with *apcupsd*. SUPPORTED VARIABLES ------------------- The following variables are translated from *apcupsd* to NUT. All times should be converted to seconds (please file a bug if you notice a mismatch in units). [width="50%",cols="m,m",options="header"] |=============================== | apcupsd variable | NUT variable(s) | BCHARGE | battery.charge | MBATTCHG | battery.charge.low | RETPCT | battery.charge.restart | BATTDATE | battery.date | TIMELEFT | battery.runtime | MINTIMEL | battery.runtime.low | BATTV | battery.voltage | NOMBATTV | battery.voltage.nominal | LINEFREQ | input.frequency | SENSE | input.sensitivity | HITRANS | input.transfer.high | LOTRANS | input.transfer.low | LASTXFER | input.transfer.reason | LINEV | input.voltage | MAXLINEV | input.voltage.maximum | MINLINEV | input.voltage.minimum | NOMINV | input.voltage.nominal | LINEFREQ | output.frequency | OUTPUTV | output.voltage | NOMOUTV | output.voltage.nominal | DATE | ups.date, ups.time | DSHUTD | ups.delay.shutdown | DWAKE | ups.delay.start | FIRMWARE | ups.firmware, ups.firmware.aux | UPSNAME | ups.id | LOADPCT | ups.load | MANDATE | ups.mfr.date | NOMPOWER | ups.realpower.nominal | SERIALNO | ups.serial | STATUS | ups.status | ITEMP | ups.temperature | STESTI | ups.test.interval | SELFTEST | ups.test.result |=============================== LIMITATIONS ----------- Access to *apcupsd* is strictly read only: no commands can be issued. This stems from the design of *apcupsd*, where the settings are changed in *apctest*. In order to run *apctest*, *apcupsd* must be stopped (and *apcupsd* exposes the UPS to the network). AUTHOR ------ Andreas Steinmetz SEE ALSO -------- linkman:ups.conf[5], linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ The apcupsd home page: http://www.apcupsd.org/ nut-2.7.4/docs/man/nut-scanner.80000644000175000017500000002541712640476471013330 00000000000000'\" t .\" Title: nut-scanner .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUT\-SCANNER" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nut-scanner \- scan communication buses for NUT devices .SH "SYNOPSIS" .sp \fBnut\-scanner\fR \-h .sp \fBnut\-scanner\fR [\fIOPTIONS\fR] .SH "DESCRIPTION" .sp \fBnut\-scanner\fR scans available communication buses and displays any NUT\-compatible devices it has found\&. .SH "INSTALLATION" .sp \fBnut\-scanner\fR is only built if libltdl (part of libtool development suite) is available\&. Available options (USB, SNMP, IPMI, \&...) will vary according to the available compile time and runtime dependencies\&. For example, if Net\-SNMP is installed, thus providing libsnmp (\&.so or \&.dll) and headers, both during compilation and runtime, then SNMP discovery will be available\&. .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display the help text\&. .RE .SH "DISPLAY OPTIONS" .PP \fB\-N\fR | \fB\-\-disp_nut_conf\fR .RS 4 Display result in the \fIups\&.conf\fR format\&. .RE .PP \fB\-P\fR | \fB\-\-disp_parsable\fR .RS 4 Display result in a parsable format\&. .RE .SH "BUS OPTIONS" .PP \fB\-C\fR | \fB\-\-complete_scan\fR .RS 4 Scan all available communication buses (default behavior) .RE .PP \fB\-U\fR | \fB\-\-usb_scan\fR .RS 4 List all NUT\-compatible USB devices currently plugged in\&. .RE .PP \fB\-S\fR | \fB\-\-snmp_scan\fR .RS 4 Scan SNMP devices\&. Requires at least a \fIstart IP\fR, and optionally, an \fIend IP\fR\&. See specific SNMP OPTIONS for community and security settings\&. .RE .PP \fB\-M\fR | \fB\-\-xml_scan\fR .RS 4 Scan XML/HTTP devices\&. Broadcast a network message on the current network interfaces to retrieve XML/HTTP capable devices\&. No IP required\&. .RE .PP \fB\-O\fR | \fB\-\-oldnut_scan\fR .RS 4 Scan NUT devices (i\&.e\&. upsd daemon) on IP ranging from \fIstart IP\fR to \fIend IP\fR\&. .RE .PP \fB\-A\fR | \fB\-\-avahi_scan\fR .RS 4 Scan NUT servers using Avahi request on the current network interfaces\&. No IP required\&. .RE .PP \fB\-I\fR | \fB\-\-ipmi_scan\fR .RS 4 Scan NUT compatible power supplies available via IPMI on the current host, or over the network\&. .RE .PP \fB\-E\fR | \fB\-\-eaton_serial\fR \fIserial ports\fR .RS 4 Scan Eaton devices (XCP and SHUT) available via serial bus on the current host\&. This option must be requested explicitely, even for a complete scan\&. \fIserial ports\fR can be expressed in various forms: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fIauto\fR to scan all serial ports\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a single charcater indicating a port number (\fI0\fR (zero) for /dev/ttyS0 and /dev/ttyUSB0 on Linux, \fI1\fR for COM1 on Windows, \fIa\fR for /dev/ttya on Solaris\&...) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a range of N characters, hyphen separated, describing the range of ports using \fIX\-Y\fR, where X and Y are characters refering to the port number\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a single port name\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a list of ports name, coma separated, like \fI/dev/ttyS1,/dev/ttyS4\fR\&. .RE .RE .SH "NETWORK OPTIONS" .PP \fB\-t\fR | \fB\-\-timeout\fR \fItimeout\fR .RS 4 Set the network timeout in seconds\&. Default timeout is 5 seconds\&. .RE .PP \fB\-s\fR | \fB\-\-start_ip\fR \fIstart IP\fR .RS 4 Set the first IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut)\&. .RE .PP \fB\-e\fR | \fB\-\-end_ip\fR \fIend IP\fR .RS 4 Set the last IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut)\&. If this parameter is omitted, only the \fIstart IP\fR is scanned\&. If \fIend IP\fR is less than \fIstart IP\fR, both parameters are internally permuted\&. .RE .PP \fB\-m\fR | \fB\-\-mask_cidr\fR \fIIP address/mask\fR .RS 4 Set a range of IP using CIDR notation\&. .RE .SH "NUT DEVICE OPTION" .PP \fB\-p\fR | \fB\-\-port\fR \fIport number\fR .RS 4 Set the port number of scanned NUT devices (default 3493)\&. .RE .SH "SNMP V1 OPTION" .PP \fB\-c\fR | \fB\-\-community\fR \fIcommunity\fR .RS 4 Set SNMP v1 community name (default = public)\&. .RE .SH "SNMP V3 OPTIONS" .PP \fB\-l\fR | \fB\-\-secLevel\fR \fIsecurity level\fR .RS 4 Set the \fIsecurity level\fR used for SNMPv3 messages\&. Allowed values are: noAuthNoPriv, authNoPriv and authPriv\&. .RE .PP \fB\-u\fR | \fB\-\-secName\fR \fIsecurity name\fR .RS 4 Set the \fIsecurity name\fR used for authenticated SNMPv3 messages\&. This parameter is mandatory if you set \fIsecurity level\fR\&. .RE .PP \fB\-w\fR | \fB\-\-authProtocol\fR \fIauthentication protocol\fR .RS 4 Set the \fIauthentication protocol\fR used for authenticated SNMPv3 messages\&. Allowed values are MD5 or SHA\&. Default value is MD5\&. .RE .PP \fB\-W\fR | \fB\-\-authPassword\fR \fIauthentication pass phrase\fR .RS 4 Set the \fIauthentication pass phrase\fR used for authenticated SNMPv3 messages\&. This parameter is mandatory if you set \fIsecurity level\fR to authNoPriv or authPriv\&. .RE .PP \fB\-x\fR | \fB\-\-privProtocol\fR \fIprivacy protocol\fR .RS 4 Set the \fIprivacy protocol\fR used for encrypted SNMPv3 messages\&. Allowed values are DES or AES\&. Default value is DES\&. .RE .PP \fB\-X\fR | \fB\-\-privPassword\fR \fIprivacy pass phrase\fR .RS 4 Set the \fIprivacy pass phrase\fR used for encrypted SNMPv3 messages\&. This parameter is mandatory if you set \fIsecurity level\fR to authPriv\&. .RE .SH "IPMI OPTIONS" .PP \fB\-b\fR | \fB\-\-username\fR \fIusername\fR .RS 4 Set the username used for authenticating IPMI over LAN connections (mandatory for IPMI over LAN\&. No default)\&. .RE .PP \fB\-B\fR | \fB\-\-password\fR \fIpassword\fR .RS 4 Specify the password to use when authenticating with the remote host (mandatory for IPMI over LAN\&. No default)\&. .RE .PP \fB\-d\fR | \fB\-\-authType\fR \fIauthentication type\fR .RS 4 Specify the IPMI 1\&.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, MD2, and MD5) with the remote host (default=MD5)\&. This forces connection through the \fIlan\fR IPMI interface , thus in IPMI 1\&.5 mode\&. .RE .PP \fB\-D\fR | \fB\-\-cipher_suite_id\fR \fIcipher suite identifier\fR .RS 4 Specify the IPMI 2\&.0 cipher suite ID to use\&. The Cipher Suite ID identifies a set of authentication, integrity, and confidentiality algorithms to use for IPMI 2\&.0 communication\&. The authentication algorithm identifies the algorithm to use for session setup, the integrity algorithm identifies the algorithm to use for session packet signatures, and the confidentiality algorithm identifies the algorithm to use for payload encryption (default=3)\&. .sp The following cipher suite ids are currently supported (Authentication; Integrity; Confidentiality): .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB0\fR: None; None; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB1\fR: HMAC\-SHA1; None; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB2\fR: HMAC\-SHA1; HMAC\-SHA1\-96; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB3\fR: HMAC\-SHA1; HMAC\-SHA1\-96; AES\-CBC\-128 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB6\fR: HMAC\-MD5; None; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB7\fR: HMAC\-MD5; HMAC\-MD5\-128; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB8\fR: HMAC\-MD5; HMAC\-MD5\-128; AES\-CBC\-128 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB11\fR: HMAC\-MD5; MD5\-128; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB12\fR: HMAC\-MD5; MD5\-128; AES\-CBC\-128 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB15\fR: HMAC\-SHA256; None; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB16\fR: HMAC\-SHA256; HMAC_SHA256_128; None .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fB17\fR: HMAC\-SHA256; HMAC_SHA256_128; AES\-CBC\-128 .RE .RE .SH "MISCELLANEOUS OPTIONS" .PP \fB\-V\fR | \fB\-\-version\fR .RS 4 Display NUT version\&. .RE .PP \fB\-a\fR | \fB\-\-available\fR .RS 4 Display available bus that can be scanned , depending on how the binary has been compiled\&. (OLDNUT, USB, SNMP, XML, AVAHI, IPMI)\&. .RE .PP \fB\-q\fR | \fB\-\-quiet\fR .RS 4 Display only scan result\&. No information on currently scanned bus is displayed\&. .RE .SH "EXAMPLES" .sp To scan USB devices only: .sp \fBnut\-scanner \-U\fR .sp To scan SNMP v1 device with public community on address range 192\&.168\&.0\&.0 to 192\&.168\&.0\&.255: .sp \fBnut\-scanner \-S \-s 192\&.168\&.0\&.0 \-e 192\&.168\&.0\&.255\fR .sp The same using CIDR notation: .sp \fBnut\-scanner \-S \-m 192\&.168\&.0\&.0/24\fR .sp To scan NUT servers with a timeout of 10 seconds on IP range 192\&.168\&.0\&.0 to 192\&.168\&.0\&.127 using CIDR notation: .sp \fBnut\-scanner \-O \-t 10 \-m 192\&.168\&.0\&.0/25\fR .sp To scan for power supplies, through IPMI (1\&.5 mode) over the network, on address range 192\&.168\&.0\&.0 to 192\&.168\&.0\&.255: .sp \fBnut\-scanner \-I \-m 192\&.168\&.0\&.0/24 \-b username \-B password\fR .sp To scan for Eaton serial devices on ports 0 and 1 (/dev/ttyS0, /dev/ttyUSB0, /dev/ttyS1 and /dev/ttyUSB1 on Linux): .sp \fBnut\-scanner \-\-eaton_serial 0\-1\fR .sp To scan for Eaton serial devices on ports 1 and 2 (COM1 and COM2 on Windows): .sp \fBnut\-scanner \-\-eaton_serial 1\-2\fR .SH "SEE ALSO" .sp \fBups.conf\fR(5) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/powerpanel.txt0000644000175000017500000001103512640443572013702 00000000000000POWERPANEL(8) ============= NAME ---- powerpanel - Driver for PowerPanel Plus compatible UPS equipment NOTE ---- This man page only documents the hardware-specific features of the powerpanel driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports CyberPower BC1200, PR2200 and many other similar devices, both for the text and binary protocols. The driver will autodetect which protocol is used. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in linkman:ups.conf[5]: *protocol=*['text,binary']:: Override the default autodetection of the protocol. *manufacturer=*'value':: If you don't like the autodetected value, you can override this by setting it here. *model=*'value':: Like manufacturer above. *serial=*'value':: Like manufacturer above. *ondelay=*'value':: Time to wait before switching on the UPS (1 - 9999 minutes, 0=indefinite). Only available with the text protocol driver (see <<_support_status,Support Status>>). *offdelay=*'value':: Time to wait before shutting down the UPS (6 - 600 seconds). Values below 60 seconds will be truncated to 6 seconds intervals, values above 60 seconds to 60 seconds intervals. Only available with the text protocol driver (see <<_support_status,Support Status>>). VARIABLES --------- Depending on the type of your UPS unit, some of the following variables may be changed with linkman:upsrw[8]. If the driver can't read a variable from the UPS, it will not be made available. *input.transfer.high*:: writable: high transfer voltage point in V *input.transfer.low*:: writable: low transfer voltage point in V *battery.charge.low*:: writable: remaining battery charge percentage for low battery warning *output.voltage.nominal*:: writable: nominal output voltage in V *ups.start.battery*:: writable: allow cold start from battery COMMANDS -------- Depending on the type of your UPS unit, some of the following commands may be available. * test.battery.start.quick, test.battery.stop * beeper.enable, beeper.disable, beeper.toggle * shutdown.return, shutdown.reboot, shutdown.stayoff On many devices, these commands are unreliable, so before using them you must verify that these work as expected (see <<_shutdown_issues,Shutdown Issues>>). * shutdown.stop SUPPORT STATUS -------------- Vendor support is absent for this driver, so if you need some features that are currently not available, provide ample documentation on what the driver should sent to the UPS in order to make this work. If more information would be available on the binary protocol, it would probably be possible to make 'ondelay' and 'offdelay' configurable. So far, nobody has taken the time to investigate what we should tell the UPS to make this work, and CyberPower isn't willing to share this with us. SHUTDOWN ISSUES --------------- If the *shutdown.return* command on your UPS doesn't seem to work, chances are that your UPS is an older model. Try a couple of different settings for 'offdelay'. If no value in the range 6..600 works, your UPS likely doesn't support this. In order to get the expected behaviour, it requires *shutdown.stayoff* (when on battery) and *shutdown.reboot* (when on mains). The driver will automatically fallback to these commands if *shutdown.return* fails, and tries to detect which one should be used when called with the '-k' option (or through *upsdrvctl shutdown*). This isn't bullet-proof, however, and you should be prepared that the power will either not be shutdown or that it doesn't return when the power comes back. All models supported by the binary protocol and many supported through the text protocol are affected by this. KNOWN PROBLEMS -------------- The CyberPower OP series don't offer direct voltage, charge, frequency and temperature readings. Instead, they will return a binary value that needs conversion to the actual value. The exact conversion needed is unknown at the time of this writing, hence an estimation was made based om readings from actual devices. This may (probably will) be off, possibly a lot. Unless you can tell us the exact mapping between values from the UPS and actual readings, don't bother to complain. We've done the best we can based on the limited information available. Remember, a UPS isn't a measuring instrument. AUTHORS ------- Arjen de Korte , Doug Reynolds SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/riello_ser.txt0000644000175000017500000000135312640444140013657 00000000000000RIELLO_SER(8) =========== NAME ---- riello_ser - Driver for Riello UPS Protocol UPS equipment SYNOPSIS -------- *riello_ser* -h *riello_ser* -a 'UPS_NAME' ['OPTIONS'] NOTE: This man page only documents the hardware-specific features of the riello_ser driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ riello_ser supports all recent Riello UPS, Aros UPS models which use the Riello UPS GPSER and SENTR protocols. Older Riello UPS products are not supported. AUTHOR ------ Massimo Zampieri SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_new_device.30000644000175000017500000000401712665610654014722 00000000000000'\" t .\" Title: nutscan_new_device .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_NEW_DEVICE" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_new_device \- Create a new nutscan_device_t structure\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_new_device(); .fi .SH "DESCRIPTION" .sp The \fBnutscan_new_device()\fR function allocates a new nutscan_device_type_t structure\&. .SH "RETURN VALUE" .sp The \fBnutscan_new_device()\fR function returns the newly allocated nutscan_device_type_t structure .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3) \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3) \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3) \fBnutscan_add_device_to_device\fR(3) nut-2.7.4/docs/man/upscli_disconnect.30000644000175000017500000000401112665610632014555 00000000000000'\" t .\" Title: upscli_disconnect .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_DISCONNECT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_disconnect \- disconnect from a UPS server .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_disconnect(UPSCONN_t *ups); .fi .SH "DESCRIPTION" .sp The \fBupscli_disconnect()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure, shuts down the connection to the server, and frees dynamic memory used by the state structure\&. The UPSCONN_t structure is no longer valid after this function is called\&. .sp This function must be called, or your program will leak memory and file descriptors\&. .SH "RETURN VALUE" .sp The \fBupscli_disconnect()\fR function returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_connect\fR(3), \fBupscli_fd\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/clone.80000644000175000017500000001212212640476500012151 00000000000000'\" t .\" Title: clone .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "CLONE" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" clone \- UPS driver clone .SH "NOTE" .sp This man page only documents the specific features of the clone driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "DESCRIPTION" .sp This driver, which sits on top of another driver socket, allows users to group clients to a particular outlet of a device and deal with this output as if it was a normal UPS\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following settings: .PP \fBload\&.off\fR=\fIcommand\fR .RS 4 Recommended\&. Set the command on the "real" UPS driver that will be used to switch off the outlet\&. You need both \fBload\&.off\fR and \fBload\&.on\fR in order to power cycle the outlet\&. Otherwise, shutting down the clients powered by an outlet is a one way street (see IMPORTANT)\&. .RE .PP \fBload\&.on\fR=\fIcommand\fR .RS 4 Recommended\&. Set the command on the "real" UPS driver that will be used to switch on the outlet\&. You need both \fBload\&.off\fR and \fBload\&.on\fR in order to power cycle the outlet\&. Otherwise, shutting down the clients powered by an outlet is a one way street (see IMPORTANT)\&. .RE .PP \fBload\&.status\fR=\fIvalue\fR .RS 4 Recommended\&. Set the variable on the "real" UPS driver that will be used to indicate the outlet status (i\&.e\&. on/off)\&. If not specified, the clone driver will attempt to keep track of the outlet status, but this is less reliable\&. .RE .PP \fBoffdelay\fR=\fInum\fR .RS 4 Set the timer (in seconds) before the outlet is turned off after the shutdown condition (OB LB) for this outlet is met or a command to shutdown was issued\&. Defaults to 120 seconds\&. .RE .PP \fBondelay\fR=\fInum\fR .RS 4 Set the timer (in seconds) for the outlet to switch on in case the power returns after the oulet has been switched off\&. Defaults to 30 seconds\&. .RE .PP \fBmincharge\fR=\fIvalue\fR .RS 4 Set the remaining battery level when the clone UPS switches to LB (percent)\&. .RE .PP \fBminruntime\fR=\fIvalue\fR .RS 4 Set the remaining battery runtime when the clone UPS switches to LB (seconds)\&. .RE .SH "IMPLEMENTATION" .sp The port specification in the \fBups.conf\fR(5) reference the driver socket that the "real" UPS driver is using\&. For example: .sp .if n \{\ .RS 4 .\} .nf [realups] driver = usbhid\-ups port = auto .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf [clone\-outlet\-1] driver = clone port = usbhid\-ups\-realups load\&.on = outlet\&.1\&.load\&.on load\&.off = outlet\&.1\&.load\&.off load\&.status = outlet\&.1\&.status [\&.\&.\&.] .fi .if n \{\ .RE .\} .SH "IMPORTANT" .sp Unlike a real UPS, you should \fBnot\fR configure a upsmon master for this driver\&. When a upsmon master sees the OB LB flags and tells the upsd server it is OK to initiate the shutdown sequence, the server will latch the FSD status and it will not be possible to restart the systems connected without restarting the upsd server\&. .sp This will be a problem if the power returns after the clone UPS initiated the shutdown sequence on it\(cqs outlet, but returns before the real UPS begins shutting down\&. The solution is in the clone driver, that will insert the FSD flag if needed without the help of a upsmon master\&. .SH "CAVEATS" .sp The clone UPS will follow the status on the real UPS driver\&. You can only make the clone UPS shutdown earlier than the real UPS driver, not later\&. If the real UPS driver initiates a shutdown, the clone UPS driver will immediately follow\&. .sp Be aware that the commands to shutdown/restart an outlet on the real UPS drivers are not affected, so if you tell the real UPS driver to shutdown the outlet of the clone UPS driver, your clients will lose power without warning\&. .SH "AUTHOR" .sp Arjen de Korte .SH "SEE ALSO" .sp \fBupscmd\fR(1), \fBupsrw\fR(1), \fBups.conf\fR(5), \fBnutupsdrv\fR(8) .SS "Internet Resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscli_list_start.30000644000175000017500000000745212665610636014634 00000000000000'\" t .\" Title: upscli_list_start .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_LIST_START" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_list_start \- begin multi\-item retrieval from a UPS .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_list_start(UPSCONN_t *ups, unsigned int numq, const char **query) .fi .SH "DESCRIPTION" .sp The \fBupscli_list_start()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure, and the pointer \fIquery\fR to an array of \fInumq\fR query elements\&. It builds a properly\-formatted request from those elements and transmits it to \fBupsd\fR(8)\&. .sp Upon success, the caller must call \fBupscli_list_next\fR(3) to retrieve the elements of the list\&. Failure to retrieve the list will most likely result in the client getting out of sync with the server due to buffered data\&. .SH "USES" .sp This function implements the "LIST" command in the protocol\&. As a result, you can use it to request many different things from the server\&. Some examples are: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LIST UPS .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LIST VAR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LIST RW .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LIST CMD .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LIST ENUM .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} LIST RANGE .RE .SH "QUERY FORMATTING" .sp To see the list of variables on a UPS called \fIsu700\fR, the protocol command would be LIST VAR su700\&. To start that list with this function, you would populate query and numq as follows: .sp .if n \{\ .RS 4 .\} .nf unsigned int numq; const char *query[2]; .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf query[0] = "VAR"; query[1] = "su700"; numq = 2; .fi .if n \{\ .RE .\} .sp All escaping of special characters and quoting of elements with spaces are handled for you inside this function\&. .SH "ERROR CHECKING" .sp This function checks the response from \fBupsd\fR(8) against your query\&. If it is not starting a list, or is starting the wrong type of list, it will return an error code\&. .sp When this happens, \fBupscli_upserror\fR(3) will return UPSCLI_ERR_PROTOCOL\&. .SH "RETURN VALUE" .sp The \fBupscli_list_start()\fR function returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/asciidoc.conf0000644000175000017500000000164312640473702013414 00000000000000## Borrowed from 'linkgit' in the Git distribution. ## linkman: macro # # Usage: linkman:command[manpage-section] # # Note, {0} is the manpage section, while {target} is the command. # # Show NUT link as: (
); if section is defined, else just show # the command. [macros] (?su)[\\]?(?Plinkman):(?P\S*?)\[(?P.*?)\]= ifdef::backend-docbook[] [linkman-inlinemacro] {0%{target}} {0#} {0#{target}{0}} {0#} endif::backend-docbook[] ifdef::backend-xhtml11[] [linkman-inlinemacro] {target}{0?({0})} # Override HTML footer, to include NUT version [footer-text] Last updated {docdate} {doctime} -- Network UPS Tools {nutversion} # Format-detection to prevent smartphones from being too smart [+docinfo] endif::backend-xhtml11[] nut-2.7.4/docs/man/nut-ipmipsu.txt0000644000175000017500000000553012640443572014023 00000000000000NUT-IPMIPSU(8) ============== NAME ---- nut-ipmipsu - Driver for IPMI Power Supply Units (PSU) SYNOPSIS -------- *nut-ipmipsu* -h *nut-ipmipsu* -a 'PSU_NAME' ['OPTIONS'] NOTE: This driver is experimental, and still a work-in-progress. Feedback is encouraged. NOTE: This man page only documents the hardware-specific features of the nut-ipmipsu driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver should support a wide range of PSUs through local IPMI interface. nut-ipmipsu currently use the GNU FreeIPMI project, for IPMI implementation. EXTRA ARGUMENTS --------------- This driver doesn't support any optional settings. INSTALLATION ------------ This driver is not built by default. You can build it by using "configure --with-ipmi=yes". You also need to give proper permissions on the local IPMI device file (/dev/ipmi0 for example) to allow the NUT user to access it. An udev rules file (nut-ipmipsu.rules) is provided and automatically installed on udev enabled system. This file is generally installed in /etc/udev/rules.d/ or /lib/udev/rules.d/ on newer systems, to address the permission settings problem. For more information, refer to nut/scripts/udev/README. INSTANT COMMANDS ---------------- This driver doesn't support any instant commands. IMPLEMENTATION -------------- The "port" value is used to identify the PSU. For instance, to target FRU 0x2, use the following in *ups.conf*: [pdu] driver = nut-ipmipsu port = id2 This driver will report various information related to a PSU, including: - manufacturer, model, serial and part numbers, - nominal voltage and frequency, - actual current and voltage, - status of the PSU: * 'OL' means that the PSU is present and providing power, * 'OFF' means that the PSU is present but not providing power (power cable removed), * 'stale' (no data) means that the PSU is not present (ie physically removed). Here is an example output for a Dell r610 server: device.mfr: DELL device.mfr.date: 01/05/11 - 08:51:00 device.model: PWR SPLY,717W,RDNT device.part: 0RN442A01 device.serial: CN179721130031 device.type: psu driver.name: nut-ipmipsu driver.parameter.pollinterval: 2 driver.parameter.port: id2 driver.version: 2.6.1-3139M driver.version.data: IPMI PSU driver driver.version.internal: 0.01 input.current: 0.20 input.frequency.high: 63 input.frequency.low: 47 input.voltage: 232.00 input.voltage.maximum: 264 input.voltage.minimum: 90 ups.id: 2 ups.realpower.nominal: 717 ups.status: OL ups.voltage: 12 AUTHOR ------ Arnaud Quette SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ GNU FreeIPMI home page: http://www.gnu.org/software/freeipmi/ nut-2.7.4/docs/man/nutclient_get_device_commands.30000644000175000017500000000003412665610643017114 00000000000000.so libnutclient_commands.3 nut-2.7.4/docs/man/everups.txt0000644000175000017500000000155312640443572013223 00000000000000EVERUPS(8) ========== NAME ---- everups - Driver for Ever UPS models NOTE ---- This man page only documents the hardware-specific features of the everups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver should recognize the NET *-DPC and AP *-PRO models. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. BUGS ---- This UPS can only switch off the load if it's running on battery. This means you may be vulnerable to power races if your shutdown scripts don't sleep and force a reboot. AUTHOR ------ Bartek Szady SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/libnutclient.txt0000644000175000017500000000270112640473702014220 00000000000000LIBNUTCLIENT(3) =============== NAME ---- libnutclient - Network UPS Tools high-level client access library SYNOPSIS -------- #include Refer to this file for more information. DESCRIPTION ----------- The Network UPS Tools (NUT) *nutclient* library provides a number of useful functions for programs to use when communicating with linkman:upsd[8]. It provides high-level representation of NUT data through client connection, devices, variables and commands. Unlike linkman:upsclient[3], all low-level protocol details are hidden. State is maintained across calls in an opaque structure called `NUTCLIENT_t`. Callers are expected to create one per client connection. These will be provided to most of the *nutclient* functions. The format of this structure is subject to change, and client programs must not reference elements within it directly. `NUTCLIENT_t` represents the common connection information. Derived versions exist for each connection type (`NUTCLIENT_TCP_t` for TCP connection; actually the unique connection type, `NUTCLIENT_TCP_t` can be passed as `NUTCLIENT_t` parameter). See the `nutclient.h` header for more information. ERROR HANDLING -------------- There is currently no specific mechanism around error handling. SEE ALSO -------- linkman:libnutclient_devices[3] linkman:libnutclient_commands[3] linkman:libnutclient_general[3] linkman:libnutclient_misc[3] linkman:libnutclient_tcp[3] linkman:libnutclient_variables[3] nut-2.7.4/docs/man/upsset.cgi.80000644000175000017500000001071112665610626013145 00000000000000'\" t .\" Title: upsset.cgi .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSSET\&.CGI" "8" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsset.cgi \- Web\-based UPS administration program .SH "SYNOPSIS" .sp \fBupsset\&.cgi\fR .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp As a CGI program, this should be invoked through your web server\&. If you run it from the command line, it will sit there until you give it input resembling a POST request\&. .sp .5v .RE .SH "DESCRIPTION" .sp \fBupsset\&.cgi\fR lets you access many administrative functions within the UPS software from your web browser\&. You can change settings and invoke instant commands where available\&. .SH "CHANGING SETTINGS" .sp Some UPS hardware allows you to change certain variables to other values\&. To see what\(cqs available, pick a UPS from the chooser and select "settings", then select "View" to update the page\&. .sp You should see a list of items with the descriptions on the left side and the possible options or input spaces on the right\&. After changing something, be sure to "Save changes" to update the values in your UPS\&. .sp If your UPS doesn\(cqt support any read/write variables, there will be nothing to do on this page\&. .sp Setting values in read/write variables can also be done from the command line with \fBupsrw\fR(8)\&. .SH "INSTANT COMMANDS" .sp Some UPS hardware also has provisions for performing certain actions at the user\(cqs command\&. These include battery tests, battery calibration, front panel tests (beep!) and more\&. To access this section, do as above, but pick "Commands" as the function\&. .sp If your UPS supports any instant commands, they will be listed in a chooser widget\&. Pick the one you like and "Issue command" to make it happen\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp some dangerous commands like "Turn off load" may not happen right away\&. This is a feature, not a bug\&. .sp .5v .RE .sp The apcsmart driver and some others require that you send this command twice within a short window in order to make it happen\&. This is to keep you from accidentally killing your systems by picking the wrong one\&. .sp To actually turn off the load, you have to send the command once, then send it again after 3 seconds elapse but before 15 seconds pass\&. If you do it too quickly or slowly, you have to wait at least 3 seconds but not 15 seconds again\&. .sp You can also invoke instant commands from the command line with \fBupscmd\fR(8)\&. .SH "ACCESS CONTROL" .sp upsset will only talk to \fBupsd\fR(8) servers that have been defined in your \fBhosts.conf\fR(8)\&. If it complains about "Access to that host is not authorized", check your hosts\&.conf first\&. .SH "SECURITY" .sp upsset will not run until you convince it that your CGI directory has been secured\&. This is due to the possibility of someone using upsset to try password combinations against your \fBupsd\fR(8) server\&. .sp See the example upsset\&.conf file for more information on how you do this\&. The short explanation is\(emif you can\(cqt lock it down, don\(cqt try to run it\&. .SH "FILES" .sp \fBhosts.conf\fR(5), \fBupsset.conf\fR(5) .SH "SEE ALSO" .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/tripplite.txt0000644000175000017500000000315012640443572013541 00000000000000TRIPPLITE(8) ============ NAME ---- tripplite - Driver for Tripp-Lite SmartPro UPS equipment NOTE ---- This man page only documents the hardware-specific features of the tripplite driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver should work on the SmartPro line, including the SMART700 and SMART700SER. It only supports SmartPro models that communicate using the serial port. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *offdelay*='num':: Time to wait before the UPS is turned off after the kill power command is sent. The default value is 64 (in seconds). *rebootdelay*='num':: Set the timer before the UPS is cycled after the reboot command is sent. The default value is 64 (in seconds). *startdelay*='num':: Set the time that the UPS waits before it turns itself back on after a reboot command. The default value is 60 (in seconds). KNOWN ISSUES AND BUGS --------------------- Battery charge information may not be correct for all UPSes. It is tuned to be correct for a SMART700SER. Other models may not provide correct information. Information from the manufacturer would be helpful. AUTHORS ------- Rickard E. (Rik) Faith, Nicholas Kain SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ linkman:tripplitesu[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/liebert-esp2.txt0000644000175000017500000000157712640443572014035 00000000000000LIEBERT-ESP2(8) ============== NAME ---- liebert-esp2 - Driver for Liebert UPS, using the ESP-II serial protocol NOTE ---- This man page only documents the hardware-specific features of the liebert-esp2 driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This is an experimental driver. You have been warned. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in linkman:ups.conf[5]: *baudrate=*'num':: Set the speed of the serial connection - 1200, 2400 (default), 4800, 9600 or 19200. AUTHOR ------ Richard Gregory , Arjen de Korte SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_get_device_command_description.30000644000175000017500000000003412665610643021334 00000000000000.so libnutclient_commands.3 nut-2.7.4/docs/man/genericups.txt0000644000175000017500000002676512640444140013702 00000000000000GENERICUPS(8) ============= NAME ---- genericups - Driver for contact-closure UPS equipment NOTE ---- This man page only documents the specific features of the genericups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports hardware from many different manufacturers as it only uses the very simplest of signaling schemes. Contact closure refers to a kind of interface where basic high/low signals are provided to indicate status. This kind of UPS can only report line power and battery status. This means that you will only get the essentials in ups.status: OL, OB, and LB. Anything else requires a smarter UPS. CABLING ------- Cabling is different for every kind of UPS. See the table below for information on what is known to work with a given UPS type. EXTRA ARGUMENTS --------------- This driver supports the following settings in the linkman:ups.conf[5]: upstype='type':: Required. Configures the driver for a specific kind of UPS. See the <<_ups_types,UPS Types>> section below for more information on which entries are available. mfr='string':: Optional. The very nature of a generic UPS driver sometimes means that the stock manufacturer data has no relation to the actual hardware that is attached. With the `mfr` setting, you can change the value that is seen by clients that monitor this UPS. model='string':: Optional. This is like `mfr` above, but it overrides the model string instead. serial='string':: Optional. This is like `mfr` above and intended to record the identification string of the UPS. It is titled "serial" because usually this string is referred to as the serial number. sdtime='value':: Optional. The driver will sleep for this many seconds after setting the shutdown signal. This is necessary for some hardware which requires a sustained level to activate the shutdown sequence. + The default behavior of the driver is to exit immediately. If this doesn't reliably trigger a shutdown in your UPS hardware, use this setting to give it more time to react. NOTE: very large values for +sdtime+ may create warnings from upsdrvctl if it gets tired of waiting for the driver to return. CUSTOM CONFIGURATIONS --------------------- You may override the values for CP, OL, LB, and SD by defining them in the linkman:ups.conf[5] after the upstype setting. For example, to set the cable power to DTR and the low battery value to DCD, it would look like this: CP = DTR LB = DCD Recognized values for input lines are CTS, DCD, and RNG. Recognized values for output lines are DTR, RTS, and ST. See below for more about what these signals mean. These values may be negated for active low signals. That is, "LB=-DCD" recognizes a low battery condition when DCD is not held high. TYPE INFORMATION ---------------- The essence of a UPS definition in this driver is how it uses the serial lines that are available. These are the abbreviations you will see below: OL:: On line (no power failure) (opposite of OB - on battery) LB:: Low battery SD:: Shutdown load CP:: Cable power (must be present for cable to have valid reading) CTS:: Clear to Send. Received from the UPS. RTS:: Ready to Send. Sent by the PC. DCD:: Data Carrier Detect. Received from the UPS. RNG:: Ring indicate. Received from the UPS. DTR:: Data Terminal Ready. Sent by the PC. ST:: Send a BREAK on the transmit data line A "-" in front of a signal name (like -RNG) means that the indicated condition is signaled with an active low signal. For example, [LB=-RNG] means the battery is low when the ring indicate line goes low, and that the battery is OK when that line is held high. UPS TYPES --------- 0 = UPSonic LAN Saver 600 [CP=DTR+RTS] [OL=-CTS] [LB=DCD] [SD=DTR] 1 = APC Back-UPS/Back-UPS Pro/Smart-UPS with 940-0095A/C cable [CP=DTR] [OL=-RNG] [LB=DCD] [SD=RTS] 2 = APC Back-UPS/Back-UPS Pro/Smart-UPS with 940-0020B cable [CP=RTS] [OL=-CTS] [LB=DCD] [SD=DTR+RTS] Type 2 has also been reported to work with the 940-0020C cable. 3 = PowerTech Comp1000 with DTR cable power [CP=DTR] [OL=CTS] [LB=DCD] [SD=DTR+RTS] 4 = Generic RUPS Model [CP=RTS] [OL=CTS] [LB=-DCD] [SD=-RTS] 5 = Tripp Lite UPS with Lan2.2 interface (black 73-0844 cable) [CP=DTR] [OL=CTS] [LB=-DCD] [SD=DTR+RTS] 6 = Best Patriot with INT51 cable [CP=DTR] [OL=CTS] [LB=-DCD] [SD=RTS] 7 = CyberPower Power99 Also Upsonic Power Guardian PG-500, Belkin Belkin Home Office, F6H350-SER, F6H500-SER, F6H650-SER, Eaton Management Card Contact - Config3 with cable 66033 (shutdown does not work) [CP=RTS] [OL=CTS] [LB=-DCD] [SD=DTR] 8 = Nitram Elite 500 [CP=DTR] [OL=CTS] [LB=-DCD] [SD=???] 9 = APC Back-UPS/Back-UPS Pro/Smart-UPS with 940-0023A cable [CP=none] [OL=-DCD] [LB=CTS] [SD=RTS] 10 = Victron Lite with crack cable [CP=RTS] [OL=CTS] [LB=-DCD] [SD=DTR] 11 = Powerware 3115 [CP=DTR] [OL=-CTS] [LB=-DCD] [SD=ST] 12 = APC Back-UPS Office with 940-0119A cable [CP=RTS] [OL=-CTS] [LB=DCD] [SD=DTR] 13 = RPT Repoteck RPT-800A/RPT-162A [CP=DTR+RTS] [OL=DCD] [LB=-CTS] [SD=ST] 14 = Online P-series [CP=DTR] [OL=DCD] [LB=-CTS] [SD=RTS] 15 = Powerware 5119, 5125 [CP=DTR] [OL=CTS] [LB=-DCD] [SD=ST] 16 = Nitram Elite 2002 [CP=DTR+RTS] [OL=CTS] [LB=-DCD] [SD=???] 17 = PowerKinetics 9001 [CP=DTR] [OL=CTS] [LB=-DCD] [SD=???] 18 = TrippLite Omni 450LAN with Martin's cabling [CP=DTR] [OL=CTS] [LB=DCD] [SD=none] 19 = Fideltronic Ares Series [CP=DTR] [OL=CTS] [LB=-DCD] [SD=RTS] 20 = Powerware 5119 RM [CP=DTR] [OL=-CTS] [LB=DCD] [SD=ST] Check docs/cables/powerware.txt 21 = Generic RUPS 2000 (Megatec M2501 cable) [CP=RTS] [OL=CTS] [LB=-DCD] [SD=RTS+DTR] 22 = Gamatronic All models with alarm interface (also CyberPower SL series) [CP=RTS] [OL=CTS] [LB=-DCD] [SD=DTR] SIMILAR MODELS -------------- Many different UPS companies make models with similar interfaces. The RUPS cable seems to be especially popular in the "power strip" variety of UPS found in office supply stores. If your UPS works with an entry in the table above, but the model or manufacturer information don't match, don't despair. You can fix that easily by using the mfr and model variables documented above in your linkman:ups.conf[5]. TESTING COMPATIBILITY --------------------- If your UPS isn't listed above, you can try going through the list until you find one that works. There is a lot of cable and interface reuse in the UPS world, and you may find a match. To do this, first make sure nothing important is plugged into the outlets on the UPS, as you may inadvertently switch it off. Definitely make sure that the computer you're using is not plugged into that UPS. Plug in something small like a lamp so you know when power is being supplied to the outlets. Now, you can either attempt to make an educated guess based on the documentation your manufacturer has provided (if any), or just start going down the list. Step 1 ~~~~~~ Pick a driver to try from the list (genericups -h) and go to step 2. Step 2 ~~~~~~ Start the driver with the type you want to try - genericups -x upstype=n /dev/port Let upsd sync up (watch the syslog), and then run upsc to see what it found. If the STATUS is right (should be OL for on line), continue to <<_step_3,Step 3>>, otherwise go back to step 1. Alternatively, you can run genericups in debug mode - genericups -DDDDD -x upstype=n /dev/port In this mode it will be running in the foreground and continuously display the line and battery status of the UPS. Step 3 ~~~~~~ Disconnect the UPS from the wall/mains power. This is easiest if you have a switched outlet in between it and the wall, but you can also just pull the plug to test. The lamp should stay lit, and the status should switch to "OB". If the lamp went out or the status didn't go to "OB" within about 15 seconds, go to <<_step_1,Step 1>>. Otherwise, continue to <<_step_4,Step 4>>. Step 4 ~~~~~~ At this point, we know that OL and OB work. If nothing else beyond this point works, you at least know what your OL/OB value should be. Wait for the UPS to start complaining about a low battery. Depending on the size of your UPS battery and the lamp's bulb, this could take awhile. It should start complaining audibly at some point. When this happens, STATUS should show "OB LB" within 15 seconds. If not, go to <<_step_1,Step 1>>, otherwise continue to <<_step_5,Step 5>>. Step 5 ~~~~~~ So far: OL works, OB works, and LB works. With the UPS running on battery, run the genericups driver with the -k switch to shut it down. genericups -x upstype=n -k /dev/port If the UPS turns off the lamp, you're done. At this point, you have verified that the shutdown sequence actually does what you want. You can start using the genericups driver with this type number for normal operations. You should use your findings to add a section to your ups.conf. Here is a quick example: [myups] driver = genericups port = /dev/ttyS0 upstype = 1 Change the port and upstype values to match your system. NEW SUPPORT ----------- If the above testing sequence fails, you will probably need to create a new entry to support your hardware. All UPS types are determined from the table in the genericups.h file in the source tree. On a standard 9 pin serial port, there are 6 lines that are used as the standard "high/low" signal levels. 4 of them are incoming (to the PC, from the UPS), and the other 2 are outgoing (to the UPS, from the PC). The other 3 are the receive/transmit lines and the ground. Be aware that many manufacturers remap pins within the cable. If you have any doubts, a quick check with a multimeter should confirm whether the cable is straight-through or not. Another thing to keep in mind is that some cables have electronics in them to do special things. Some have resistors and transistors on board to change behavior depending on what's being supplied by the PC. SPECIFIC MODEL NOTES -------------------- These have been contributed by users of this driver. The Centralion CL series may power down the load if the driver starts up with the UPS running on battery as the default line settings contain the shutdown sequence. - Neil Muller The Tripp-Lite Internet Office 700 must be used with the black 73-0844 cable instead of the gray 73-0743 cable. This entry should work with any of their models with the Lan 2.2 interface - see the sticker by the DB9 connector on the UPS. - Stephen Brown Type 5 should work with the Tripp-Lite Lan 2.1 interface and the 73-0724 cable. This was tested with the OmniSmart 675 PNP on Red Hat 7.2. - Q Giese Types 7 and 10 should both work with the PhoenixTec A1000. BUGS ---- There is no way to reliably detect a contact-closure UPS. This means the driver will start up happily even if no UPS is detected. It also means that if the connection between the UPS and computer is interrupted, you may not be able to sense this in software. Most contact-closure UPSes will not power down the load if the line power is present. This can create a race when using slave linkman:upsmon[8] systems. See the linkman:upsmon[8] man page for more information. The solution to both of these problems is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command. SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/tripplitesu.txt0000644000175000017500000000216412640443572014115 00000000000000TRIPPLITESU(8) ============== NAME ---- tripplitesu - Driver for Tripp-Lite SmartOnline (SU) UPS equipment NOTE ---- This man page only documents the hardware-specific features of the tripplitesu driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports the Tripp Lite SmartOnline family (via the serial port). EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *lowbatt*='num':: Set the low battery warning threshold in percent at which shutdown is initiated by linkman:upsmon[8]. By default, the UPS may not report low battery until there are only a few seconds left. Common values are around 25--30. AUTHOR ------ Allan N. Hessenflow SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ linkman:tripplite[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/asem.80000644000175000017500000000654112665610660012012 00000000000000'\" t .\" Title: asem .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "ASEM" "8" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" asem \- driver for UPS in ASEM PB1300 .SH "NOTE" .sp This man page only documents the hardware\-specific features of the \fBasem\fR driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The \fBasem\fR driver supports the UPS in ASEM PB1300 embedded PCs\&. Likely other I2C devices from the same manufacturer will work too, since this is a "custom" charger\&. .sp Seems that there are two versions of the charger\&. Older one is based on Max1667, newer one is a custom solution\&. Both are on I2C address 0x09\&. To be compatible with both versions, the driver just reads bit 15 of address 0x13 which yields online/on battery status\&. Battery monitor is a BQ2060 at address 0x0B\&. .SH "EXTRA ARGUMENTS" .sp The required parameter for this driver is the I2C bus name: .PP \fBport\fR=\fIdev\-node\fR .RS 4 On the Asem PB1300, this should be /dev/i2c\-7 for the i801 SMBUS adapter\&. .RE .sp This driver also supports the following optional settings: .PP \fBlb\fR=\fInum\fR .RS 4 Set the low battery threshold to \fInum\fR volts\&. .RE .PP \fBhb\fR=\fInum\fR .RS 4 Set the high battery threshold to \fInum\fR volts\&. .RE .SH "INSTALLATION" .sp This driver is specific to the Linux I2C API, and requires the lm_sensors libi2c\-dev or its equivalent to compile\&. .sp Beware that the SystemIO memory used by the I2C controller is reserved by ACPI\&. If only a native I2C driver (e\&.g\&. i2c_i801, as of 3\&.5\&.X Linux kernels) is available, then you\(cqll need to relax the ACPI resources check\&. For example, you can boot with the acpi_enforce_resources=lax option\&. .SH "KNOWN ISSUES AND BUGS" .sp The driver shutdown function is not implemented, so other arrangements must be made to turn off the UPS\&. .SH "AUTHORS" .sp Giuseppe Corbelli .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp PB1300 specifications: http://www\&.asem\&.it/en/products/industrial\-automation/box\-pcs/performance/pb1300/ .sp BQ2060 datasheet: http://www\&.ti\&.com/lit/ds/symlink/bq2060\&.pdf .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upsstats.html.50000644000175000017500000002117412665610626013714 00000000000000'\" t .\" Title: upsstats.html .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSSTATS\&.HTML" "5" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsstats.html \- HTML template for Network UPS Tools upsstats .SH "DESCRIPTION" .sp This file is used by \fBupsstats.cgi\fR(8) to generate status pages\&. Certain commands are recognized, and will be replaced with various status elements on the fly\&. .SH "FORMATTING" .sp Commands can be placed anywhere on a line, but must start and end with @\&. Any extra characters before or after the commands will be passed through unchanged\&. It is allowed to use more than one command on a single line, as long as each command has its own start and end character\&. If you need to use the @ sign, use @ to prevent it from being treated as a start character\&. .SH "BLOCK CONTROL" .sp Some commands begin blocks \- sections of the template that will be included, excluded, or repeated depending on certain parameters\&. .SS "BLOCK CONTROL \- ITERATION" .PP \fB@FOREACHUPS@\fR .RS 4 Starts a block that will be repeated for each MONITOR directive in the \fBhosts.conf\fR(5)\&. This is how you can generate pages that monitor all of your systems simultaneously\&. .RE .PP \fB@ENDFOR@\fR .RS 4 Ends a FOREACHUPS block\&. .RE .SS "BLOCK CONTROL \- MATCHING SPECIFIC CASES" .PP \fB@IFSUPP \fR\fB\fIvar\fR\fR@* .RS 4 Starts a block that will only be printed if the variable var is supported by the current UPS\&. This is generally used to suppress "not supported" messages by avoiding the label and variable call entirely\&. .RE .PP \fB@IFEQ\fR \fIvar\fR \fIvalue\fR\fB@\fR .RS 4 Starts a block if the value returned from the variable \fIvar\fR matches \fIvalue\fR\&. .RE .PP \fB@IFBETWEEN\fR \fIvarlow\fR \fIvarhigh\fR \fIvarvalue\fR\fB@\fR .RS 4 Starts a block if the value returned by the variable \fIvarvalue\fR is between the values returned by the variables \fIvarlow\fR and \fIvarhigh\fR\&. .RE .PP \fB@ELSE@\fR .RS 4 If the previous IF\-command did not match, perform this instead\&. .RE .PP \fB@ENDIF@\fR .RS 4 Ends an IF/ELSE\-block\&. .RE .SS "BLOCK CONTROL \- ADVANCED EXPRESSIONS" .sp Even though the parser is pretty limited, it\(cqs still possible to create rather advanced expressions\&. The key to this is the fact that multiple block control commands are AND:ed\&. This is illustrated with an example (more examples are available in upsstats\&.html)\&. .sp .if n \{\ .RS 4 .\} .nf @IFSUPP ambient\&.humidity@ @IFSUPP ambient\&.temperature@ This UPS knows both ambient temperature and humidity\&. @ELSE@ @IFSUPP ambient\&.humidity@ This UPS only knows ambient humidity\&. @ELSE@ @IFSUPP ambient\&.temperature@ This UPS only knows ambient temperature\&. @ELSE@ This UPS knows nothing, how annoying\&. @ENDIF@ .fi .if n \{\ .RE .\} .SH "OTHER COMMANDS" .PP \fB@AMBTEMP@\fR .RS 4 Insert the ambient temperature in the current temperature scale\&. .RE .PP \fB@DATE\fR \fIformat\fR\fB@\fR .RS 4 Insert the current date and time\&. The format string is passed to strftime, so almost anything is possible\&. See \fBstrftime\fR(3) for possible values\&. .RE .PP \fB@DEGREES@\fR .RS 4 Insert the entity for degrees (\(de) and either C or F depending on the current temperature scale\&. .RE .PP \fB@HOST@\fR .RS 4 Insert the designation of the host being monitored, like myups@localhost\&. .RE .PP \fB@HOSTDESC@\fR .RS 4 Insert the hout\(cqs description from \fBhosts.conf\fR(5)\&. .RE .PP \fB@HOSTLINK@\fR .RS 4 Insert a link to upsstats\&.cgi with the "host" variable set to the current UPS\&. This is only useful within a FOREACHUPS block\&. .RE .PP \fB@IMG\fR \fIvarname\fR \fB@\fR .RS 4 Insert an IMG SRC to \fBupsimage.cgi\fR(8) for one of these status variables: .PP battery\&.charge .RS 4 Battery charge \- a percentage .RE .PP battery\&.voltage .RS 4 The charge on the battery in volts .RE .PP input\&.frequency .RS 4 Incoming utility frequency (Hz) .RE .PP input\&.voltage .RS 4 Incoming utility voltage .RE .PP input\&.L1\-L2\&.voltage .RS 4 Incoming voltage, L1\-L2 (3phase) .RE .PP input\&.L2\-L3\&.voltage .RS 4 Incoming voltage, L2\-L3 (3phase) .RE .PP input\&.L3\-L1\&.voltage .RS 4 Incoming voltage, L3\-L1 (3phase) .RE .PP output\&.frequency .RS 4 Outgoing utility frequency (Hz) .RE .PP output\&.voltage .RS 4 Outgoing voltage (from the UPS) .RE .PP output\&.L1\-L2\&.voltage .RS 4 Outgoing voltage, L1\-L2 (3phase) .RE .PP output\&.L2\-L3\&.voltage .RS 4 Outgoing voltage, L2\-L3 (3phase) .RE .PP output\&.L3\-L1\&.voltage .RS 4 Outgoing voltage, L3\-L1 (3phase) .RE .PP output\&.L1\&.power\&.percent .RS 4 UPS load, L1 (3phase) .RE .PP output\&.L2\&.power\&.percent .RS 4 UPS load, L2 (3phase) .RE .PP output\&.L3\&.power\&.percent .RS 4 UPS load, L3 (3phase) .RE .PP ups\&.load .RS 4 UPS load \- percentage .RE .PP ups\&.temperature .RS 4 UPS temperature .RE .RE .sp \fIextra\fR is where you can put additional definitions\&. Right now the valid definitions are colors for various parts of the bars drawn by upsimage\&.cgi\&. Possible color names are: .PP back_col .RS 4 background color .RE .PP scale_num_col .RS 4 scale number color .RE .PP summary_col .RS 4 summary color (number at the bottom) .RE .PP ok_zone_maj_col .RS 4 major scale color for the normal ("ok") zone .RE .PP ok_zone_min_col .RS 4 minor scale color for the normal ("ok") zone .RE .PP neutral_zone_maj_col .RS 4 major scale color for the neutral zone .RE .PP neutral_zone_min_col .RS 4 minor scale color for the neutral zone .RE .PP warn_zone_maj_col .RS 4 major scale color for the warning zone .RE .PP warn_zone_min_col .RS 4 minor scale color for the warning zone .RE .PP bar_col .RS 4 the color of the bar in the middle .RE .sp All colors are hex triplets \- 0xff0000 is red, 0x00ff00 is green, and 0x0000ff is blue\&. .sp Examples: .sp .if n \{\ .RS 4 .\} .nf @IMG battery\&.charge@ @IMG battery\&.charge back_col=0xff00ff bar_col=0xaabbcc@ @IMG input\&.voltage ok_zone_maj_col=0x123456@ .fi .if n \{\ .RE .\} .PP \fB@REFRESH@\fR .RS 4 Insert the META header magic for refreshing the page if that variable has been set by the browser\&. This needs to be in the HEAD section of the page\&. .RE .PP \fB@STATUS@\fR .RS 4 Expand the abbreviations in the ups\&.status variable \- OL becomes "On line", OB becomes "On battery", and so on\&. .RE .PP \fB@STATUSCOLOR@\fR .RS 4 Insert red, green, or yellow color triplets depending on the severity of the current UPS status\&. Normal operations are green, warnings like voltage trim/boost or "off" are yellow, and other events like being on battery or having a low battery are red\&. .RE .PP \fB@VAR\fR \fIvarname\fR\fB@\fR .RS 4 Insert the current value of the status variable varname on the host being monitored, or "Not supported"\&. .RE .PP \fB@RUNTIME@\fR .RS 4 Inserts the current runtime, in hh:mm:ss format\&. .RE .PP \fB@TEMPC@\fR .RS 4 Use the Celsius scale for temperature data (default)\&. .RE .PP \fB@TEMPF@\fR .RS 4 Use the Fahrenheit scale for temperature data\&. .RE .PP \fB@UPSTEMP@\fR .RS 4 Insert the UPS temperature in the current scale\&. .RE .PP \fB@BATTTEMP@\fR .RS 4 Insert the battery temperature in the current scale\&. .RE .PP \fB@UTILITYCOLOR@\fR .RS 4 Obsoleted\&. Use IFBETWEEN instead (see example in upsstats\&.html)\&. .RE .PP \fB@VERSION@\fR .RS 4 Insert the version number of the software\&. .RE .SH "OTHER TEMPLATES" .sp \fBupsstats.cgi\fR(8) will also open a file called upsstats\-single\&.html if you call it with "host=" set in the URL\&. That file uses the same rules and techniques documented here\&. .SH "SEE ALSO" .sp \fBupsstats.cgi\fR(8), \fBupsimage.cgi\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/snmp-ups.80000644000175000017500000001543112640476516012650 00000000000000'\" t .\" Title: snmp-ups .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "SNMP\-UPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" snmp-ups \- Multi\-MIB Driver for SNMP UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the snmp\-ups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The snmp\-ups driver automatically detects and supports a wide range of devices by loading various MIBS: .PP \fBietf\fR .RS 4 UPS that is RFC 1628 (UPS MIB) compliant, e\&.g\&. MGE UPS SYSTEMS, Liebert, perhaps others (default) .RE .PP \fBmge\fR .RS 4 MGE UPS SYSTEMS and MGE Office Protection Systems devices with SNMP cards (ref 66062, 66045, 66074 and 66244) .RE .PP \fBapcc\fR .RS 4 APC AP9605, AP9606, AP9617, and AP9618 APC network management cards, as well as any others supporting the APC POWERNET MIB .RE .PP \fBnetvision\fR .RS 4 Socomec Sicon UPS with Netvision Web/SNMP management card/external box .RE .PP \fBpw\fR .RS 4 Powerware devices with ConnectUPS SNMP cards .RE .PP \fBpxgx_ups\fR .RS 4 Eaton devices with Power Xpert Gateway UPS Card .RE .PP \fBaphel_genesisII\fR .RS 4 Eaton Powerware ePDU Monitored .RE .PP \fBaphel_revelation\fR .RS 4 Eaton Powerware ePDU Managed .RE .PP \fBraritan\fR .RS 4 Various Raritan PDUs .RE .PP \fBbaytech\fR .RS 4 Various BayTech PDUs .RE .PP \fBcpqpower\fR .RS 4 HP/Compaq AF401A management card, perhaps others .RE .PP \fBcyberpower\fR .RS 4 Cyberpower RMCARD201\&. Should also support RMCARD100 (net version), RMCARD202 and RMCARD301 .RE .PP \fBhuawei\fR .RS 4 Huawei UPS5000\-E, perhaps others .RE .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBmibs\fR=\fIname\fR .RS 4 Set MIB compliance (default=auto, allowed entries: refer to SUPPORTED HARDWARE above)\&. With "auto", the driver will try a select set of SNMP objects until it finds one that the device responds to\&. Note that since NUT 2\&.6\&.2, snmp\-ups has a new method that uses sysObjectID (which is a pointer to the prefered MIB of the device) to detect supported devices\&. This renders void the use of "mibs" option\&. .RE .PP \fBcommunity\fR=\fIname\fR .RS 4 Set community name (default = public)\&. Note that a RW community name is required to change UPS settings (as for a powerdown)\&. .RE .PP \fBsnmp_version\fR=\fIversion\fR .RS 4 Set SNMP version (default = v1, allowed: v2c, v3) .RE .PP \fBsnmp_retries\fR=\fIretries\fR .RS 4 Specifies the number of Net\-SNMP retries to be used in the requests (default=5) .RE .PP \fBsnmp_timeout\fR=\fItimeout\fR .RS 4 Specifies the Net\-SNMP timeout in seconds between retries (default=1) .RE .PP \fBpollfreq\fR=\fIvalue\fR .RS 4 Set polling frequency in seconds, to reduce network flow (default=30) .RE .PP \fBnotransferoids\fR .RS 4 Disable the monitoring of the low and high voltage transfer OIDs in the hardware\&. This will remove input\&.transfer\&.low and input\&.transfer\&.high from the list of variables\&. This should only be used on APCC Symmetra equipment which has strangeness in the three\-phase power reporting\&. .RE .PP \fBsecLevel\fR=\fIvalue\fR .RS 4 Set the securityLevel used for SNMPv3 messages (default=noAuthNoPriv, allowed: authNoPriv,authPriv) .RE .PP \fBsecName\fR=\fIvalue\fR .RS 4 Set the securityName used for authenticated SNMPv3 messages (no default) .RE .PP \fBauthPassword\fR=\fIvalue\fR .RS 4 Set the authentication pass phrase used for authenticated SNMPv3 messages (no default) .RE .PP \fBprivPassword\fR=\fIvalue\fR .RS 4 Set the privacy pass phrase used for encrypted SNMPv3 messages (no default) .RE .PP \fBauthProtocol\fR=\fIvalue\fR .RS 4 Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5) .RE .PP \fBprivProtocol\fR=\fIvalue\fR .RS 4 Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES) .RE .SH "REQUIREMENTS" .sp You will need to install the Net\-SNMP package from http://www\&.net\-snmp\&.org/ before building this driver\&. .sp SNMP v3 also requires OpenSSL support from http://www\&.openssl\&.org\&. .SH "LIMITATIONS" .SS "Shutdown" .sp The shutdown sequence should be tested before relying on NUT to send a shutdown command to the UPS\&. The problem is that the host network stack may have been torn down by the time the driver is invoked to send the shutdown command\&. The driver attempts to send shutdown\&.return, shutdown\&.reboot, and load\&.off\&.delay commands to the UPS in sequence, stopping after the first supported command\&. .SH "INSTALLATION" .sp This driver is only built if the Net\-SNMP development files are present at configuration time\&. You can also force it to be built by using configure \-\-with\-snmp=yes before calling make\&. .SH "EXAMPLES" .sp The hostname of the UPS is specified with the "port" value in ups\&.conf: .sp .if n \{\ .RS 4 .\} .nf [snmpv1] driver = snmp\-ups port = snmp\-ups\&.example\&.com community = public snmp_version = v1 pollfreq = 15 desc = "Example SNMP v1 device" .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf [snmpv3] driver = snmp\-ups port = 166\&.99\&.224\&.132 snmp_version = v3 secLevel = authPriv secName = mysecurityname authPassword = myauthenticationpassphrase privPassword = myprivatepassphrase desc = "Example SNMP v3 device, with the highest security level" .fi .if n \{\ .RE .\} .SH "AUTHORS" .sp Arnaud Quette, Dmitry Frolov .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "NUT SNMP Protocols Library" .sp Available at: http://www\&.networkupstools\&.org/protocols/snmp/ .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/belkinunv.80000644000175000017500000003243412640476475013071 00000000000000'\" t .\" Title: belkinunv .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BELKINUNV" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" belkinunv \- Driver for Belkin "Universal UPS" and compatible .SH "NOTE" .sp This man page only documents the hardware\-specific features of the belkin driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The belkinunv driver is known to work with the Belkin Universal UPS models F6C800\-UNV and F6C120\-UNV, and is expected to work with other Belkin Universal UPS models\&. The driver only supports serial communication, not USB\&. .sp The Trust UPS and older Belkin units are not supported by this driver, and neither are the Belkin Home Office models (F6H500\-SER and so forth)\&. However, some Belkin models, such as the Regulator Pro, are supported by the \fBbelkin\fR(8) driver, and the Home Office models are supported using the \fBgenericups\fR(8) driver with upstype=7\&. .SH "SOFT SHUTDOWN WORKAROUND" .sp One problem with the Belkin Universal UPS is that it cannot enter a soft shutdown (shut down the load until AC power returns) unless the batteries are completely depleted\&. Thus, one cannot just shut off the UPS after operating system shutdown; it will not come back on when the power comes back on\&. Therefore, the belkinunv driver should never be used with the \fB\-k\fR option\&. Instead, the \fB\-x wait\fR option is provided as a workaround\&. .sp When called with the \fB\-x wait\fR option, \fBbelkinunv\fR behaves as a standalone program (i\&.e\&., it does not fork into the background)\&. It performs one simple task: it connects to the UPS, waits for AC power to return, and then exits with status 0\&. .sp This is meant to be used in a shutdown script as follows: during a shutdown, after all filesystems have been remounted read\-only, and just before the system would normally be halted: check /etc/killpower (or similar) to see if this shutdown was caused by \fBupsmon\fR(8), and if yes, call \fBbelkinunv \-x wait\fR\&. If AC power comes back on, \fBbelkinunv\fR exits, and things should be arranged so that the system reboots in this case\&. If AC power does not come back on, the UPS will eventually run out of batteries, kill the computer\(cqs power supply, and go into soft shutdown mode, which means everything will reboot properly when the power returns\&. In either case, a deadlock is avoided\&. .sp In addition, if an optional integer argument is given to the \fB\-x wait\fR option, this causes \fBbelkinunv\fR to wait not only for AC power to be present, but also for the battery charge to reach the given level\&. I use this as part of my startup scripts, to ensure that the batteries are sufficiently charged before the computer continues booting\&. This should be put very early in the startup script, before any filesystems are mounted read/write, and before any filesystem checks are performed\&. .sp Several other \fB\-x\fR options are provided to fine\-tune this behavior\&. See the options below for detailed descriptions\&. See the examples below for examples of how to use \fBbelkinunv\fR in shutdown and startup scripts\&. .SH "OPTIONS" .sp See also \fBnutupsdrv\fR(8) for generic options\&. Never use the \fB\-k\fR option with this driver; it does not work properly\&. .PP \fB\-x wait\fR[=\fIlevel\fR] .RS 4 When this option is used, \fBbelkinunv\fR does not fork into the background, but behaves as a standalone program\&. It connects to the UPS and waits until AC power is present\&. If \fIlevel\fR is specified, it also waits until the battery charge reaches at least the given level in percent\&. Then, and only then, \fBbelkinunv\fR exits\&. In addition, while \fBbelkinunv\fR runs in this mode, it displays a status line with information on the UPS status and battery level\&. This is intended for use in the computer\(cqs shutdown and startup scripts, as described under Soft Shutdown Workaround above\&. .RE .PP \fB\-x nohang\fR .RS 4 This option only has an effect when used in conjunction with the \fB\-x wait\fR option\&. It causes \fBbelkinunv\fR to exit if a connection with the UPS cannot be established or is lost, instead of retrying forever, which is the default behavior\&. The \fB\-x nohang\fR option should be used in a startup script, to ensure the computer remains bootable even if the UPS has been disconnected during the power failure (for instance, you attached your computer to a generator, carried it to a neighbor\(cqs house, or whatever)\&. .RE .PP \fB\-x flash\fR .RS 4 This option only has an effect when used in conjunction with the \fB\-x wait\fR option\&. It causes the UPS load to be shut off for a short time ("flashed") just after the AC power has returned and the requested battery level (if any) has been attained\&. This is useful if slaves are attached to this UPS; the flash will cause all of them to reboot\&. Note that, due to the design of the Belkin UPS hardware, the load shutdown lasts ca\&. 1\(em2 minutes; a shorter flash cannot be performed reliably\&. Also, the computers will reboot at the scheduled time, on battery power if necessary, even if AC power fails again in the meantime\&. This should not be a problem, as your startup scripts can catch this situation\&. .RE .PP \fB\-x silent\fR .RS 4 This option only has an effect when used in conjunction with the \fB\-x wait\fR option\&. It suppresses the status line which \fBbelkinunv\fR would normally print\&. .RE .PP \fB\-x dumbterm\fR .RS 4 This option only has an effect when used in conjunction with the \fB\-x wait\fR option\&. It changes the way in which \fBbelkinunv\fR prints its status line\&. Normally, terminal control sequences are used to overwrite the same line with new status information, each time the status is updated\&. This may not work on all terminals\&. If the \fB\-x dumbterm\fR option is given, each status update is written on a new line\&. .RE .SH "VARIABLES" .PP \fBbattery\&.charge\fR, \fBbattery\&.runtime\fR .RS 4 not supported by all hardware\&. .RE .PP \fBbattery\&.voltage\fR, \fBbattery\&.voltage\&.nominal\fR, \fBinput\&.frequency\fR, \fBinput\&.frequency\&.nominal\fR .RS 4 e\&.g\&. 60 for 60Hz .RE .PP \fBinput\&.sensitivity\fR .RS 4 writable: normal/medium/low .RE .PP \fBinput\&.transfer\&.high\fR .RS 4 writable: high transfer voltage point in V .RE .PP \fBinput\&.transfer\&.low\fR .RS 4 writable: low transfer voltage point in V .RE .PP \fBinput\&.voltage\fR, \fBinput\&.voltage\&.maximum\fR, \fBinput\&.voltage\&.minimum\fR, \fBinput\&.voltage\&.nominal\fR, \fBoutput\&.frequency\fR, \fBoutput\&.voltage\fR, \fBups\&.beeper\&.status\fR .RS 4 writable\&. Values: enabled/disabled/muted\&. This variable controls the state of the panel beeper\&. Enabled means sound when the alarm is present, disabled means never sound, and muted means the sound is temporarily disabled until the alarm would normally stop sounding\&. In the muted state, the beeper is automatically turned back on at the next event (AC failure, battery test, etc)\&. Also, the beeper can\(cqt be turned off during a critical event (low battery)\&. Note that not all UPS models support the "disabled" state\&. .RE .PP \fBups\&.firmware\fR, \fBups\&.load\fR, \fBups\&.model\fR, \fBups\&.power\&.nominal\fR .RS 4 e\&.g\&. 800 for an 800VA system .RE .PP \fBups\&.status\fR .RS 4 a list of flags; see the status flags below\&. .RE .PP \fBups\&.temperature\fR .RS 4 not supported by all hardware\&. .RE .PP \fBups\&.test\&.result\fR, \fBups\&.delay\&.restart\fR .RS 4 time to restart (read only) .RE .PP \fBups\&.delay\&.shutdown\fR .RS 4 time to shutdown (read only)\&. This is always a multiple of 60 seconds\&. .RE .PP \fBups\&.type\fR .RS 4 ONLINE/OFFLINE/LINEINT\&. This describes the basic layout of this UPS (for GUI clients which want to draw an animated picture of power flow)\&. An offline UPS has a direct connection from AC input to AC output, and also a connection from AC input to the battery, and from the battery to AC output\&. An online UPS lacks the direct connection from AC input to AC output, whereas a line interactive UPS lacks the connection from AC input to the battery\&. .RE .SH "COMMANDS" .PP \fBbeeper\&.enable, beeper\&.disable, beeper\&.mute\fR .RS 4 Enable, disable or mute the panel beeper\&. Note that if the beeper is muted, it is automatically turned back on at the next event (AC failure, battery test, etc)\&. Also, the beeper can\(cqt be turned muted during a critical event (low battery)\&. .RE .PP \fBreset\&.input\&.minmax\fR .RS 4 Reset the variables \fBinput\&.voltage\&.minimum\fR and \fBinput\&.voltage\&.maximum\fR\&. .RE .PP \fBshutdown\&.reboot\fR .RS 4 Shut down load immediately for about 1\(em2 minutes\&. .RE .PP \fBshutdown\&.reboot\&.graceful\fR .RS 4 After 40 second delay, shut down load for about 1\(em2 minutes\&. .RE .PP \fBshutdown\&.stayoff\fR .RS 4 Shut down load immediately and stay off\&. The only way it can be turned back on is by manually pressing the front panel button\&. .RE .PP \fBtest\&.battery\&.start, test\&.battery\&.stop\fR .RS 4 Start/stop 10 second battery test\&. .RE .PP \fBtest\&.failure\&.start, test\&.failure\&.stop\fR .RS 4 Start/stop "deep" battery test\&. .RE .SH "STATUS FLAGS" .PP \fBOB\fR .RS 4 load is on battery, including during tests .RE .PP \fBOFF\fR .RS 4 load is off .RE .PP \fBOL\fR .RS 4 load is online .RE .PP \fBACFAIL\fR .RS 4 AC failure\&. Note that this refers to the AC input, and thus it is not the same as "OB"\&. An AC failure can occur at any time, for instance, during a battery test, or when the UPS load is off\&. .RE .PP \fBOVER\fR .RS 4 overload .RE .PP \fBOVERHEAT\fR .RS 4 overheat .RE .PP \fBCOMMFAULT\fR .RS 4 UPS fault .RE .PP \fBLB\fR .RS 4 low battery .RE .PP \fBCHRG\fR .RS 4 charging .RE .PP \fBDEPLETED\fR .RS 4 the battery is depleted\&. When the UPS raises this flag, it simultaneously switches off the load\&. .RE .PP \fBRB\fR .RS 4 replace battery .RE .SH "EXAMPLES" .sp Here is an example for how \fBbelkinunv\fR should be used in a computer\(cqs shutdown script\&. These commands should go in the very last part of the shutdown script, after all file systems have been mounted read\-only, and just before the computer halts\&. Note that \fBbelkinunv\fR must be installed in a directory which is still readable at that point\&. .sp .if n \{\ .RS 4 .\} .nf # NEAR END OF SHUTDOWN SCRIPT: # if shutdown was caused by UPS, perform Belkin UPS workaround\&. if [ \-f /etc/killpower ] ; then echo "Waiting for AC power, or for UPS batteries to run out\&.\&.\&." /usr/bin/belkinunv \-x wait /dev/ttyS1 .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf # we get here if the power came back on\&. Reboot\&. echo "Power is back\&. Rebooting\&.\&.\&." reboot fi .fi .if n \{\ .RE .\} .sp And here is an example of how to use \fBbelkinunv\fR in the startup script\&. These commands should go near the beginning of the startup script, before any file systems are mounted read/write, and before any file system integrity checks are done\&. .sp .if n \{\ .RS 4 .\} .nf # NEAR BEGINNING OF STARTUP SCRIPT: # if we are recovering from a power failure, wait for the UPS to # charge to a comfortable level before writing anything to disk if [ \-f /etc/killpower ] ; then echo "Waiting for UPS battery charge to reach 60%\&.\&.\&." /usr/bin/belkinunv \-x wait=60 \-x nohang /dev/ttyS1 fi .fi .if n \{\ .RE .\} .SH "EXIT STATUS" .sp When used normally, \fBbelkinunv\fR forks into the background and its diagnostics are the same as for all NUT drivers, see \fBnutupsdrv\fR(8)\&. .sp When used with the \fB\-x wait\fR option, the exit status is normally \fB0\fR\&. If the \fB\-x nohang\fR option has also been specified, an exit status of \fB1\fR indicates that communication with the UPS was lost\&. If the \fB\-x flash\fR option has been specified, an exit status of \fB2\fR indicates that the timed shutdown has failed\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in \fBups.conf\fR(5)\&. .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} The documentation for the protocol used by this UPS: belkin\-universal\-ups\&.html .RE .SH "AUTHOR" .sp Peter Selinger nut-2.7.4/docs/man/usbhid-ups.80000644000175000017500000002412612640476522013147 00000000000000'\" t .\" Title: usbhid-ups .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "USBHID\-UPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" usbhid-ups \- Driver for USB/HID UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the usbhid\-ups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp This driver, formerly called \fInewhidups\fR, replaces the legacy \fIhidups\fR driver, which only supported Linux systems\&. .SH "SUPPORTED HARDWARE" .sp \fBusbhid\-ups\fR brings USB/HID UPS monitoring to NUT on all platform supporting USB through libusb\&. It should detect any UPS that uses the HID power device class, but the amount of data will vary depending on the manufacturer and model\&. .sp At the present time, usbhid\-ups supports: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} the newer Eaton USB models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} all MGE USB models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} all Dell USB models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} some APC models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} some Belkin models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} some Cyber Power Systems models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} some Powercom models, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} some TrippLite models\&. .RE .sp For a more complete list, refer to the NUT hardware compatibility list, available in the source distribution as data/drivers\&.list, or on the NUT website\&. You may use the "explore" driver option to gather information from HID UPSes which are not yet supported; see below for details\&. .sp This driver is known to work on: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} most Linux systems, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} FreeBSD (beta stage) and maybe other *BSD, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Darwin / Mac OS X, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Solaris 10\&. .RE .SH "EXTRA ARGUMENTS" .sp This driver also supports the following optional settings: .PP \fBoffdelay\fR=\fInum\fR .RS 4 Set the timer before the UPS is turned off after the kill power command is sent (via the \fB\-k\fR switch)\&. .sp The default value is 20 (in seconds)\&. Usually this \fBmust be lower\fR than \fIondelay\fR, but the driver will \fBnot\fR warn you upon startup if it isn\(cqt\&. .RE .PP \fBondelay\fR=\fInum\fR .RS 4 Set the timer for the UPS to switch on in case the power returns after the kill power command had been sent but before the actual switch off\&. This ensures the machines connected to the UPS are, in all cases, rebooted after a power failure\&. .sp The default value is 30 (in seconds)\&. Usually this \fBmust be greater\fR than offdelay, but the driver will \fBnot\fR warn you upon startup if it isn\(cqt\&. Some UPS\(cqes will restart no matter what, even if the power is (still) out at the moment this timer elapses\&. In that case, you could try if setting \fIondelay = \-1\fR in \fBups\&.conf\fR helps\&. .RE .PP \fBpollfreq\fR=\fInum\fR .RS 4 Set polling frequency, in seconds, to reduce the USB data flow\&. Between two polling requests, the driver will wait for interrupts (aka UPS notifications), which are data changes returned by the UPS by itself\&. This mechanism allow to avoid or reduce staleness message, due to the UPS being temporarily overloaded with too much polling requests\&. The default value is 30 (in seconds)\&. .RE .PP \fBpollonly\fR .RS 4 If this flag is set, the driver will ignore interrupts it receives from the UPS (not recommended, but needed if these reports are broken on your UPS)\&. .RE .PP \fBvendor\fR=\fIregex\fR, \fBproduct\fR=\fIregex\fR, \fBserial\fR=\fIregex\fR, \fBvendorid\fR=\fIregex\fR, \fBproductid\fR=\fIregex\fR .RS 4 Select a specific UPS, in case there is more than one connected via USB Each option specifies an extended regular expression (see regex(7)) that must match the UPS\(cqs entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4\-digit hexadecimal code for vendorid and productid\&. Try \fB\-DD\fR for finding out the strings to match\&. .sp Examples: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x vendor="Foo\&.Corporation\&.*" .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x vendorid=051d* (APC) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x product="\&.*(Smart|Back)\-?UPS\&.*" .RE .RE .PP \fBbus\fR=\fIregex\fR .RS 4 Select a UPS on a specific USB bus or group of busses\&. The argument is a regular expression that must match the bus name where the UPS is connected (e\&.g\&. bus="002", bus="00[2\-3]")\&. .RE .PP \fBexplore\fR .RS 4 With this option, the driver will connect to any device, including ones that are not yet supported\&. This must always be combined with the "vendorid" option\&. In this mode, the driver will not do anything useful except for printing debugging information (typically used with \-DD)\&. .RE .PP \fBmaxreport\fR .RS 4 With this option, the driver activates a tweak to workaround buggy firmware returning invalid HID report length\&. Some APC Back\-UPS units are known to have this bug\&. .RE .PP \fBinterruptonly\fR .RS 4 If this flag is set, the driver will not poll UPS\&. This also implies using of INPUT flagged objects\&. Some Powercom units need this option\&. .RE .PP \fBinterruptsize\fR=\fInum\fR .RS 4 Limit the number of bytes to read from interrupt pipe\&. For some Powercom units this option should be equal to 8\&. .RE .SH "INSTALLATION" .sp This driver is not built by default\&. You can build it by using "configure \-\-with\-usb=yes"\&. Note that it will also install other USB drivers\&. .sp You also need to install manually the legacy hotplug files (libhidups and libhid\&.usermap, generally in /etc/hotplug/usb/), or the udev file (nut\-usbups\&.rules, generally in /etc/udev/rules\&.d/) to address the permission settings problem\&. For more information, refer to the README file in nut/scripts/hotplug or nut/scripts/udev\&. .sp On Linux with MGE equipment, you will need at least a 2\&.4\&.25 or 2\&.6\&.2 kernel as well as libusb\-0\&.1\&.8 or later to disable hiddev support and avoid conflict\&. .SH "IMPLEMENTATION" .sp The driver ignores the "port" value in \fBups\&.conf\fR\&. Unlike previous versions of this driver, it is now possible to control multiple UPS units simultaneously with this driver, provided they can be distinguished by setting some combination of the "vendor", "product", "serial", "vendorid", and "productid" options\&. For instance: .sp .if n \{\ .RS 4 .\} .nf [mge] driver = usbhid\-ups port = auto vendorid = 0463 [tripplite] driver = usbhid\-ups port = auto vendorid = 09ae .fi .if n \{\ .RE .\} .SH "KNOWN ISSUES AND BUGS" .SS "Repetitive timeout and staleness" .sp Some models tends to be unresponsive with the default polling frequency\&. The result is that your system log will have lots of messages like: .sp .if n \{\ .RS 4 .\} .nf usb 2\-1: control timeout on ep0in usb 2\-1: usbfs: USBDEVFS_CONTROL failed cmd usbhid\-ups rqt 128 rq 6 len 256 ret \-110 .fi .if n \{\ .RE .\} .sp In this case, simply modify the general parameter "pollinterval" to a higher value (like 10 for 10 seconds)\&. This should solve the issue\&. .SS "Got EPERM: Operation not permitted upon driver startup" .sp You have forgotten to install the hotplug files, as explained in the INSTALLATION section above\&. Don\(cqt forget to restart hotplug so that it applies these changes\&. .SS "Unattended shutdowns" .sp The hardware which was used for development of this driver is almost certainly different from what you have, and not all manufacturers follow the USB HID Power Device Class specifications to the letter\&. You don\(cqt want to find out that yours has issues here when a power failure hits your server room and you\(cqre not around to manually restart your servers\&. .sp If you rely on the UPS to shutdown your systems in case of mains failure and to restart them when the power returns, you \fBmust\fR test this\&. You can do so by running \fIupsmon \-c fsd\fR\&. With the mains present, this should bring your systems down and then cycle the power to restart them again\&. If you do the same without mains present, it should do the same, but in this case, the outputs shall remain off until mains power is applied again\&. .SH "AUTHORS" .sp Originally sponsored by MGE UPS SYSTEMS\&. Now sponsored by Eaton http://opensource\&.eaton\&.com Arnaud Quette, Peter Selinger, Arjen de Korte .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/ivtscd.80000644000175000017500000000350612640476503012356 00000000000000'\" t .\" Title: ivtscd .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "IVTSCD" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ivtscd \- driver for the IVT Solar Controller Device .SH "NOTE" .sp This man page only documents the hardware\-specific features of the \fBivtscd\fR driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "DESCRIPTION" .sp This driver allows to access the IVT SCD\-series devices\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra argument\&. .SH "AUTHOR" .sp Arjen de Korte .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upsrw.80000644000175000017500000001130112667757214012244 00000000000000'\" t .\" Title: upsrw .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/09/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSRW" "8" "03/09/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsrw \- UPS variable administration tool .SH "SYNOPSIS" .sp \fBupsrw\fR \fIups\fR .sp \fBupsrw\fR \-h .sp \fBupsrw\fR \-s \fIvariable\fR [\-u \fIusername\fR] [\-p \fIpassword\fR] \fIups\fR .SH "DESCRIPTION" .sp \fBupsrw\fR allows you to view and change the read/write variables inside your UPS\&. It sends commands via the server \fBupsd\fR(8) to your driver, which configures the hardware for you\&. .sp The list of variables that allow you to change their values is based on the capabilities of your UPS equipment\&. Not all models support this feature\&. Typically, cheaper hardware does not support any of them\&. Run upsrw with a UPS identifier to see what will work for you\&. .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display the help message\&. .RE .PP \fB\-s\fR \fIvariable\fR .RS 4 Specify the variable to be changed inside the UPS\&. For unattended mode such as in shell scripts, use the format VAR=VALUE to specify both the variable and the value, for example: .sp .if n \{\ .RS 4 .\} .nf \-s input\&.transfer\&.high=129 .fi .if n \{\ .RE .\} .sp Without this argument, upsrw will just display the list of the variables and their possible values\&. .sp Some variables are strings, and can be set to any value within the length limit\&. Others are enumerated types and can only be set to one of those values\&. Others may be within an allowed range of values\&. Refer to the list to know what\(cqs available in your hardware\&. .RE .PP \fB\-u\fR \fIusername\fR .RS 4 Set the NUT username for the connection to the server\&. This is optional, and you will be prompted for this when using the \-s option if you don\(cqt specify \-u on the command line\&. NUT usernames are defined in \fBupsd.users\fR(5), and are not linked to system usernames\&. .RE .PP \fB\-p\fR \fIpassword\fR .RS 4 Set the password to authenticate to the server\&. This is also optional like \-u, and you will be prompted for it if necessary\&. .RE .PP \fIups\fR .RS 4 View or change the settings on this UPS\&. The format for this option is upsname[@hostname[:port]]\&. The default hostname is "localhost"\&. .RE .SH "UNATTENDED MODE" .sp If you run this program inside a shell script or similar to set variables, you will need to specify all of the information on the command line\&. This means using \-s VAR=VALUE, \-u and \-p\&. Otherwise it will put up a prompt and your program will hang\&. .sp This is not necessary when displaying the list, as the username and password are not required for read\-only mode\&. .sp Moreover, if you run this program inside a shell script or similar, you should only consider using output from stdout, not stderr\&. .SH "DIAGNOSTICS" .sp \fBupsrw\fR can\(cqt set variables on your UPS unless you provide a valid username and password\&. If you get "access denied" errors, make sure that your \fBupsd.users\fR(5) has an entry for you, and that the username you are using has permissions to SET variables\&. .SH "VALUE FORMAT" .sp When using \fBupsrw\fR to modify a numeric float value, that values must be given using decimal (base 10) english\-based representation, so using a dot, in non\-scientific notation\&. So hexadecimal, exponents, and comma for thousands separator are forbiden\&. For example: "1200\&.20" is valid, while "1,200\&.20" and "1200,20" are invalid\&. .SH "HISTORY" .sp This program used to be called upsct2, which was ambiguous and confusing\&. .SH "SEE ALSO" .sp \fBupsd\fR(8), \fBupscmd\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_device_login.30000644000175000017500000000003012665610645015562 00000000000000.so libnutclient_misc.3 nut-2.7.4/docs/man/libnutclient_misc.txt0000644000175000017500000000277512640444140015240 00000000000000LIBNUTCLIENT_MISC(3) ==================== NAME ---- libnutclient_misc, nutclient_authenticate, nutclient_logout, nutclient_device_login, nutclient_get_device_num_logins, nutclient_device_master, nutclient_device_forced_shutdown - Miscelaneous functions in Network UPS Tools high-level client access library SYNOPSIS -------- #include typedef void* NUTCLIENT_t; void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd); void nutclient_logout(NUTCLIENT_t client); void nutclient_device_login(NUTCLIENT_t client, const char* dev); int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); void nutclient_device_master(NUTCLIENT_t client, const char* dev); void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev); DESCRIPTION ----------- The *nutclient_authenticate()* function authenticate the user. 'login' is the user name. 'passwd' is the user password. The *nutclient_logout()* function disconnect gracefully from the server. The *nutclient_device_login()* function log the fact that a system is drawing power from this UPS. The *nutclient_get_device_num_logins()* function retrieve the number of clients which have been logged for this device. The *nutclient_device_master()* function make sure that master-level functions like FSD are available if necessary. The *nutclient_device_forced_shutdown()* function sets the "forced shutdown" flag on the device. 'dev' is the device name. SEE ALSO -------- linkman:libnutclient[3] nut-2.7.4/docs/man/upsstats.cgi.txt0000644000175000017500000000303712640443572014160 00000000000000UPSSTATS.CGI(8) =============== NAME ---- upsstats.cgi - Web-based UPS status viewer SYNOPSIS -------- *upsstats.cgi* NOTE: As a CGI program, this should be invoked through your web server. If you run it from the command line, it will either complain about unauthorized access or spew a bunch of HTML at you. DESCRIPTION ----------- *upsstats.cgi* uses template files to build web pages containing status information from UPS hardware. It can repeat sections of those template files to monitor several UPSes simultaneously, or focus on a single UPS. These templates can also include references to linkman:upsimage.cgi[8] for graphical displays of battery charge levels, voltage readings, and the UPS load. ACCESS CONTROL -------------- upsstats will only talk to linkman:upsd[8] servers that have been defined in your linkman:hosts.conf[5]. If it complains that "Access to that host is not authorized", check that file first. TEMPLATES --------- The web page that is displayed is actually a template containing commands to upsstats which are replaced by status information. The default file used for the overview is upsstats.html. When monitoring a single UPS, the file displayed is `upsstats-single.html`. The format of these files, including the possible commands, is documented in linkman:upsstats.html[5]. FILES ----- linkman:hosts.conf[5], linkman:upsstats.html[5], upsstats-single.html SEE ALSO -------- linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/powercom.txt0000644000175000017500000001657712640473702013377 00000000000000POWERCOM(8) =========== NAME ---- powercom - UPS driver for serial Powercom/Trust/Advice UPS equipment NOTE ---- This man page only documents the hardware-specific features of the powercom driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports many similar kinds of serial UPS hardware (as well as a few USB UPS models with USB-to-serial adapters). The most common ones are the Trust 425/625, Powercom, and Advice Partner/King PR750. Others using the same protocol may also work. For USB connections, you might need linkman:usbhid-ups[8]. For more specific guidance on which driver is applicable for a USB connection, see the NUT Hardware Compatibility List (HCL). EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5] file: *linevoltage*='value':: An integer specifying the line voltage. It can't be auto detected. Acceptable values are in the range of 110-120 or 220-240. The default is '230'. *manufacturer*='name':: Specify the manufacturer name, which also can't be auto detected. This is a user defined string, so any name is acceptable. The default is 'PowerCom'. *modelname*='name':: Specify the model name, which also can't be auto detected. This is a user defined string, so any name is acceptable. The default is 'Unknown'. *serialnumber*='value':: Like modelname above, but for the serial number. The default is 'Unknown'. *type*='name':: The exact type of the communication protocol within the powercom family, that will be used to communicate with the UPS. The type is named after the first modelname that was coded with that protocol. The acceptable names are 'Trust', 'Egys', 'KP625AP', 'IMP', 'KIN', 'BNT', 'BNT-other' and 'OPTI'. The default is 'Trust'. + 'BNT-other' is a special type for other BNT models (such as the 1500A at 120V and can be used to override ALL models using ALL of the following values. *shutdownArguments*={{'minutes','seconds'},'whether_minutes_should_be_used'}:: The minutes and seconds that the UPS should wait between receiving the shutdown command and actually shutting off. The other argument should be set to the character 'n' only when the minutes value should be skipped and not sent to the UPS. The default is type-dependent and is given below. The braces and commas are mandatory. Note that there should be no whitespace characters. *numOfBytesFromUPS*='value':: The number of bytes in a UPS frame: 16 is common, 11 for 'Trust'. The default is type-dependent and is given below. *methodOfFlowControl*='name':: The method of serial communication flow control that is engaged by the UPS. The default is type-dependent and is given below. Acceptable names are 'dtr0rts1', 'dtr1' or 'no_flow_control'. *validationSequence*={{'index1','value1'},{'index2','value2'},{'index3','value3'}}:: (Only for types KP625AP, Trust, Egys.) 3 pairs to be used for validating the UPS by comparing bytes of the raw data with constant values. The index selects the byte from the UPS (see numOfBytesFromUPS) and the value is for matching to the byte. The default is type-dependent and is given below. The braces and commas are mandatory, as the lack of white space characters. *frequency*={'A','B'}:: (Only for types KP625AP, Trust, Egys.) A pair to convert the raw frequency data to a human-readable frequency reading using the function 1/(A*x+B). If the raw value x IS the frequency, then set A=1/(x^2) and B=0. The default is type-dependent and is given below. Do note that the braces and commas are mandatory as well, as the lack of whitespace characters. *loadPercentage*={'BatteryA','BatteryB','LineA','LineB'}:: (Only for types KP625AP, Trust, Egys.) A quad to convert the raw load data to human readable load percentage reading using the function A*x+B. If the raw value x IS the Load Percent, then set A=1 and B=0. The default is type-dependent and is given below. Do note that the braces and commas are mandatory, as the lack of white space characters. *batteryPercentage*={'Battery1','Battery2','Battery3','Line4','Line5'}:: (Only for KP625AP, Trust, Egys.) A 5 tuple to convert the raw battery and line data to a human-readable battery and line percentage reading using the functions (Battery) A*x+B*y+C and (Line) D*x+E. If the raw value x IS the Battery Percent, then set A=1, B=0, C=0, D=1, E=0. The default is type-dependent and is given below. Do note that the braces and commas are mandatory, as the lack of white space characters. *voltage*={'240A','240B','120A','120B'}:: (Only for types KP625AP, Trust, Egys.) A quad that is used convert the raw voltage data to a human-readable voltage reading using the function A*x+B. If the raw value x IS HALF the Voltage, then set A=2, B=0. The default is type-dependent and is given below. Do note that the braces and commas are mandatory, as well as the lack of whitespace characters. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- linevoltage = 230 manufacturer = PowerCom modelname = Unknown serialnumber = Unknown type = Trust The rest of the default values for the extra arguments are type-dependent. However, 'BNT-other' is a special type that can be used to override ALL values for ALL models. Trust ~~~~~ numOfBytesFromUPS = 11 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0},{7,0},{8,0}} shutdownArguments = {{0,10},n} frequency = {0.00020997,0.00020928} loadPercentage = {6.1343,-0.3808,4.3110,0.1811} batteryPercentage = {5.0000,0.3268,-825.00,4.5639,-835.82} voltage = {1.9216,-0.0977,0.9545,0.0000} KP625AP ~~~~~~~ numOfBytesFromUPS = 16 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0x80},{7,0},{8,0}} shutdownArguments = {{0,10},n} frequency = {0.00020997,0.00020928} loadPercentage = {6.1343,-0.3808,4.3110,0.1811} batteryPercentage = {5.0000,0.3268,-825.00,4.5639,-835.82} voltage = {1.9216,-0.0977,0.9545,0.0000} Egys ~~~~ numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0x80},{7,0},{8,0}} shutdownArguments = {{0,10},n} frequency = {0.00020997,0.00020928} loadPercentage = {6.1343,-0.3808,1.3333,0.6667} batteryPercentage = {5.0000,0.3268,-825.00,2.2105,-355.37} voltage = {1.9216,-0.0977,0.9545,0.0000} IMP ~~~ numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} shutdownArguments = {{1,30},y} KIN ~~~ numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x4b},{8,0},{8,0}} shutdownArguments = {{1,30},y} BNT ~~~ numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x42},{8,0},{8,0}} shutdownArguments = {{1,30},y} BNT-other ~~~~~~~~~ numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{8,0},{8,0},{8,0}} shutdownArguments = {{1,30},y} frequency = {0.00027778,0.0000} loadPercentage = {1.0000,0.0,1.0000,0.0} batteryPercentage = {1.0000,0.0000,0.0000,1.0000,0.0000} voltage = {2.0000,0.0000,2.0000,0.0000} OPTI ~~~~ numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} shutdownArguments = {{1,30},y} AUTHOR ------ Peter Bieringer , Alexey Sidorov , Keven L. Ates SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/powercom.80000644000175000017500000002304212640476511012711 00000000000000'\" t .\" Title: powercom .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "POWERCOM" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" powercom \- UPS driver for serial Powercom/Trust/Advice UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the powercom driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports many similar kinds of serial UPS hardware (as well as a few USB UPS models with USB\-to\-serial adapters)\&. The most common ones are the Trust 425/625, Powercom, and Advice Partner/King PR750\&. Others using the same protocol may also work\&. For USB connections, you might need \fBusbhid-ups\fR(8)\&. .sp For more specific guidance on which driver is applicable for a USB connection, see the NUT Hardware Compatibility List (HCL)\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5) file: .PP \fBlinevoltage\fR=\fIvalue\fR .RS 4 An integer specifying the line voltage\&. It can\(cqt be auto detected\&. Acceptable values are in the range of 110\-120 or 220\-240\&. The default is \fI230\fR\&. .RE .PP \fBmanufacturer\fR=\fIname\fR .RS 4 Specify the manufacturer name, which also can\(cqt be auto detected\&. This is a user defined string, so any name is acceptable\&. The default is \fIPowerCom\fR\&. .RE .PP \fBmodelname\fR=\fIname\fR .RS 4 Specify the model name, which also can\(cqt be auto detected\&. This is a user defined string, so any name is acceptable\&. The default is \fIUnknown\fR\&. .RE .PP \fBserialnumber\fR=\fIvalue\fR .RS 4 Like modelname above, but for the serial number\&. The default is \fIUnknown\fR\&. .RE .PP \fBtype\fR=\fIname\fR .RS 4 The exact type of the communication protocol within the powercom family, that will be used to communicate with the UPS\&. The type is named after the first modelname that was coded with that protocol\&. The acceptable names are \fITrust\fR, \fIEgys\fR, \fIKP625AP\fR, \fIIMP\fR, \fIKIN\fR, \fIBNT\fR, \fIBNT\-other\fR and \fIOPTI\fR\&. The default is \fITrust\fR\&. .sp \fIBNT\-other\fR is a special type for other BNT models (such as the 1500A at 120V and can be used to override ALL models using ALL of the following values\&. .RE .PP \fBshutdownArguments\fR={{\fIminutes\fR,\fIseconds\fR},\fIwhether_minutes_should_be_used\fR} .RS 4 The minutes and seconds that the UPS should wait between receiving the shutdown command and actually shutting off\&. The other argument should be set to the character \fIn\fR only when the minutes value should be skipped and not sent to the UPS\&. The default is type\-dependent and is given below\&. The braces and commas are mandatory\&. Note that there should be no whitespace characters\&. .RE .PP \fBnumOfBytesFromUPS\fR=\fIvalue\fR .RS 4 The number of bytes in a UPS frame: 16 is common, 11 for \fITrust\fR\&. The default is type\-dependent and is given below\&. .RE .PP \fBmethodOfFlowControl\fR=\fIname\fR .RS 4 The method of serial communication flow control that is engaged by the UPS\&. The default is type\-dependent and is given below\&. Acceptable names are \fIdtr0rts1\fR, \fIdtr1\fR or \fIno_flow_control\fR\&. .RE .PP \fBvalidationSequence\fR={{\fIindex1\fR,\fIvalue1\fR},{\fIindex2\fR,\fIvalue2\fR},{\fIindex3\fR,\fIvalue3\fR}} .RS 4 (Only for types KP625AP, Trust, Egys\&.) 3 pairs to be used for validating the UPS by comparing bytes of the raw data with constant values\&. The index selects the byte from the UPS (see numOfBytesFromUPS) and the value is for matching to the byte\&. The default is type\-dependent and is given below\&. The braces and commas are mandatory, as the lack of white space characters\&. .RE .PP \fBfrequency\fR={\fIA\fR,\fIB\fR} .RS 4 (Only for types KP625AP, Trust, Egys\&.) A pair to convert the raw frequency data to a human\-readable frequency reading using the function 1/(A*x+B)\&. If the raw value x IS the frequency, then set A=1/(x^2) and B=0\&. The default is type\-dependent and is given below\&. Do note that the braces and commas are mandatory as well, as the lack of whitespace characters\&. .RE .PP \fBloadPercentage\fR={\fIBatteryA\fR,\fIBatteryB\fR,\fILineA\fR,\fILineB\fR} .RS 4 (Only for types KP625AP, Trust, Egys\&.) A quad to convert the raw load data to human readable load percentage reading using the function A*x+B\&. If the raw value x IS the Load Percent, then set A=1 and B=0\&. The default is type\-dependent and is given below\&. Do note that the braces and commas are mandatory, as the lack of white space characters\&. .RE .PP \fBbatteryPercentage\fR={\fIBattery1\fR,\fIBattery2\fR,\fIBattery3\fR,\fILine4\fR,\fILine5\fR} .RS 4 (Only for KP625AP, Trust, Egys\&.) A 5 tuple to convert the raw battery and line data to a human\-readable battery and line percentage reading using the functions (Battery) A*x+B*y+C and (Line) D*x+E\&. If the raw value x IS the Battery Percent, then set A=1, B=0, C=0, D=1, E=0\&. The default is type\-dependent and is given below\&. Do note that the braces and commas are mandatory, as the lack of white space characters\&. .RE .PP \fBvoltage\fR={\fI240A\fR,\fI240B\fR,\fI120A\fR,\fI120B\fR} .RS 4 (Only for types KP625AP, Trust, Egys\&.) A quad that is used convert the raw voltage data to a human\-readable voltage reading using the function A*x+B\&. If the raw value x IS HALF the Voltage, then set A=2, B=0\&. The default is type\-dependent and is given below\&. Do note that the braces and commas are mandatory, as well as the lack of whitespace characters\&. .RE .SH "DEFAULT VALUES FOR THE EXTRA ARGUMENTS" .sp .if n \{\ .RS 4 .\} .nf linevoltage = 230 manufacturer = PowerCom modelname = Unknown serialnumber = Unknown type = Trust .fi .if n \{\ .RE .\} .sp The rest of the default values for the extra arguments are type\-dependent\&. However, \fIBNT\-other\fR is a special type that can be used to override ALL values for ALL models\&. .SS "Trust" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 11 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0},{7,0},{8,0}} shutdownArguments = {{0,10},n} frequency = {0\&.00020997,0\&.00020928} loadPercentage = {6\&.1343,\-0\&.3808,4\&.3110,0\&.1811} batteryPercentage = {5\&.0000,0\&.3268,\-825\&.00,4\&.5639,\-835\&.82} voltage = {1\&.9216,\-0\&.0977,0\&.9545,0\&.0000} .fi .if n \{\ .RE .\} .SS "KP625AP" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0x80},{7,0},{8,0}} shutdownArguments = {{0,10},n} frequency = {0\&.00020997,0\&.00020928} loadPercentage = {6\&.1343,\-0\&.3808,4\&.3110,0\&.1811} batteryPercentage = {5\&.0000,0\&.3268,\-825\&.00,4\&.5639,\-835\&.82} voltage = {1\&.9216,\-0\&.0977,0\&.9545,0\&.0000} .fi .if n \{\ .RE .\} .SS "Egys" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0x80},{7,0},{8,0}} shutdownArguments = {{0,10},n} frequency = {0\&.00020997,0\&.00020928} loadPercentage = {6\&.1343,\-0\&.3808,1\&.3333,0\&.6667} batteryPercentage = {5\&.0000,0\&.3268,\-825\&.00,2\&.2105,\-355\&.37} voltage = {1\&.9216,\-0\&.0977,0\&.9545,0\&.0000} .fi .if n \{\ .RE .\} .SS "IMP" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} shutdownArguments = {{1,30},y} .fi .if n \{\ .RE .\} .SS "KIN" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x4b},{8,0},{8,0}} shutdownArguments = {{1,30},y} .fi .if n \{\ .RE .\} .SS "BNT" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x42},{8,0},{8,0}} shutdownArguments = {{1,30},y} .fi .if n \{\ .RE .\} .SS "BNT\-other" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{8,0},{8,0},{8,0}} shutdownArguments = {{1,30},y} frequency = {0\&.00027778,0\&.0000} loadPercentage = {1\&.0000,0\&.0,1\&.0000,0\&.0} batteryPercentage = {1\&.0000,0\&.0000,0\&.0000,1\&.0000,0\&.0000} voltage = {2\&.0000,0\&.0000,2\&.0000,0\&.0000} .fi .if n \{\ .RE .\} .SS "OPTI" .sp .if n \{\ .RS 4 .\} .nf numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} shutdownArguments = {{1,30},y} .fi .if n \{\ .RE .\} .SH "AUTHOR" .sp Peter Bieringer , Alexey Sidorov , Keven L\&. Ates .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/riello_ser.80000644000175000017500000000404012640476513013214 00000000000000'\" t .\" Title: riello_ser .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "RIELLO_SER" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" riello_ser \- Driver for Riello UPS Protocol UPS equipment .SH "SYNOPSIS" .sp \fBriello_ser\fR \-h .sp \fBriello_ser\fR \-a \fIUPS_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the riello_ser driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp riello_ser supports all recent Riello UPS, Aros UPS models which use the Riello UPS GPSER and SENTR protocols\&. .sp Older Riello UPS products are not supported\&. .SH "AUTHOR" .sp Massimo Zampieri .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscli_strerror.30000644000175000017500000000402512665610641014313 00000000000000'\" t .\" Title: upscli_strerror .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_STRERROR" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_strerror \- return string describing error condition .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf char *upscli_strerror(UPSCONN_t *ups); .fi .SH "DESCRIPTION" .sp The \fBupscli_strerror\fR() function takes the pointer \fIups\fR to a UPSCONN_t state structure and returns a string describing the last error which occurred on this connection\&. The string is valid until the next call to \fBupscli_strerror\fR()\&. .SH "RETURN VALUE" .sp The \fBupscli_strerror\fR() function returns a description of the error, or an unknown error message if the error code is not recognized\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutscan_scan_eaton_serial.txt0000644000175000017500000000244112640444140016723 00000000000000NUTSCAN_SCAN_EATON_SERIAL(3) ============================ NAME ---- nutscan_scan_eaton_serial - Scan serial ports for Eaton devices (XCP, SHUT and Q1). SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_list); DESCRIPTION ----------- The *nutscan_scan_eaton_serial()* function tries to detect NUT devices which are compatible with Eaton's serial device protocols (SHUT, XCP and Q1 (aka blazer)). 'ports_list' is a NULL terminated array of pointers to strings containing serial device name (/dev/ttyS0, COM1, /dev/ttya...) You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ The *nutscan_scan_eaton_serial()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_get_serial_ports_list[3] nut-2.7.4/docs/man/upscli_get.txt0000644000175000017500000000656412640473702013675 00000000000000UPSCLI_GET(3) ============= NAME ---- upscli_get - retrieve data from a UPS SYNOPSIS -------- #include int upscli_get(UPSCONN_t *ups, unsigned int numq, const char **query, unsigned int *numa, char ***answer) DESCRIPTION ----------- The *upscli_get()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, and the pointer 'query' to an array of 'numq' query elements. It builds a properly-formatted request from those elements and transmits it to linkman:upsd[8]. Upon success, the response will be split into separate components. A pointer to those components will be returned in 'answer'. The number of usable answer components will be returned in 'numa'. USES ---- This function implements the "GET" command in the protocol. As a result, you can use it to request many different things from the server. Some examples are: * GET NUMLOGINS * GET UPSDESC * GET VAR * GET TYPE * GET DESC * GET CMDDESC QUERY FORMATTING ---------------- To generate a request for `GET NUMLOGINS su700`, you would populate query and numq as follows: unsigned int numq; const char *query[2]; query[0] = "NUMLOGINS"; query[1] = "su700"; numq = 2; All escaping of special characters and quoting of elements with spaces is handled for you inside this function. ANSWER FORMATTING ----------------- The raw response from upsd to the above query would be `NUMLOGINS su700 1`. Since this is split up for you, the values work out like this: unsigned int numa; numa = 3; answer[0] = "NUMLOGINS" answer[1] = "su700" answer[2] = "1" Notice that the value which you seek typically starts at answer[numq]. ERROR CHECKING -------------- This function will check your query against the response from linkman:upsd[8]. For example, if you send "VAR" "su700" "ups.status", it will expect to see those at the beginning of the response. If the results from *upsd* do not pass this case-insensitive test against your request, this function will return an error. When this happens, linkman:upscli_upserror[3] will return 'UPSCLI_ERR_PROTOCOL'. ANSWER ARRAY LIFETIME --------------------- The pointers contained within the 'answer' array are only valid until the next call to a 'upsclient' function which references them. If you need to use data from multiple calls, you must copy it somewhere else first. The 'answer' array and its elements may change locations, so you must not rely on previous addresses. You must only use the addresses which were returned by the most recent call. You also must not attempt to use more than 'numa' elements in 'answer'. Such behavior is undefined, and may yield bogus data or a crash. The array will be deleted after calling linkman:upscli_disconnect[3]. Any access after that point is also undefined. RETURN VALUE ------------ The *upscli_get()* function returns 0 on success, or -1 if an error occurs. If *upsd* disconnects, you may need to handle or ignore `SIGPIPE` in order to prevent your program from terminating the next time that the library writes to the disconnected socket. The following code in your initialization function will allow the *upscli_get()* call to return an error in that case: #include ... signal (SIGPIPE, SIG_IGN); ... SEE ALSO -------- linkman:upscli_list_start[3], linkman:upscli_list_next[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/nutscan_scan_avahi.30000644000175000017500000000473012665610652014706 00000000000000'\" t .\" Title: nutscan_scan_avahi .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_AVAHI" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_avahi \- Scan network for NUT services via mDNS .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_avahi(long usec_timeout); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_avahi()\fR function tries to detect the NUT service via mDNS, and its associated devices\&. It uses the Avahi library to do so\&. .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .sp This function waits up to \fIusec_timeout\fR microseconds before considering an IP address to be unresponsive\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_avahi()\fR function returns a pointer to a nutscan_device_t structure containing all found devices\&. It returns NULL if an error occurs, or if no device is found\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_cidr_to_ip\fR(3), \fBnutscan_scan_eaton_serial\fR(3), http://avahi\&.org/ nut-2.7.4/docs/man/apcsmart.txt0000644000175000017500000003332612640473702013345 00000000000000APCSMART(8) =========== NAME ---- apcsmart - Driver for American Power Conversion Smart Protocol UPS equipment SYNOPSIS -------- *apcsmart* -h *apcsmart* -a 'UPS_NAME' [-x option=value ...] NOTE: This man page only documents the hardware-specific features of the apcsmart driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The apcsmart driver should recognize (or at the very least, work with) the majority of Smart-UPS models - which includes Smart-UPS, Matrix-UPS and Back-UPS lineups, among few other ones. Currently, we can roughly divide APC hardware into four groups (note that the division isn't strict by any means, and the borders between those are pretty fuzzy): [very] "old" models:: These models usually have old APC logo, white color and _no_ programmable EEPROM; you won't find them listed anywhere on APC's site either. The support for those will be usually based on driver's compatibility tables, or if the model (firmware) is not listed in those - the driver will try to follow the very basic subset of features, while still trying to remain useful. Despite "smart" tagname, they often tend to behave in pretty dumb way (see the section below about shutdown behaviour). + -- .Example models: * Smart-UPS 2000I * Smart-UPS 900I -- "new" models:: These models usually come from late 1990s / pre-2009 times. They are often referred as "3rd. gen". For the most part, they have programmable EEPROM, report supported commands and capabilites, and should work just fine with the apcsmart driver. "microlink" models:: WARNING: these are not _natively_ supported by *apcsmart* (or *apcupsd*, for that matter, if you're wondering). Around 2007, APC (now APC Schneider) decided to go back to its proprietry roots, and all the new models (SMT, SMX, SURTD) use completely different protocol and cables. If you purchased a new APC UPS - that uses cable with RJ45 on the one end, and DB-9 on the other - then you have such model. Your only option to support it through *NUT* is to purchase a "legacy communications card" - part #AP9620 (google \'AP9620' for more details). Or if that's not an option, rely on official software. Microsol models:: Several Microsol serial models sold in Brazil have been rebranded as APC Back-UPS, and the model numbers tend to start with "BZ". If you have one of these "Nobreaks", they will not work with the *apcsmart* driver - please see the linkman:solis[8] driver instead. + -- .Example models: * Back-UPS BZ1200-BR * Back-UPS BZ2200BI-BR -- Another thing to remember is that Smart protocol is not USB protocol. If you have UPS with both USB and serial ports, then depending on how you connect it, you will need either apcsmart or usbhid-ups driver. CABLING ------- This driver expects to see a 940-0024C cable or a clone by default. You can switch to the 940-0095B dual-mode cable support with the \'cable=' definition described below. If your 940-xx24X cable is broken or missing, use this diagram to build a clone: http://www.networkupstools.org/cables.html#_940_0024c_clone NOTE: The "xx" is either "00" for a short cable, or the number of feet of a longer cable. The "X" is a letter representing the minor revision of the physical cable and its connectors ("C" and "E" are commonly found revisions). All minor revisions should use the same pin-outs and wiring. You can specify alternate cable in linkman:ups.conf[5]: *cable*=940-0095B Alternatively, you can also provide it on the command line using: -x *cable*=940-0095B TTY MODES --------- By default the driver works in canonical mode, but it proved to be a problem in Windows systems. Furthermore there's a possibility of some obscure serial cards or serial-USB converters that could cause problems as well. You can use \'ttymode=' option to force non-canonical discipline in linkman:ups.conf[5]: *ttymode*=raw Alternatively, you can also provide it on the command line using: -x *ttymode*=raw NOTE: Any other value will make the driver work in the canonical mode. EXPLANATION OF SHUTDOWN METHODS SUPPORTED BY APC UPSES ------------------------------------------------------ APC hardware supports a lot of shutdown methods, that themselves can differ in behaviour quite a bit, depending on the model. *S* (soft hibernate):: This is most basic command present in probably all APC models. It will hibernate the UPS, and subsequently wake it up when the mains supply returns. *The command doesn't work if the UPS is running on mains.* "old" models::: The behaviour here is unfortunately pretty primitive - when the power returns, the UPS just wakes up. No grace periods, no min. battery charge condition, etc. This is probably not what you want. "new" models::: The behaviour here is as expected - the power is cut off after the EEPROM defined grace period. The UPS will wake up when the power returns, after the EEPROM defined delay AND if the EEPROM defined min. battery charge level is met. The delay is counted from the power\'s return. *CS* (aka "force OB hack"):: This is a trick to make UPS power down even if it\'s running on mains. Immediately before issuing *S*, "simulate power failure" is issued. The remaining behaviour is as in *S* case. + The name came from APC CS models, where such trick was used to power down UPSes in consistent fashion using only *S*. It\'s better to use *@nnn* command if your UPS supports it (and is not too old, see below). *@nnn* (hard hibernate):: This is basic command used to hibernate UPS regardless if it\'s running on batteries or on mains. The option takes 3 digits argument which can be used to specify additional wakeup delay (in 6 minute units). + -- "old" models::: The behaviour is - unfortunately - similary primitive to *S*. The UPS unconditionally wakes up after $$nnn*6$$ minutes - *it doesn\'t care if the power returned !* If nnn = 000, then UPS will do precisely nothing. On those models you\'re better specifying nnn > 0, if you can estimate the kind of power problems that might be happening in your environment. Another thing to consider with "old" models - you might lose the connection with the UPS, until it wakes up (with *S*, the serial connection is kept alive). "new" models::: All the usual variables defined in EEPROM are respected (see *S*). Additionally, if nnn > 0, the $$nnn*6$$ minutes are added to EEPROM defined delay. UPS will not power up if it\'s running on batteries, contrary to what "old" models used to do - the combined delay is counted from the moment of power return. -- + Supposedly there exist models that take 2 digits instead of 3. Just in case, NUT also supports such variation. You have to provide exactly 2 digits to trigger it (*awd* option, or argument to one of the supported instant commands). *K* (delayed poweroff):: This is permanent poweroff - the UPS will not wake up automatically. On newer units, it will respect applicable EEPROM variables. *Z* (instant poweroff):: This is also permanent poweroff - the UPS will not wake up automatically. The poweroff is executed immediately. SHUTDOWN CONTROL BY NUT ----------------------- There are three options used to control the shutdown behaviour. *sdtype*=[0-5]:: This option takes a single digit (0-5) as an argument. See below for details. *advorder*=no|[0-4]+:: This option takes string of digits as an argument. Methods listed are tried in turn until one of them succeedes. Note that the meaning of digits is different from *sdtype*. See below for details. *awd*=[0-9]{1,3}:: This option lets you specify additional wakeup delay used by *@*. If you provide exactly 2 digits, the driver will try 2 digits variation (see previous section for more info). Otherwise standard 3 digits variation is used. *Note: the time unit is 6 minutes !* Keep in mind that *sdtype* and *advorder* are mutually exclusive. If *advorder* is provided, *sdtype* is ignored. If *advorder* is set to \'no', *sdtype* is used instead. If nothing is provided, *NUT* will assume *sdtype*=0 - which is generally fine for anything not too ancient or not too quirky. SDTYPE ~~~~~~ The values permitted are from 0 to 5. Only one can be specified. Anything else will cause apcsmart to exit. 0:: issue soft hibernate (*S*) if the UPS is running on batteries, otherwise issue hard hibernate (*@*) 1:: issue soft hibernate (*S*) (if on batteries), and if it fails (or on mains) - try hard hibernate (*@*) 2:: issue instant poweroff (*Z*) 3:: issue delayed poweroff (*K*) 4:: issue "force OB hack" (*CS*) 5:: issue hard hibernate (*@*) NOTE: Hard hibernate\'s additional wakeup delay can be provided by *awd*. ADVORDER ~~~~~~~~ The argument is either a word \'no', or a string of 1 - 5 digits in [0 - 4] range. Each digit maps to the one of shutdown methods supported by APC UPSes. Methods listed in this way are tried in order, until one of them succedes. If *advorder* is undefined or set to \'no', *sdtype* is used instead. The mapping is as follows: [horizontal] 0:: soft hibernate (*S*) 1:: hard hibernate (*@*) 2:: delayed poweroff (*K*) 3:: instant poweroff (*Z*) 4:: "force OB hack" (*CS*) NOTE: Hard hibernate\'s additional wakeup delay can be provided by *awd*. IGNORING LB STATE ----------------- APC units - even if they report LB mode - will not go into shutdown automatically. This gives us even more control with reference to "when to actually shutdown PSU". Since version 2.6.2, NUT supports *ignorelb* option in driver's section of linkman:ups.conf[5]. When such option is in effect, the core driver will ignore LB state as reported by specific driver and start shutdown basing the decision _only_ on two conditions: battery.charge < battery.charge.low *OR* battery.runtime < battery.runtime.low Of course - if any of the variables are not available, the appropriate condition is not checked. If you want to explicitly disable one of the conditions, simply override the right hand variable causing the condition to always evaluate to false (you can even provide negative numbers). APC UPSes don't have battery.charge.low - you will have to define it if you want to use such condition (prefix the variable with `override.` or `default.`). "New" units have battery.runtime.low, but depending on battery quality, firmware version, calibration and UPS load - this variable can be underestimated quite a bit - especially right after going into OB state. This in turn can cause LB to be asserted, which under normal conditions will cause *NUT* to initiate the shutdown. You might want to disable this condition entirely, when relying on *ignorelb* option (this was actually the main motivation behind introduction of such feature). Simple example: [source,conf] ---- [apc] ignorelb override.battery.charge.low = 15 override.battery.runtime.low = -1 ---- This would cause apcsmart to go into shutdown _only_ if detected battery charge < 15%. Runtime condition is always false in this example. You could ask - why bother ? Well, the reason is already hinted above. APC units can be very picky about the batteries, and their firmware can underestimate the remaining runtime (especially right after going into OB state). *ignorelb* option and *$$override.*$$* let you remain in control of the UPS, not UPS in control of you. Furthermore, this allows to specify conditions similary to how it's done in apcupsd daemon, so it should be welcome by people used to that software. SUPPORTED INSTANT COMMANDS -------------------------- The apcsmart driver exposes following instant commands: shutdown.return:: executes soft hibernate shutdown.return cs:: executes "force OB hack" shutdown.return at::: executes "hard hibernate" with $$*6$$ minutes additional wakeup delay ( format is the same as of *awd* option) shutdown.stayoff:: executes "delayed poweroff" load.off:: executes "instant poweroff" All the above commands must be issued 2nd time to have any effect (no less than 3 seconds, and no more than 15 seconds after the initial call). Those commands are mostly useful for manual testing, when your machine is not powered by the UPS you\'re testing. Other supported commands: - load.on - test.panel.start - test.failure.start - test.battery.start - test.battery.stop - bypass.start - bypass.stop - calibrate.start - calibrate.stop PREVIOUS DRIVER VERSION ----------------------- Previous driver is still available as *apcsmart-old*, should there be any need to use earlier version (bugs, incompatiblities with new functionality, etc.). In due time, *apcsmart-old* will be phased out completely, but this won't happen until the new version gets solid exposure with no pending issues. BUGS ---- Some older APC UPS models return bogus data in the status register during a front panel test. This is usually detected and discarded, but some other unexpected values have occasionally slipped through. APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa. AUTHOR ------ Nigel Metheringham (drawing heavily on the original apcsmart driver by Russell Kroll). This driver was called newapc for a time and was renamed in the 1.5 series. In 2.6.2 it was renamed to apcsmart-old, being superseded by updated version with new features, which is maintained by Michal Soltys SEE ALSO -------- linkman:nutupsdrv[8], linkman:ups.conf[5], linkman:usbhid-ups[8], linkman:solis[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ // vim: tw=80 ai si ts=8 sts=4 sw=4 et : nut-2.7.4/docs/man/upscli_disconnect.txt0000644000175000017500000000144012640443572015235 00000000000000UPSCLI_DISCONNECT(3) ==================== NAME ---- upscli_disconnect - disconnect from a UPS server SYNOPSIS -------- #include int upscli_disconnect(UPSCONN_t *ups); DESCRIPTION ----------- The *upscli_disconnect()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, shuts down the connection to the server, and frees dynamic memory used by the state structure. The `UPSCONN_t` structure is no longer valid after this function is called. This function must be called, or your program will leak memory and file descriptors. RETURN VALUE ------------ The *upscli_disconnect()* function returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_connect[3], linkman:upscli_fd[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/libnutclient_misc.30000644000175000017500000000546212665610645014573 00000000000000'\" t .\" Title: libnutclient_misc .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT_MISC" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient_misc, nutclient_authenticate, nutclient_logout, nutclient_device_login, nutclient_get_device_num_logins, nutclient_device_master, nutclient_device_forced_shutdown \- Miscelaneous functions in Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf typedef void* NUTCLIENT_t; .fi .sp .nf void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd); void nutclient_logout(NUTCLIENT_t client); void nutclient_device_login(NUTCLIENT_t client, const char* dev); int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); void nutclient_device_master(NUTCLIENT_t client, const char* dev); void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev); .fi .SH "DESCRIPTION" .sp The \fBnutclient_authenticate()\fR function authenticate the user\&. .sp \fIlogin\fR is the user name\&. .sp \fIpasswd\fR is the user password\&. .sp The \fBnutclient_logout()\fR function disconnect gracefully from the server\&. .sp The \fBnutclient_device_login()\fR function log the fact that a system is drawing power from this UPS\&. .sp The \fBnutclient_get_device_num_logins()\fR function retrieve the number of clients which have been logged for this device\&. .sp The \fBnutclient_device_master()\fR function make sure that master\-level functions like FSD are available if necessary\&. .sp The \fBnutclient_device_forced_shutdown()\fR function sets the "forced shutdown" flag on the device\&. .sp \fIdev\fR is the device name\&. .SH "SEE ALSO" .sp \fBlibnutclient\fR(3) nut-2.7.4/docs/man/nutclient_get_devices.30000644000175000017500000000003312665610643015415 00000000000000.so libnutclient_devices.3 nut-2.7.4/docs/man/optiups.80000644000175000017500000001003112640476511012553 00000000000000'\" t .\" Title: optiups .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "OPTIUPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" optiups \- Driver for Opti\-UPS (Viewsonic) UPS and Zinto D (ONLINE\-USV) equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the optiups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp \fBoptiups\fR was originally written against a PowerES 280es in nut\-0\&.45\&. It was revised for nut\-2\&.0\&.1 and tested against a PowerES 420E\&. It is expected to work with at least the PowerES, PowerPS, and PowerVS models\&. .sp This driver additionally supports a Zinto D from ONLINE USV\-Systeme AG because of their very similar commands, but it is unknown if it also works with other UPS from them\&. .sp This driver will not work with the PowerES stock serial cable\&. You will need to construct your own three conductor cable: .sp .if n \{\ .RS 4 .\} .nf UPS 6 \-> PC 3 UPS 9 \-> PC 2 UPS 4 \-> PC 5 .fi .if n \{\ .RE .\} .sp The cable for Online\-USV uses pin UPS 7 (not UPS 4) → PC 5\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5) file: .PP \fBstatus_only\fR .RS 4 Only poll for critical status information\&. Without this, \fBoptiups\fR (and all NUT drivers) poll all sorts of information from the UPS fairly often\&. It is probably not often enough to hurt anything, so this option probably is not very useful, unless you have a flaky serial connection or a highly loaded machine\&. .RE .PP \fBnowarn_noimp\fR .RS 4 Does not print warnings when the UPS reports that a variable is not implemented or not pollable\&. Without the option you will get a message sent to your system logs each time NUT polls the UPS\&. If you specify \fBnowarn_noimp\fR, this message will only be logged once\&. .RE .PP \fBfake_lowbatt\fR .RS 4 This forces the low battery flag true\&. Without it, if you want to test your UPS, you will have to unplug it and wait until the battery drops to a low/critical voltage level before NUT will respond and power down your system\&. With the flag, NUT should power down the system soon after you pull the plug\&. When you are done testing, you should remove this flag\&. .sp For basic shutdown configuration testing, the command \fIupsmon \-c fsd\fR is preferred\&. .RE .PP \fBpowerup\fR .RS 4 Zinto D from ONLINE\-USV cannot be identified when switched to standby\&. Set this flag to allow the driver to power\-up your Zinto UPS\&. This will also power\-up your equipment connected to the UPS! .RE .SH "BUGS" .sp On the 420E, ups\&.serial and ups\&.temperature are unsupported features\&. This is not a bug in NUT or the NUT driver, just the way things are with this UPS\&. .SH "AUTHOR" .sp Russell Kroll, Scott Heavner, Matthias Goebl .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/masterguard.80000644000175000017500000000350312640476505013377 00000000000000'\" t .\" Title: masterguard .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "MASTERGUARD" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" masterguard \- Driver for Masterguard UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the masterguard driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports Masterguard UPS equipment\&. .SH "EXTRA ARGUMENTS" .PP \fBCS\fR .RS 4 Cancel the shutdown procedure\&. .RE .SH "AUTHOR" .sp Michael Spanier .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/libnutclient_devices.30000644000175000017500000000457612665610643015265 00000000000000'\" t .\" Title: libnutclient_devices .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT_DEVICES" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient_devices, nutclient_get_devices, nutclient_has_device, nutclient_get_device_description \- Device related functions in Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf typedef void* NUTCLIENT_t; .fi .sp .nf strarr nutclient_get_devices(NUTCLIENT_t client); int nutclient_has_device(NUTCLIENT_t client, const char* dev); char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev); .fi .SH "DESCRIPTION" .sp These functions allow to manage devices\&. .sp The \fBnutclient_get_devices()\fR function retrieve the list of devices monitored by a client\&. The returned strarr must be freed by \fIstrarr_free\fR\&. .sp The \fBnutclient_has_device()\fR function test if a device is monitored by a client\&. .sp The \fBnutclient_get_device_description()\fR function retrieve the device description\&. The returned description string must be freed\&. .sp \fIdev\fR is the device name\&. .SH "SEE ALSO" .sp \fBlibnutclient\fR(3) \fBlibnutclient_commands\fR(3) \fBlibnutclient_devices\fR(3) \fBlibnutclient_general\fR(3) \fBlibnutclient_variables\fR(3) nut-2.7.4/docs/man/tripplitesu.80000644000175000017500000000441312640476514013446 00000000000000'\" t .\" Title: tripplitesu .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "TRIPPLITESU" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" tripplitesu \- Driver for Tripp\-Lite SmartOnline (SU) UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the tripplitesu driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports the Tripp Lite SmartOnline family (via the serial port)\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBlowbatt\fR=\fInum\fR .RS 4 Set the low battery warning threshold in percent at which shutdown is initiated by \fBupsmon\fR(8)\&. By default, the UPS may not report low battery until there are only a few seconds left\&. Common values are around 25\(em30\&. .RE .SH "AUTHOR" .sp Allan N\&. Hessenflow .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Other drivers for Tripp\-Lite hardware:" .sp \fBtripplite\fR(8), \fBtripplite_usb\fR(8), \fBusbhid-ups\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_tcp_create_client.30000644000175000017500000000002712665610645016610 00000000000000.so libnutclient_tcp.3 nut-2.7.4/docs/man/victronups.80000644000175000017500000000472312640476515013303 00000000000000'\" t .\" Title: victronups .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "VICTRONUPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" victronups \- Driver for IMV/Victron UPS unit Match, Match Lite, NetUps .SH "NOTE" .sp This man page only documents the hardware\-specific features of the the victronups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The victronups driver should recognize all Victron models that use a serial protocol at 1200 bps\&. These include Match Lite, Match and the NetUps line\&. .sp The Match Lite line may only report a handful of variables\&. This is usually not a bug \- they just don\(cqt support anything else\&. .SH "CABLING" .sp If your Victron cable is broken or missing, use this diagram to build a clone: .sp docs/cables/victron\&.txt .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional setting in the \fBups.conf\fR(5): .PP \fBmodelname\fR=\fIname\fR .RS 4 Set model name .RE .PP \fBusd\fR=\fIdelay\fR .RS 4 Set delay before shutdown on UPS .RE .SH "BUGS" .sp The protocol for this UPS is not officially documented\&. .SH "AUTHOR" .sp Radek Benedikt , Daniel Prynych .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/gamatronic.80000644000175000017500000000364012640476502013204 00000000000000'\" t .\" Title: gamatronic .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "GAMATRONIC" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" gamatronic \- Driver for Gamatronic UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the gamatronic driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp Various \- Rebuilt to work with Gamatronic UPS Units, but should recognize any UPS that speaks the SEC protocol at 1200\-19200 bps\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "AUTHOR" .sp Nadav Moskovitch .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/Makefile.in0000644000175000017500000012105712667762001013040 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: man # # Notes: # - sources (.txt) and groff formats are both distributed, # - only sources are versioned ; groff files are generated at worst # during 'make dist' # - HTML files are built upon request, if AsciiDoc is available, # - groff update will only happen if AsciiDoc is available too, # - all this can probably (and hopefully) be improved, but I've not # found a way to do pattern replacement on the fly for target deps! # FIXME: investigate an autogen.sh hook # - Ref: http://www.gnu.org/software/hello/manual/automake/Man-pages.html VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_CGI_TRUE@am__append_1 = $(MAN5_CGI_PAGES) @WITH_CGI_TRUE@am__append_2 = $(MAN8_CGI_PAGES) # Drivers related manpages # (--with-drivers=...) @SOME_DRIVERS_TRUE@am__append_3 = $(DRIVER_MAN_LIST) @SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__append_4 = $(MAN_SERIAL_PAGES) @SOME_DRIVERS_FALSE@@WITH_SNMP_TRUE@am__append_5 = $(MAN_SNMP_PAGES) @SOME_DRIVERS_FALSE@@WITH_USB_TRUE@am__append_6 = $(MAN_USB_LIBUSB_PAGES) @SOME_DRIVERS_FALSE@@WITH_NEON_TRUE@am__append_7 = $(MAN_NETXML_PAGES) @SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__append_8 = $(MAN_POWERMAN_PAGES) @SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__append_9 = $(MAN_IPMIPSU_PAGES) @SOME_DRIVERS_FALSE@@WITH_MACOSX_TRUE@am__append_10 = $(MAN_MACOSX_PAGES) @SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@am__append_11 = $(LINUX_I2C_PAGES) subdir = docs/man DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)" \ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" man3dir = $(mandir)/man3 man5dir = $(mandir)/man5 man8dir = $(mandir)/man8 NROFF = nroff MANS = $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # Base configuration and client manpages, always installed SRC_CONF_PAGES = \ nut.conf.txt \ ups.conf.txt \ upsd.conf.txt \ upsd.users.txt \ upsmon.conf.txt \ upssched.conf.txt MAN_CONF_PAGES = \ nut.conf.5 \ ups.conf.5 \ upsd.conf.5 \ upsd.users.5 \ upsmon.conf.5 \ upssched.conf.5 man5_MANS = $(MAN_CONF_PAGES) $(am__append_1) HTML_CONF_MANS = \ nut.conf.html \ ups.conf.html \ upsd.conf.html \ upsd.users.html \ upsmon.conf.html \ upssched.conf.html SRC_CLIENT_PAGES = \ nutupsdrv.txt \ upsc.txt \ upscmd.txt \ upsd.txt \ upsdrvctl.txt \ upslog.txt \ upsmon.txt \ upsrw.txt \ upssched.txt MAN_CLIENT_PAGES = \ nutupsdrv.8 \ upsc.8 \ upscmd.8 \ upsd.8 \ upsdrvctl.8 \ upslog.8 \ upsmon.8 \ upsrw.8 \ upssched.8 man8_MANS = $(MAN_CLIENT_PAGES) $(MAN_TOOL_PAGES) $(am__append_2) \ $(am__append_3) $(am__append_4) $(am__append_5) \ $(am__append_6) $(am__append_7) $(am__append_8) \ $(am__append_9) $(am__append_10) $(am__append_11) HTML_CLIENT_MANS = \ nutupsdrv.html \ upsc.html \ upscmd.html \ upsd.html \ upsdrvctl.html \ upslog.html \ upsmon.html \ upsrw.html \ upssched.html SRC_TOOL_PAGES = nut-scanner.txt nut-recorder.txt MAN_TOOL_PAGES = nut-scanner.8 nut-recorder.8 HTML_TOOL_MANS = nut-scanner.html nut-recorder.html # CGI (--with-cgi) related manpages SRC_CGI_PAGES = \ hosts.conf.txt \ upsset.conf.txt \ upsstats.html.txt \ upsset.cgi.txt \ upsstats.cgi.txt \ upsimage.cgi.txt MAN5_CGI_PAGES = \ hosts.conf.5 \ upsset.conf.5 \ upsstats.html.5 MAN8_CGI_PAGES = \ upsset.cgi.8 \ upsstats.cgi.8 \ upsimage.cgi.8 HTML_CGI_MANS = \ hosts.conf.html \ upsset.conf.html \ upsstats.html.html \ upsset.cgi.html \ upsstats.cgi.html \ upsimage.cgi.html # Development (--with-dev) related manpages SRC_DEV_PAGES = \ upsclient.txt \ upscli_add_host_cert.txt \ upscli_cleanup.txt \ upscli_connect.txt \ upscli_disconnect.txt \ upscli_fd.txt \ upscli_get.txt \ upscli_init.txt \ upscli_list_next.txt \ upscli_list_start.txt \ upscli_readline.txt \ upscli_sendline.txt \ upscli_splitaddr.txt \ upscli_splitname.txt \ upscli_ssl.txt \ upscli_strerror.txt \ upscli_upserror.txt \ libnutclient.txt \ libnutclient_commands.txt \ libnutclient_devices.txt \ libnutclient_general.txt \ libnutclient_misc.txt \ libnutclient_tcp.txt \ libnutclient_variables.txt \ nutscan.txt \ nutscan_scan_snmp.txt \ nutscan_scan_usb.txt \ nutscan_scan_xml_http.txt \ nutscan_scan_nut.txt \ nutscan_scan_avahi.txt \ nutscan_scan_ipmi.txt \ nutscan_scan_eaton_serial.txt \ nutscan_display_ups_conf.txt \ nutscan_display_parsable.txt \ nutscan_cidr_to_ip.txt \ nutscan_new_device.txt \ nutscan_free_device.txt \ nutscan_add_option_to_device.txt \ nutscan_add_device_to_device.txt \ nutscan_init.txt \ nutscan_get_serial_ports_list.txt \ libupsclient-config.txt \ skel.txt # NOTE: nutclient_*.3 has no source counterpart (libnutclient_*.txt) MAN3_DEV_PAGES = \ upsclient.3 \ upscli_add_host_cert.3 \ upscli_cleanup.3 \ upscli_connect.3 \ upscli_disconnect.3 \ upscli_fd.3 \ upscli_get.3 \ upscli_init.3 \ upscli_list_next.3 \ upscli_list_start.3 \ upscli_readline.3 \ upscli_sendline.3 \ upscli_splitaddr.3 \ upscli_splitname.3 \ upscli_ssl.3 \ upscli_strerror.3 \ upscli_upserror.3 \ libnutclient.3 \ libnutclient_commands.3 \ libnutclient_devices.3 \ libnutclient_general.3 \ libnutclient_misc.3 \ libnutclient_tcp.3 \ libnutclient_variables.3 \ nutclient_authenticate.3 \ nutclient_destroy.3 \ nutclient_device_forced_shutdown.3 \ nutclient_device_login.3 \ nutclient_device_master.3 \ nutclient_execute_device_command.3 \ nutclient_get_device_command_description.3 \ nutclient_get_device_commands.3 \ nutclient_get_device_description.3 \ nutclient_get_device_num_logins.3 \ nutclient_get_device_rw_variables.3 \ nutclient_get_devices.3 \ nutclient_get_device_variable_description.3 \ nutclient_get_device_variables.3 \ nutclient_get_device_variable_values.3 \ nutclient_has_device.3 \ nutclient_has_device_command.3 \ nutclient_has_device_variable.3 \ nutclient_logout.3 \ nutclient_set_device_variable_value.3 \ nutclient_set_device_variable_values.3 \ nutclient_tcp_create_client.3 \ nutclient_tcp_disconnect.3 \ nutclient_tcp_get_timeout.3 \ nutclient_tcp_is_connected.3 \ nutclient_tcp_reconnect.3 \ nutclient_tcp_set_timeout.3 \ nutscan.3 \ nutscan_scan_snmp.3 \ nutscan_scan_usb.3 \ nutscan_scan_xml_http.3 \ nutscan_scan_nut.3 \ nutscan_scan_avahi.3 \ nutscan_scan_ipmi.3 \ nutscan_scan_eaton_serial.3 \ nutscan_display_ups_conf.3 \ nutscan_display_parsable.3 \ nutscan_cidr_to_ip.3 \ nutscan_new_device.3 \ nutscan_free_device.3 \ nutscan_add_option_to_device.3 \ nutscan_add_device_to_device.3 \ nutscan_get_serial_ports_list.3 \ nutscan_init.3 MAN1_DEV_PAGES = \ libupsclient-config.1 @WITH_DEV_TRUE@man3_MANS = $(MAN3_DEV_PAGES) @WITH_DEV_TRUE@@WITH_PKG_CONFIG_FALSE@man1_MANS = $(MAN1_DEV_PAGES) # WITH_DEV HTML_DEV_MANS = \ upsclient.html \ upscli_add_host_cert.html \ upscli_cleanup.html \ upscli_connect.html \ upscli_disconnect.html \ upscli_fd.html \ upscli_get.html \ upscli_init.html \ upscli_list_next.html \ upscli_list_start.html \ upscli_readline.html \ upscli_sendline.html \ upscli_splitaddr.html \ upscli_splitname.html \ upscli_ssl.html \ upscli_strerror.html \ upscli_upserror.html \ libnutclient.html \ libnutclient_commands.html \ libnutclient_devices.html \ libnutclient_general.html \ libnutclient_misc.html \ libnutclient_tcp.html \ libnutclient_variables.html \ nutscan.html \ nutscan_scan_snmp.html \ nutscan_scan_usb.html \ nutscan_scan_xml_http.html \ nutscan_scan_nut.html \ nutscan_scan_avahi.html \ nutscan_scan_ipmi.html \ nutscan_scan_eaton_serial.html \ nutscan_display_ups_conf.html \ nutscan_display_parsable.html \ nutscan_cidr_to_ip.html \ nutscan_new_device.html \ nutscan_free_device.html \ nutscan_add_option_to_device.html \ nutscan_add_device_to_device.html \ nutscan_get_serial_ports_list.html \ nutscan_init.html \ libupsclient-config.html \ skel.html # (--with-serial) @SOME_DRIVERS_FALSE@SRC_SERIAL_PAGES = \ @SOME_DRIVERS_FALSE@ al175.txt \ @SOME_DRIVERS_FALSE@ apcsmart.txt \ @SOME_DRIVERS_FALSE@ apcsmart-old.txt \ @SOME_DRIVERS_FALSE@ bcmxcp.txt \ @SOME_DRIVERS_FALSE@ belkin.txt \ @SOME_DRIVERS_FALSE@ belkinunv.txt \ @SOME_DRIVERS_FALSE@ bestfortress.txt \ @SOME_DRIVERS_FALSE@ bestuferrups.txt \ @SOME_DRIVERS_FALSE@ bestups.txt \ @SOME_DRIVERS_FALSE@ bestfcom.txt \ @SOME_DRIVERS_FALSE@ blazer-common.txt \ @SOME_DRIVERS_FALSE@ blazer_ser.txt \ @SOME_DRIVERS_FALSE@ clone.txt \ @SOME_DRIVERS_FALSE@ dummy-ups.txt \ @SOME_DRIVERS_FALSE@ etapro.txt \ @SOME_DRIVERS_FALSE@ everups.txt \ @SOME_DRIVERS_FALSE@ gamatronic.txt \ @SOME_DRIVERS_FALSE@ genericups.txt \ @SOME_DRIVERS_FALSE@ isbmex.txt \ @SOME_DRIVERS_FALSE@ ivtscd.txt \ @SOME_DRIVERS_FALSE@ liebert.txt \ @SOME_DRIVERS_FALSE@ liebert-esp2.txt \ @SOME_DRIVERS_FALSE@ masterguard.txt \ @SOME_DRIVERS_FALSE@ metasys.txt \ @SOME_DRIVERS_FALSE@ mge-shut.txt \ @SOME_DRIVERS_FALSE@ mge-utalk.txt \ @SOME_DRIVERS_FALSE@ oneac.txt \ @SOME_DRIVERS_FALSE@ microdowell.txt \ @SOME_DRIVERS_FALSE@ nutdrv_qx.txt \ @SOME_DRIVERS_FALSE@ optiups.txt \ @SOME_DRIVERS_FALSE@ powercom.txt \ @SOME_DRIVERS_FALSE@ powerpanel.txt \ @SOME_DRIVERS_FALSE@ rhino.txt \ @SOME_DRIVERS_FALSE@ riello_ser.txt \ @SOME_DRIVERS_FALSE@ safenet.txt \ @SOME_DRIVERS_FALSE@ solis.txt \ @SOME_DRIVERS_FALSE@ tripplite.txt \ @SOME_DRIVERS_FALSE@ tripplitesu.txt \ @SOME_DRIVERS_FALSE@ upscode2.txt \ @SOME_DRIVERS_FALSE@ victronups.txt \ @SOME_DRIVERS_FALSE@ apcupsd-ups.txt @SOME_DRIVERS_FALSE@MAN_SERIAL_PAGES = \ @SOME_DRIVERS_FALSE@ al175.8 \ @SOME_DRIVERS_FALSE@ apcsmart.8 \ @SOME_DRIVERS_FALSE@ apcsmart-old.8 \ @SOME_DRIVERS_FALSE@ bcmxcp.8 \ @SOME_DRIVERS_FALSE@ belkin.8 \ @SOME_DRIVERS_FALSE@ belkinunv.8 \ @SOME_DRIVERS_FALSE@ bestfortress.8 \ @SOME_DRIVERS_FALSE@ bestuferrups.8 \ @SOME_DRIVERS_FALSE@ bestups.8 \ @SOME_DRIVERS_FALSE@ bestfcom.8 \ @SOME_DRIVERS_FALSE@ blazer_ser.8 \ @SOME_DRIVERS_FALSE@ clone.8 \ @SOME_DRIVERS_FALSE@ dummy-ups.8 \ @SOME_DRIVERS_FALSE@ etapro.8 \ @SOME_DRIVERS_FALSE@ everups.8 \ @SOME_DRIVERS_FALSE@ gamatronic.8 \ @SOME_DRIVERS_FALSE@ genericups.8 \ @SOME_DRIVERS_FALSE@ isbmex.8 \ @SOME_DRIVERS_FALSE@ ivtscd.8 \ @SOME_DRIVERS_FALSE@ liebert.8 \ @SOME_DRIVERS_FALSE@ liebert-esp2.8 \ @SOME_DRIVERS_FALSE@ masterguard.8 \ @SOME_DRIVERS_FALSE@ metasys.8 \ @SOME_DRIVERS_FALSE@ mge-shut.8 \ @SOME_DRIVERS_FALSE@ mge-utalk.8 \ @SOME_DRIVERS_FALSE@ nutdrv_qx.8 \ @SOME_DRIVERS_FALSE@ oneac.8 \ @SOME_DRIVERS_FALSE@ microdowell.8 \ @SOME_DRIVERS_FALSE@ optiups.8 \ @SOME_DRIVERS_FALSE@ powercom.8 \ @SOME_DRIVERS_FALSE@ powerpanel.8 \ @SOME_DRIVERS_FALSE@ rhino.8 \ @SOME_DRIVERS_FALSE@ riello_ser.8 \ @SOME_DRIVERS_FALSE@ safenet.8 \ @SOME_DRIVERS_FALSE@ solis.8 \ @SOME_DRIVERS_FALSE@ tripplite.8 \ @SOME_DRIVERS_FALSE@ tripplitesu.8 \ @SOME_DRIVERS_FALSE@ upscode2.8 \ @SOME_DRIVERS_FALSE@ victronups.8 \ @SOME_DRIVERS_FALSE@ apcupsd-ups.8 @SOME_DRIVERS_FALSE@HTML_SERIAL_MANS = \ @SOME_DRIVERS_FALSE@ al175.html \ @SOME_DRIVERS_FALSE@ apcsmart.html \ @SOME_DRIVERS_FALSE@ apcsmart-old.html \ @SOME_DRIVERS_FALSE@ bcmxcp.html \ @SOME_DRIVERS_FALSE@ belkin.html \ @SOME_DRIVERS_FALSE@ belkinunv.html \ @SOME_DRIVERS_FALSE@ bestfortress.html \ @SOME_DRIVERS_FALSE@ bestuferrups.html \ @SOME_DRIVERS_FALSE@ bestups.html \ @SOME_DRIVERS_FALSE@ bestfcom.html \ @SOME_DRIVERS_FALSE@ blazer_ser.html \ @SOME_DRIVERS_FALSE@ clone.html \ @SOME_DRIVERS_FALSE@ dummy-ups.html \ @SOME_DRIVERS_FALSE@ etapro.html \ @SOME_DRIVERS_FALSE@ everups.html \ @SOME_DRIVERS_FALSE@ gamatronic.html \ @SOME_DRIVERS_FALSE@ genericups.html \ @SOME_DRIVERS_FALSE@ isbmex.html \ @SOME_DRIVERS_FALSE@ ivtscd.html \ @SOME_DRIVERS_FALSE@ liebert.html \ @SOME_DRIVERS_FALSE@ liebert-esp2.html \ @SOME_DRIVERS_FALSE@ masterguard.html \ @SOME_DRIVERS_FALSE@ metasys.html \ @SOME_DRIVERS_FALSE@ mge-shut.html \ @SOME_DRIVERS_FALSE@ mge-utalk.html \ @SOME_DRIVERS_FALSE@ nutdrv_qx.html \ @SOME_DRIVERS_FALSE@ oneac.html \ @SOME_DRIVERS_FALSE@ microdowell.html \ @SOME_DRIVERS_FALSE@ optiups.html \ @SOME_DRIVERS_FALSE@ powercom.html \ @SOME_DRIVERS_FALSE@ powerpanel.html \ @SOME_DRIVERS_FALSE@ rhino.html \ @SOME_DRIVERS_FALSE@ riello_ser.html \ @SOME_DRIVERS_FALSE@ safenet.html \ @SOME_DRIVERS_FALSE@ solis.html \ @SOME_DRIVERS_FALSE@ tripplite.html \ @SOME_DRIVERS_FALSE@ tripplitesu.html \ @SOME_DRIVERS_FALSE@ upscode2.html \ @SOME_DRIVERS_FALSE@ victronups.html \ @SOME_DRIVERS_FALSE@ apcupsd-ups.html # (--with-snmp) @SOME_DRIVERS_FALSE@SRC_SNMP_PAGES = snmp-ups.txt @SOME_DRIVERS_FALSE@MAN_SNMP_PAGES = snmp-ups.8 @SOME_DRIVERS_FALSE@HTML_SNMP_MANS = snmp-ups.html # (--with-usb) @SOME_DRIVERS_FALSE@SRC_USB_LIBUSB_PAGES = \ @SOME_DRIVERS_FALSE@ bcmxcp_usb.txt \ @SOME_DRIVERS_FALSE@ blazer-common.txt \ @SOME_DRIVERS_FALSE@ blazer_usb.txt \ @SOME_DRIVERS_FALSE@ nutdrv_atcl_usb.txt \ @SOME_DRIVERS_FALSE@ nutdrv_qx.txt \ @SOME_DRIVERS_FALSE@ richcomm_usb.txt \ @SOME_DRIVERS_FALSE@ riello_usb.txt \ @SOME_DRIVERS_FALSE@ tripplite_usb.txt \ @SOME_DRIVERS_FALSE@ usbhid-ups.txt @SOME_DRIVERS_FALSE@MAN_USB_LIBUSB_PAGES = \ @SOME_DRIVERS_FALSE@ bcmxcp_usb.8 \ @SOME_DRIVERS_FALSE@ blazer_usb.8 \ @SOME_DRIVERS_FALSE@ nutdrv_atcl_usb.8 \ @SOME_DRIVERS_FALSE@ nutdrv_qx.8 \ @SOME_DRIVERS_FALSE@ richcomm_usb.8 \ @SOME_DRIVERS_FALSE@ riello_usb.8 \ @SOME_DRIVERS_FALSE@ tripplite_usb.8 \ @SOME_DRIVERS_FALSE@ usbhid-ups.8 @SOME_DRIVERS_FALSE@HTML_USB_LIBUSB_MANS = \ @SOME_DRIVERS_FALSE@ bcmxcp_usb.html \ @SOME_DRIVERS_FALSE@ blazer_usb.html \ @SOME_DRIVERS_FALSE@ nutdrv_qx.html \ @SOME_DRIVERS_FALSE@ nutdrv_atcl_usb.html \ @SOME_DRIVERS_FALSE@ richcomm_usb.html \ @SOME_DRIVERS_FALSE@ riello_usb.html \ @SOME_DRIVERS_FALSE@ tripplite_usb.html \ @SOME_DRIVERS_FALSE@ usbhid-ups.html # (--with-neon) @SOME_DRIVERS_FALSE@SRC_NETXML_PAGES = netxml-ups.txt @SOME_DRIVERS_FALSE@MAN_NETXML_PAGES = netxml-ups.8 @SOME_DRIVERS_FALSE@HTML_NETXML_MANS = netxml-ups.html # (--with-powerman) @SOME_DRIVERS_FALSE@SRC_POWERMAN_PAGES = powerman-pdu.txt @SOME_DRIVERS_FALSE@MAN_POWERMAN_PAGES = powerman-pdu.8 @SOME_DRIVERS_FALSE@HTML_POWERMAN_MANS = powerman-pdu.html # (--with-ipmi) @SOME_DRIVERS_FALSE@SRC_IPMIPSU_PAGES = nut-ipmipsu.txt @SOME_DRIVERS_FALSE@MAN_IPMIPSU_PAGES = nut-ipmipsu.8 @SOME_DRIVERS_FALSE@HTML_IPMIPSU_MANS = nut-ipmipsu.html @SOME_DRIVERS_FALSE@SRC_MACOSX_PAGES = macosx-ups.txt @SOME_DRIVERS_FALSE@MAN_MACOSX_PAGES = macosx-ups.8 @SOME_DRIVERS_FALSE@HTML_MACOSX_MANS = macosx-ups.html @SOME_DRIVERS_FALSE@SRC_LINUX_I2C_PAGES = asem.txt @SOME_DRIVERS_FALSE@MAN_LINUX_I2C_PAGES = asem.8 @SOME_DRIVERS_FALSE@HTML_LINUX_I2C_MANS = asem.html # SOME_DRIVERS MAN_MANS = \ $(MAN_CONF_PAGES) \ $(MAN_CLIENT_PAGES) \ $(MAN_TOOL_PAGES) \ $(MAN5_CGI_PAGES) \ $(MAN8_CGI_PAGES) \ $(MAN1_DEV_PAGES) \ $(MAN3_DEV_PAGES) \ $(MAN_SERIAL_PAGES) \ $(MAN_SNMP_PAGES) \ $(MAN_USB_LIBUSB_PAGES) \ $(MAN_NETXML_PAGES) \ $(MAN_POWERMAN_PAGES) \ $(MAN_IPMIPSU_PAGES) \ $(MAN_MACOSX_PAGES) \ $(MAN_LINUX_I2C_PAGES) # distribute everything, even those not installed by default # Note that 'dist' target requires AsciiDoc! EXTRA_DIST = \ $(SRC_CONF_PAGES) \ $(SRC_CLIENT_PAGES) \ $(SRC_TOOL_PAGES) \ $(SRC_CGI_PAGES) \ $(SRC_DEV_PAGES) \ $(SRC_SERIAL_PAGES) \ $(SRC_SNMP_PAGES) \ $(SRC_USB_LIBUSB_PAGES) \ $(SRC_NETXML_PAGES) \ $(SRC_POWERMAN_PAGES) \ $(SRC_IPMIPSU_PAGES) \ $(SRC_MACOSX_PAGES) \ $(SRC_LINUX_I2C_PAGES) \ $(MAN_MANS) \ asciidoc.conf HTML_MANS = \ $(HTML_CONF_MANS) \ $(HTML_CLIENT_MANS) \ $(HTML_TOOL_MANS) \ $(HTML_CGI_MANS) \ $(HTML_DEV_MANS) \ $(HTML_SERIAL_MANS) \ $(HTML_SNMP_MANS) \ $(HTML_USB_LIBUSB_MANS) \ $(HTML_NETXML_MANS) \ $(HTML_POWERMAN_MANS) \ $(HTML_IPMIPSU_MANS) \ $(HTML_MACOSX_MANS) \ $(HTML_LINUX_I2C_MANS) CLEANFILES = *.xml *.html SUFFIXES = .txt .html .1 .3 .5 .8 ### Prior to Asciidoc ~8.6.8, the --destination-dir flag didn't seem to affect the location of the intermediate .xml file. @HAVE_ASCIIDOC_TRUE@A2X_MANPAGE_OPTS = --doctype manpage --format manpage \ @HAVE_ASCIIDOC_TRUE@ --xsltproc-opts "--nonet" \ @HAVE_ASCIIDOC_TRUE@ --attribute mansource="Network UPS Tools" \ @HAVE_ASCIIDOC_TRUE@ --attribute manversion="@PACKAGE_VERSION@" \ @HAVE_ASCIIDOC_TRUE@ --attribute manmanual="NUT Manual" \ @HAVE_ASCIIDOC_TRUE@ --destination-dir=. all: all-am .SUFFIXES: .SUFFIXES: .txt .html .1 .3 .5 .8 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu docs/man/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu docs/man/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-man1: $(man1_MANS) @$(NORMAL_INSTALL) @list1='$(man1_MANS)'; \ list2=''; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-man3: $(man3_MANS) @$(NORMAL_INSTALL) @list1='$(man3_MANS)'; \ list2=''; \ test -n "$(man3dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.3[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man3dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \ done; } uninstall-man3: @$(NORMAL_UNINSTALL) @list='$(man3_MANS)'; test -n "$(man3dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir) install-man5: $(man5_MANS) @$(NORMAL_INSTALL) @list1='$(man5_MANS)'; \ list2=''; \ test -n "$(man5dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.5[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) install-man8: $(man8_MANS) @$(NORMAL_INSTALL) @list1='$(man8_MANS)'; \ list2=''; \ test -n "$(man8dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.8[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ done; } uninstall-man8: @$(NORMAL_UNINSTALL) @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) installdirs: for dir in "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-man3 install-man5 install-man8 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-man uninstall-man: uninstall-man1 uninstall-man3 uninstall-man5 \ uninstall-man8 .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-man1 install-man3 install-man5 install-man8 \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am uninstall-man uninstall-man1 \ uninstall-man3 uninstall-man5 uninstall-man8 all: html-man: $(HTML_MANS) index.html @HAVE_ASCIIDOC_TRUE@.txt.html: @HAVE_ASCIIDOC_TRUE@ $(ASCIIDOC) --backend=xhtml11 \ @HAVE_ASCIIDOC_TRUE@ --attribute localdate=`TZ=UTC date +%Y-%m-%d` \ @HAVE_ASCIIDOC_TRUE@ --attribute localtime=`TZ=UTC date +%H:%M:%S` \ @HAVE_ASCIIDOC_TRUE@ --attribute nutversion="@PACKAGE_VERSION@" \ @HAVE_ASCIIDOC_TRUE@ -o $@ $< @HAVE_ASCIIDOC_TRUE@.txt.1: @HAVE_ASCIIDOC_TRUE@ $(A2X) $(A2X_MANPAGE_OPTS) $< @HAVE_ASCIIDOC_TRUE@.txt.3: @HAVE_ASCIIDOC_TRUE@ $(A2X) $(A2X_MANPAGE_OPTS) $< @HAVE_ASCIIDOC_TRUE@.txt.5: @HAVE_ASCIIDOC_TRUE@ $(A2X) $(A2X_MANPAGE_OPTS) $< @HAVE_ASCIIDOC_TRUE@.txt.8: @HAVE_ASCIIDOC_TRUE@ $(A2X) $(A2X_MANPAGE_OPTS) $< @HAVE_ASCIIDOC_FALSE@.txt.html: @HAVE_ASCIIDOC_FALSE@ @if [ -r "$@" ]; then \ @HAVE_ASCIIDOC_FALSE@ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ @HAVE_ASCIIDOC_FALSE@ else \ @HAVE_ASCIIDOC_FALSE@ echo "Could not find prebuilt $@ manual page." ; \ @HAVE_ASCIIDOC_FALSE@ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ @HAVE_ASCIIDOC_FALSE@ exit 1; \ @HAVE_ASCIIDOC_FALSE@ fi @HAVE_ASCIIDOC_FALSE@.txt.1: @HAVE_ASCIIDOC_FALSE@ @if [ -r "$@" ]; then \ @HAVE_ASCIIDOC_FALSE@ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ @HAVE_ASCIIDOC_FALSE@ else \ @HAVE_ASCIIDOC_FALSE@ echo "Could not find prebuilt $@ manual page." ; \ @HAVE_ASCIIDOC_FALSE@ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ @HAVE_ASCIIDOC_FALSE@ exit 1; \ @HAVE_ASCIIDOC_FALSE@ fi @HAVE_ASCIIDOC_FALSE@.txt.3: @HAVE_ASCIIDOC_FALSE@ @if [ -r "$@" ]; then \ @HAVE_ASCIIDOC_FALSE@ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ @HAVE_ASCIIDOC_FALSE@ else \ @HAVE_ASCIIDOC_FALSE@ echo "Could not find prebuilt $@ manual page." ; \ @HAVE_ASCIIDOC_FALSE@ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ @HAVE_ASCIIDOC_FALSE@ exit 1; \ @HAVE_ASCIIDOC_FALSE@ fi @HAVE_ASCIIDOC_FALSE@.txt.5: @HAVE_ASCIIDOC_FALSE@ @if [ -r "$@" ]; then \ @HAVE_ASCIIDOC_FALSE@ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ @HAVE_ASCIIDOC_FALSE@ else \ @HAVE_ASCIIDOC_FALSE@ echo "Could not find prebuilt $@ manual page." ; \ @HAVE_ASCIIDOC_FALSE@ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ @HAVE_ASCIIDOC_FALSE@ exit 1; \ @HAVE_ASCIIDOC_FALSE@ fi @HAVE_ASCIIDOC_FALSE@.txt.8: @HAVE_ASCIIDOC_FALSE@ @if [ -r "$@" ]; then \ @HAVE_ASCIIDOC_FALSE@ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ @HAVE_ASCIIDOC_FALSE@ else \ @HAVE_ASCIIDOC_FALSE@ echo "Could not find prebuilt $@ manual page." ; \ @HAVE_ASCIIDOC_FALSE@ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ @HAVE_ASCIIDOC_FALSE@ exit 1; \ @HAVE_ASCIIDOC_FALSE@ fi # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/docs/man/apcsmart.80000644000175000017500000004230112640476473012676 00000000000000'\" t .\" Title: apcsmart .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "APCSMART" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" apcsmart \- Driver for American Power Conversion Smart Protocol UPS equipment .SH "SYNOPSIS" .sp \fBapcsmart\fR \-h .sp \fBapcsmart\fR \-a \fIUPS_NAME\fR [\-x option=value \&...] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the apcsmart driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp The apcsmart driver should recognize (or at the very least, work with) the majority of Smart\-UPS models \- which includes Smart\-UPS, Matrix\-UPS and Back\-UPS lineups, among few other ones\&. .sp Currently, we can roughly divide APC hardware into four groups (note that the division isn\(cqt strict by any means, and the borders between those are pretty fuzzy): .PP [very] "old" models .RS 4 These models usually have old APC logo, white color and \fIno\fR programmable EEPROM; you won\(cqt find them listed anywhere on APC\(cqs site either\&. The support for those will be usually based on driver\(cqs compatibility tables, or if the model (firmware) is not listed in those \- the driver will try to follow the very basic subset of features, while still trying to remain useful\&. Despite "smart" tagname, they often tend to behave in pretty dumb way (see the section below about shutdown behaviour)\&. .PP \fBExample models:\fR .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Smart\-UPS 2000I .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Smart\-UPS 900I .RE .RE .PP "new" models .RS 4 These models usually come from late 1990s / pre\-2009 times\&. They are often referred as "3rd\&. gen"\&. For the most part, they have programmable EEPROM, report supported commands and capabilites, and should work just fine with the apcsmart driver\&. .RE .PP "microlink" models .RS 4 WARNING: these are not \fInatively\fR supported by \fBapcsmart\fR (or \fBapcupsd\fR, for that matter, if you\(cqre wondering)\&. Around 2007, APC (now APC Schneider) decided to go back to its proprietry roots, and all the new models (SMT, SMX, SURTD) use completely different protocol and cables\&. If you purchased a new APC UPS \- that uses cable with RJ45 on the one end, and DB\-9 on the other \- then you have such model\&. Your only option to support it through \fBNUT\fR is to purchase a "legacy communications card" \- part #AP9620 (google \*(AqAP9620\*(Aq for more details)\&. Or if that\(cqs not an option, rely on official software\&. .RE .PP Microsol models .RS 4 Several Microsol serial models sold in Brazil have been rebranded as APC Back\-UPS, and the model numbers tend to start with "BZ"\&. If you have one of these "Nobreaks", they will not work with the \fBapcsmart\fR driver \- please see the \fBsolis\fR(8) driver instead\&. .PP \fBExample models:\fR .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Back\-UPS BZ1200\-BR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Back\-UPS BZ2200BI\-BR .RE .RE .sp Another thing to remember is that Smart protocol is not USB protocol\&. If you have UPS with both USB and serial ports, then depending on how you connect it, you will need either apcsmart or usbhid\-ups driver\&. .SH "CABLING" .sp This driver expects to see a 940\-0024C cable or a clone by default\&. You can switch to the 940\-0095B dual\-mode cable support with the \*(Aqcable=\*(Aq definition described below\&. .sp If your 940\-xx24X cable is broken or missing, use this diagram to build a clone: .sp http://www\&.networkupstools\&.org/cables\&.html#_940_0024c_clone .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp The "xx" is either "00" for a short cable, or the number of feet of a longer cable\&. The "X" is a letter representing the minor revision of the physical cable and its connectors ("C" and "E" are commonly found revisions)\&. All minor revisions should use the same pin\-outs and wiring\&. .sp .5v .RE .sp You can specify alternate cable in \fBups.conf\fR(5): .sp \fBcable\fR=940\-0095B .sp Alternatively, you can also provide it on the command line using: .sp \-x \fBcable\fR=940\-0095B .SH "TTY MODES" .sp By default the driver works in canonical mode, but it proved to be a problem in Windows systems\&. Furthermore there\(cqs a possibility of some obscure serial cards or serial\-USB converters that could cause problems as well\&. You can use \*(Aqttymode=\*(Aq option to force non\-canonical discipline in \fBups.conf\fR(5): .sp \fBttymode\fR=raw .sp Alternatively, you can also provide it on the command line using: .sp \-x \fBttymode\fR=raw .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp Any other value will make the driver work in the canonical mode\&. .sp .5v .RE .SH "EXPLANATION OF SHUTDOWN METHODS SUPPORTED BY APC UPSES" .sp APC hardware supports a lot of shutdown methods, that themselves can differ in behaviour quite a bit, depending on the model\&. .PP \fBS\fR (soft hibernate) .RS 4 This is most basic command present in probably all APC models\&. It will hibernate the UPS, and subsequently wake it up when the mains supply returns\&. \fBThe command doesn\(cqt work if the UPS is running on mains\&.\fR .PP "old" models .RS 4 The behaviour here is unfortunately pretty primitive \- when the power returns, the UPS just wakes up\&. No grace periods, no min\&. battery charge condition, etc\&. This is probably not what you want\&. .RE .PP "new" models .RS 4 The behaviour here is as expected \- the power is cut off after the EEPROM defined grace period\&. The UPS will wake up when the power returns, after the EEPROM defined delay AND if the EEPROM defined min\&. battery charge level is met\&. The delay is counted from the power\*(Aqs return\&. .RE .RE .PP \fBCS\fR (aka "force OB hack") .RS 4 This is a trick to make UPS power down even if it\*(Aqs running on mains\&. Immediately before issuing \fBS\fR, "simulate power failure" is issued\&. The remaining behaviour is as in \fBS\fR case\&. .sp The name came from APC CS models, where such trick was used to power down UPSes in consistent fashion using only \fBS\fR\&. It\*(Aqs better to use \fB@nnn\fR command if your UPS supports it (and is not too old, see below)\&. .RE .PP \fB@nnn\fR (hard hibernate) .RS 4 This is basic command used to hibernate UPS regardless if it\*(Aqs running on batteries or on mains\&. The option takes 3 digits argument which can be used to specify additional wakeup delay (in 6 minute units)\&. .PP "old" models .RS 4 The behaviour is \- unfortunately \- similary primitive to \fBS\fR\&. The UPS unconditionally wakes up after nnn*6 minutes \- \fBit doesn\*(Aqt care if the power returned !\fR If nnn = 000, then UPS will do precisely nothing\&. On those models you\*(Aqre better specifying nnn > 0, if you can estimate the kind of power problems that might be happening in your environment\&. Another thing to consider with "old" models \- you might lose the connection with the UPS, until it wakes up (with \fBS\fR, the serial connection is kept alive)\&. .RE .PP "new" models .RS 4 All the usual variables defined in EEPROM are respected (see \fBS\fR)\&. Additionally, if nnn > 0, the nnn*6 minutes are added to EEPROM defined delay\&. UPS will not power up if it\*(Aqs running on batteries, contrary to what "old" models used to do \- the combined delay is counted from the moment of power return\&. .RE .sp Supposedly there exist models that take 2 digits instead of 3\&. Just in case, NUT also supports such variation\&. You have to provide exactly 2 digits to trigger it (\fBawd\fR option, or argument to one of the supported instant commands)\&. .RE .PP \fBK\fR (delayed poweroff) .RS 4 This is permanent poweroff \- the UPS will not wake up automatically\&. On newer units, it will respect applicable EEPROM variables\&. .RE .PP \fBZ\fR (instant poweroff) .RS 4 This is also permanent poweroff \- the UPS will not wake up automatically\&. The poweroff is executed immediately\&. .RE .SH "SHUTDOWN CONTROL BY NUT" .sp There are three options used to control the shutdown behaviour\&. .PP \fBsdtype\fR=[0\-5] .RS 4 This option takes a single digit (0\-5) as an argument\&. See below for details\&. .RE .PP \fBadvorder\fR=no|[0\-4]+ .RS 4 This option takes string of digits as an argument\&. Methods listed are tried in turn until one of them succeedes\&. Note that the meaning of digits is different from \fBsdtype\fR\&. See below for details\&. .RE .PP \fBawd\fR=[0\-9]{1,3} .RS 4 This option lets you specify additional wakeup delay used by \fB@\fR\&. If you provide exactly 2 digits, the driver will try 2 digits variation (see previous section for more info)\&. Otherwise standard 3 digits variation is used\&. \fBNote: the time unit is 6 minutes !\fR .RE .sp Keep in mind that \fBsdtype\fR and \fBadvorder\fR are mutually exclusive\&. If \fBadvorder\fR is provided, \fBsdtype\fR is ignored\&. If \fBadvorder\fR is set to \*(Aqno\*(Aq, \fBsdtype\fR is used instead\&. .sp If nothing is provided, \fBNUT\fR will assume \fBsdtype\fR=0 \- which is generally fine for anything not too ancient or not too quirky\&. .SS "SDTYPE" .sp The values permitted are from 0 to 5\&. Only one can be specified\&. Anything else will cause apcsmart to exit\&. .PP 0 .RS 4 issue soft hibernate (\fBS\fR) if the UPS is running on batteries, otherwise issue hard hibernate (\fB@\fR) .RE .PP 1 .RS 4 issue soft hibernate (\fBS\fR) (if on batteries), and if it fails (or on mains) \- try hard hibernate (\fB@\fR) .RE .PP 2 .RS 4 issue instant poweroff (\fBZ\fR) .RE .PP 3 .RS 4 issue delayed poweroff (\fBK\fR) .RE .PP 4 .RS 4 issue "force OB hack" (\fBCS\fR) .RE .PP 5 .RS 4 issue hard hibernate (\fB@\fR) .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp Hard hibernate\*(Aqs additional wakeup delay can be provided by \fBawd\fR\&. .sp .5v .RE .SS "ADVORDER" .sp The argument is either a word \*(Aqno\*(Aq, or a string of 1 \- 5 digits in [0 \- 4] range\&. Each digit maps to the one of shutdown methods supported by APC UPSes\&. Methods listed in this way are tried in order, until one of them succedes\&. .sp If \fBadvorder\fR is undefined or set to \*(Aqno\*(Aq, \fBsdtype\fR is used instead\&. .sp The mapping is as follows: .TS tab(:); lt lt lt lt lt lt lt lt lt lt. T{ .sp 0 T}:T{ .sp soft hibernate (\fBS\fR) T} T{ .sp 1 T}:T{ .sp hard hibernate (\fB@\fR) T} T{ .sp 2 T}:T{ .sp delayed poweroff (\fBK\fR) T} T{ .sp 3 T}:T{ .sp instant poweroff (\fBZ\fR) T} T{ .sp 4 T}:T{ .sp "force OB hack" (\fBCS\fR) T} .TE .sp 1 .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp Hard hibernate\*(Aqs additional wakeup delay can be provided by \fBawd\fR\&. .sp .5v .RE .SH "IGNORING LB STATE" .sp APC units \- even if they report LB mode \- will not go into shutdown automatically\&. This gives us even more control with reference to "when to actually shutdown PSU"\&. Since version 2\&.6\&.2, NUT supports \fBignorelb\fR option in driver\(cqs section of \fBups.conf\fR(5)\&. When such option is in effect, the core driver will ignore LB state as reported by specific driver and start shutdown basing the decision \fIonly\fR on two conditions: .sp battery\&.charge < battery\&.charge\&.low .sp \fBOR\fR .sp battery\&.runtime < battery\&.runtime\&.low .sp Of course \- if any of the variables are not available, the appropriate condition is not checked\&. If you want to explicitly disable one of the conditions, simply override the right hand variable causing the condition to always evaluate to false (you can even provide negative numbers)\&. .sp APC UPSes don\(cqt have battery\&.charge\&.low \- you will have to define it if you want to use such condition (prefix the variable with override\&. or default\&.)\&. .sp "New" units have battery\&.runtime\&.low, but depending on battery quality, firmware version, calibration and UPS load \- this variable can be underestimated quite a bit \- especially right after going into OB state\&. This in turn can cause LB to be asserted, which under normal conditions will cause \fBNUT\fR to initiate the shutdown\&. You might want to disable this condition entirely, when relying on \fBignorelb\fR option (this was actually the main motivation behind introduction of such feature)\&. .sp Simple example: .sp .if n \{\ .RS 4 .\} .nf [apc] ignorelb override\&.battery\&.charge\&.low = 15 override\&.battery\&.runtime\&.low = \-1 .fi .if n \{\ .RE .\} .sp This would cause apcsmart to go into shutdown \fIonly\fR if detected battery charge < 15%\&. Runtime condition is always false in this example\&. .sp You could ask \- why bother ? Well, the reason is already hinted above\&. APC units can be very picky about the batteries, and their firmware can underestimate the remaining runtime (especially right after going into OB state)\&. \fBignorelb\fR option and \fBoverride\&.*\fR let you remain in control of the UPS, not UPS in control of you\&. .sp Furthermore, this allows to specify conditions similary to how it\(cqs done in apcupsd daemon, so it should be welcome by people used to that software\&. .SH "SUPPORTED INSTANT COMMANDS" .sp The apcsmart driver exposes following instant commands: .PP shutdown\&.return .RS 4 executes soft hibernate .RE .PP shutdown\&.return cs .RS 4 executes "force OB hack" .RE .PP shutdown\&.return at: .RS 4 executes "hard hibernate" with *6 minutes additional wakeup delay ( format is the same as of \fBawd\fR option) .RE .PP shutdown\&.stayoff .RS 4 executes "delayed poweroff" .RE .PP load\&.off .RS 4 executes "instant poweroff" .RE .sp All the above commands must be issued 2nd time to have any effect (no less than 3 seconds, and no more than 15 seconds after the initial call)\&. Those commands are mostly useful for manual testing, when your machine is not powered by the UPS you\*(Aqre testing\&. .sp Other supported commands: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} load\&.on .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.panel\&.start .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.failure\&.start .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.battery\&.start .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.battery\&.stop .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} bypass\&.start .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} bypass\&.stop .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} calibrate\&.start .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} calibrate\&.stop .RE .SH "PREVIOUS DRIVER VERSION" .sp Previous driver is still available as \fBapcsmart\-old\fR, should there be any need to use earlier version (bugs, incompatiblities with new functionality, etc\&.)\&. In due time, \fBapcsmart\-old\fR will be phased out completely, but this won\(cqt happen until the new version gets solid exposure with no pending issues\&. .SH "BUGS" .sp Some older APC UPS models return bogus data in the status register during a front panel test\&. This is usually detected and discarded, but some other unexpected values have occasionally slipped through\&. .sp APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa\&. .SH "AUTHOR" .sp Nigel Metheringham (drawing heavily on the original apcsmart driver by Russell Kroll)\&. This driver was called newapc for a time and was renamed in the 1\&.5 series\&. In 2\&.6\&.2 it was renamed to apcsmart\-old, being superseded by updated version with new features, which is maintained by Michal Soltys .SH "SEE ALSO" .sp \fBnutupsdrv\fR(8), \fBups.conf\fR(5), \fBusbhid-ups\fR(8), \fBsolis\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscli_readline.txt0000644000175000017500000000161112640443572014667 00000000000000UPSCLI_READLINE(3) ================== NAME ---- upscli_readline - read a single response from a UPS SYNOPSIS -------- #include int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); DESCRIPTION ----------- The *upscli_readline()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, receives a single line from the server, and copies up to 'buflen' bytes of the response into the buffer 'buf'. Some parsing of the string occurs during reception. In particular, ERR messages from linkman:upsd[8] are detected and will cause this function to return -1. RETURN VALUE ------------ The *upscli_readline()* function returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/hosts.conf.50000644000175000017500000000443012665610625013142 00000000000000'\" t .\" Title: hosts.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "HOSTS\&.CONF" "5" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" hosts.conf \- Access control for Network UPS Tools CGI programs .SH "DESCRIPTION" .sp The CGI programs (\fBupsset.cgi\fR(8), \fBupsstats.cgi\fR(8), \fBupsimage.cgi\fR(8)) use this file to determine if they are allowed to talk to a host\&. This keeps random visitors from using your web server to annoy others by creating outgoing connections\&. .SH "DIRECTIVES" .PP \fBMONITOR\fR \fIups\fR \fIdescription\fR .RS 4 The \fIups\fR element is in the form upsname[@hostname[:port]]\&. To allow connections to a UPS called "snoopy" on a system called "doghouse" that runs upsd on port 7877, it would look like this: .sp .if n \{\ .RS 4 .\} .nf MONITOR snoopy@doghouse:7877 "Joe Cool" .fi .if n \{\ .RE .\} .sp The description must be one element, so if it has spaces, then it must be wrapped with quotes as shown above\&. The default hostname is "localhost"\&. .RE .SH "SEE ALSO" .sp \fBupsset.cgi\fR(8), \fBupsstats.cgi\fR(8), \fBupsimage.cgi\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/libnutclient_variables.30000644000175000017500000000733612665610646015613 00000000000000'\" t .\" Title: libnutclient_variables .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT_VARIABL" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient_variables, nutclient_get_device_variables, nutclient_get_device_rw_variables, nutclient_has_device_variable, nutclient_get_device_variable_description, nutclient_get_device_variable_values, nutclient_set_device_variable_value, nutclient_set_device_variable_values \- Variable related functions in Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf typedef void* NUTCLIENT_t; .fi .sp .nf strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev); strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev); int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var); char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var); strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var); void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value); void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values); .fi .SH "DESCRIPTION" .sp These functions allow to manage variables of devices\&. .sp The \fBnutclient_get_device_variables()\fR function retrieve the list of variables names for a device\&. The returned strarr must be freed by \fIstrarr_free\fR\&. .sp The \fBnutclient_get_device_rw_variables\fR function retrieve the list of read\-write variables names for a device\&. The returned strarr must be freed by \fIstrarr_free\fR\&. .sp The \fBnutclient_has_device_variable\fR function test if the specified variable is supported by the device\&. Return 1 is supported and 0 if not\&. .sp The \fBnutclient_get_device_variable_description\fR function retrieve the variable description, if any\&. The resturned string must be freed\&. .sp The \fBnutclient_get_device_variable_values\fR returns variable values (generally only one)\&. The returned strarr must be freed by \fIstrarr_free\fR\&. .sp The \fBnutclient_set_device_variable_value\fR intend to set the value of the specified variable\&. .sp The \fBnutclient_set_device_variable_values\fR intend to set multiple values of the specified variable\&. .sp \fIdev\fR is the device name\&. .sp \fIvar\fR is the variable name\&. .sp \fIvalue\fR is the variable value\&. .sp \fIvalues\fR is the variable array of values\&. .SH "SEE ALSO" .sp \fBlibnutclient\fR(3) \fBlibnutclient_devices\fR(3) \fBlibnutclient_general\fR(3) nut-2.7.4/docs/man/upscli_splitname.txt0000644000175000017500000000243412640443572015104 00000000000000UPSCLI_SPLITNAME(3) =================== NAME ---- upscli_splitname - split a UPS definition into its components SYNOPSIS -------- #include int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port) DESCRIPTION ----------- The *upscli_splitname()* function takes a pointer to the raw UPS definition 'buf' and returns pointers to dynamically allocated memory in 'upsname' and 'hostname'. It also copies the port number into 'port'. FORMATTING ---------- A UPS definition is specified according to this format: [@[:]] When the UPS name is not given, this function will print an error to stderr and return -1 without changing anything. Definitions without an explicit port value receive the default value of 3493. The default hostname is "localhost". MEMORY USAGE ------------ You must *free*(3) the pointers to 'upsname' and 'hostname' when you are done with them to avoid memory leaks. RETURN VALUE ------------ The *upscli_splitname()* function returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_splitaddr[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/nut-scanner.txt0000644000175000017500000001622612640473702013770 00000000000000NUT-SCANNER(8) ============== NAME ---- nut-scanner - scan communication buses for NUT devices SYNOPSIS -------- *nut-scanner* -h *nut-scanner* ['OPTIONS'] DESCRIPTION ----------- *nut-scanner* scans available communication buses and displays any NUT-compatible devices it has found. INSTALLATION ------------ *nut-scanner* is only built if libltdl (part of libtool development suite) is available. Available options (USB, SNMP, IPMI, ...) will vary according to the available compile time and runtime dependencies. For example, if Net-SNMP is installed, thus providing libsnmp (.so or .dll) and headers, both during compilation and runtime, then SNMP discovery will be available. OPTIONS ------- *-h*:: Display the help text. DISPLAY OPTIONS --------------- *-N* | *--disp_nut_conf*:: Display result in the 'ups.conf' format. *-P* | *--disp_parsable*:: Display result in a parsable format. BUS OPTIONS ----------- *-C* | *--complete_scan*:: Scan all available communication buses (default behavior) *-U* | *--usb_scan*:: List all NUT-compatible USB devices currently plugged in. *-S* | *--snmp_scan*:: Scan SNMP devices. Requires at least a 'start IP', and optionally, an 'end IP'. See specific SNMP OPTIONS for community and security settings. *-M* | *--xml_scan*:: Scan XML/HTTP devices. Broadcast a network message on the current network interfaces to retrieve XML/HTTP capable devices. No IP required. *-O* | *--oldnut_scan*:: Scan NUT devices (i.e. upsd daemon) on IP ranging from 'start IP' to 'end IP'. *-A* | *--avahi_scan*:: Scan NUT servers using Avahi request on the current network interfaces. No IP required. *-I* | *--ipmi_scan*:: Scan NUT compatible power supplies available via IPMI on the current host, or over the network. *-E* | *--eaton_serial* 'serial ports':: Scan Eaton devices (XCP and SHUT) available via serial bus on the current host. This option must be requested explicitely, even for a complete scan. 'serial ports' can be expressed in various forms: - 'auto' to scan all serial ports. - a single charcater indicating a port number ('0' (zero) for /dev/ttyS0 and /dev/ttyUSB0 on Linux, '1' for COM1 on Windows, 'a' for /dev/ttya on Solaris...) - a range of N characters, hyphen separated, describing the range of ports using 'X-Y', where X and Y are characters refering to the port number. - a single port name. - a list of ports name, coma separated, like '/dev/ttyS1,/dev/ttyS4'. NETWORK OPTIONS --------------- *-t* | *--timeout* 'timeout':: Set the network timeout in seconds. Default timeout is 5 seconds. *-s* | *--start_ip* 'start IP':: Set the first IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut). *-e* | *--end_ip* 'end IP':: Set the last IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut). If this parameter is omitted, only the 'start IP' is scanned. If 'end IP' is less than 'start IP', both parameters are internally permuted. *-m* | *--mask_cidr* 'IP address/mask':: Set a range of IP using CIDR notation. NUT DEVICE OPTION ----------------- *-p* | *--port* 'port number':: Set the port number of scanned NUT devices (default 3493). SNMP V1 OPTION -------------- *-c* | *--community* 'community':: Set SNMP v1 community name (default = public). SNMP V3 OPTIONS --------------- *-l* | *--secLevel* 'security level':: Set the 'security level' used for SNMPv3 messages. Allowed values are: noAuthNoPriv, authNoPriv and authPriv. *-u* | *--secName* 'security name':: Set the 'security name' used for authenticated SNMPv3 messages. This parameter is mandatory if you set 'security level'. *-w* | *--authProtocol* 'authentication protocol':: Set the 'authentication protocol' used for authenticated SNMPv3 messages. Allowed values are MD5 or SHA. Default value is MD5. *-W* | *--authPassword* 'authentication pass phrase':: Set the 'authentication pass phrase' used for authenticated SNMPv3 messages. This parameter is mandatory if you set 'security level' to authNoPriv or authPriv. *-x* | *--privProtocol* 'privacy protocol':: Set the 'privacy protocol' used for encrypted SNMPv3 messages. Allowed values are DES or AES. Default value is DES. *-X* | *--privPassword* 'privacy pass phrase':: Set the 'privacy pass phrase' used for encrypted SNMPv3 messages. This parameter is mandatory if you set 'security level' to authPriv. IPMI OPTIONS ------------ *-b* | *--username* 'username':: Set the username used for authenticating IPMI over LAN connections (mandatory for IPMI over LAN. No default). *-B* | *--password* 'password':: Specify the password to use when authenticating with the remote host (mandatory for IPMI over LAN. No default). *-d* | *--authType* 'authentication type':: Specify the IPMI 1.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, MD2, and MD5) with the remote host (default=MD5). This forces connection through the 'lan' IPMI interface , thus in IPMI 1.5 mode. *-D* | *--cipher_suite_id* 'cipher suite identifier':: Specify the IPMI 2.0 cipher suite ID to use. The Cipher Suite ID identifies a set of authentication, integrity, and confidentiality algorithms to use for IPMI 2.0 communication. The authentication algorithm identifies the algorithm to use for session setup, the integrity algorithm identifies the algorithm to use for session packet signatures, and the confidentiality algorithm identifies the algorithm to use for payload encryption (default=3). + The following cipher suite ids are currently supported (Authentication; Integrity; Confidentiality): - *0*: None; None; None - *1*: HMAC-SHA1; None; None - *2*: HMAC-SHA1; HMAC-SHA1-96; None - *3*: HMAC-SHA1; HMAC-SHA1-96; AES-CBC-128 - *6*: HMAC-MD5; None; None - *7*: HMAC-MD5; HMAC-MD5-128; None - *8*: HMAC-MD5; HMAC-MD5-128; AES-CBC-128 - *11*: HMAC-MD5; MD5-128; None - *12*: HMAC-MD5; MD5-128; AES-CBC-128 - *15*: HMAC-SHA256; None; None - *16*: HMAC-SHA256; HMAC_SHA256_128; None - *17*: HMAC-SHA256; HMAC_SHA256_128; AES-CBC-128 MISCELLANEOUS OPTIONS --------------------- *-V* | *--version*:: Display NUT version. *-a* | *--available*:: Display available bus that can be scanned , depending on how the binary has been compiled. (OLDNUT, USB, SNMP, XML, AVAHI, IPMI). *-q* | *--quiet*:: Display only scan result. No information on currently scanned bus is displayed. EXAMPLES -------- To scan USB devices only: *nut-scanner -U* To scan SNMP v1 device with public community on address range 192.168.0.0 to 192.168.0.255: *nut-scanner -S -s 192.168.0.0 -e 192.168.0.255* The same using CIDR notation: *nut-scanner -S -m 192.168.0.0/24* To scan NUT servers with a timeout of 10 seconds on IP range 192.168.0.0 to 192.168.0.127 using CIDR notation: *nut-scanner -O -t 10 -m 192.168.0.0/25* To scan for power supplies, through IPMI (1.5 mode) over the network, on address range 192.168.0.0 to 192.168.0.255: *nut-scanner -I -m 192.168.0.0/24 -b username -B password* To scan for Eaton serial devices on ports 0 and 1 (/dev/ttyS0, /dev/ttyUSB0, /dev/ttyS1 and /dev/ttyUSB1 on Linux): *nut-scanner --eaton_serial 0-1* To scan for Eaton serial devices on ports 1 and 2 (COM1 and COM2 on Windows): *nut-scanner --eaton_serial 1-2* SEE ALSO -------- linkman:ups.conf[5] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/asem.txt0000644000175000017500000000451612640473702012457 00000000000000ASEM(8) ======= NAME ---- asem - driver for UPS in ASEM PB1300 NOTE ---- This man page only documents the hardware-specific features of the *asem* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The *asem* driver supports the UPS in ASEM PB1300 embedded PCs. Likely other I2C devices from the same manufacturer will work too, since this is a "custom" charger. Seems that there are two versions of the charger. Older one is based on Max1667, newer one is a custom solution. Both are on I2C address 0x09. To be compatible with both versions, the driver just reads bit 15 of address 0x13 which yields online/on battery status. Battery monitor is a BQ2060 at address 0x0B. EXTRA ARGUMENTS --------------- The required parameter for this driver is the I2C bus name: *port*='dev-node':: On the Asem PB1300, this should be `/dev/i2c-7` for the i801 SMBUS adapter. This driver also supports the following optional settings: *lb*='num':: Set the low battery threshold to 'num' volts. *hb*='num':: Set the high battery threshold to 'num' volts. INSTALLATION ------------ This driver is specific to the Linux I2C API, and requires the lm_sensors libi2c-dev or its equivalent to compile. Beware that the SystemIO memory used by the I2C controller is reserved by ACPI. If only a native I2C driver (e.g. i2c_i801, as of 3.5.X Linux kernels) is available, then you'll need to relax the ACPI resources check. For example, you can boot with the `acpi_enforce_resources=lax` option. ////////////////////////////////////////// Optional: use DIAGNOSTICS to describe troubleshooting techniques that are longer than what can be conveniently described in the driver error messages. DIAGNOSTICS ----------- ////////////////////////////////////////// KNOWN ISSUES AND BUGS --------------------- The driver shutdown function is not implemented, so other arrangements must be made to turn off the UPS. AUTHORS ------- Giuseppe Corbelli SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ PB1300 specifications: http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ BQ2060 datasheet: http://www.ti.com/lit/ds/symlink/bq2060.pdf The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutupsdrv.80000644000175000017500000001672212640476464013146 00000000000000'\" t .\" Title: nutupsdrv .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTUPSDRV" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutupsdrv \- generic manual for unified NUT drivers .SH "SYNOPSIS" .sp \fBnutupsdrv\fR \fI\-h\fR .sp \fBnutupsdrv\fR [OPTIONS] .SH "DESCRIPTION" .sp \fBnutupsdrv\fR is not actually a driver\&. This is a combined man page for the shared code that is the core of many drivers within the Network UPS Tools package\&. .sp For information on the specific drivers, see their individual man pages\&. .sp UPS drivers provide a communication channel between the physical UPS hardware and the \fBupsd\fR(8) server\&. The driver is responsible for translating the native protocol of the UPS to the common format used by the rest of this package\&. .sp The core has two modes of operation which are determined by the command line switches\&. In the normal mode, the driver will periodically poll the UPS for its state and parameters\&. The results of this command is presented to upsd\&. The driver will also handle setting variables and instant commands if available\&. .sp The driver can also instruct the UPS to shut down the load, possibly after some delay\&. This mode of operation is intended for cases when it is known that the UPS is running out of battery power and the systems attached must be turned off to ensure a proper reboot when power returns\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp You probably don\(cqt want to use any of these options directly\&. You should use \fBupsdrvctl\fR(8) to control your drivers, and \fBups.conf\fR(5) to configure them\&. The rest of this manual describes options and parameters that generally are not needed by normal users\&. .sp .5v .RE .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display a help message without doing anything else\&. This will also list possible values for \fI\-x\fR in that driver, and other help text that the driver\(cqs author may have provided\&. .RE .PP \fB\-a\fR \fIid\fR .RS 4 Autoconfigure this driver using the \fIid\fR section of \fBups.conf\fR(5)\&. \fBThis argument is mandatory when calling the driver directly\&.\fR .RE .PP \fB\-D\fR .RS 4 Raise the debugging level\&. Use this multiple times to see more details\&. Running a driver in debug mode will prevent it from backgrounding after startup\&. It will keep on logging information to the console until it receives a SIGINT (usually Ctrl\-C) or SIGTERM signal\&. .sp The level of debugging needed depends both on the driver and the problem you\(cqre trying to diagnose\&. Therefore, first explain the problem you have with a driver to a developer/maintainer, before sending them debugging output\&. More often than not, if you just pick a level, the output may be either too limited or too verbose to be of any use\&. .RE .PP \fB\-q\fR .RS 4 Raise log level threshold\&. Use this multiple times to log more details\&. .sp The debugging comment above also applies here\&. .RE .PP \fB\-i\fR \fIinterval\fR .RS 4 Set the poll interval for the device\&. .RE .PP \fB\-V\fR .RS 4 Print only version information, then exit\&. .RE .PP \fB\-L\fR .RS 4 Print a parseable list of driver variables\&. Mostly useful for configuration wizard programs\&. .RE .PP \fB\-k\fR .RS 4 ("Kill" power) Forced shutdown mode\&. The UPS will power off the attached load, if possible\&. .sp You should use upsdrvctl shutdown whenever possible instead of calling this directly\&. .RE .PP \fB\-r\fR \fIdirectory\fR .RS 4 The driver will chroot(2) to \fIdirectory\fR during initialization\&. This can be useful when securing systems\&. .sp In addition to the state path, many systems will require /dev/null to exist within \fIdirectory\fR for this to work\&. The serial ports are opened before the chroot call, so you do not need to create them inside the jail\&. In fact, it is somewhat safer if you do not\&. .RE .PP \fB\-u\fR \fIusername\fR .RS 4 If started as root, the driver will setuid(2) to the user id associated with \fIusername\fR\&. .sp If you do not specify this value and start it as root, the driver will switch to the default value that was compiled into the code\&. This is typically \fInobody\fR, and is far from ideal\&. .RE .PP \fB\-x\fR \fIvar\fR=\fIval\fR .RS 4 Define a variable called \fIvar\fR with the value of \fIvar\fR in the driver\&. This varies from driver to driver \- see the specific man pages for more information\&. .sp This is like setting \fIvar\fR=\fIval\fR in \fBups.conf\fR(5), but \fB\-x\fR overrides any settings from that file\&. .RE .SH "DIAGNOSTICS" .sp Information about the startup process is printed to stdout\&. Additional messages after that point are available in the syslog\&. After \fBupsd\fR(8) starts, the UPS clients such as \fBupsc\fR(8) can be used to query the status of an UPS\&. .SH "PROGRAM CONTROL" .sp You should always use \fBupsdrvctl\fR(8) to control the drivers\&. While drivers can be started by hand for testing purposes, it is not recommended for production use\&. .SH "FILES" .PP ups\&.conf .RS 4 Required configuration file\&. This contains all details on which drivers to start and where the hardware is attached\&. .RE .SH "BUGS" .sp Some of the drivers may have bugs\&. See their manuals for more information\&. .SH "SEE ALSO" .sp Server: \fBupsd\fR(8) .sp Clients: \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsrw\fR(8), \fBupslog\fR(8), \fBupsmon\fR(8) .sp CGI programs: \fBupsset.cgi\fR(8), \fBupsstats.cgi\fR(8), \fBupsimage.cgi\fR(8) .sp Driver control: \fBupsdrvctl\fR(8) .sp Drivers: \fBal175\fR(8) \fBapcsmart\fR(8), \fBbcmxcp\fR(8), \fBbcmxcp_usb\fR(8), \fBbelkin\fR(8), \fBbelkinunv\fR(8), \fBbestfcom\fR(8), \fBbestuferrups\fR(8), \fBbestups\fR(8), \fBblazer_ser\fR(8), \fBblazer_usb\fR(8), \fBcyberpower\fR(8), \fBdummy-ups\fR(8), \fBetapro\fR(8), \fBeverups\fR(8), \fBgamatronic\fR(8), \fBgenericups\fR(8), \fBisbmex\fR(8), \fBliebert\fR(8), \fBmasterguard\fR(8), \fBmetasys\fR(8), \fBmge-shut\fR(8), \fBmge-utalk\fR(8), \fBmge-xml\fR(8), \fBnewmge-shut\fR(8), \fBnitram\fR(8), \fBnutdrv_qx\fR(8), \fBoneac\fR(8), \fBoptiups\fR(8), \fBpowercom\fR(8), \fBpowerman-pdu\fR(8), \fBpowerpanel\fR(8), \fBrhino\fR(8), \fBrichcomm_usb\fR(8), \fBsafenet\fR(8), \fBsnmp-ups\fR(8), \fBsolis\fR(8), \fBtripplite\fR(8), \fBtripplitesu\fR(8), \fBtripplite_usb\fR(8), \fBusbhid-ups\fR(8), \fBupscode2\fR(8), \fBvictronups\fR(8) .sp Internet resources: The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/mge-shut.txt0000644000175000017500000000530712640443572013264 00000000000000MGE-SHUT(8) =========== NAME ---- mge-shut - Driver for SHUT Protocol UPS equipment SYNOPSIS -------- *mge-shut* -h *mge-shut* -a 'UPS_NAME' ['OPTIONS'] NOTE: This man page only documents the hardware-specific features of the mge-shut driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ mge-shut supports all recent Eaton, MGE and Dell UPS models which use the SHUT (Serial HID UPS Transfer) protocol. Older MGE models, such as Pulsar ESV+, Pulsar EX and Pulsar ES+, use the U-Talk protocol and should use the mge-utalk driver. EXTRA ARGUMENTS --------------- This driver also supports the following optional settings: *lowbatt*='num':: Set the low battery warning threshold at which shutdown is initiated by linkman:upsmon[8]. + The factory default value is 30 (in percent), and can be settable depending on the exact model. *offdelay*='num':: Set the timer before the UPS is turned off after the kill power command is sent (via the *-k* switch). + The default value is 20 (in seconds). Usually this *must be lower* than 'ondelay', but the driver will *not* warn you upon startup if it isn't. *ondelay*='num':: Set the timer for the UPS to switch on in case the power returns after the kill power command had been sent but before the actual switch off. This ensures the machines connected to the UPS are, in all cases, rebooted after a power failure. + The default value is 30 (in seconds). Usually this *must be greater* than offdelay, but the driver will *not* warn you upon startup if it isn't. Some UPS'es will restart no matter what, even if the power is (still) out at the moment this timer elapses. In that case, you could try if setting 'ondelay = -1' in *ups.conf* helps. + WARNING: ondelay parameter was set in ten seconds unit in the legacy mge-shut driver ( 3 for 30 seconds) . It is now set in seconds ( 30 for 30 seconds). Make sure you use the correct unit in your configuration. *notification*='num':: Set notification type to 1 (no), 2 (light) or 3 (yes). + This argument is ignored. It is only here for backward compatibility. KNOWN ISSUES ------------ Repetitive timeout and staleness ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some models tends to be unresponsive with the default polling frequency. The result is that you have some "data stale" errors in your system log. In this case, simply modify the general parameter "pollinterval" to a higher value (like 10 for 10 seconds). This should solve the issue. Using 'notification=3' might also help. AUTHOR ------ Arnaud Quette SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upssched.conf.txt0000644000175000017500000000654012640443572014275 00000000000000UPSSCHED.CONF(5) ================ NAME ---- upssched.conf - Configuration for upssched timer program DESCRIPTION ----------- This file controls the operations of linkman:upssched[8], the timer-based helper program for linkman:upsmon[8]. CONFIGURATION DIRECTIVES ------------------------ *CMDSCRIPT* 'scriptname':: Required. This must be above any AT lines. This script is used to invoke commands when your timers are triggered. It receives a single argument which is the name of the timer that caused it to trigger. *PIPEFN* 'filename':: Required. This sets the file name of the socket which will be used for interprocess communications. This should be in a directory where normal users can't create the file, due to the possibility of symlinking and other evil. CAUTION: if you are running Solaris or similar, the permissions that upssched sets on this file *are not enough* to keep you safe. If your OS ignores the permissions on a FIFO, then you MUST put this in a protected directory! NOTE: by default, linkman:upsmon[8] will run upssched as whatever user you have defined with RUN_AS_USER in linkman:upsmon.conf[8]. Make sure that user can create files and write to files in the path you use for PIPEFN and LOCKFN. My recommendation: create a special directory for upssched, make it owned by your upsmon user, then use it for both. The stock version of the upssched.conf ships with PIPEFN disabled to make you visit this portion of the documentation and think about how your system works before potentially opening a security hole. *LOCKFN* 'filename':: Required. upssched attempts to create this file in order to avoid a race condition when two events are dispatched from upsmon at nearly the same time. This file will only exist briefly. It must not be created by any other process. + You should put this in the same directory as PIPEFN. *AT* 'notifytype' 'upsname' 'command':: Define a handler for a specific event 'notifytype' on UPS 'upsname'. 'upsname' can be the special value * to apply this handler to every UPS. + This will perform the command 'command' when the 'notifytype' and 'upsname' match the current activity. Possible values for 'command' are: *START-TIMER* 'timername' 'interval';; Start a timer of 'interval' seconds. When it triggers, it will pass the argument 'timername' as an argument to your CMDSCRIPT. + Example: + Start a timer that'll execute when any UPS (*) has been gone for 10 seconds AT COMMBAD * START-TIMER upsgone 10 *CANCEL-TIMER* 'timername' ['cmd'];; Cancel a running timer called 'timername', if possible. If the timer has passed then pass the optional argument 'cmd' to CMDSCRIPT. + Example: + If a specific UPS (+myups@localhost+) comes back online, then stop the timer before it triggers AT COMMOK myups@localhost CANCEL-TIMER upsgone *EXECUTE* 'command';; Immediately pass 'command' as an argument to CMDSCRIPT. + Example: + If any UPS (*) reverts to utility power, then execute `ups-back-on-line` via CMDSCRIPT. AT ONLINE * EXECUTE ups-back-on-line Note that any AT that matches both the 'notifytype' and the 'upsname' for the current event will be used. For a complete list of 'notifytype' possible values, refer to the section NOTIFY EVENTS in linkman:upsmon[8]. SEE ALSO -------- linkman:upssched[8], linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_connect.30000644000175000017500000000476512665610632014075 00000000000000'\" t .\" Title: upscli_connect .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_CONNECT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_connect \- Open a connection to a NUT upsd .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); .fi .SH "DESCRIPTION" .sp The \fBupscli_connect()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure and opens a TCP connection to the \fIhost\fR on the given \fIport\fR\&. .sp \fIflags\fR may be either UPSCLI_CONN_TRYSSL to try a SSL connection, or UPSCLI_CONN_REQSSL to require a SSL connection\&. .sp If SSL mode is required, this function will only return successfully if it is able to establish a SSL connection with the server\&. Possible reasons for failure include no SSL support on the server, and if \fBupsclient\fR itself hasn\(cqt been compiled with SSL support\&. .sp You must call \fBupscli_disconnect\fR(3) when finished with a connection, or your program will slowly leak memory and file descriptors\&. .SH "RETURN VALUE" .sp The \fBupscli_connect()\fR function modifies the UPSCONN_t structure and returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_disconnect\fR(3), \fBupscli_fd\fR(3), \fBupscli_splitaddr\fR(3), \fBupscli_splitname\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/upscli_readline.30000644000175000017500000000416612665610636014226 00000000000000'\" t .\" Title: upscli_readline .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_READLINE" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_readline \- read a single response from a UPS .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); .fi .SH "DESCRIPTION" .sp The \fBupscli_readline()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure, receives a single line from the server, and copies up to \fIbuflen\fR bytes of the response into the buffer \fIbuf\fR\&. .sp Some parsing of the string occurs during reception\&. In particular, ERR messages from \fBupsd\fR(8) are detected and will cause this function to return \-1\&. .SH "RETURN VALUE" .sp The \fBupscli_readline()\fR function returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutclient_get_device_variable_description.30000644000175000017500000000003512665610646021507 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/upsd.conf.50000644000175000017500000001245312640476462012763 00000000000000'\" t .\" Title: upsd.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSD\&.CONF" "5" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsd.conf \- Configuration for Network UPS Tools upsd .SH "DESCRIPTION" .sp upsd uses this file to control access to the server and set some other miscellaneous configuration values\&. This file contains details on access controls, so keep it secure\&. Ideally, only the upsd process should be able to read it\&. .SH "CONFIGURATION DIRECTIVES" .PP "MAXAGE \fIseconds\fR" .RS 4 upsd usually allows a driver to stop responding for up to 15 seconds before declaring the data "stale"\&. If your driver takes a very long time to process updates but is otherwise operational, you can use MAXAGE to make upsd wait longer\&. .sp Most users should leave this at the default value\&. .RE .PP "STATEPATH \fIpath\fR" .RS 4 Tell upsd to look for the driver state sockets in \fIpath\fR rather than the default that was compiled into the program\&. .RE .PP "LISTEN \fIinterface\fR \fIport\fR" .RS 4 Bind a listening port to the interface specified by its Internet address\&. This may be useful on hosts with multiple interfaces\&. You should not rely exclusively on this for security, as it can be subverted on many systems\&. .sp Listen on TCP port \fIport\fR instead of the default value which was compiled into the code\&. This overrides any value you may have set with \fIconfigure \-\-with\-port\fR\&. If you don\(cqt change it with configure or this value, upsd will listen on port 3493 for this interface\&. .sp Multiple LISTEN addresses may be specified\&. The default is to bind to 127\&.0\&.0\&.1 if no LISTEN addresses are specified (and ::1 if IPv6 support is compiled in)\&. .sp .if n \{\ .RS 4 .\} .nf LISTEN 127\&.0\&.0\&.1 LISTEN 192\&.168\&.50\&.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 .fi .if n \{\ .RE .\} .sp This parameter will only be read at startup\&. You\(cqll need to restart (rather than reload) upsd to apply any changes made here\&. .RE .PP "MAXCONN \fIconnections\fR" .RS 4 This defaults to maximum number allowed on your system\&. Each UPS, each LISTEN address and each client count as one connection\&. If the server runs out of connections, it will no longer accept new incoming client connections\&. Only set this if you know exactly what you\(cqre doing\&. .RE .PP "CERTFILE \fIcertificate file\fR" .RS 4 When compiled with SSL support with OpenSSL backend, you can enter the certificate file here\&. The certificates must be in PEM format and must be sorted starting with the subject\(cqs certificate (server certificate), followed by intermediate CA certificates (if applicable_ and the highest level (root) CA\&. It should end with the server key\&. See \fIdocs/security\&.txt\fR or the Security chapter of NUT user manual for more information on the SSL support in NUT\&. .RE .PP "CERTPATH \fIcertificate database\fR" .RS 4 When compiled with SSL support with NSS backend, you can enter the certificate path here\&. Certificates are stored in a dedicated database (splitted in 3 files)\&. Specify the path of the database directory\&. .RE .PP "CERTIDENT \fIcertificate name\fR \fIdatabase password\fR" .RS 4 When compiled with SSL support with NSS backend, you can specify the certificate name to retrieve from database to authenticate itself and the password required to access certificate related private key\&. .RE .PP "CERTREQUEST \fIcertificate request level\fR" .RS 4 When compiled with SSL support with NSS backend and client certificate validation (disabled by default, see \fIdocs/security\&.txt\fR), you can specify if upsd requests or requires client\(cqs\*(Aq certificates\&. Possible values are : .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fI0\fR to not request to clients to provide any certificate .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fI1\fR to require to all clients a certificate .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fI2\fR to require to all clients a valid certificate .RE .RE .SH "SEE ALSO" .sp \fBupsd\fR(8), \fBnutupsdrv\fR(8), \fBupsd.users\fR(5) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_get_device_num_logins.30000644000175000017500000000003012665610645017463 00000000000000.so libnutclient_misc.3 nut-2.7.4/docs/man/victronups.txt0000644000175000017500000000237012640443572013744 00000000000000VICTRONUPS(8) ============= NAME ---- victronups - Driver for IMV/Victron UPS unit Match, Match Lite, NetUps NOTE ---- This man page only documents the hardware-specific features of the the victronups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The victronups driver should recognize all Victron models that use a serial protocol at 1200 bps. These include Match Lite, Match and the NetUps line. The Match Lite line may only report a handful of variables. This is usually not a bug - they just don't support anything else. CABLING ------- If your Victron cable is broken or missing, use this diagram to build a clone: docs/cables/victron.txt EXTRA ARGUMENTS --------------- This driver supports the following optional setting in the linkman:ups.conf[5]: *modelname*='name':: Set model name *usd*='delay':: Set delay before shutdown on UPS BUGS ---- The protocol for this UPS is not officially documented. AUTHOR ------ Radek Benedikt , Daniel Prynych SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_has_device_command.30000644000175000017500000000003412665610643016725 00000000000000.so libnutclient_commands.3 nut-2.7.4/docs/man/bestfortress.txt0000644000175000017500000000205012640443572014250 00000000000000BESTFORTRESS(8) =============== NAME ---- bestfortress - Driver for old Best Fortress UPS equipment NOTE ---- This man page only documents the hardware-specific features of the bestfortress driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports old Best Fortress UPS equipment using a serial connection. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *baudrate*='num':: Set the speed of the serial connection - 1200, 2400, 4800 or 9600. *max_load*='VA':: Set the full-scale value of the *ups.load* variable. AUTHOR ------ Holger Dietze , Stuart D. Gathman SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] The newer Best Power drivers: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ linkman:bestups[8], linkman:bestuferrups[8], linkman:bestfcom[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_cleanup.txt0000644000175000017500000000077412640443572014544 00000000000000UPSCLI_CLEANUP(3) ================= NAME ---- upscli_cleanup - Clean-up upsclient module after usage. SYNOPSIS -------- #include int upscli_cleanup(); DESCRIPTION ----------- The *upscli_cleanup()* function flushes SSL caches and frees memory used internally in upsclient module. RETURN VALUE ------------ The *upscli_cleanup()* function returns 1 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_init[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/upscode2.txt0000644000175000017500000000533712640443572013262 00000000000000UPSCODE2(8) =========== NAME ---- upscode2 - Driver for UPScode II compatible UPS equipment NOTE ---- This man page only documents the hardware-specific features of the upscode2 driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports UPS equipment which can be controlled via the UPScode II protocol. This is mainly Fiskars, Powerware equipment, but also some (probably OEM'ed) products from Compaq. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *manufacturer*='value':: Autodetection of this parameter is not possible yet (and it probably never will be). Therefore, this user-defined string accepts any name. The default is 'unknown'. *input_timeout*='value':: The timeout waiting for a response from the UPS. Some UPS models have shown to be rather slow, resulting in frequent messages about empty responses from the UPS. If you see this, try increasing this value. *output_pace*='value':: Delay between characters sent to the UPS. This was added for completeness with the above parameter. It has not shown to be needed yet. *baudrate*='value':: The default baudrate is 1200, which is the standard for the UPScode II protocol. *full_update_timer*='value':: Number of seconds between collection of normative values. *use_crlf*:: Flag to set if commands towards to UPS need to be terminated with CR-LF, and not just CR. *use_pre_lf*:: Flag to set if commands towards to UPS need to be introduced with an LF. A Compaq T1500h is known to need this. COMMANDS -------- The driver supports the following commands for those UPSen that support them. The available commands are autodetected during initialization, so you should check availability with 'upscmd -l'. * test.panel.start - Start UPS self test * test.battery.start - Start battery self test * beeper.enable - Enable UPS beeper * beeper.disable - Disable UPS beeper * shutdown.return - Shut down in 1 second and wait for power to return * shutdown.stayoff - Shut down in 1 seconds * shutdown.reboot - Shut down in 1 seconds and reboot after 1 minute * shutdown.reboot.graceful - Shut down in 20 seconds and reboot after 1 minute NOTES ----- The Powerware UPS models that this driver has been tested against until now have not returned a value for 'battery.charge'. Therefore, the driver will guestimate a value based on the nominal battery min/max and the current battery voltage. AUTHOR ------ HÃ¥vard Lygre , Niels Baggesen SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_add_host_cert.txt0000644000175000017500000000155212640443572015712 00000000000000UPSCLI_ADD_HOST_CERT(3) ======================= NAME ---- upscli_add_host_cert - Register a security rule for an host. SYNOPSIS -------- #include void upscli_add_host_cert(const char* hostname, const char* certname, int certverify, int forcessl); DESCRIPTION ----------- The *upscli_add_host_cert()* function register a security rule associated to the 'hostname'. All connections to this host use this rule. The rule is composed of the certificate name 'certname 'expected for the host, 'certverify' if the certificate must be validated for the host and 'forcessl' if a secured connection must be used to connect to the host. RETURN VALUE ------------ *upscli_add_host_cert()* returns no value. SEE ALSO -------- linkman:upscli_init[3], linkman:upscli_connect[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/bestuferrups.txt0000644000175000017500000000141012640443572014253 00000000000000BESTUFERRUPS(8) =============== NAME ---- bestuferrups - Driver for Best Power Micro-Ferrups NOTE ---- This man page only documents the hardware-specific features of the bestuferrups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ Best Power Micro-Ferrups ME3100, probably other similar models too. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHORS ------- Andreas Wrede, John Stone (bestuferrups) Grant Taylor (bestfort) Russell Kroll (bestups) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/blazer-common.txt0000644000175000017500000002512112640444140014264 00000000000000NOTE ---- This man page only documents the hardware-specific features of the blazer driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The blazer driver is known to work with various UPSes from Blazer, Energy Sistem, Fenton Technologies, General Electric, Mustek and many others. The NUT compatibility table lists all the known supported models. Keep in mind, however, that other models not listed there may also be supported, but haven't been tested. All devices with a serial interface (use the *blazer_ser* driver) and many with a USB interface (use the *blazer_usb* driver) are supported. EXTRA ARGUMENTS --------------- You may need to override or provide defaults for some values, depending on the make and model of your UPS. The following are the ones that most likely will need changing (see linkman:ups.conf[5]): *default.battery.voltage.high =* 'value':: Maximum battery voltage that is reached after about 12 to 24 hours charging. If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge,BATTERY CHARGE>>). *default.battery.voltage.low =* 'value':: Minimum battery voltage just before the UPS automatically shuts down. If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge,BATTERY CHARGE>>). *default.battery.voltage.nominal =* 'value':: *override.battery.voltage.nominal =* 'value':: Some devices show a wrong nominal battery voltage (or none at all), so you may need to override or set a default value. *override.battery.packs =* 'value':: Some devices report a part of the total battery voltage. For instance, if *battery.voltage.nominal* is 24 V, but it reports a *battery.voltage* of around 2 V, the number of *battery.packs* to correct this reading would be 12. The driver will attempt to detect this automatically, but if this fails somehow, you may want to override this value. *ondelay =* 'value':: Time to wait before switching on the UPS (minutes). Note that a value below 3 minutes, may cause earlier firmware versions to not switch on automatically, so it defaults to 3 minutes. The acceptable range is +0..9999+ minutes. *offdelay =* 'value':: Time to wait before shutting down the UPS (seconds). This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds). Defaults to 30 seconds. The acceptable range is +12..600+ seconds. *norating*:: Some UPSes will lock up if you attempt to read rating information from them. Setting this flag will make the driver skip this step. *novendor*:: Some UPSes will lock up if you attempt to read vendor information from them. Setting this flag will make the driver skip this step. *protocol =* 'string':: Skip autodetection of the protocol to use and only use the one specified. Supported values 'megatec', 'megatec/old', 'mustek' and 'zinto'. *runtimecal =* 'value,value,value,value':: Parameter used in the (optional) runtime estimation. This takes two runtimes at different loads. Typically, this uses the runtime at full load and the runtime at half load. For instance, if your UPS has a rated runtime of 240 seconds at full load and 720 seconds at half load, you would enter + runtimecal = 240,100,720,50 + The first load should always be higher than the second. If you have values available for loads other than 100 and 50 % respectively, you can use those too, but keep them spaced apart as far as reasonably possible. Just don't get too close to no load (prediction of runtime depends more on idle load for the battery then). *chargetime =* 'value':: The time needed to fully recharge the battery after being fully discharged. If not specified, the driver defaults to 43200 seconds (12 hours). Only used if *runtimecal* is also specified. *idleload =* 'value':: Minimum battery load used by the driver to estimate the runtime. If not specified, the driver defaults to 10%. Only used if *runtimecal* is also specified. ifndef::blazer_usb[] SERIAL INTERFACE ONLY ~~~~~~~~~~~~~~~~~~~~~ *cablepower =* 'string':: By default the driver will set DTR and clear RTS ('normal'). If you find that your UPS isn't detected or the communication with the UPS is unreliable, you may try if clear DTR and set RTS ('reverse'), set DTR and RTS ('both') or clear DTR and RTS ('none') improves this situation. endif::blazer_usb[] ifdef::blazer_usb[] USB INTERFACE ONLY ~~~~~~~~~~~~~~~~~~ *vendorid =* 'regex':: *productid =* 'regex':: *vendor =* 'regex':: *product =* 'regex':: *serial =* 'regex':: Select a specific UPS, in case there is more than one connected via USB. Each option specifies an extended regular expression (see *regex(7)*) that must match the UPS's entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4-digit hexadecimal code for vendorid and productid. Try *-DD* for finding out the strings to match. + Examples: - `-x vendor="Foo.Corporation.*"` - `-x vendorid=051d*` (APC) - `-x product=".*(Smart|Back)-?UPS.*"` *bus =* 'regex':: Select a UPS on a specific USB bus or group of busses. The argument is a regular expression that must match the bus name where the UPS is connected (e.g. bus="002", bus="00[2-3]"). *subdriver =* 'string':: Select a serial-over-USB subdriver to use. You have a choice between *phoenix*, *ippon*, *cypress*, and *krauler*. When using this option, it is mandatory to also specify the *vendorid* and *productid*. *langid_fix =* 'value':: Apply the language ID workaround to the krauler subdriver. This is mandatory for some devices to work (LDLC, Dynamix and others). You must to provide *value* (0x409 or 0x4095), according to your device entry in NUT hardware compatibility list (HCL). endif::blazer_usb[] UPS COMMANDS ------------ This driver supports some instant commands (see linkman:upscmd[8]): *beeper.toggle*:: Toggle the UPS beeper. (Not available on some hardware.) *load.on*:: Turn on the load immediately. *load.off*:: Turn off the load immediately (see <<_known_problems,KNOWN PROBLEMS>>). *shutdown.return*:: Turn off the load and return when power is back. Uses the timers defined by *ondelay* and *offdelay*. *shutdown.stayoff*:: Turn off the load and remain off (see <<_known_problems,KNOWN PROBLEMS>>). Uses the timer defined by *offdelay*. *shutdown.stop*:: Stop a shutdown in progress. *test.battery.start.deep*:: Perform a long battery test (Not available on some hardware.) *test.battery.start.quick*:: Perform a (10 second) battery test. *test.battery.start* 'value':: Perform a battery test for the duration of 'value' minutes. *test.battery.stop*:: Stop a running battery test (not available on some hardware.) BATTERY CHARGE -------------- Due to popular demand, this driver will report a guesstimated *battery.charge* and optionally *battery.runtime*, provided you specified a couple of the <<_extra_arguments,EXTRA ARGUMENTS>> listed above. If you specify both *battery.voltage.high* and *battery.voltage.low* in linkman:ups.conf[5], but don't enter *runtimecal*, it will guesstimate the state of charge by looking at the battery voltage alone. This is not reliable under load, as this only gives reasonably accurate readings if you disconnect the load, let the battery rest for a couple of minutes and then measure the open cell voltage. This just isn't practical if the power went out and the UPS is providing power for your systems. battery.voltage - battery.voltage.low battery.charge = ------------------------------------------ x 100 % battery.voltage.high - battery.voltage.low There is a way to get better readings without disconnecting the load but this requires one to keep track on how much (and how fast) current is going in- and out of the battery. If you specified the *runtimecal*, the driver will attempt to do this. Note however, that this heavily relies on the values you enter and that the UPS must be able to report the load as well. There are quite a couple of devices that report 0 % (or any other fixed value) at all times, in which case this obviously doesn't work. The driver also has no way of determining the degradation of the battery capacity over time, so you'll have to deal with this yourself (by adjusting the values in *runtimecal*). Also note that the driver guesses the initial state of charge based on the battery voltage, so this may be less than 100 %, even when you are certain that they are full. There is just no way to reliably measure this between 0 and 100 % full charge. This is better than nothing (but not by much). If any of the above calculations is giving you incorrect readings, you are the one that put in the values in linkman:ups.conf[5], so don't complain with the author. If you need something better, buy a UPS that reports *battery.charge* and *battery.runtime* all by itself without the help of a NUT driver. NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS ---------------------------------------------- The blazer drivers having replaced the megatec ones, some configuration changes may be required by users switching to blazer. Part of this, the following megatec options, in ups.conf, have to be changed: *battvolts*:: You need to use 'default.battery.voltage.high' and 'default.battery.voltage.low' *dtr and rts*:: You need to use 'cablepower' *ignoreoff*:: This parameter can simply be discarded, since it was a wrong understanding of the specification. KNOWN PROBLEMS -------------- Some UPS commands aren't supported by all models. In most cases, the driver will send a message to the system log when the user tries to execute an unsupported command. Unfortunately, some models don't even provide a way for the driver to check for this, so the unsupported commands will silently fail. Both the *load.off* and *shutdown.stayoff* instant commands are meant to turn the load off indefinitely. However, some UPS models don't allow this. Some models report a bogus value for the beeper status (will always be 'enabled' or 'disabled'). So, the *beeper.toggle* command may appear to have no effect in the status reported by the driver when, in fact, it is working fine. The temperature and load value is known to be bogus in some models. AUTHORS ------- Arjen de Korte , Alexander Gordeev SEE ALSO -------- ifdef::blazer_usb[] linkman:blazer_ser[8], endif::blazer_usb[] ifndef::blazer_usb[] linkman:blazer_usb[8], endif::blazer_usb[] linkman:nutupsdrv[8], linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ The NUT HCL: http://www.networkupstools.org/stable-hcl.html nut-2.7.4/docs/man/upscli_ssl.txt0000644000175000017500000000135112640443572013706 00000000000000UPSCLI_SSL(3) ============= NAME ---- upscli_ssl - Check SSL mode for current connection SYNOPSIS -------- #include int upscli_ssl(UPSCONN_t *ups); DESCRIPTION ----------- The *upscli_ssl*() function takes the pointer 'ups' to a `UPSCONN_t` state structure. It only returns 1 if SSL support has been compiled into the linkman:upsclient[3] library, and if it was successfully enabled for this connection. RETURN VALUE ------------ The *upscli_ssl*() function returns 1 if SSL is running, and 0 if not. It returns -1 in the event of an error. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/blazer_usb.txt0000644000175000017500000000023012640444140013641 00000000000000BLAZER_USB(8) ============= :blazer_usb: NAME ---- blazer_usb - Driver for Megatec/Q1 protocol USB based UPS equipment include::blazer-common.txt[] nut-2.7.4/docs/man/rhino.txt0000644000175000017500000000216412640443572012650 00000000000000RHINO(8) ======== NAME ---- rhino - Driver for Brazilian Microsol RHINO UPS equipment NOTE ---- This man page only documents the hardware-specific features of the rhino driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver has been tested with : * Rhino 6000 VA * Rhino 7500 VA * Rhino 10000 VA * Rhino 20000 VA All Rhino models are sinusoidal on-line. EXTRA ARGUMENTS --------------- This driver support the following extra optional settings in linkman:ups.conf[5]: *battext = n* - (default = 0, no extra battery, where n = Ampere*hour ) COMMANDS -------- * load.on - Turn on the load immediately * load.off - Turn off the load immediately * bypass.start - Put the UPS in bypass mode * bypass.stop - Put the UPS out of bypass mode * shutdown.stayoff - Shut down in 3 minutes and do not return AUTHOR ------ Silvino B. Magalhães SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsdrvctl.80000644000175000017500000001070712640476466013121 00000000000000'\" t .\" Title: upsdrvctl .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSDRVCTL" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsdrvctl \- UPS driver controller .SH "SYNOPSIS" .sp \fBupsdrvctl\fR \-h .sp \fBupsdrvctl\fR [\fIOPTIONS\fR] {start | stop | shutdown} [\fIups\fR] .SH "DESCRIPTION" .sp \fBupsdrvctl\fR provides a uniform interface for controlling your UPS drivers\&. You should use upsdrvctl instead of direct calls to the drivers whenever possible\&. .sp When used properly, upsdrvctl lets you maintain identical startup scripts across multiple systems with different UPS configurations\&. .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display the help text\&. .RE .PP \fB\-r\fR \fIdirectory\fR .RS 4 If starting a driver, this value will direct it to \fBchroot\fR(2) into \fIdirectory\fR\&. This can be useful when securing systems\&. .RE .sp This may be set in the ups\&.conf with "chroot" in the global section\&. .PP \fB\-t\fR .RS 4 Enable testing mode\&. This also enables debug mode\&. Testing mode makes upsdrvctl display the actions it would execute without actually doing them\&. Use this to test out your configuration without actually doing anything to your UPS drivers\&. This may be helpful when defining the \fIsdorder\fR directive in your \fBups.conf\fR(5)\&. .RE .PP \fB\-u\fR \fIusername\fR .RS 4 If starting a driver, this value will direct it to \fBsetuid\fR(2) to the user id associated with \fIusername\fR\&. .RE .sp If the driver is started as root without specifying this value, it will use the username that was compiled into the binary\&. This defaults to "nobody", and is far from ideal\&. .sp This may be set in ups\&.conf with "user" in the global section\&. .PP \fB\-D\fR .RS 4 Raise the debug level\&. Use this multiple times for additional details\&. .RE .SH "COMMANDS" .sp upsdrvctl supports three commands \- start, stop and shutdown\&. They take an optional argument which is a UPS name from \fBups.conf\fR(5)\&. Without that argument, they operate on every UPS that is currently configured\&. .PP \fBstart\fR .RS 4 Start the UPS driver(s)\&. In case of failure, further attempts may be executed by using the \fImaxretry\fR and \fIretrydelay\fR options \- see \fBups.conf\fR(5)\&. .RE .PP \fBstop\fR .RS 4 Stop the UPS driver(s)\&. .RE .PP \fBshutdown\fR .RS 4 Command the UPS driver(s) to run their shutdown sequence\&. Drivers are stopped according to their sdorder value \- see \fBups.conf\fR(5)\&. .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBWarning\fR .ps -1 .br .sp this will probably power off your computers, so don\(cqt play around with this option\&. Only use it when your systems are prepared to lose power\&. .sp .5v .RE .SH "ENVIRONMENT VARIABLES" .sp \fBNUT_CONFPATH\fR is the path name of the directory that contains upsd\&.conf and other configuration files\&. If this variable is not set, \fBupsdrvctl\fR the driver use a built\-in default, which is often /usr/local/ups/etc\&. .SH "DIAGNOSTICS" .sp upsdrvctl will return a nonzero exit code if it encounters an error while performing the desired operation\&. This will also happen if a driver takes longer than the \fImaxstartdelay\fR period to enter the background\&. .SH "SEE ALSO" .sp \fBnutupsdrv\fR(8), \fBupsd\fR(8), \fBups.conf\fR(5) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/mge-utalk.txt0000644000175000017500000000456612640443572013427 00000000000000MGE-UTALK(8) ============ NAME ---- mge-utalk - Driver for MGE UPS SYSTEMS UTalk protocol equipment SYNOPSIS -------- *mge-utalk* -h *mge-utalk* -a 'UPS_NAME' ['OPTIONS'] NOTE: This man page only documents the hardware-specific features of the mge-utalk driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ mge-utalk supports the following legacy units, using the MGE UTalk protocol: Pulsar ESV+, Pulsar ES+, Pulsar EL, Pulsar EX, Pulsar EXtreme, Comet EXtreme, Comet (Utalk Serial Card, ref 66060), Galaxy (Utalk Serial Card, ref 66060). This driver also support some newer models with backward UTalk compatibility, such as Pulsar Evolution and Pulsar EXtreme C. As these models also support the SHUT protocol, prefer mge-shut for serial communication, or use the USB port, if available, with the usbhid-ups driver. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *lowbatt*='num':: Set the low battery warning threshold at which shutdown is initiated by linkman:upsmon[8]. + The factory default value is 30 (in percent), and can be settable depending on the exact model. *offdelay*='num':: Set the timer before the UPS is turned off after the kill power command is sent (via the *-k* switch). + The default value is 20 (in seconds). *ondelay*='num':: Set the delay before the UPS is turned on, after the power returns. + The default value is 1 (in minutes). *oldmac*:: Set this flag if you are running Linux on an Oldworld Macintosh box (all beige Apple Macintosh). This might also be needed for other OSs (like *BSD) running on PowerMac. KNOWN ISSUES ------------ Repetitive timeout and staleness ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Older models, such as ES/ESV ones, might report repetitive "data stale" errors. This is due to the fact that these models don't support too much polling. To solve this problem, add "pollinterval=20" in ups.conf, and change the value of MAXAGE to 25 in upsd.conf, and DEADTIME to 25 in upsmon.conf. AUTHOR ------ Hans Ekkehard Plesser, Arnaud Quette, Martin Loyer, Patrick Agrain, Nicholas Reilly, Dave Abbott, Marek Kralewski SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsstats.html.txt0000644000175000017500000001513212640443572014361 00000000000000UPSSTATS.HTML(5) ================ NAME ---- upsstats.html - HTML template for Network UPS Tools upsstats DESCRIPTION ----------- This file is used by linkman:upsstats.cgi[8] to generate status pages. Certain commands are recognized, and will be replaced with various status elements on the fly. FORMATTING ---------- Commands can be placed anywhere on a line, but must start and end with `@`. Any extra characters before or after the commands will be passed through unchanged. It is allowed to use more than one command on a single line, as long as each command has its own start and end character. If you need to use the `@` sign, use @ to prevent it from being treated as a start character. BLOCK CONTROL ------------- Some commands begin blocks - sections of the template that will be included, excluded, or repeated depending on certain parameters. BLOCK CONTROL - ITERATION ~~~~~~~~~~~~~~~~~~~~~~~~~ *@FOREACHUPS@*:: Starts a block that will be repeated for each MONITOR directive in the linkman:hosts.conf[5]. This is how you can generate pages that monitor all of your systems simultaneously. *@ENDFOR@*:: Ends a FOREACHUPS block. BLOCK CONTROL - MATCHING SPECIFIC CASES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *@IFSUPP 'var'*@*:: Starts a block that will only be printed if the variable var is supported by the current UPS. This is generally used to suppress "not supported" messages by avoiding the label and variable call entirely. *@IFEQ* 'var' 'value'*@*:: Starts a block if the value returned from the variable 'var' matches 'value'. *@IFBETWEEN* 'varlow' 'varhigh' 'varvalue'*@*:: Starts a block if the value returned by the variable 'varvalue' is between the values returned by the variables 'varlow' and 'varhigh'. *@ELSE@*:: If the previous IF-command did not match, perform this instead. *@ENDIF@*:: Ends an IF/ELSE-block. BLOCK CONTROL - ADVANCED EXPRESSIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Even though the parser is pretty limited, it's still possible to create rather advanced expressions. The key to this is the fact that multiple block control commands are AND:ed. This is illustrated with an example (more examples are available in upsstats.html). @IFSUPP ambient.humidity@ @IFSUPP ambient.temperature@ This UPS knows both ambient temperature and humidity. @ELSE@ @IFSUPP ambient.humidity@ This UPS only knows ambient humidity. @ELSE@ @IFSUPP ambient.temperature@ This UPS only knows ambient temperature. @ELSE@ This UPS knows nothing, how annoying. @ENDIF@ OTHER COMMANDS -------------- *@AMBTEMP@*:: Insert the ambient temperature in the current temperature scale. *@DATE* 'format'*@*:: Insert the current date and time. The format string is passed to strftime, so almost anything is possible. See *strftime*(3) for possible values. *@DEGREES@*:: Insert the entity for degrees (°) and either C or F depending on the current temperature scale. *@HOST@*:: Insert the designation of the host being monitored, like `myups@localhost`. *@HOSTDESC@*:: Insert the hout's description from linkman:hosts.conf[5]. *@HOSTLINK@*:: Insert a link to upsstats.cgi with the "host" variable set to the current UPS. This is only useful within a FOREACHUPS block. *@IMG* 'varname' ['extra']*@*:: Insert an IMG SRC to linkman:upsimage.cgi[8] for one of these status variables: battery.charge;; Battery charge - a percentage battery.voltage;; The charge on the battery in volts input.frequency;; Incoming utility frequency (Hz) input.voltage;; Incoming utility voltage input.L1-L2.voltage;; Incoming voltage, L1-L2 (3phase) input.L2-L3.voltage;; Incoming voltage, L2-L3 (3phase) input.L3-L1.voltage;; Incoming voltage, L3-L1 (3phase) output.frequency;; Outgoing utility frequency (Hz) output.voltage;; Outgoing voltage (from the UPS) output.L1-L2.voltage;; Outgoing voltage, L1-L2 (3phase) output.L2-L3.voltage;; Outgoing voltage, L2-L3 (3phase) output.L3-L1.voltage;; Outgoing voltage, L3-L1 (3phase) output.L1.power.percent;; UPS load, L1 (3phase) output.L2.power.percent;; UPS load, L2 (3phase) output.L3.power.percent;; UPS load, L3 (3phase) ups.load;; UPS load - percentage ups.temperature;; UPS temperature 'extra' is where you can put additional definitions. Right now the valid definitions are colors for various parts of the bars drawn by upsimage.cgi. Possible color names are: back_col;; background color scale_num_col;; scale number color summary_col;; summary color (number at the bottom) ok_zone_maj_col;; major scale color for the normal ("ok") zone ok_zone_min_col;; minor scale color for the normal ("ok") zone neutral_zone_maj_col;; major scale color for the neutral zone neutral_zone_min_col;; minor scale color for the neutral zone warn_zone_maj_col;; major scale color for the warning zone warn_zone_min_col;; minor scale color for the warning zone bar_col;; the color of the bar in the middle All colors are hex triplets - 0xff0000 is red, 0x00ff00 is green, and 0x0000ff is blue. Examples: @IMG battery.charge@ @IMG battery.charge back_col=0xff00ff bar_col=0xaabbcc@ @IMG input.voltage ok_zone_maj_col=0x123456@ *@REFRESH@*:: Insert the META header magic for refreshing the page if that variable has been set by the browser. This needs to be in the HEAD section of the page. *@STATUS@*:: Expand the abbreviations in the ups.status variable - OL becomes "On line", OB becomes "On battery", and so on. *@STATUSCOLOR@*:: Insert red, green, or yellow color triplets depending on the severity of the current UPS status. Normal operations are green, warnings like voltage trim/boost or "off" are yellow, and other events like being on battery or having a low battery are red. *@VAR* 'varname'*@*:: Insert the current value of the status variable varname on the host being monitored, or "Not supported". *@RUNTIME@*:: Inserts the current runtime, in `hh:mm:ss` format. *@TEMPC@*:: Use the Celsius scale for temperature data (default). *@TEMPF@*:: Use the Fahrenheit scale for temperature data. *@UPSTEMP@*:: Insert the UPS temperature in the current scale. *@BATTTEMP@*:: Insert the battery temperature in the current scale. *@UTILITYCOLOR@*:: Obsoleted. Use IFBETWEEN instead (see example in upsstats.html). *@VERSION@*:: Insert the version number of the software. OTHER TEMPLATES --------------- linkman:upsstats.cgi[8] will also open a file called `upsstats-single.html` if you call it with "host=" set in the URL. That file uses the same rules and techniques documented here. SEE ALSO -------- linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsimage.cgi.txt0000644000175000017500000000215212640473702014077 00000000000000UPSIMAGE.CGI(8) =============== NAME ---- upsimage.cgi - Image-generating helper for upsstats.cgi SYNOPSIS -------- *upsimage.cgi* NOTE: As a CGI program, this should be invoked through your web server. If you run it from the command line, it will either complain about unauthorized access or spew a PNG at you. DESCRIPTION ----------- *upsimage.cgi* generates the graphical bars that make up the right side of the page generated by linkman:upsstats.cgi[8]. These represent the current battery charge, utility voltage, and UPS load where available. The images are in PNG format, and are created by linking to Boutell's excellent gd library. ACCESS CONTROL -------------- upsstats will only talk to linkman:upsd[8] servers that have been defined in your linkman:hosts.conf[5]. If it complains about "Access to that host is not authorized", check that file first. FILES ----- linkman:hosts.conf[5] SEE ALSO -------- linkman:upsd[8], linkman:upsstats.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The gd home page: http://libgd.bitbucket.org The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/al175.80000644000175000017500000000710212640476472011714 00000000000000'\" t .\" Title: al175 .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "AL175" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" al175 \- Driver for Eltek UPS models with AL175 alarm module .SH "NOTE" .sp This man page only documents the hardware\-specific features of the \fBal175\fR driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The \fBal175\fR driver is known to work with the following UPSes: .sp .if n \{\ .RS 4 .\} .nf Eltek MPSU4000 with AL175 alarm module .fi .if n \{\ .RE .\} .sp It may also work with other UPSes equiped with AL175 alarm module, but they have not been tested\&. .sp AL175 have to be configured to operate in COMLI mode\&. See documentation supplied with your hardware on how to do it\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "INSTANT COMMANDS" .sp This driver supports some extra commands (see \fBupscmd\fR(8)): .PP \fBtest\&.battery\&.start\fR .RS 4 Start a battery test\&. .RE .PP \fBtest\&.battery\&.stop\fR .RS 4 Stop a battery test\&. .RE .SH "VARIABLES" .sp Besides status, this driver reads UPS state into following variables: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBups\&.test\&.result\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBoutput\&.voltage\&.nominal\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBoutput\&.current\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBbattery\&.voltage\&.nominal\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBbattery\&.current\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBbattery\&.temperature\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBinput\&.transfer\&.boost\&.low\fR .RE .SH "KNOWN ISSUES AND BUGS" .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Shutdown is not supported\&. FIXME .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} The driver was reworked to meet the project code quality criteria, without testing on real hardware\&. Some bugs may have crept in\&. .RE .SH "AUTHOR" .sp Kirill Smelkov .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/apcsmart-old.txt0000644000175000017500000000551712640443572014124 00000000000000APCSMART-OLD(8) =============== NAME ---- apcsmart-old - Driver for American Power Conversion Smart Protocol UPS equipment SYNOPSIS -------- *apcsmart-old* -h *apcsmart-old* -a 'UPS_NAME' ['OPTIONS'] NOTE: This man page only documents the hardware-specific features of the apcsmart-old driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ apcsmart-old should recognize all recent APC models that use a serial protocol at 2400 bps. This is primarily the Smart-UPS, Matrix-UPS and Back-UPS Pro lines. The driver attempts to support every bell and whistle of the APC reporting interface, whether or not this is strictly sensible. Some older hardware may only report a handful of variables. This is usually not a bug--they just don't support anything else. CABLING ------- This driver expects to see a 940-0024C cable or a clone by default. You can switch to the 940-0095B dual-mode cable support with the `cable=` definition described below. If your 940-0024C cable is broken or missing, use this diagram to build a clone: http://www.networkupstools.org/cables/940-0024C.jpg EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *cable=940-0095B*:: Configure the serial port for the APC 940-0095B dual-mode cable. *sdtype=*'num':: Use shutdown type 'num', according to this table: 0;; soft shutdown or powerdown, depending on battery status 1;; soft shutdown followed by powerdown 2;; instant power off 3;; power off with grace period 4;; "force OB" hack method for CS 350 Modes 0 and 1 will power up the load when power returns. Modes 2 and 3 will keep the load turned off when the power returns. Mode 4 exploits an oddity in the CS 350 models since they only seem to support the S command, but then only when running on battery. As a result, the driver will force the UPS to go on battery if necessary before sending the shutdown command. This ensures that the load gets reset. BUGS ---- Some older APC UPS models return bogus data in the status register during a front panel test. This is usually detected and discarded, but some other unexpected values have occasionally slipped through. APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa. AUTHOR ------ Nigel Metheringham (drawing heavily on the original apcsmart driver by Russell Kroll). This driver was called newapc for a time and was renamed in the 1.5 series. In 2.6.2 the driver was renamed to apcsmart-old, being superseded by updated version with new features. SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/bestfcom.80000644000175000017500000000370512640476477012677 00000000000000'\" t .\" Title: bestfcom .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BESTFCOM" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" bestfcom \- Driver for Best Power Fortress/Ferrups .SH "NOTE" .sp This man page only documents the hardware\-specific features of the bestfcom driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp Best Power Fortress/Ferrups implementing the Fortress UPS Protocol (f\-command set)\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "AUTHORS" .sp Kent Polk (bestfcom) .sp Andreas Wrede, John Stone (bestuferrups) .sp Grant Taylor (bestfort) .sp Russell Kroll (bestups) .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_get_device_variable_values.30000644000175000017500000000003512665610646020463 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/optiups.txt0000644000175000017500000000531512640443572013235 00000000000000OPTIUPS(8) ========== NAME ---- optiups - Driver for Opti-UPS (Viewsonic) UPS and Zinto D (ONLINE-USV) equipment NOTE ---- This man page only documents the hardware-specific features of the optiups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ *optiups* was originally written against a PowerES 280es in nut-0.45. It was revised for nut-2.0.1 and tested against a PowerES 420E. It is expected to work with at least the PowerES, PowerPS, and PowerVS models. This driver additionally supports a Zinto D from ONLINE USV-Systeme AG because of their very similar commands, but it is unknown if it also works with other UPS from them. This driver will not work with the PowerES stock serial cable. You will need to construct your own three conductor cable: UPS 6 -> PC 3 UPS 9 -> PC 2 UPS 4 -> PC 5 The cable for Online-USV uses pin UPS 7 (not UPS 4) -> PC 5. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5] file: *status_only*:: Only poll for critical status information. Without this, *optiups* (and all NUT drivers) poll all sorts of information from the UPS fairly often. It is probably not often enough to hurt anything, so this option probably is not very useful, unless you have a flaky serial connection or a highly loaded machine. *nowarn_noimp*:: Does not print warnings when the UPS reports that a variable is not implemented or not pollable. Without the option you will get a message sent to your system logs each time NUT polls the UPS. If you specify *nowarn_noimp*, this message will only be logged once. *fake_lowbatt*:: This forces the low battery flag true. Without it, if you want to test your UPS, you will have to unplug it and wait until the battery drops to a low/critical voltage level before NUT will respond and power down your system. With the flag, NUT should power down the system soon after you pull the plug. When you are done testing, you should remove this flag. + For basic shutdown configuration testing, the command 'upsmon -c fsd' is preferred. *powerup*:: Zinto D from ONLINE-USV cannot be identified when switched to standby. Set this flag to allow the driver to power-up your Zinto UPS. This will also power-up your equipment connected to the UPS! BUGS ---- On the 420E, `ups.serial` and `ups.temperature` are unsupported features. This is not a bug in NUT or the NUT driver, just the way things are with this UPS. AUTHOR ------ Russell Kroll, Scott Heavner, Matthias Goebl SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upssched.80000644000175000017500000001250112640476471012677 00000000000000'\" t .\" Title: upssched .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSSCHED" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upssched \- Timer helper for scheduling events from upsmon .SH "SYNOPSIS" .sp \fBupssched\fR .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp \fBupssched\fR should be run from \fBupsmon\fR(8) via the NOTIFYCMD\&. You should never run it directly during normal operations\&. .sp .5v .RE .SH "DESCRIPTION" .sp \fBupssched\fR was created to allow users to execute programs at times relative to events being monitored by \fBupsmon\fR(8)\&. The original purpose was to allow for a shutdown to occur after some fixed period on battery, but there are other uses that are possible\&. .SH "INTEGRATION" .sp upssched needs to be called as the NOTIFYCMD in your \fBupsmon.conf\fR(5)\&. It determines what is happening based on the UPSNAME and NOTIFYTYPE environment variables\&. You should never have to deal with them directly\&. .sp Set the EXEC flag on the events that you want to see in upssched\&. For example, to make sure that upssched hears about ONLINE, ONBATT and LOWBATT events, the flags would look like this: .sp .if n \{\ .RS 4 .\} .nf NOTIFYFLAG ONLINE EXEC NOTIFYFLAG ONBATT EXEC NOTIFYFLAG LOWBATT EXEC .fi .if n \{\ .RE .\} .sp If you also want to continue writing to the syslog, just add it in: .sp .if n \{\ .RS 4 .\} .nf NOTIFYFLAG ONLINE SYSLOG+EXEC NOTIFYFLAG ONBATT SYSLOG+EXEC NOTIFYFLAG LOWBATT SYSLOG+EXEC .fi .if n \{\ .RE .\} .sp For a full list of notify flags, see the \fBupsmon\fR(8) documentation\&. .SH "CONFIGURATION" .sp See \fBupssched.conf\fR(5) for information on configuring this program\&. .SH "EARLY SHUTDOWNS" .sp To shut down the system early, define a timer that starts due to an ONBATT condition\&. When it triggers, make your CMDSCRIPT call your shutdown routine\&. It should finish by calling upsmon \-c fsd so that upsmon gets to shut down the slaves in a controlled manner\&. .sp Be sure you cancel the timer if power returns (ONLINE)\&. .SH "DEBOUNCING EVENTS" .sp If your UPS goes on and off battery frequently, you can use this program to reduce the number of pager messages that are sent out\&. Rather than sending pages directly from \fBupsmon\fR(8), use a short timer here\&. If the timer triggers with the UPS still on battery, then send the page\&. If the power returns before then, the timer can be cancelled and no page is necessary\&. .SH "BACKGROUND" .sp This program was written primarily to fulfill the requests of users for the early shutdown scenario\&. The "outboard" design of the program (relative to upsmon) was intended to reduce the load on the average system\&. Most people don\(cqt have the requirement of shutting down after \fIN\fR seconds on battery, since the usual OB+LB testing is sufficient\&. .sp This program was created separately so those people don\(cqt have to spend CPU time and RAM on something that will never be used in their environments\&. .sp The design of the timer handler is also geared towards minimizing impact\&. It will come and go from the process list as necessary\&. When a new timer is started, a process will be forked to actually watch the clock and eventually start the CMDSCRIPT\&. When a timer triggers, it is removed from the queue\&. Cancelling a timer will also remove it from the queue\&. When no timers are present in the queue, the background process exits\&. .sp This means that you will only see upssched running when one of two things is happening: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} There\(cqs a timer of some sort currently running .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} upsmon just called it, and you managed to catch the brief instance .RE .sp The final optimization handles the possibility of trying to cancel a timer when there are none running\&. If the timer daemon isn\(cqt running, there are no timers to cancel, and furthermore there is no need to start a clock\-watcher\&. So, it skips that step and exits sooner\&. .SH "FILES" .sp \fBupssched.conf\fR(5) .SH "SEE ALSO" .sp \fBupsmon\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upsset.cgi.txt0000644000175000017500000000571612640443572013623 00000000000000UPSSET.CGI(8) ============= NAME ---- upsset.cgi - Web-based UPS administration program SYNOPSIS -------- *upsset.cgi* NOTE: As a CGI program, this should be invoked through your web server. If you run it from the command line, it will sit there until you give it input resembling a POST request. DESCRIPTION ----------- *upsset.cgi* lets you access many administrative functions within the UPS software from your web browser. You can change settings and invoke instant commands where available. CHANGING SETTINGS ----------------- Some UPS hardware allows you to change certain variables to other values. To see what's available, pick a UPS from the chooser and select "settings", then select "View" to update the page. You should see a list of items with the descriptions on the left side and the possible options or input spaces on the right. After changing something, be sure to "Save changes" to update the values in your UPS. If your UPS doesn't support any read/write variables, there will be nothing to do on this page. Setting values in read/write variables can also be done from the command line with linkman:upsrw[8]. INSTANT COMMANDS ---------------- Some UPS hardware also has provisions for performing certain actions at the user's command. These include battery tests, battery calibration, front panel tests (beep!) and more. To access this section, do as above, but pick "Commands" as the function. If your UPS supports any instant commands, they will be listed in a chooser widget. Pick the one you like and "Issue command" to make it happen. NOTE: some dangerous commands like "Turn off load" may not happen right away. This is a feature, not a bug. The apcsmart driver and some others require that you send this command twice within a short window in order to make it happen. This is to keep you from accidentally killing your systems by picking the wrong one. To actually turn off the load, you have to send the command once, then send it again after 3 seconds elapse but before 15 seconds pass. If you do it too quickly or slowly, you have to wait at least 3 seconds but not 15 seconds again. You can also invoke instant commands from the command line with linkman:upscmd[8]. ACCESS CONTROL -------------- upsset will only talk to linkman:upsd[8] servers that have been defined in your linkman:hosts.conf[8]. If it complains about "Access to that host is not authorized", check your hosts.conf first. SECURITY -------- upsset will not run until you convince it that your CGI directory has been secured. This is due to the possibility of someone using upsset to try password combinations against your linkman:upsd[8] server. See the example `upsset.conf` file for more information on how you do this. The short explanation is--if you can't lock it down, don't try to run it. FILES ----- linkman:hosts.conf[5], linkman:upsset.conf[5] SEE ALSO -------- Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/oneac.txt0000644000175000017500000000577612640443572012632 00000000000000ONEAC(8) ======== NAME ---- oneac - Driver for Oneac UPS equipment NOTE ---- This man page only documents the hardware-specific features of the oneac driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports various Oneac UPS families: - EG (late 80s, early 90s, plug-in serial interface card) - ON (early and mid-90s, plug-in serial interface card) - OZ (mid-90s on, DB-25 std., interface slot) - OB (early 2000's on, big cabinet, DB-25 std., interface slot) If your UPS is equipped with the Basic Interface card, use the linkman:genericups[8] driver. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5] file: *testtime*='num':: Change battery test time from the 2 minute default. *offdelay*='num':: Change shutdown delay time from 0 second default. INSTANT COMMANDS ---------------- This driver supports the following Instant Commands. (See linkman:upscmd[8]) All UPS units ~~~~~~~~~~~~~ *shutdown.return*:: Turn off the load possibly after a delay and return when power is back. *shutdown.stop*:: Stop a shutdown in progress. *shutdown.reboot*:: Shut down the load briefly while rebooting the UPS. *test.failure.start*:: Starts a 15 second long simulation of an input power failure. *test.battery.start.quick*:: Start a "quick" battery test. The default time is 2 minutes. This time can be set in the *ups.conf* file. See *testime* above. *test.battery.stop*:: Stops a battery test that is in progress. All ON UPS units ~~~~~~~~~~~~~~~~ *reset.input.minmax*:: Reset the minimum and maximum input line voltage values seen since the last reset or power on. Newer ON UPS units ~~~~~~~~~~~~~~~~~~ *test.panel.start*:: Start testing the UPS panel. *test.battery.start.deep*:: Start a "deep" battery test. This test runs the UPS until the low battery point and then returns to the AC line. *reset.input.minmax*:: Reset the minimum and maximum input line voltage values seen since the last reset or power on. *beeper.enable*:: Enable UPS beeper/buzzer. *beeper.disable*:: Disable UPS beeper/buzzer. *beeper.mute*:: Mutes the UPS beeper/buzzer for the current alarm condition(s). Writable Variables ------------------ See linkman:upsrw[8] to see what variables are writable for the UPS. NOTE: If your UPS supports writing battery.runtime.low, the new set value is to be entered in minutes (up to 99) but the reported value is reported in seconds (set value * 60). NOTE: If your UPS supports input.transfer.low and input.transfer.high, those values are used to create an allowable output range. The UPS will do what it can to keep the output voltage value within the defined range (for example: tap change or switch to inverter). AUTHOR ------ Bill Elliot , Eric Lawson SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/libnutclient_variables.txt0000644000175000017500000000460112640444140016243 00000000000000LIBNUTCLIENT_VARIABLES(3) ========================= NAME ---- libnutclient_variables, nutclient_get_device_variables, nutclient_get_device_rw_variables, nutclient_has_device_variable, nutclient_get_device_variable_description, nutclient_get_device_variable_values, nutclient_set_device_variable_value, nutclient_set_device_variable_values - Variable related functions in Network UPS Tools high-level client access library SYNOPSIS -------- #include typedef void* NUTCLIENT_t; strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev); strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev); int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var); char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var); strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var); void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value); void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values); DESCRIPTION ----------- These functions allow to manage variables of devices. The *nutclient_get_device_variables()* function retrieve the list of variables names for a device. The returned strarr must be freed by 'strarr_free'. The *nutclient_get_device_rw_variables* function retrieve the list of read-write variables names for a device. The returned strarr must be freed by 'strarr_free'. The *nutclient_has_device_variable* function test if the specified variable is supported by the device. Return 1 is supported and 0 if not. The *nutclient_get_device_variable_description* function retrieve the variable description, if any. The resturned string must be freed. The *nutclient_get_device_variable_values* returns variable values (generally only one). The returned strarr must be freed by 'strarr_free'. The *nutclient_set_device_variable_value* intend to set the value of the specified variable. The *nutclient_set_device_variable_values* intend to set multiple values of the specified variable. 'dev' is the device name. 'var' is the variable name. 'value' is the variable value. 'values' is the variable array of values. SEE ALSO -------- linkman:libnutclient[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] nut-2.7.4/docs/man/macosx-ups.txt0000644000175000017500000000503612640473702013627 00000000000000MACOSX-UPS(8) ============= NAME ---- macosx-ups - monitor for Mac OS X built-in UPS and battery driver NOTE ---- This man page only documents the hardware-specific features of the *macosx-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ *macosx-ups* supports any USB HID Power Device Class (PDC) UPS which is matched by the Mac OS X built-in drivers. It also can monitor a laptop internal battery as though it were an UPS. If the UPS is visible in the Energy Saver preferences pane of System Preferences, this driver should be able to monitor it. EXTRA ARGUMENTS ---------------- *port*=auto:: Due to changes in the way that Mac OS X lists power sources, the *port* parameter no longer has any effect. The rest of NUT still requres a value here, and our traditional "don't care" value is `auto`. *model*='regex':: Likewise, if you have more than one UPS, it may be necessary to specify a *model* name to match against. This parameter is also a case-insensitive extended regular expression. DIAGNOSTICS ----------- If the driver cannot find an UPS, first open System Preferences and see if there is an "UPS" tab on the Energy Saver panel. If so, re-run the driver with the *-D* flag to list the names of the power sources found. KNOWN ISSUES AND BUGS --------------------- This driver is a monitoring-only driver, and cannot shut down an UPS on its own. However, this should not be a problem in practice: it is monitoring the built-in Mac OS X UPS driver, which has configuration options for several shutdown scenarios. Consult the Energy Saver control panel or *pmset*(8) for more information. The default distribution of *apcupsd* installs a kernel extension which prevents Mac OS X from attaching to the UPS. In order to use this driver after installing apcupsd, you must first run the `apcupsd-uninstall` script and reboot. Note that other UPS monitoring solutions may show more detail than what is provided by the built-in Mac OS X driver. In particular, voltages other than the battery voltage, as well as current and frequency, are typically not shown. It may be possible to monitor these values with *apcupsd* (for APC hardware only) or linkman:usbhid-ups[8]. AUTHORS ------- Charles Lepple SEE ALSO -------- linkman:usbhid-ups[8], *pmset*(8), *regex*(3) The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ The apcupsd home page: http://www.apcupsd.org/ nut-2.7.4/docs/man/powerpanel.80000644000175000017500000001444512640476512013242 00000000000000'\" t .\" Title: powerpanel .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "POWERPANEL" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" powerpanel \- Driver for PowerPanel Plus compatible UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the powerpanel driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports CyberPower BC1200, PR2200 and many other similar devices, both for the text and binary protocols\&. The driver will autodetect which protocol is used\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in \fBups.conf\fR(5): .PP \fBprotocol=\fR[\fItext,binary\fR] .RS 4 Override the default autodetection of the protocol\&. .RE .PP \fBmanufacturer=\fR\fIvalue\fR .RS 4 If you don\(cqt like the autodetected value, you can override this by setting it here\&. .RE .PP \fBmodel=\fR\fIvalue\fR .RS 4 Like manufacturer above\&. .RE .PP \fBserial=\fR\fIvalue\fR .RS 4 Like manufacturer above\&. .RE .PP \fBondelay=\fR\fIvalue\fR .RS 4 Time to wait before switching on the UPS (1 \- 9999 minutes, 0=indefinite)\&. Only available with the text protocol driver (see Support Status)\&. .RE .PP \fBoffdelay=\fR\fIvalue\fR .RS 4 Time to wait before shutting down the UPS (6 \- 600 seconds)\&. Values below 60 seconds will be truncated to 6 seconds intervals, values above 60 seconds to 60 seconds intervals\&. Only available with the text protocol driver (see Support Status)\&. .RE .SH "VARIABLES" .sp Depending on the type of your UPS unit, some of the following variables may be changed with \fBupsrw\fR(8)\&. If the driver can\(cqt read a variable from the UPS, it will not be made available\&. .PP \fBinput\&.transfer\&.high\fR .RS 4 writable: high transfer voltage point in V .RE .PP \fBinput\&.transfer\&.low\fR .RS 4 writable: low transfer voltage point in V .RE .PP \fBbattery\&.charge\&.low\fR .RS 4 writable: remaining battery charge percentage for low battery warning .RE .PP \fBoutput\&.voltage\&.nominal\fR .RS 4 writable: nominal output voltage in V .RE .PP \fBups\&.start\&.battery\fR .RS 4 writable: allow cold start from battery .RE .SH "COMMANDS" .sp Depending on the type of your UPS unit, some of the following commands may be available\&. .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.battery\&.start\&.quick, test\&.battery\&.stop .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} beeper\&.enable, beeper\&.disable, beeper\&.toggle .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.return, shutdown\&.reboot, shutdown\&.stayoff .RE .sp On many devices, these commands are unreliable, so before using them you must verify that these work as expected (see Shutdown Issues)\&. .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.stop .RE .SH "SUPPORT STATUS" .sp Vendor support is absent for this driver, so if you need some features that are currently not available, provide ample documentation on what the driver should sent to the UPS in order to make this work\&. If more information would be available on the binary protocol, it would probably be possible to make \fIondelay\fR and \fIoffdelay\fR configurable\&. So far, nobody has taken the time to investigate what we should tell the UPS to make this work, and CyberPower isn\(cqt willing to share this with us\&. .SH "SHUTDOWN ISSUES" .sp If the \fBshutdown\&.return\fR command on your UPS doesn\(cqt seem to work, chances are that your UPS is an older model\&. Try a couple of different settings for \fIoffdelay\fR\&. If no value in the range 6\&.\&.600 works, your UPS likely doesn\(cqt support this\&. In order to get the expected behaviour, it requires \fBshutdown\&.stayoff\fR (when on battery) and \fBshutdown\&.reboot\fR (when on mains)\&. The driver will automatically fallback to these commands if \fBshutdown\&.return\fR fails, and tries to detect which one should be used when called with the \fI\-k\fR option (or through \fBupsdrvctl shutdown\fR)\&. .sp This isn\(cqt bullet\-proof, however, and you should be prepared that the power will either not be shutdown or that it doesn\(cqt return when the power comes back\&. All models supported by the binary protocol and many supported through the text protocol are affected by this\&. .SH "KNOWN PROBLEMS" .sp The CyberPower OP series don\(cqt offer direct voltage, charge, frequency and temperature readings\&. Instead, they will return a binary value that needs conversion to the actual value\&. .sp The exact conversion needed is unknown at the time of this writing, hence an estimation was made based om readings from actual devices\&. This may (probably will) be off, possibly a lot\&. Unless you can tell us the exact mapping between values from the UPS and actual readings, don\(cqt bother to complain\&. We\(cqve done the best we can based on the limited information available\&. Remember, a UPS isn\(cqt a measuring instrument\&. .SH "AUTHORS" .sp Arjen de Korte , Doug Reynolds .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_scan_snmp.txt0000644000175000017500000000501012640444140015226 00000000000000NUTSCAN_SCAN_SNMP(3) ==================== NAME ---- nutscan_scan_snmp - Scan network for SNMP devices. SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_snmp(const char * start_ip,const char * stop_ip,long timeout, nutscan_snmp_t * sec); DESCRIPTION ----------- The *nutscan_scan_snmp()* function try to detect NUT compatible SNMP devices. It tries SNMP queries on every IP ranging from 'start_ip' to 'stop_ip'. Those IP may be either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. This function waits up to 'timeout' microseconds before considering an IP address does not respond to SNMP queries. A valid `nutscan_snmp_t` structure must be passed to this function. The `nutscan_snmp_t` structure contains the following members which must be filled as described below: char * 'community'; char * 'secLevel'; char * 'secName'; char * 'authPassword'; char * 'privPassword'; char * 'authProtocol'; char * 'privProtocol'; If 'community' is not NULL, SNMP v1 request are sent using this 'community'. If 'community' is NULL and 'secLevel' is NULL, SNMP v1 is selected and 'community' is set to "public". In the other cases, SNMP v3 is used. 'secLevel' may be one of `SNMP_SEC_LEVEL_NOAUTH`, `SNMP_SEC_LEVEL_AUTHNOPRIV` or `SNMP_SEC_LEVEL_AUTHPRIV`. 'secName' is the security name and must be non NULL. If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHNOPRIV`, 'authPassword' must be non NULL. If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHPRIV`, 'authPassword' and 'privPassword' must be non NULL. If 'authProtocol' is NULL, MD5 protocol is used. Else you can set 'authProtocol' to either "MD5" or "SHA". If 'privProtocol' is NULL, DES protocol is used. Else you can set 'privProtocol' to either "AES" or "DES". 'peername' and 'handle' are used internally and do not need any initialization. RETURN VALUE ------------ The *nutscan_scan_snmp()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], linkman:nutscan_cidr_to_ip[3] nut-2.7.4/docs/man/nutclient_authenticate.30000644000175000017500000000003012665610645015611 00000000000000.so libnutclient_misc.3 nut-2.7.4/docs/man/bcmxcp.80000644000175000017500000000712512640476474012346 00000000000000'\" t .\" Title: bcmxcp .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BCMXCP" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" bcmxcp \- Driver for UPSes supporting the serial BCM/XCP protocol .SH "NOTE" .sp This man page only documents the hardware\-specific features of the bcmxcp driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver should recognize all serial BCM/XCP\-compatible UPSes\&. It has been developed and tested on Powerware PW5115 and PW9120 hardware\&. If your UPS has a USB connection, you may also consult the \fBbcmxcp_usb\fR(8) driver documentation\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5)\&. .PP \fBshutdown_delay=\fR\fIdelay\fR .RS 4 The number of seconds that the UPS should wait between receiving the shutdown command (upsdrvctl shutdown) and actually shutting off\&. .RE .PP \fBbaud_rate=\fR\fIrate\fR .RS 4 Communication speed for the UPS\&. If this is set to 9600, it tries to connect to the UPS at 9600bps\&. If it fails to communicate, it will go into baud\-hunting\&. It starts at 1200 and goes up to 19200\&. If it succeeds, it tell you the speed it connected with\&. If not included in the config, it defaults to baud\-hunting\&. .RE .SH "DEFAULT VALUES FOR THE EXTRA ARGUMENTS" .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBshutdown_delay =\fR\fI120\fR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBbaud_rate =\fR\fInone\fR .RE .SH "INSTANT COMMANDS" .sp This driver supports the following Instant Commands: .PP \fBshutdown\&.return\fR .RS 4 Turn off the load and return when power is back\&. .RE .PP \fBshutdown\&.stayoff\fR .RS 4 Turn off the load and remain off\&. .RE .PP \fBtest\&.battery\&.start\fR .RS 4 Start a battery test\&. .RE .PP \fBoutlet\&.n\&.shutdown\&.return\fR .RS 4 Turn off the load on outlet \fIn\fR and return when power is back\&. (\fIn\fR is the outlet number reported by the upsc command) .RE .SH "TODO LIST" .PP Report UPS statistics information .RS 4 BCM/XCP supports reporting of UPS statistics data\&. .RE .PP Change settings .RS 4 Access the config register to change settings\&. .RE .SH "BUGS" .sp None known\&. .SH "AUTHOR" .sp Tore Ørpetveit .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "The USB BCM/XCP driver:" .sp \fBbcmxcp_usb\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_get_device_rw_variables.30000644000175000017500000000003512665610646017777 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/nutclient_get_device_variables.30000644000175000017500000000003512665610646017267 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/upscli_list_next.txt0000644000175000017500000000370612640473702015122 00000000000000UPSCLI_LIST_NEXT(3) =================== NAME ---- upscli_list_next - retrieve list items from a UPS SYNOPSIS -------- #include int upscli_list_next(UPSCONN_t *ups, unsigned int numq, const char **query, unsigned int *numa, char ***answer) DESCRIPTION ----------- The *upscli_list_next()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, and the pointer 'query' to an array of 'numq' query elements. It performs a read from the network and expects to find either another list item or the end of a list. You must call linkman:upscli_list_start[3] before calling this function. This function will return 1 and set values in 'numa' and 'answer' if a list item is received. If the list is done, it will return 0, and the values in 'numa' and 'answer' are undefined. Calling this function after it returns something other than 1 is undefined. QUERY FORMATTING ---------------- You may not change the values of 'numq' or 'query' between the call to linkman:upscli_list_start[3] and the first call to this function. You also may not change the values between calls to this function. ANSWER FORMATTING ----------------- The contents of 'numa' and 'answer' work just like a call to linkman:upscli_get[3]. The values returned by linkman:upsd[8] are identical to a single item request, so this is not surprising. ERROR CHECKING -------------- This function checks the response from linkman:upsd[8] against your query. If the response is not part of the list you have requested, it will return an error code. When this happens, linkman:upscli_upserror[3] will return `UPSCLI_ERR_PROTOCOL`. RETURN VALUE ------------ The *upscli_list_next()* function returns 1 when list data is present, 0 if the list is finished, or -1 if an error occurs. It is possible to have an empty list. The function will return 0 for its first call in that case. SEE ALSO -------- linkman:upscli_list_start[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/bestfcom.txt0000644000175000017500000000144112640443572013330 00000000000000BESTFCOM(8) =========== NAME ---- bestfcom - Driver for Best Power Fortress/Ferrups NOTE ---- This man page only documents the hardware-specific features of the bestfcom driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ Best Power Fortress/Ferrups implementing the Fortress UPS Protocol (f-command set). EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHORS ------- Kent Polk (bestfcom) Andreas Wrede, John Stone (bestuferrups) Grant Taylor (bestfort) Russell Kroll (bestups) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/blazer_ser.80000644000175000017500000002633712640476477013233 00000000000000'\" t .\" Title: blazer_ser .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BLAZER_SER" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" blazer_ser \- Driver for Megatec/Q1 protocol serial based UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the blazer driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The blazer driver is known to work with various UPSes from Blazer, Energy Sistem, Fenton Technologies, General Electric, Mustek and many others\&. The NUT compatibility table lists all the known supported models\&. Keep in mind, however, that other models not listed there may also be supported, but haven\(cqt been tested\&. .sp All devices with a serial interface (use the \fBblazer_ser\fR driver) and many with a USB interface (use the \fBblazer_usb\fR driver) are supported\&. .SH "EXTRA ARGUMENTS" .sp You may need to override or provide defaults for some values, depending on the make and model of your UPS\&. The following are the ones that most likely will need changing (see \fBups.conf\fR(5)): .PP \fBdefault\&.battery\&.voltage\&.high =\fR \fIvalue\fR .RS 4 Maximum battery voltage that is reached after about 12 to 24 hours charging\&. If you want the driver to report a guesstimated \fBbattery\&.charge\fR, you need to specify this (see BATTERY CHARGE)\&. .RE .PP \fBdefault\&.battery\&.voltage\&.low =\fR \fIvalue\fR .RS 4 Minimum battery voltage just before the UPS automatically shuts down\&. If you want the driver to report a guesstimated \fBbattery\&.charge\fR, you need to specify this (see BATTERY CHARGE)\&. .RE .PP \fBdefault\&.battery\&.voltage\&.nominal =\fR \fIvalue\fR, \fBoverride\&.battery\&.voltage\&.nominal =\fR \fIvalue\fR .RS 4 Some devices show a wrong nominal battery voltage (or none at all), so you may need to override or set a default value\&. .RE .PP \fBoverride\&.battery\&.packs =\fR \fIvalue\fR .RS 4 Some devices report a part of the total battery voltage\&. For instance, if \fBbattery\&.voltage\&.nominal\fR is 24 V, but it reports a \fBbattery\&.voltage\fR of around 2 V, the number of \fBbattery\&.packs\fR to correct this reading would be 12\&. The driver will attempt to detect this automatically, but if this fails somehow, you may want to override this value\&. .RE .PP \fBondelay =\fR \fIvalue\fR .RS 4 Time to wait before switching on the UPS (minutes)\&. Note that a value below 3 minutes, may cause earlier firmware versions to not switch on automatically, so it defaults to 3 minutes\&. The acceptable range is 0\&.\&.9999 minutes\&. .RE .PP \fBoffdelay =\fR \fIvalue\fR .RS 4 Time to wait before shutting down the UPS (seconds)\&. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds)\&. Defaults to 30 seconds\&. The acceptable range is 12\&.\&.600 seconds\&. .RE .PP \fBnorating\fR .RS 4 Some UPSes will lock up if you attempt to read rating information from them\&. Setting this flag will make the driver skip this step\&. .RE .PP \fBnovendor\fR .RS 4 Some UPSes will lock up if you attempt to read vendor information from them\&. Setting this flag will make the driver skip this step\&. .RE .PP \fBprotocol =\fR \fIstring\fR .RS 4 Skip autodetection of the protocol to use and only use the one specified\&. Supported values \fImegatec\fR, \fImegatec/old\fR, \fImustek\fR and \fIzinto\fR\&. .RE .PP \fBruntimecal =\fR \fIvalue,value,value,value\fR .RS 4 Parameter used in the (optional) runtime estimation\&. This takes two runtimes at different loads\&. Typically, this uses the runtime at full load and the runtime at half load\&. For instance, if your UPS has a rated runtime of 240 seconds at full load and 720 seconds at half load, you would enter .sp .if n \{\ .RS 4 .\} .nf runtimecal = 240,100,720,50 .fi .if n \{\ .RE .\} .sp The first load should always be higher than the second\&. If you have values available for loads other than 100 and 50 % respectively, you can use those too, but keep them spaced apart as far as reasonably possible\&. Just don\(cqt get too close to no load (prediction of runtime depends more on idle load for the battery then)\&. .RE .PP \fBchargetime =\fR \fIvalue\fR .RS 4 The time needed to fully recharge the battery after being fully discharged\&. If not specified, the driver defaults to 43200 seconds (12 hours)\&. Only used if \fBruntimecal\fR is also specified\&. .RE .PP \fBidleload =\fR \fIvalue\fR .RS 4 Minimum battery load used by the driver to estimate the runtime\&. If not specified, the driver defaults to 10%\&. Only used if \fBruntimecal\fR is also specified\&. .RE .SS "SERIAL INTERFACE ONLY" .PP \fBcablepower =\fR \fIstring\fR .RS 4 By default the driver will set DTR and clear RTS (\fInormal\fR)\&. If you find that your UPS isn\(cqt detected or the communication with the UPS is unreliable, you may try if clear DTR and set RTS (\fIreverse\fR), set DTR and RTS (\fIboth\fR) or clear DTR and RTS (\fInone\fR) improves this situation\&. .RE .SH "UPS COMMANDS" .sp This driver supports some instant commands (see \fBupscmd\fR(8)): .PP \fBbeeper\&.toggle\fR .RS 4 Toggle the UPS beeper\&. (Not available on some hardware\&.) .RE .PP \fBload\&.on\fR .RS 4 Turn on the load immediately\&. .RE .PP \fBload\&.off\fR .RS 4 Turn off the load immediately (see KNOWN PROBLEMS)\&. .RE .PP \fBshutdown\&.return\fR .RS 4 Turn off the load and return when power is back\&. Uses the timers defined by \fBondelay\fR and \fBoffdelay\fR\&. .RE .PP \fBshutdown\&.stayoff\fR .RS 4 Turn off the load and remain off (see KNOWN PROBLEMS)\&. Uses the timer defined by \fBoffdelay\fR\&. .RE .PP \fBshutdown\&.stop\fR .RS 4 Stop a shutdown in progress\&. .RE .PP \fBtest\&.battery\&.start\&.deep\fR .RS 4 Perform a long battery test (Not available on some hardware\&.) .RE .PP \fBtest\&.battery\&.start\&.quick\fR .RS 4 Perform a (10 second) battery test\&. .RE .PP \fBtest\&.battery\&.start\fR \fIvalue\fR .RS 4 Perform a battery test for the duration of \fIvalue\fR minutes\&. .RE .PP \fBtest\&.battery\&.stop\fR .RS 4 Stop a running battery test (not available on some hardware\&.) .RE .SH "BATTERY CHARGE" .sp Due to popular demand, this driver will report a guesstimated \fBbattery\&.charge\fR and optionally \fBbattery\&.runtime\fR, provided you specified a couple of the EXTRA ARGUMENTS listed above\&. .sp If you specify both \fBbattery\&.voltage\&.high\fR and \fBbattery\&.voltage\&.low\fR in \fBups.conf\fR(5), but don\(cqt enter \fBruntimecal\fR, it will guesstimate the state of charge by looking at the battery voltage alone\&. This is not reliable under load, as this only gives reasonably accurate readings if you disconnect the load, let the battery rest for a couple of minutes and then measure the open cell voltage\&. This just isn\(cqt practical if the power went out and the UPS is providing power for your systems\&. .sp .if n \{\ .RS 4 .\} .nf battery\&.voltage \- battery\&.voltage\&.low battery\&.charge = \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- x 100 % battery\&.voltage\&.high \- battery\&.voltage\&.low .fi .if n \{\ .RE .\} .sp There is a way to get better readings without disconnecting the load but this requires one to keep track on how much (and how fast) current is going in\- and out of the battery\&. If you specified the \fBruntimecal\fR, the driver will attempt to do this\&. Note however, that this heavily relies on the values you enter and that the UPS must be able to report the load as well\&. There are quite a couple of devices that report 0 % (or any other fixed value) at all times, in which case this obviously doesn\(cqt work\&. .sp The driver also has no way of determining the degradation of the battery capacity over time, so you\(cqll have to deal with this yourself (by adjusting the values in \fBruntimecal\fR)\&. Also note that the driver guesses the initial state of charge based on the battery voltage, so this may be less than 100 %, even when you are certain that they are full\&. There is just no way to reliably measure this between 0 and 100 % full charge\&. .sp This is better than nothing (but not by much)\&. If any of the above calculations is giving you incorrect readings, you are the one that put in the values in \fBups.conf\fR(5), so don\(cqt complain with the author\&. If you need something better, buy a UPS that reports \fBbattery\&.charge\fR and \fBbattery\&.runtime\fR all by itself without the help of a NUT driver\&. .SH "NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS" .sp The blazer drivers having replaced the megatec ones, some configuration changes may be required by users switching to blazer\&. .sp Part of this, the following megatec options, in ups\&.conf, have to be changed: .PP \fBbattvolts\fR .RS 4 You need to use \fIdefault\&.battery\&.voltage\&.high\fR and \fIdefault\&.battery\&.voltage\&.low\fR .RE .PP \fBdtr and rts\fR .RS 4 You need to use \fIcablepower\fR .RE .PP \fBignoreoff\fR .RS 4 This parameter can simply be discarded, since it was a wrong understanding of the specification\&. .RE .SH "KNOWN PROBLEMS" .sp Some UPS commands aren\(cqt supported by all models\&. In most cases, the driver will send a message to the system log when the user tries to execute an unsupported command\&. Unfortunately, some models don\(cqt even provide a way for the driver to check for this, so the unsupported commands will silently fail\&. .sp Both the \fBload\&.off\fR and \fBshutdown\&.stayoff\fR instant commands are meant to turn the load off indefinitely\&. However, some UPS models don\(cqt allow this\&. .sp Some models report a bogus value for the beeper status (will always be \fIenabled\fR or \fIdisabled\fR)\&. So, the \fBbeeper\&.toggle\fR command may appear to have no effect in the status reported by the driver when, in fact, it is working fine\&. .sp The temperature and load value is known to be bogus in some models\&. .SH "AUTHORS" .sp Arjen de Korte , Alexander Gordeev .SH "SEE ALSO" .sp \fBblazer_usb\fR(8), \fBnutupsdrv\fR(8), \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsrw\fR(8) .SS "Internet Resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp The NUT HCL: http://www\&.networkupstools\&.org/stable\-hcl\&.html nut-2.7.4/docs/man/libnutclient_commands.txt0000644000175000017500000000274212640444140016100 00000000000000LIBNUTCLIENT_COMMANDS(3) ======================== NAME ---- libnutclient_commands, nutclient_get_device_commands, nutclient_has_device_command, nutclient_get_device_command_description, nutclient_execute_device_command - Instant command related functions in Network UPS Tools high-level client access library SYNOPSIS -------- #include typedef void* NUTCLIENT_t; strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev); int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd); void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); DESCRIPTION ----------- These functions allow to manage instant commands of devices. The *nutclient_get_device_commands()* function retrieve the list of command names for a device. The returned strarr must be freed by 'strarr_free'. The *nutclient_has_device_command* function test if the specified command is supported by the device. Return 1 is supported and 0 if not. The *nutclient_get_device_command_description* function retrieve the command description, if any. The resturned string must be freed. The *nutclient_execute_device_command* intend to execute the instant command. 'dev' is the device name. 'cmd' is the instant command name. SEE ALSO -------- linkman:libnutclient[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] nut-2.7.4/docs/man/upssched.txt0000644000175000017500000000731712640443572013354 00000000000000UPSSCHED(8) =========== NAME ---- upssched - Timer helper for scheduling events from upsmon SYNOPSIS -------- *upssched* NOTE: *upssched* should be run from linkman:upsmon[8] via the NOTIFYCMD. You should never run it directly during normal operations. DESCRIPTION ----------- *upssched* was created to allow users to execute programs at times relative to events being monitored by linkman:upsmon[8]. The original purpose was to allow for a shutdown to occur after some fixed period on battery, but there are other uses that are possible. INTEGRATION ----------- upssched needs to be called as the NOTIFYCMD in your linkman:upsmon.conf[5]. It determines what is happening based on the UPSNAME and NOTIFYTYPE environment variables. You should never have to deal with them directly. Set the EXEC flag on the events that you want to see in upssched. For example, to make sure that upssched hears about ONLINE, ONBATT and LOWBATT events, the flags would look like this: NOTIFYFLAG ONLINE EXEC NOTIFYFLAG ONBATT EXEC NOTIFYFLAG LOWBATT EXEC If you also want to continue writing to the syslog, just add it in: NOTIFYFLAG ONLINE SYSLOG+EXEC NOTIFYFLAG ONBATT SYSLOG+EXEC NOTIFYFLAG LOWBATT SYSLOG+EXEC For a full list of notify flags, see the linkman:upsmon[8] documentation. CONFIGURATION ------------- See linkman:upssched.conf[5] for information on configuring this program. EARLY SHUTDOWNS --------------- To shut down the system early, define a timer that starts due to an ONBATT condition. When it triggers, make your CMDSCRIPT call your shutdown routine. It should finish by calling `upsmon -c fsd` so that upsmon gets to shut down the slaves in a controlled manner. Be sure you cancel the timer if power returns (ONLINE). DEBOUNCING EVENTS ----------------- If your UPS goes on and off battery frequently, you can use this program to reduce the number of pager messages that are sent out. Rather than sending pages directly from linkman:upsmon[8], use a short timer here. If the timer triggers with the UPS still on battery, then send the page. If the power returns before then, the timer can be cancelled and no page is necessary. BACKGROUND ---------- This program was written primarily to fulfill the requests of users for the early shutdown scenario. The "outboard" design of the program (relative to upsmon) was intended to reduce the load on the average system. Most people don't have the requirement of shutting down after 'N' seconds on battery, since the usual OB+LB testing is sufficient. This program was created separately so those people don't have to spend CPU time and RAM on something that will never be used in their environments. The design of the timer handler is also geared towards minimizing impact. It will come and go from the process list as necessary. When a new timer is started, a process will be forked to actually watch the clock and eventually start the CMDSCRIPT. When a timer triggers, it is removed from the queue. Cancelling a timer will also remove it from the queue. When no timers are present in the queue, the background process exits. This means that you will only see upssched running when one of two things is happening: - There's a timer of some sort currently running - upsmon just called it, and you managed to catch the brief instance The final optimization handles the possibility of trying to cancel a timer when there are none running. If the timer daemon isn't running, there are no timers to cancel, and furthermore there is no need to start a clock-watcher. So, it skips that step and exits sooner. FILES ----- linkman:upssched.conf[5] SEE ALSO -------- linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsdrvctl.txt0000644000175000017500000000562712640473702013564 00000000000000UPSDRVCTL(8) ============ NAME ---- upsdrvctl - UPS driver controller SYNOPSIS -------- *upsdrvctl* -h *upsdrvctl* ['OPTIONS'] {start | stop | shutdown} ['ups'] DESCRIPTION ----------- *upsdrvctl* provides a uniform interface for controlling your UPS drivers. You should use upsdrvctl instead of direct calls to the drivers whenever possible. When used properly, upsdrvctl lets you maintain identical startup scripts across multiple systems with different UPS configurations. OPTIONS ------- *-h*:: Display the help text. *-r* 'directory':: If starting a driver, this value will direct it to *chroot*(2) into 'directory'. This can be useful when securing systems. This may be set in the ups.conf with "chroot" in the global section. *-t*:: Enable testing mode. This also enables debug mode. Testing mode makes upsdrvctl display the actions it would execute without actually doing them. Use this to test out your configuration without actually doing anything to your UPS drivers. This may be helpful when defining the 'sdorder' directive in your linkman:ups.conf[5]. *-u* 'username':: If starting a driver, this value will direct it to *setuid*(2) to the user id associated with 'username'. If the driver is started as root without specifying this value, it will use the username that was compiled into the binary. This defaults to "nobody", and is far from ideal. This may be set in ups.conf with "user" in the global section. *-D*:: Raise the debug level. Use this multiple times for additional details. COMMANDS -------- upsdrvctl supports three commands - start, stop and shutdown. They take an optional argument which is a UPS name from linkman:ups.conf[5]. Without that argument, they operate on every UPS that is currently configured. *start*:: Start the UPS driver(s). In case of failure, further attempts may be executed by using the 'maxretry' and 'retrydelay' options - see linkman:ups.conf[5]. *stop*:: Stop the UPS driver(s). *shutdown*:: Command the UPS driver(s) to run their shutdown sequence. Drivers are stopped according to their sdorder value - see linkman:ups.conf[5]. WARNING: this will probably power off your computers, so don't play around with this option. Only use it when your systems are prepared to lose power. ENVIRONMENT VARIABLES --------------------- *NUT_CONFPATH* is the path name of the directory that contains `upsd.conf` and other configuration files. If this variable is not set, *upsdrvctl* the driver use a built-in default, which is often `/usr/local/ups/etc`. DIAGNOSTICS ----------- upsdrvctl will return a nonzero exit code if it encounters an error while performing the desired operation. This will also happen if a driver takes longer than the 'maxstartdelay' period to enter the background. SEE ALSO -------- linkman:nutupsdrv[8], linkman:upsd[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/richcomm_usb.80000644000175000017500000000503412640476520013531 00000000000000'\" t .\" Title: richcomm_usb .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "RICHCOMM_USB" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" richcomm_usb \- Driver UPS equipment using Richcomm dry\-contact to USB solution .SH "NOTE" .sp This man page only documents the specific features of the richcomm_usb driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The Richcomm dry\-contact to USB solution is a generic interface that is used to upgrade an existing (RS\-232) contact closure UPS interface to USB\&. As such, all the limitations of the underlying contact closure interface apply\&. This means that you will only get the essentials in ups\&.status: OL, OB, and LB\&. See also \fBgenericups\fR(8)\&. .SH "BUGS" .sp Most contact\-closure UPSes will not power down the load if the line power is present\&. This can create a race when using slave \fBupsmon\fR(8) systems\&. See the \fBupsmon\fR(8) man page for more information\&. .sp The solution to both of these problems is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command\&. .SH "AUTHORS" .sp Peter van Valderen , Dirk Teurlings .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "The generic serial driver:" .sp \fBgenericups\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/richcomm_usb.txt0000644000175000017500000000256612640443572014211 00000000000000RICHCOMM_USB(8) =============== NAME ---- richcomm_usb - Driver UPS equipment using Richcomm dry-contact to USB solution NOTE ---- This man page only documents the specific features of the richcomm_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The Richcomm dry-contact to USB solution is a generic interface that is used to upgrade an existing (RS-232) contact closure UPS interface to USB. As such, all the limitations of the underlying contact closure interface apply. This means that you will only get the essentials in ups.status: OL, OB, and LB. See also linkman:genericups[8]. BUGS ---- Most contact-closure UPSes will not power down the load if the line power is present. This can create a race when using slave linkman:upsmon[8] systems. See the linkman:upsmon[8] man page for more information. The solution to both of these problems is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command. AUTHORS ------- Peter van Valderen , Dirk Teurlings SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] The generic serial driver: ~~~~~~~~~~~~~~~~~~~~~~~~~~ linkman:genericups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsd.users.txt0000644000175000017500000000432412640443572013644 00000000000000UPSD.USERS(5) ============= NAME ---- upsd.users - Administrative user definitions for NUT upsd DESCRIPTION ----------- Administrative commands such as setting variables and the instant commands are powerful, and access to them needs to be restricted. This file defines who may access them, and what is available. Each user gets its own section. The fields in that section set the parameters associated with that user's privileges. The section begins with the name of the user in brackets, and continues until the next user name in brackets or EOF. These users are independent of /etc/passwd. Here are some examples to get you started: [admin] password = mypass actions = set actions = fsd instcmds = all [pfy] password = duh instcmds = test.panel.start instcmds = test.panel.stop [monmaster] password = blah upsmon master [monslave] password = abcd upsmon slave FIELDS ------ *password*:: Set the password for this user. *actions*:: Allow the user to do certain things with upsd. To specify multiple actions, use multiple instances of the *actions* field. Valid actions are: SET;; change the value of certain variables in the UPS FSD;; set the forced shutdown flag in the UPS. This is equivalent to an "on battery + low battery" situation for the purposes of monitoring. The list of actions is expected to grow in the future. *instcmds*:: Let a user initiate specific instant commands. Use "ALL" to grant all commands automatically. To specify multiple commands, use multiple instances of the *instcmds* field. For the full list of what your UPS supports, use "upscmd -l". + The +cmdvartab+ file supplied with the distribution contains a list of most of the known command names. *upsmon*:: Add the necessary actions for a upsmon process to work. This is either set to "master" or "slave". + Do not attempt to assign actions to upsmon by hand, as you may miss something important. This method of designating a "upsmon user" was created so internal capabilities could be changed later on without breaking existing installations. SEE ALSO -------- linkman:upsd[8], linkman:upsd.conf[5] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/genericups.80000644000175000017500000003472212640476502013231 00000000000000'\" t .\" Title: genericups .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "GENERICUPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" genericups \- Driver for contact\-closure UPS equipment .SH "NOTE" .sp This man page only documents the specific features of the genericups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports hardware from many different manufacturers as it only uses the very simplest of signaling schemes\&. Contact closure refers to a kind of interface where basic high/low signals are provided to indicate status\&. This kind of UPS can only report line power and battery status\&. .sp This means that you will only get the essentials in ups\&.status: OL, OB, and LB\&. Anything else requires a smarter UPS\&. .SH "CABLING" .sp Cabling is different for every kind of UPS\&. See the table below for information on what is known to work with a given UPS type\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following settings in the \fBups.conf\fR(5): .PP upstype=\fItype\fR .RS 4 Required\&. Configures the driver for a specific kind of UPS\&. See the UPS Types section below for more information on which entries are available\&. .RE .PP mfr=\fIstring\fR .RS 4 Optional\&. The very nature of a generic UPS driver sometimes means that the stock manufacturer data has no relation to the actual hardware that is attached\&. With the mfr setting, you can change the value that is seen by clients that monitor this UPS\&. .RE .PP model=\fIstring\fR .RS 4 Optional\&. This is like mfr above, but it overrides the model string instead\&. .RE .PP serial=\fIstring\fR .RS 4 Optional\&. This is like mfr above and intended to record the identification string of the UPS\&. It is titled "serial" because usually this string is referred to as the serial number\&. .RE .PP sdtime=\fIvalue\fR .RS 4 Optional\&. The driver will sleep for this many seconds after setting the shutdown signal\&. This is necessary for some hardware which requires a sustained level to activate the shutdown sequence\&. .sp The default behavior of the driver is to exit immediately\&. If this doesn\(cqt reliably trigger a shutdown in your UPS hardware, use this setting to give it more time to react\&. .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp very large values for sdtime may create warnings from upsdrvctl if it gets tired of waiting for the driver to return\&. .sp .5v .RE .SH "CUSTOM CONFIGURATIONS" .sp You may override the values for CP, OL, LB, and SD by defining them in the \fBups.conf\fR(5) after the upstype setting\&. .sp For example, to set the cable power to DTR and the low battery value to DCD, it would look like this: .sp .if n \{\ .RS 4 .\} .nf CP = DTR .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf LB = DCD .fi .if n \{\ .RE .\} .sp Recognized values for input lines are CTS, DCD, and RNG\&. Recognized values for output lines are DTR, RTS, and ST\&. See below for more about what these signals mean\&. .sp These values may be negated for active low signals\&. That is, "LB=\-DCD" recognizes a low battery condition when DCD is not held high\&. .SH "TYPE INFORMATION" .sp The essence of a UPS definition in this driver is how it uses the serial lines that are available\&. These are the abbreviations you will see below: .PP OL .RS 4 On line (no power failure) (opposite of OB \- on battery) .RE .PP LB .RS 4 Low battery .RE .PP SD .RS 4 Shutdown load .RE .PP CP .RS 4 Cable power (must be present for cable to have valid reading) .RE .PP CTS .RS 4 Clear to Send\&. Received from the UPS\&. .RE .PP RTS .RS 4 Ready to Send\&. Sent by the PC\&. .RE .PP DCD .RS 4 Data Carrier Detect\&. Received from the UPS\&. .RE .PP RNG .RS 4 Ring indicate\&. Received from the UPS\&. .RE .PP DTR .RS 4 Data Terminal Ready\&. Sent by the PC\&. .RE .PP ST .RS 4 Send a BREAK on the transmit data line .RE .sp A "\-" in front of a signal name (like \-RNG) means that the indicated condition is signaled with an active low signal\&. For example, [LB=\-RNG] means the battery is low when the ring indicate line goes low, and that the battery is OK when that line is held high\&. .SH "UPS TYPES" .sp 0 = UPSonic LAN Saver 600 .sp .if n \{\ .RS 4 .\} .nf [CP=DTR+RTS] [OL=\-CTS] [LB=DCD] [SD=DTR] .fi .if n \{\ .RE .\} .sp 1 = APC Back\-UPS/Back\-UPS Pro/Smart\-UPS with 940\-0095A/C cable .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=\-RNG] [LB=DCD] [SD=RTS] .fi .if n \{\ .RE .\} .sp 2 = APC Back\-UPS/Back\-UPS Pro/Smart\-UPS with 940\-0020B cable .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=\-CTS] [LB=DCD] [SD=DTR+RTS] .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf Type 2 has also been reported to work with the 940\-0020C cable\&. .fi .if n \{\ .RE .\} .sp 3 = PowerTech Comp1000 with DTR cable power .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=DCD] [SD=DTR+RTS] .fi .if n \{\ .RE .\} .sp 4 = Generic RUPS Model .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=CTS] [LB=\-DCD] [SD=\-RTS] .fi .if n \{\ .RE .\} .sp 5 = Tripp Lite UPS with Lan2\&.2 interface (black 73\-0844 cable) .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=\-DCD] [SD=DTR+RTS] .fi .if n \{\ .RE .\} .sp 6 = Best Patriot with INT51 cable .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=\-DCD] [SD=RTS] .fi .if n \{\ .RE .\} .sp 7 = CyberPower Power99 Also Upsonic Power Guardian PG\-500, Belkin Belkin Home Office, F6H350\-SER, F6H500\-SER, F6H650\-SER, Eaton Management Card Contact \- Config3 with cable 66033 (shutdown does not work) .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=CTS] [LB=\-DCD] [SD=DTR] .fi .if n \{\ .RE .\} .sp 8 = Nitram Elite 500 .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=\-DCD] [SD=???] .fi .if n \{\ .RE .\} .sp 9 = APC Back\-UPS/Back\-UPS Pro/Smart\-UPS with 940\-0023A cable .sp .if n \{\ .RS 4 .\} .nf [CP=none] [OL=\-DCD] [LB=CTS] [SD=RTS] .fi .if n \{\ .RE .\} .sp 10 = Victron Lite with crack cable .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=CTS] [LB=\-DCD] [SD=DTR] .fi .if n \{\ .RE .\} .sp 11 = Powerware 3115 .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=\-CTS] [LB=\-DCD] [SD=ST] .fi .if n \{\ .RE .\} .sp 12 = APC Back\-UPS Office with 940\-0119A cable .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=\-CTS] [LB=DCD] [SD=DTR] .fi .if n \{\ .RE .\} .sp 13 = RPT Repoteck RPT\-800A/RPT\-162A .sp .if n \{\ .RS 4 .\} .nf [CP=DTR+RTS] [OL=DCD] [LB=\-CTS] [SD=ST] .fi .if n \{\ .RE .\} .sp 14 = Online P\-series .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=DCD] [LB=\-CTS] [SD=RTS] .fi .if n \{\ .RE .\} .sp 15 = Powerware 5119, 5125 .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=\-DCD] [SD=ST] .fi .if n \{\ .RE .\} .sp 16 = Nitram Elite 2002 .sp .if n \{\ .RS 4 .\} .nf [CP=DTR+RTS] [OL=CTS] [LB=\-DCD] [SD=???] .fi .if n \{\ .RE .\} .sp 17 = PowerKinetics 9001 .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=\-DCD] [SD=???] .fi .if n \{\ .RE .\} .sp 18 = TrippLite Omni 450LAN with Martin\(cqs cabling .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=DCD] [SD=none] .fi .if n \{\ .RE .\} .sp 19 = Fideltronic Ares Series .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=CTS] [LB=\-DCD] [SD=RTS] .fi .if n \{\ .RE .\} .sp 20 = Powerware 5119 RM .sp .if n \{\ .RS 4 .\} .nf [CP=DTR] [OL=\-CTS] [LB=DCD] [SD=ST] .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf Check docs/cables/powerware\&.txt .fi .if n \{\ .RE .\} .sp 21 = Generic RUPS 2000 (Megatec M2501 cable) .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=CTS] [LB=\-DCD] [SD=RTS+DTR] .fi .if n \{\ .RE .\} .sp 22 = Gamatronic All models with alarm interface (also CyberPower SL series) .sp .if n \{\ .RS 4 .\} .nf [CP=RTS] [OL=CTS] [LB=\-DCD] [SD=DTR] .fi .if n \{\ .RE .\} .SH "SIMILAR MODELS" .sp Many different UPS companies make models with similar interfaces\&. The RUPS cable seems to be especially popular in the "power strip" variety of UPS found in office supply stores\&. If your UPS works with an entry in the table above, but the model or manufacturer information don\(cqt match, don\(cqt despair\&. You can fix that easily by using the mfr and model variables documented above in your \fBups.conf\fR(5)\&. .SH "TESTING COMPATIBILITY" .sp If your UPS isn\(cqt listed above, you can try going through the list until you find one that works\&. There is a lot of cable and interface reuse in the UPS world, and you may find a match\&. .sp To do this, first make sure nothing important is plugged into the outlets on the UPS, as you may inadvertently switch it off\&. Definitely make sure that the computer you\(cqre using is not plugged into that UPS\&. Plug in something small like a lamp so you know when power is being supplied to the outlets\&. .sp Now, you can either attempt to make an educated guess based on the documentation your manufacturer has provided (if any), or just start going down the list\&. .SS "Step 1" .sp Pick a driver to try from the list (genericups \-h) and go to step 2\&. .SS "Step 2" .sp Start the driver with the type you want to try \- .sp .if n \{\ .RS 4 .\} .nf genericups \-x upstype=n /dev/port .fi .if n \{\ .RE .\} .sp Let upsd sync up (watch the syslog), and then run upsc to see what it found\&. If the STATUS is right (should be OL for on line), continue to Step 3, otherwise go back to step 1\&. .sp Alternatively, you can run genericups in debug mode \- .sp .if n \{\ .RS 4 .\} .nf genericups \-DDDDD \-x upstype=n /dev/port .fi .if n \{\ .RE .\} .sp In this mode it will be running in the foreground and continuously display the line and battery status of the UPS\&. .SS "Step 3" .sp Disconnect the UPS from the wall/mains power\&. This is easiest if you have a switched outlet in between it and the wall, but you can also just pull the plug to test\&. The lamp should stay lit, and the status should switch to "OB"\&. If the lamp went out or the status didn\(cqt go to "OB" within about 15 seconds, go to Step 1\&. Otherwise, continue to Step 4\&. .SS "Step 4" .sp At this point, we know that OL and OB work\&. If nothing else beyond this point works, you at least know what your OL/OB value should be\&. .sp Wait for the UPS to start complaining about a low battery\&. Depending on the size of your UPS battery and the lamp\(cqs bulb, this could take awhile\&. It should start complaining audibly at some point\&. When this happens, STATUS should show "OB LB" within 15 seconds\&. If not, go to Step 1, otherwise continue to Step 5\&. .SS "Step 5" .sp So far: OL works, OB works, and LB works\&. .sp With the UPS running on battery, run the genericups driver with the \-k switch to shut it down\&. .sp .if n \{\ .RS 4 .\} .nf genericups \-x upstype=n \-k /dev/port .fi .if n \{\ .RE .\} .sp If the UPS turns off the lamp, you\(cqre done\&. At this point, you have verified that the shutdown sequence actually does what you want\&. You can start using the genericups driver with this type number for normal operations\&. .sp You should use your findings to add a section to your ups\&.conf\&. Here is a quick example: .sp .if n \{\ .RS 4 .\} .nf [myups] driver = genericups port = /dev/ttyS0 upstype = 1 .fi .if n \{\ .RE .\} .sp Change the port and upstype values to match your system\&. .SH "NEW SUPPORT" .sp If the above testing sequence fails, you will probably need to create a new entry to support your hardware\&. All UPS types are determined from the table in the genericups\&.h file in the source tree\&. .sp On a standard 9 pin serial port, there are 6 lines that are used as the standard "high/low" signal levels\&. 4 of them are incoming (to the PC, from the UPS), and the other 2 are outgoing (to the UPS, from the PC)\&. The other 3 are the receive/transmit lines and the ground\&. .sp Be aware that many manufacturers remap pins within the cable\&. If you have any doubts, a quick check with a multimeter should confirm whether the cable is straight\-through or not\&. Another thing to keep in mind is that some cables have electronics in them to do special things\&. Some have resistors and transistors on board to change behavior depending on what\(cqs being supplied by the PC\&. .SH "SPECIFIC MODEL NOTES" .sp These have been contributed by users of this driver\&. .sp The Centralion CL series may power down the load if the driver starts up with the UPS running on battery as the default line settings contain the shutdown sequence\&. \- Neil Muller .sp The Tripp\-Lite Internet Office 700 must be used with the black 73\-0844 cable instead of the gray 73\-0743 cable\&. This entry should work with any of their models with the Lan 2\&.2 interface \- see the sticker by the DB9 connector on the UPS\&. \- Stephen Brown .sp Type 5 should work with the Tripp\-Lite Lan 2\&.1 interface and the 73\-0724 cable\&. This was tested with the OmniSmart 675 PNP on Red Hat 7\&.2\&. \- Q Giese .sp Types 7 and 10 should both work with the PhoenixTec A1000\&. .SH "BUGS" .sp There is no way to reliably detect a contact\-closure UPS\&. This means the driver will start up happily even if no UPS is detected\&. It also means that if the connection between the UPS and computer is interrupted, you may not be able to sense this in software\&. .sp Most contact\-closure UPSes will not power down the load if the line power is present\&. This can create a race when using slave \fBupsmon\fR(8) systems\&. See the \fBupsmon\fR(8) man page for more information\&. .sp The solution to both of these problems is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command\&. .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upsmon.80000644000175000017500000004354112640476470012411 00000000000000'\" t .\" Title: upsmon .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSMON" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsmon \- UPS monitor and shutdown controller .SH "SYNOPSIS" .sp \fBupsmon\fR \-h .sp \fBupsmon\fR \-c \fIcommand\fR .sp \fBupsmon\fR [\-D] [\-K] [\-p] [\-u \fIuser\fR] .SH "DESCRIPTION" .sp \fBupsmon\fR is the client process that is responsible for the most important part of UPS monitoring\(emshutting down the system when the power goes out\&. It can call out to other helper programs for notification purposes during power events\&. .sp upsmon can monitor multiple systems using a single process\&. Every UPS that is defined in the \fBupsmon.conf\fR(5) configuration file is assigned a power value and a type (\fBslave\fR or \fBmaster\fR)\&. .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display the help message\&. .RE .PP \fB\-c\fR \fIcommand\fR .RS 4 Send the command \fIcommand\fR to the existing upsmon process\&. Valid commands are: .PP \fBfsd\fR .RS 4 shutdown all master UPSes (use with caution) .RE .PP \fBstop\fR .RS 4 stop monitoring and exit .RE .PP \fBreload\fR .RS 4 reread \fBupsmon.conf\fR(5) configuration file\&. See "reloading nuances" below if this doesn\(cqt work\&. .RE .RE .PP \fB\-D\fR .RS 4 Raise the debugging level\&. upsmon will run in the foreground and prints information on stdout about the monitoring process\&. Use this multiple times for more details\&. .RE .PP \fB\-K\fR .RS 4 Test for the shutdown flag\&. If it exists and contains the magic string from upsmon, then upsmon will exit with EXIT_SUCCESS\&. Any other condition will make upsmon exit with EXIT_FAILURE\&. .sp You can test for a successful exit from upsmon \-K in your shutdown scripts to know when to call \fBupsdrvctl\fR(8) to shut down the UPS\&. .RE .PP \fB\-p\fR .RS 4 Run privileged all the time\&. Normally upsmon will split into two processes\&. The majority of the code runs as an unprivileged user, and only a tiny stub runs as root\&. This switch will disable that mode, and run the old "all root all the time" system\&. .sp This is not the recommended mode, and you should not use this unless you have a very good reason\&. .RE .PP \fB\-u\fR \fIuser\fR .RS 4 Set the user for the unprivileged monitoring process\&. This has no effect when using \-p\&. .sp The default user is set at configure time with \fIconfigure \-\-with\-user=\&...\fR\&. Typically this is \fInobody\fR, but other distributions will probably have a specific \fInut\fR user for this task\&. If your notification scripts need to run as a specific user, set it here\&. .sp You can also set this in the \fBupsmon.conf\fR(5) file with the RUN_AS_USER directive\&. .RE .SH "UPS DEFINITIONS" .sp In the \fBupsmon.conf\fR(5), you must specify at least one UPS that will be monitored\&. Use the MONITOR directive\&. .sp .if n \{\ .RS 4 .\} .nf MONITOR \*(Aqsystem\*(Aq \*(Aqpowervalue\*(Aq \*(Aqusername\*(Aq \*(Aqpassword\*(Aq \*(Aqtype\*(Aq .fi .if n \{\ .RE .\} .sp The \fIsystem\fR refers to a \fBupsd\fR(8) server, in the form upsname[@hostname[:port]]\&. The default hostname is "localhost"\&. Some examples follow: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} "su700@mybox" means a UPS called "su700" on a system called "mybox"\&. This is the normal form\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} "fenton@bigbox:5678" is a UPS called "fenton" on a system called "bigbox" which runs \fBupsd\fR(8) on port "5678"\&. .RE .sp The \fIpowervalue\fR refers to how many power supplies on this system are being driven this UPS\&. This is typically set to 1, but see the section on power values below\&. .sp The \fIusername\fR is a section in your \fBupsd.users\fR(5) file\&. Whatever password you set in that section must match the \fIpassword\fR set in this file\&. .sp The type set in that section must also match the \fItype\fR here\-\- \fBmaster\fR or \fBslave\fR\&. In general, a master process is one running on the system with the UPS actually plugged into a serial port, and a slave is drawing power from the UPS but can\(cqt talk to it directly\&. See the section on UPS types for more\&. .SH "NOTIFY EVENTS" .sp \fBupsmon\fR senses several events as it monitors each UPS\&. They are called notify events as they can be used to tell the users and admins about the change in status\&. See the additional NOTIFY\-related sections below for information on customizing the delivery of these messages\&. .PP \fBONLINE\fR .RS 4 The UPS is back on line\&. .RE .PP \fBONBATT\fR .RS 4 The UPS is on battery\&. .RE .PP \fBLOWBATT\fR .RS 4 The UPS battery is low (as determined by the driver)\&. .RE .PP \fBFSD\fR .RS 4 The UPS has been commanded into the "forced shutdown" mode\&. .RE .PP \fBCOMMOK\fR .RS 4 Communication with the UPS has been established\&. .RE .PP \fBCOMMBAD\fR .RS 4 Communication with the UPS was just lost\&. .RE .PP \fBSHUTDOWN\fR .RS 4 The local system is being shut down\&. .RE .PP \fBREPLBATT\fR .RS 4 The UPS needs to have its battery replaced\&. .RE .PP \fBNOCOMM\fR .RS 4 The UPS can\(cqt be contacted for monitoring\&. .RE .SH "NOTIFY COMMAND" .sp In \fBupsmon.conf\fR(5), you can configure a program called the NOTIFYCMD that will handle events that occur\&. .sp NOTIFYCMD "\fIpath to program\fR" .sp NOTIFYCMD "/usr/local/bin/notifyme" .sp Remember to wrap the path in "quotes" if it contains any spaces\&. .sp The program you run as your NOTIFYCMD can use the environment variables NOTIFYTYPE and UPSNAME to know what has happened and on which UPS\&. It also receives the notification message (see below) as the first (and only) argument, so you can deliver a preformatted message too\&. .sp Note that the NOTIFYCMD will only be called for a given event when you set the EXEC flag by using the notify flags, below: .SH "NOTIFY FLAGS" .sp By default, all notify events (see above) generate a global message (wall) to all users, plus they are logged via the syslog\&. You can change this with the NOTIFYFLAG directive in the configuration file: .sp NOTIFYFLAG \fInotifytype\fR \fIflags\fR .sp Examples: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NOTIFYFLAG ONLINE SYSLOG .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NOTIFYFLAG ONBATT SYSLOG+WALL .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC .RE .sp The flags that can be set on a given notify event are: .PP \fBSYSLOG\fR .RS 4 Write this message to the syslog\&. .RE .PP \fBWALL\fR .RS 4 Send this message to all users on the system via \fBwall\fR(1)\&. .RE .PP \fBEXEC\fR .RS 4 Execute the NOTIFYCMD\&. .RE .PP \fBIGNORE\fR .RS 4 Don\(cqt do anything\&. If you use this, don\(cqt use any of the other flags\&. .RE .sp You can mix these flags\&. "SYSLOG+WALL+EXEC" does all three for a given event\&. .SH "NOTIFY MESSAGES" .sp upsmon comes with default messages for each of the NOTIFY events\&. These can be changed with the NOTIFYMSG directive\&. .sp NOTIFYMSG \fItype\fR "\fImessage\fR" .sp Examples: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} NOTIFYMSG ONLINE "UPS %s is getting line power" .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ` NOTIFYMSG ONBATT "Someone pulled the plug on %s"` .RE .sp The first instance of %s is replaced with the identifier of the UPS that generated the event\&. These messages are used when sending walls to the users directly from upsmon, and are also passed to the NOTIFYCMD\&. .SH "POWER VALUES" .sp The "current overall power value" is the sum of all UPSes that are currently able to supply power to the system hosting upsmon\&. Any UPS that is either on line or just on battery contributes to this number\&. If a UPS is critical (on battery and low battery) or has been put into "forced shutdown" mode, it no longer contributes\&. .sp A "power value" on a MONITOR line in the config file is the number of power supplies that the UPS runs on the current system\&. .sp MONITOR \fIupsname\fR \fIpowervalue\fR \fIusername\fR \fIpassword\fR \fItype\fR .sp Normally, you only have one power supply, so it will be set to 1\&. .sp MONITOR myups@myhost 1 username mypassword master .sp On a large server with redundant power supplies, the power value for a UPS may be greater than 1\&. You may also have more than one of them defined\&. .sp MONITOR ups\-alpha@myhost 2 username mypassword master .sp MONITOR ups\-beta@myhost 2 username mypassword master .sp You can also set the power value for a UPS to 0 if it does not supply any power to that system\&. This is generally used when you want to use the upsmon notification features for a UPS even though it\(cqs not actually running the system that hosts upsmon\&. Don\(cqt set this to "master" unless you really want to power this UPS off when this instance of upsmon needs to shut down for its own reasons\&. .sp MONITOR faraway@anotherbox 0 username mypassword slave .sp The "minimum power value" is the number of power supplies that must be receiving power in order to keep the computer running\&. .sp MINSUPPLIES \fIvalue\fR .sp Typical PCs only have 1, so most users will leave this at the default\&. .sp MINSUPPLIES 1 .sp If you have a server or similar system with redundant power, then this value will usually be set higher\&. One that requires three power supplies to be running at all times would simply set it to 3\&. .sp MINSUPPLIES 3 .sp When the current overall power value drops below the minimum power value, upsmon starts the shutdown sequence\&. This design allows you to lose some of your power supplies in a redundant power environment without bringing down the entire system while still working properly for smaller systems\&. .SH "UPS TYPES" .sp \fBupsmon\fR and \fBupsd\fR(8) don\(cqt always run on the same system\&. When they do, any UPSes that are directly attached to the upsmon host should be monitored in "master" mode\&. This makes upsmon take charge of that equipment, and it will wait for slaves to disconnect before shutting down the local system\&. This allows the distant systems (monitoring over the network) to shut down cleanly before upsdrvctl shutdown runs and turns them all off\&. .sp When upsmon runs as a slave, it is relying on the distant system to tell it about the state of the UPS\&. When that UPS goes critical (on battery and low battery), it immediately invokes the local shutdown command\&. This needs to happen quickly\&. Once it disconnects from the distant \fBupsd\fR(8) server, the master upsmon will start its own shutdown process\&. Your slaves must all shut down before the master turns off the power or filesystem damage may result\&. .sp upsmon deals with slaves that get wedged, hang, or otherwise fail to disconnect from \fBupsd\fR(8) in a timely manner with the HOSTSYNC timer\&. During a shutdown situation, the master upsmon will give up after this interval and it will shut down anyway\&. This keeps the master from sitting there forever (which would endanger that host) if a slave should break somehow\&. This defaults to 15 seconds\&. .sp If your master system is shutting down too quickly, set the FINALDELAY interval to something greater than the default 15 seconds\&. Don\(cqt set this too high, or your UPS battery may run out of power before the master upsmon process shuts down that system\&. .SH "TIMED SHUTDOWNS" .sp For those rare situations where the shutdown process can\(cqt be completed between the time that low battery is signalled and the UPS actually powers off the load, use the \fBupssched\fR(8) helper program\&. You can use it along with upsmon to schedule a shutdown based on the "on battery" event\&. upssched can then come back to upsmon to initiate the shutdown once it has run on battery too long\&. .sp This can be complicated and messy, so stick to the default critical UPS handling if you can\&. .SH "REDUNDANT POWER SUPPLIES" .sp If you have more than one power supply for redundant power, you may also have more than one UPS feeding your computer\&. upsmon can handle this\&. Be sure to set the UPS power values appropriately and the MINSUPPLIES value high enough so that it keeps running until it really does need to shut down\&. .sp For example, the HP NetServer LH4 by default has 3 power supplies installed, with one bay empty\&. It has two power cords, one per side of the box\&. This means that one power cord powers two power supply bays, and that you can only have two UPSes supplying power\&. .sp Connect UPS "alpha" to the cord feeding two power supplies, and UPS "beta" to the cord that feeds the third and the empty slot\&. Define alpha as a powervalue of 2, and beta as a powervalue of 1\&. Set the MINSUPPLIES to 2\&. .sp When alpha goes on battery, your current overall power value will stay at 3, as it\(cqs still supplying power\&. However, once it goes critical (on battery and low battery), it will stop contributing to the current overall power value\&. That means the value will be 1 (beta alone), which is less than 2\&. That is insufficient to run the system, and upsmon will invoke the shutdown sequence\&. .sp However, if beta goes critical, subtracting its contribution will take the current overall value from 3 to 2\&. This is just high enough to satisfy the minimum, so the system will continue running as before\&. If beta returns later, it will be re\-added and the current value will go back to 3\&. This allows you to swap out UPSes, change a power configuration, or whatever, as long as you maintain the minimum power value at all times\&. .SH "MIXED OPERATIONS" .sp Besides being able to monitor multiple UPSes, upsmon can also monitor them as different roles\&. If you have a system with multiple power supplies serviced by separate UPS batteries, it\(cqs possible to be a master on one and a slave on the other\&. This usually happens when you run out of serial ports and need to do the monitoring through another system nearby\&. .sp This is also complicated, especially when it comes time to power down a UPS that has gone critical but doesn\(cqt supply the local system\&. You can do this with some scripting magic in your notify command script, but it\(cqs beyond the scope of this manual\&. .SH "FORCED SHUTDOWNS" .sp When upsmon is forced to bring down the local system, it sets the "FSD" (forced shutdown) flag on any UPSes that it is running in master mode\&. This is used to synchronize slaves in the event that a master UPS that is otherwise OK needs to be brought down due to some pressing event on the master\&. .sp You can manually invoke this mode on the master upsmon by starting another copy with \-c fsd\&. This is useful when you want to initiate a shutdown before the critical stage through some external means, such as \fBupssched\fR(8)\&. .SH "DEAD UPSES" .sp In the event that upsmon can\(cqt reach \fBupsd\fR(8), it declares that UPS "dead" after some interval controlled by DEADTIME in the \fBupsmon.conf\fR(5)\&. If this happens while that UPS was last known to be on battery, it is assumed to have gone critical and no longer contributes to the overall power value\&. .sp upsmon will alert you to a UPS that can\(cqt be contacted for monitoring with a "NOCOMM" notifier by default every 300 seconds\&. This can be changed with the NOCOMMWARNTIME setting\&. .SH "RELOADING NUANCES" .sp upsmon usually gives up root powers for the process that does most of the work, including handling signals like SIGHUP to reload the configuration file\&. This means your \fBupsmon.conf\fR(8) file must be readable by the non\-root account that upsmon switches to\&. .sp If you want reloads to work, upsmon must run as some user that has permissions to read the configuration file\&. I recommend making a new user just for this purpose, as making the file readable by "nobody" (the default user) would be a bad idea\&. .sp See the RUN_AS_USER section in \fBupsmon.conf\fR(8) for more on this topic\&. .sp Additionally, you can\(cqt change the SHUTDOWNCMD or POWERDOWNFLAG definitions with a reload due to the split\-process model\&. If you change those values, you \fBmust\fR stop upsmon and start it back up\&. upsmon will warn you in the syslog if you make changes to either of those values during a reload\&. .SH "SIMULATING POWER FAILURES" .sp To test a synchronized shutdown without pulling the plug on your UPS(es), you need only set the forced shutdown (FSD) flag on them\&. You can do this by calling upsmon again to set the flag, i\&.e\&.: .sp upsmon \-c fsd .sp After that, the master and the slaves will do their usual shutdown sequence as if the battery had gone critical\&. This is much easier on your UPS equipment, and it beats crawling under a desk to find the plug\&. .SH "FILES" .sp \fBupsmon.conf\fR(5) .SH "SEE ALSO" .SS "Server:" .sp \fBupsd\fR(8) .SS "Clients:" .sp \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsrw\fR(8), \fBupsmon\fR(8) .SS "CGI programs:" .sp \fBupsset.cgi\fR(8), \fBupsstats.cgi\fR(8), \fBupsimage.cgi\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_add_device_to_device.txt0000644000175000017500000000305712640443572017356 00000000000000NUTSCAN_ADD_DEVICE_TO_DEVICE(3) =============================== NAME ---- nutscan_add_device_to_device - Concatenate two devices structure. SYNOPSIS -------- #include nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); DESCRIPTION ----------- The `nutscan_device_t` contains the following variables: nutscan_device_type_t type; char * driver; char * port; nutscan_options_t opt; struct nutscan_device * prev; struct nutscan_device * next; This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. The *nutscan_add_device_to_device()* concatenates 'first' and 'second' devices to a unique device. No new device is created, the two linked list are simply linked to each other. So 'first' and 'second' devices are likely to be modified by this function. RETURN VALUE ------------ The *nutscan_add_device_to_device()* functions returns a pointer to a device containg both passed devices. Note that it's not a new device, so it is either 'first' or 'second' which is returned. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3] nut-2.7.4/docs/man/upslog.80000644000175000017500000001065112640476467012403 00000000000000'\" t .\" Title: upslog .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSLOG" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upslog \- UPS status logger .SH "SYNOPSIS" .sp \fBupslog \-h\fR .sp \fBupslog\fR [\fIOPTIONS\fR] .SH "DESCRIPTION" .sp \fBupslog\fR is a daemon that will poll a UPS at periodic intervals, fetch the variables that interest you, format them, and write them to a file\&. .sp The default format string includes variables that are supported by many common UPS models\&. See the description below to make your own\&. .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display the help message\&. .RE .PP \fB\-f\fR \fIformat\fR .RS 4 Monitor the UPS using this format string\&. Be sure to enclose \fIformat\fR in quotes so your shell doesn\(cqt split it up\&. Valid escapes within this string are: .PP %% .RS 4 Insert a single "%" .RE .PP %TIME format% .RS 4 Insert the time with strftime formatting .RE .PP %ETIME% .RS 4 Insert the number of seconds, ala time_t\&. This is now a 10 digit number\&. .RE .PP %HOST% .RS 4 insert the local hostname .RE .PP %UPSHOST% .RS 4 insert the host of the UPS being monitored .RE .PP %PID% .RS 4 insert the pid of upslog .RE .PP %VAR varname% .RS 4 insert the value of variable varname .RE .RE .sp The default format string is: .sp .if n \{\ .RS 4 .\} .nf %TIME @Y@m@d @H@M@S% %VAR battery\&.charge% %VAR input\&.voltage% %VAR ups\&.load% [%VAR ups\&.status%] %VAR ups\&.temperature% %VAR input\&.frequency% .fi .if n \{\ .RE .\} .PP \fB\-i\fR \fIinterval\fR .RS 4 Wait this many seconds between polls\&. This defaults to 30 seconds\&. .sp If you require tighter timing, you should write your own logger using the \fBupsclient\fR(3) library\&. .RE .PP \fB\-l\fR \fIlogfile\fR .RS 4 Store the results in this file\&. .sp You can use "\-" for stdout, but upslog will remain in the foreground\&. .RE .PP \fB\-s\fR \fIups\fR .RS 4 Monitor this UPS\&. The format for this option is upsname[@hostname[:port]]\&. The default hostname is "localhost"\&. .RE .PP \fB\-u\fR \fIusername\fR .RS 4 If started as root, upsmon will \fBsetuid\fR(2) to the user id associated with \fIusername\fR for security\&. .sp If \fIusername\fR is not defined, it will use the value that was compiled into the program\&. This defaults to "nobody", which is less than ideal\&. .RE .SH "SERVICE DELAYS" .sp The interval value is merely the number given to \fBsleep\fR(3) after running through the format string\&. Therefore, a query will actually take slightly longer than the interval, depending on the speed of your system\&. .SH "ON-DEMAND LOGGING" .sp Sending a USR1 signal to a running \fBupslog\fR process makes it wake from the current sleep and log immediately\&. This is useful when triggered from a \fBupssched\fR event trigger (e\&.g\&. AT ONBATT or AT ONLINE) to ensure that an entry always exists, even if the power goes away for a period of time shorter than that specified by the \-i argument\&. .SH "LOG ROTATION" .sp \fBupslog\fR writes its PID to upslog\&.pid, and will reopen the log file if you send it a SIGHUP\&. This allows it to keep running when the log is rotated by an external program\&. .SH "SEE ALSO" .SS "Server:" .sp \fBupsd\fR(8) .SS "Clients:" .sp \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsrw\fR(8), \fBupsmon\fR(8), \fBupssched\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/dummy-ups.80000644000175000017500000001432712640476500013022 00000000000000'\" t .\" Title: dummy-ups .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "DUMMY\-UPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" dummy-ups \- Driver for multi\-purpose UPS emulation .SH "NOTE" .sp This man page only documents the specific features of the dummy\-ups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "DESCRIPTION" .sp This program is a multi\-purpose UPS emulation tool\&. Its behavior depends on the running mode: .SS "Dummy Mode" .sp \fBdummy\-ups\fR looks like a standard device driver to \fBupsd\fR(8) and allows one to change any value for testing purposes\&. It is both interactive, controllable through the \fBupsrw\fR(1) and \fBupscmd\fR(1) commands (or equivalent graphical tool), and batchable through script files\&. It can be configured, launched and used as any other real driver\&. This mode is mostly useful for development and testing purposes\&. .SS "Repeater Mode" .sp \fBdummy\-ups\fR acts as a NUT client, simply forwarding data\&. This can be useful for supervision purposes\&. This can also allow some load sharing between several UPS instances, using a point\-to\-point communication with the UPS\&. .SH "IMPLEMENTATION" .sp The port specification depends on the running mode, and allows the driver to select the right mode\&. .SS "Dummy Mode" .sp Port is a definition file name for \fBdummy\-ups\fR\&. This can either be an absolute or a relative path name\&. In the latter case the NUT sysconfig directory (ie /etc/nut, /usr/local/ups/etc, \&...) is prepended\&. .sp For instance: .sp .if n \{\ .RS 4 .\} .nf [dummy] driver = dummy\-ups port = evolution500\&.dev desc = "dummy\-ups in dummy mode" .fi .if n \{\ .RE .\} .sp This file is generally named "something\&.dev"\&. It contains a list of all valid data and associated values, and has the same format as an \fBupsc\fR(8) dump (: )\&. So you can easily create definition files from an existing UPS using "upsc > file\&.dev"\&. It can also be empty, in which case only a basic set of data is available: device\&.\fB, driver\&.\fR, ups\&.mfr, ups\&.model, ups\&.status .sp Samples definition files are available in the "data" directory of the nut source tree, and generally in the sysconfig directory of your system distribution\&. .sp Since \fBdummy\-ups\fR will loop on reading this file, you can dynamically modify it to interact with the driver\&. This will avoid message spam into your system log files, if you are using NUT default configuration\&. .sp You can also use the "TIMER " instruction to create scheduled events sequences\&. For example, the following sequence will loop on switching ups\&.status between "OL", "OB" and "OB LB" every minute: .sp .if n \{\ .RS 4 .\} .nf ups\&.status: OL TIMER 60 ups\&.status: OB TIMER 60 ups\&.status: LB TIMER 60 .fi .if n \{\ .RE .\} .sp It is wise to end the script with a TIMER\&. Otherwise dummy\-ups will directly go back to the beginning of the file\&. .SS "Repeater Mode" .sp Port is the name of a remote UPS, using the NUT form, ie: .sp .if n \{\ .RS 4 .\} .nf @[:] .fi .if n \{\ .RE .\} .sp For instance: .sp .if n \{\ .RS 4 .\} .nf [repeater] driver = dummy\-ups port = ups@hostname desc = "dummy\-ups in repeater mode" .fi .if n \{\ .RE .\} .sp Unlike UPS specifications in the rest of NUT, the @hostname portion is not optional \- it is the @ character which enables Repeater Mode\&. To refer to an UPS on the same host as \fBdummy\-ups\fR, use port = upsname@localhost\&. .SH "INTERACTION" .sp Once the driver is loaded in dummy mode, you can change any variables, except those of the driver\&.* and server\&.* collections\&. You can do this by either editing the definition file, or use the \fBupsrw\fR(1) and \fBupscmd\fR(1) commands\&. .sp Note that in simulation mode, new variables can be added on the fly, by adding these to the definition file\&. Conversely, if you need to remove variable (such as transient ones, like ups\&.alarm), simply update these by setting an empty value\&. As a result, they will get removed from the data\&. .sp In repeater mode, the driver acts according to the capabilities of the UPS, and so support the same instant commands and settable values\&. .SH "BACKGROUND" .sp Dummy Mode was originally written in one evening to replace the previous dummycons testing driver, which was too limited, and required a terminal for interaction\&. .sp \fBdummy\-ups\fR is useful for NUT client development, and other testing purposes\&. .sp It also helps the NUT Quality Assurance effort, by automating some tests on the NUT framework\&. .sp It now offers a repeater mode\&. This will help in building the Meta UPS approach, which allows one to build a virtual device, composed of several other devices (either UPS, PDUs)\&. .SH "BUGS" .sp Instant commands are not yet supported in Dummy Mode, and data need name/value checking enforcement, as well as boundaries or enumeration definition\&. .SH "AUTHOR" .sp Arnaud Quette .SH "SEE ALSO" .sp \fBupscmd\fR(1), \fBupsrw\fR(1), \fBups.conf\fR(5), \fBnutupsdrv\fR(8) .SS "Internet Resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/microdowell.80000644000175000017500000000363512640476510013403 00000000000000'\" t .\" Title: microdowell .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "MICRODOWELL" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" microdowell \- Driver for Microdowell Enterprise UPS series .SH "NOTE" .sp This man page only documents the hardware\-specific features of the Microdowell driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver was developed for the Enterprise Nxx and Bxx models\&. Other Microdowell models may work, too\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "AUTHOR" .sp Elio Corbolante .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/skel.txt0000644000175000017500000000642612640443572012474 00000000000000SKEL(8) ======= NAME ---- skel - skeleton driver man page NOTE ---- This man page only documents the hardware-specific features of the *skel* driver. For information about the core driver, see linkman:nutupsdrv[8]. ////////////////////////////////////////// The following lines are comments. When copying this man page for your new driver, be sure to replace all occurrances of "skel" and "SKEL" by your actual driver name. If you have AsciiDoc installed, you can test the formatting of your man page by running: asciidoc --backend=xhtml11 -o skel.html skel.txt After writing a man page, be sure to add it to the appropriate variable in Makefile.am in this directory. In the "NAME" section, you must follow the format above, including separating the driver name from its description by "-". This is to ensure that the apropos(8) database is properly rebuilt. ////////////////////////////////////////// SUPPORTED HARDWARE ------------------ *skel* supports ... ////////////////////////////////////////// If the driver only works with certain cables, this is a good place to mention it: CABLING ------- ////////////////////////////////////////// EXTRA ARGUMENTS --------------- This driver also supports the following optional settings: *option1*='num':: Set the value of ... to 'num'. Contrast with *option2*. *option2*='string':: Some other option. ////////////////////////////////////////// Optional: list supported instant commands here: INSTANT COMMANDS ---------------- *instcmd1*:: Command 1. ////////////////////////////////////////// ////////////////////////////////////////// Optional: use INSTALLATION if you need special parameters to the configure script, or additional libraries as prerequisites. INSTALLATION ------------ ////////////////////////////////////////// ////////////////////////////////////////// Optional: use DIAGNOSTICS to describe troubleshooting techniques that are longer than what can be conveniently described in the driver error messages. DIAGNOSTICS ----------- ////////////////////////////////////////// ////////////////////////////////////////// You may leave this as "none known at this time", or describe any trouble encountered when implementing the protocol for your UPS. KNOWN ISSUES AND BUGS --------------------- *Got "EPERM: Operation not permitted" upon driver startup*:: You have forgotten to install the udev files, as explained in the INSTALLATION section above. Don't forget to restart udev so that it applies these changes. ////////////////////////////////////////// ////////////////////////////////////////// An email address is not strictly necessary, but you may wish to provide some form of contact information so that users can report bugs. ////////////////////////////////////////// AUTHORS ------- J Random User ////////////////////////////////////////// If this driver is ever made obsolete by another driver, mention the replacement driver in the "SEE ALSO" section. You may also wish to point the user to other drivers which may better support their hardware, if there is ambiguity based on the driver name. ////////////////////////////////////////// SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_add_host_cert.30000644000175000017500000000417212665610631015235 00000000000000'\" t .\" Title: upscli_add_host_cert .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_ADD_HOST_CERT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_add_host_cert \- Register a security rule for an host\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf void upscli_add_host_cert(const char* hostname, const char* certname, int certverify, int forcessl); .fi .SH "DESCRIPTION" .sp The \fBupscli_add_host_cert()\fR function register a security rule associated to the \fIhostname\fR\&. All connections to this host use this rule\&. .sp The rule is composed of the certificate name \fIcertname \*(Aqexpected for the host, \*(Aqcertverify\fR if the certificate must be validated for the host and \fIforcessl\fR if a secured connection must be used to connect to the host\&. .SH "RETURN VALUE" .sp \fBupscli_add_host_cert()\fR returns no value\&. .SH "SEE ALSO" .sp \fBupscli_init\fR(3), \fBupscli_connect\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/tripplite_usb.80000644000175000017500000002046412640476521013751 00000000000000'\" t .\" Title: tripplite_usb .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "TRIPPLITE_USB" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" tripplite_usb \- Driver for older Tripp Lite USB UPSes (not PDC HID) .SH "SYNOPSIS" .sp \fBtripplite_usb\fR \-h .sp \fBtripplite_usb\fR \-a \fIUPS_NAME\fR [\fIOPTIONS\fR] .SH "SUPPORTED HARDWARE" .sp This driver should work with older Tripp Lite UPSes which are detected as USB HID\-class devices, but are not true HID Power\-Device Class devices\&. So far, the devices supported by tripplite_usb have product ID 0001, and the newer units (such as those with "LCD" in the model name) with product ID 2001 require the \fBusbhid-ups\fR(8) driver instead\&. Please report success or failure to the nut\-upsuser mailing list\&. A key piece of information is the protocol number, returned in ups\&.debug\&.0\&. Also, be sure to turn on debugging (\fI\-DDD\fR) for more informative log messages\&. If your Tripp Lite UPS uses a serial port, you may wish to investigate the \fBtripplite\fR(8) or \fBtripplite_su\fR(8) driver\&. .sp This driver has been tested with the following models: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} INTERNETOFFICE700 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} OMNIVS1000 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} OMNIVS1500XL (some warnings) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SMART700USB .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SMART1500RM2U .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SMART2200RMXL2U .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} SMART3000RM2U .RE .sp If you have used Tripp Lite\(cqs PowerAlert software to connect to your UPS, there is a good chance that \fItripplite_usb\fR will work if it uses one of the following protocols: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Protocol 0004 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Protocol 1001 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Protocol 2001 .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Protocol 3003 .RE .sp On the other hand, if the web page for your UPS on the Tripp\-Lite website says "HID\-compliant USB port also enables direct integration with built\-in power management and auto\-shutdown features of Windows and MAC OS X", then you should use the \fBusbhid-ups\fR(8) driver instead\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5) file (or with \fI\-x\fR on the command line): .PP \fBoffdelay\fR .RS 4 This setting controls the delay between receiving the "kill" command (\fI\-k\fR) and actually cutting power to the computer\&. .RE .PP \fBbattery_min\fR, \fBbattery_max\fR .RS 4 These floating\-point values correspond to the "empty" (10%) and "full" (100%) voltages of the battery\&. They are used for an approximation of the battery state\-of\-charge\&. The calculated battery\&.charge value will be clamped to the range of 10% through 100%, so the resting voltage of the charged battery can be used for \fBbattery_max\fR, and the higher float charge voltage should not cause problems\&. .RE .PP \fBbus\fR .RS 4 This regular expression is used to match the USB bus (as seen in /proc/bus/usb/devices or lsusb(8); including leading zeroes)\&. .RE .PP \fBproduct\fR .RS 4 A regular expression to match the product string for the UPS\&. This would be useful if you have two different Tripp Lite UPS models connected to the system, and you want to be sure that you shut them down in the correct order\&. .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This regex is matched against the full USB product string as seen in lsusb(8)\&. The ups\&.model in the \fBupsc\fR(1) output only lists the name after TRIPP LITE, so to match a SMART2200RMXL2U, you could use the regex \&.*SMART2200\&.*\&. .sp .5v .RE .PP \fBproductid\fR .RS 4 The productid is a regular expression which matches the UPS PID as four hexadecimal digits\&. So far, the only devices that work with this driver have PID 0001\&. .RE .PP \fBserial\fR .RS 4 It does not appear that these particular Tripp Lite UPSes use the iSerial descriptor field to return a serial number\&. However, in case your unit does, you may specify it here\&. .RE .sp For more information on regular expressions, see regex(7) .SH "RUNTIME VARIABLES" .PP \fBups\&.delay\&.shutdown\fR .RS 4 This variable is the same as the \fIoffdelay\fR setting, but it can be changed at runtime by \fBupsrw\fR(8)\&. .RE .PP \fBups\&.id\fR .RS 4 Some SMARTPRO models feature an ID that can be set and retrieved\&. If your UPS supports this feature, this variable will be listed in the output of \fBupsrw\fR(8)\&. .RE .PP \fBoutlet\&.1\&.switch\fR .RS 4 Some Tripp Lite units have a switchable outlet (usually outlet #1) which can be turned on and off by writing \fI1\fR or \fI0\fR, respectively, to outlet\&.1\&.switch with \fBupsrw\fR(8)\&. If your unit has multiple switchable outlets, substitute the outlet number for \fI1\fR in the variable name\&. Be sure to test this first \- there is no other way to be certain that the number used by the driver matches the label on the unit\&. .RE .SH "KNOWN ISSUES AND BUGS" .sp The driver was not developed with any official documentation from Tripp Lite, so certain events may confuse the driver\&. If you observe any strange behavior, please re\-run the driver with \-DDD to increase the verbosity\&. .sp So far, the Tripp Lite UPSes do not seem to have a serial number or other globally unique identifier accessible through USB\&. Thus, when monitoring several Tripp Lite USB UPSes, you should use either the \fIbus\fR or \fIproduct\fR configuration options to uniquely specify which UPS a given driver instance should control\&. .sp For instance, you can easily monitor an OMNIVS1000 and a SMART1500RM2U at the same time, since they have different USB Product ID strings\&. If you have two SMART1500RM2U units, you would have to find which USB bus number each unit is on (via lsusb(8)), which may result in ambiguities if the available USB ports are on the same bus\&. .sp Some of the SMART*2U models have an ID number, but because this ID is not exposed as a USB string descriptor, there is no easy way to use this ID to distinguish between multiple UPS units on a single machine\&. The UPS would need to be claimed by the driver in order to read this ID\&. .SH "AUTHOR" .sp Written by Charles Lepple, based on the \fBtripplite\fR(8) driver by Rickard E\&. (Rik) Faith and Nicholas Kain\&. Please do not email the authors directly \- use the nut\-upsdev mailing list\&. .sp A Tripp Lite OMNIVS1000 was graciously donated to the NUT project by Bradley Feldman (http://www\&.bradleyloritheo\&.com) .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Other drivers for Tripp\-Lite hardware:" .sp \fBtripplite\fR(8), \fBtripplitesu\fR(8), \fBusbhid-ups\fR(8) .SS "Other tools:" .sp regex(7), lsusb(8) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_cidr_to_ip.30000644000175000017500000000436612665610654014734 00000000000000'\" t .\" Title: nutscan_cidr_to_ip .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_CIDR_TO_IP" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_cidr_to_ip \- Convert a CIDR IP to a range of IP address\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip); .fi .SH "DESCRIPTION" .sp The \fBnutscan_cidr_to_ip()\fR function converts a range of IP address in the CIDR format given as a string in \fIcidr\fR, to two IPs in strings pointed by \fIstart_ip\fR and \fIstop_ip\fR which can be used as input paramters in the scanning functions of the libnutscan API\&. It is the caller\(cqs responsability to free \fIstart_ip\fR and \fIstop_ip\fR strings\&. .SH "RETURN VALUE" .sp The \fBnutscan_cidr_to_ip()\fR function returns 0 if an error occured (invalid \fIcidr\fR address) or 1 if successful\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_display_ups_conf\fR(3) nut-2.7.4/docs/man/upsstats.cgi.80000644000175000017500000000557712665610627013527 00000000000000'\" t .\" Title: upsstats.cgi .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSSTATS\&.CGI" "8" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsstats.cgi \- Web\-based UPS status viewer .SH "SYNOPSIS" .sp \fBupsstats\&.cgi\fR .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp As a CGI program, this should be invoked through your web server\&. If you run it from the command line, it will either complain about unauthorized access or spew a bunch of HTML at you\&. .sp .5v .RE .SH "DESCRIPTION" .sp \fBupsstats\&.cgi\fR uses template files to build web pages containing status information from UPS hardware\&. It can repeat sections of those template files to monitor several UPSes simultaneously, or focus on a single UPS\&. .sp These templates can also include references to \fBupsimage.cgi\fR(8) for graphical displays of battery charge levels, voltage readings, and the UPS load\&. .SH "ACCESS CONTROL" .sp upsstats will only talk to \fBupsd\fR(8) servers that have been defined in your \fBhosts.conf\fR(5)\&. If it complains that "Access to that host is not authorized", check that file first\&. .SH "TEMPLATES" .sp The web page that is displayed is actually a template containing commands to upsstats which are replaced by status information\&. The default file used for the overview is upsstats\&.html\&. .sp When monitoring a single UPS, the file displayed is upsstats\-single\&.html\&. .sp The format of these files, including the possible commands, is documented in \fBupsstats.html\fR(5)\&. .SH "FILES" .sp \fBhosts.conf\fR(5), \fBupsstats.html\fR(5), upsstats\-single\&.html .SH "SEE ALSO" .sp \fBupsimage.cgi\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/masterguard.txt0000644000175000017500000000122312640443572014042 00000000000000MASTERGUARD(8) ============== NAME ---- masterguard - Driver for Masterguard UPS equipment NOTE ---- This man page only documents the hardware-specific features of the masterguard driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports Masterguard UPS equipment. EXTRA ARGUMENTS --------------- *CS*:: Cancel the shutdown procedure. AUTHOR ------ Michael Spanier SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nut-recorder.txt0000644000175000017500000000260212640473702014135 00000000000000NUT-RECORDER(8) =============== NAME ---- nut-recorder - utility to record device status and values changes SYNOPSIS -------- *nut-recorder* 'device-name' [output-file] [interval] DESCRIPTION ----------- *nut-recorder* is an utility to record sequences from running devices (such as power failures, or any other value changes) from upsd, and dump it in a .seq format. The .seq file can then be used by the linkman:dummy-ups[8] driver to replay the sequence. OPTIONS ------- 'device-name':: Record the changes of this device. The format for this option is 'devname[@hostname[:port]]'. The default hostname is "localhost". 'output-file':: Optional. Data will be saved to this file. The default is 'dummy-device.seq'. 'interval':: Optional. The status of the device will be checked every 'interval'. The default is 5 seconds. EXAMPLES -------- To record data from 'ups1@host1' every 10 seconds: $ nut-recorder ups1@host1' ups1-output.seq 10 . . . battery.charge: 100.0 battery.voltage: 13.9 battery.voltage.nominal: 13.6 ups.status: OL . . . battery.charge: 90.0 ups.status: OB . . . You can then define a dummy device in linkman:ups.conf[5]: [ups-test] driver = dummy-ups port = ups1-output.seq AUTHOR ------ Arnaud Quette SEE ALSO -------- linkman:dummy-ups[8] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/safenet.txt0000644000175000017500000000503512640443572013156 00000000000000SAFENET(8) ========== NAME ---- safenet - Driver for SafeNet compatible UPS equipment NOTE ---- This man page only documents the hardware-specific features of the safenet driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports UPS equipment which can be controlled via SafeNet v1.0 for Windows (serial interface only). EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5] file: *manufacturer=*'value':: Autodetection of this parameter is not possible yet (and it probably never will be). Therefore, this user-defined string accepts any name. The default is 'unknown'. *modelname=*'value':: Like manufacturer above. The default is 'unknown'. *serialnumber=*'value':: Like manufacturer above. The default is 'unknown'. *ondelay=*'value':: Time to wait before switching on the UPS (minutes). Defaults to 1 minute. *offdelay=*'value':: Time to wait before shutting down the UPS (seconds). Defaults to 30 seconds. UPSCMD ------ This driver supports some instant commands (see linkman:upscmd[8]): *test.battery.start*:: Start UPS self test *test.battery.stop*:: Cancel UPS self test *test.failure.start*:: Start simulated power failure *test.failure.stop*:: Cancel simulated power failure *beeper.enable*:: Enable the UPS beeper *beeper.mute*:: Temporarily mute the UPS beeper *beeper.toggle*:: Toggle the UPS beeper *shutdown.return*:: Turn off the load and wait for the power to return. Uses the timer defined by *offdelay*. *shutdown.reboot*:: Turn off the load and return. Uses the timers defined by *offdelay* and *ondelay*. KNOWN PROBLEMS -------------- If you run the *shutdown.return* command with mains present, the output may stay on or switch off and not back on again. The *shutdown.reboot* command will unconditionally switch on the load again (with or without mains present). If the driver is called with the '-k' option (or through *upsdrvctl shutdown*) it tries to detect which command should be used in an attempt to stay off until mains is present again or to cycle the output if the power returned in the mean time. This isn't bullet-proof, and you should be prepared that the power will either not be shutdown, or that it doesn't return when the power comes back. AUTHOR ------ Arjen de Korte SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_list_start.txt0000644000175000017500000000401512640473702015273 00000000000000UPSCLI_LIST_START(3) ==================== NAME ---- upscli_list_start - begin multi-item retrieval from a UPS SYNOPSIS -------- #include int upscli_list_start(UPSCONN_t *ups, unsigned int numq, const char **query) DESCRIPTION ----------- The *upscli_list_start()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, and the pointer 'query' to an array of 'numq' query elements. It builds a properly-formatted request from those elements and transmits it to linkman:upsd[8]. Upon success, the caller must call linkman:upscli_list_next[3] to retrieve the elements of the list. Failure to retrieve the list will most likely result in the client getting out of sync with the server due to buffered data. USES ---- This function implements the "LIST" command in the protocol. As a result, you can use it to request many different things from the server. Some examples are: - LIST UPS - LIST VAR - LIST RW - LIST CMD - LIST ENUM - LIST RANGE QUERY FORMATTING ---------------- To see the list of variables on a UPS called 'su700', the protocol command would be `LIST VAR su700`. To start that list with this function, you would populate query and numq as follows: unsigned int numq; const char *query[2]; query[0] = "VAR"; query[1] = "su700"; numq = 2; All escaping of special characters and quoting of elements with spaces are handled for you inside this function. ERROR CHECKING -------------- This function checks the response from linkman:upsd[8] against your query. If it is not starting a list, or is starting the wrong type of list, it will return an error code. When this happens, linkman:upscli_upserror[3] will return `UPSCLI_ERR_PROTOCOL`. RETURN VALUE ------------ The *upscli_list_start()* function returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/libupsclient-config.10000644000175000017500000000444212665610630015011 00000000000000'\" t .\" Title: libupsclient-config .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBUPSCLIENT\-CONFIG" "1" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libupsclient-config \- script to get information about the installed version of libupsclient .SH "SYNOPSIS" .sp \fBlibupsclient\-config\fR [\-\-version] [\-\-libs] [\-\-cflags] .SH "DESCRIPTION" .sp \fBlibupsclient\-config\fR is a tool that is used to determine the compiler and linker flags that should be used to compile and link programs that use \fBlibupsclient\fR from the Network UPS Tools project\&. .SH "OPTIONS" .sp \fBlibupsclient\-config\fR accepts the following options: .PP \fB\-\-version\fR .RS 4 Print the currently installed version of \fBlibupsclient\fR on the standard output\&. .RE .PP \fB\-\-libs\fR .RS 4 Print the linker flags that are necessary to link a \fBlibupsclient\fR program\&. .RE .PP \fB\-\-cflags\fR .RS 4 Print the compiler flags that are necessary to compile a \fBlibupsclient\fR program\&. .RE .SH "AUTHORS" .sp This manual page was written by Arnaud Quette \&. .SH "SEE ALSO" .sp \fBupsclient\fR(3) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_scan_xml_http.30000644000175000017500000000464312665610650015456 00000000000000'\" t .\" Title: nutscan_scan_xml_http .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_XML_HTT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_xml_http \- Scan network for XML/HTTP devices\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_xml_http(long usec_timeout); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_xml_http()\fR function try to detect NUT compatible XML/HTTP devices\&. It does this by issuing a broadcast message on currently configured network interfaces\&. It waits up to \fIusec_timeout\fR microseconds for a response from potential devices\&. .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_xml_http()\fR function returns a pointer to a nutscan_device_t structure containing all found devices or NULL if an error occurs or no device is found\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_scan_eaton_serial\fR(3) nut-2.7.4/docs/man/isbmex.80000644000175000017500000000352412640476503012351 00000000000000'\" t .\" Title: isbmex .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "ISBMEX" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" isbmex \- Driver for ISBMEX UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the isbmex driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports SOLA/BASIC Mexico ISBMEX protocol UPS equipment\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "AUTHOR" .sp Edscott Wilson Garcia .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/apcsmart-old.80000644000175000017500000001045212640476473013454 00000000000000'\" t .\" Title: apcsmart-old .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "APCSMART\-OLD" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" apcsmart-old \- Driver for American Power Conversion Smart Protocol UPS equipment .SH "SYNOPSIS" .sp \fBapcsmart\-old\fR \-h .sp \fBapcsmart\-old\fR \-a \fIUPS_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the apcsmart\-old driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp apcsmart\-old should recognize all recent APC models that use a serial protocol at 2400 bps\&. This is primarily the Smart\-UPS, Matrix\-UPS and Back\-UPS Pro lines\&. .sp The driver attempts to support every bell and whistle of the APC reporting interface, whether or not this is strictly sensible\&. .sp Some older hardware may only report a handful of variables\&. This is usually not a bug\(emthey just don\(cqt support anything else\&. .SH "CABLING" .sp This driver expects to see a 940\-0024C cable or a clone by default\&. You can switch to the 940\-0095B dual\-mode cable support with the cable= definition described below\&. .sp If your 940\-0024C cable is broken or missing, use this diagram to build a clone: .sp http://www\&.networkupstools\&.org/cables/940\-0024C\&.jpg .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBcable=940\-0095B\fR .RS 4 Configure the serial port for the APC 940\-0095B dual\-mode cable\&. .RE .PP \fBsdtype=\fR\fInum\fR .RS 4 Use shutdown type \fInum\fR, according to this table: .PP 0 .RS 4 soft shutdown or powerdown, depending on battery status .RE .PP 1 .RS 4 soft shutdown followed by powerdown .RE .PP 2 .RS 4 instant power off .RE .PP 3 .RS 4 power off with grace period .RE .PP 4 .RS 4 "force OB" hack method for CS 350 .RE .RE .sp Modes 0 and 1 will power up the load when power returns\&. Modes 2 and 3 will keep the load turned off when the power returns\&. .sp Mode 4 exploits an oddity in the CS 350 models since they only seem to support the S command, but then only when running on battery\&. As a result, the driver will force the UPS to go on battery if necessary before sending the shutdown command\&. This ensures that the load gets reset\&. .SH "BUGS" .sp Some older APC UPS models return bogus data in the status register during a front panel test\&. This is usually detected and discarded, but some other unexpected values have occasionally slipped through\&. .sp APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa\&. .SH "AUTHOR" .sp Nigel Metheringham (drawing heavily on the original apcsmart driver by Russell Kroll)\&. This driver was called newapc for a time and was renamed in the 1\&.5 series\&. In 2\&.6\&.2 the driver was renamed to apcsmart\-old, being superseded by updated version with new features\&. .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/macosx-ups.80000644000175000017500000000745512665610657013177 00000000000000'\" t .\" Title: macosx-ups .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "MACOSX\-UPS" "8" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" macosx-ups \- monitor for Mac OS X built\-in UPS and battery driver .SH "NOTE" .sp This man page only documents the hardware\-specific features of the \fBmacosx\-ups\fR driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp \fBmacosx\-ups\fR supports any USB HID Power Device Class (PDC) UPS which is matched by the Mac OS X built\-in drivers\&. It also can monitor a laptop internal battery as though it were an UPS\&. .sp If the UPS is visible in the Energy Saver preferences pane of System Preferences, this driver should be able to monitor it\&. .SH "EXTRA ARGUMENTS" .PP \fBport\fR=auto .RS 4 Due to changes in the way that Mac OS X lists power sources, the \fBport\fR parameter no longer has any effect\&. The rest of NUT still requres a value here, and our traditional "don\(cqt care" value is auto\&. .RE .PP \fBmodel\fR=\fIregex\fR .RS 4 Likewise, if you have more than one UPS, it may be necessary to specify a \fBmodel\fR name to match against\&. This parameter is also a case\-insensitive extended regular expression\&. .RE .SH "DIAGNOSTICS" .sp If the driver cannot find an UPS, first open System Preferences and see if there is an "UPS" tab on the Energy Saver panel\&. If so, re\-run the driver with the \fB\-D\fR flag to list the names of the power sources found\&. .SH "KNOWN ISSUES AND BUGS" .sp This driver is a monitoring\-only driver, and cannot shut down an UPS on its own\&. However, this should not be a problem in practice: it is monitoring the built\-in Mac OS X UPS driver, which has configuration options for several shutdown scenarios\&. Consult the Energy Saver control panel or \fBpmset\fR(8) for more information\&. .sp The default distribution of \fBapcupsd\fR installs a kernel extension which prevents Mac OS X from attaching to the UPS\&. In order to use this driver after installing apcupsd, you must first run the apcupsd\-uninstall script and reboot\&. .sp Note that other UPS monitoring solutions may show more detail than what is provided by the built\-in Mac OS X driver\&. In particular, voltages other than the battery voltage, as well as current and frequency, are typically not shown\&. It may be possible to monitor these values with \fBapcupsd\fR (for APC hardware only) or \fBusbhid-ups\fR(8)\&. .SH "AUTHORS" .sp Charles Lepple .SH "SEE ALSO" .sp \fBusbhid-ups\fR(8), \fBpmset\fR(8), \fBregex\fR(3) .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp The apcupsd home page: http://www\&.apcupsd\&.org/ nut-2.7.4/docs/man/upscli_init.txt0000644000175000017500000000263512640443572014056 00000000000000UPSCLI_INIT(3) ============== NAME ---- upscli_init - Initialize upsclient module specifying security properties. SYNOPSIS -------- #include int upscli_init(int certverify, const char *certpath, const char *certname, const char *certpasswd); DESCRIPTION ----------- The *upscli_init()* function initialize upsclient module and set many SSL-related properties: 'certverify' to 1 makes certificate verification required for all SSL connections and 'certpath' is the location of certificate database. If compiled with OpenSSL, certpath refers to a .pem file containing certificates and if compiled with NSS, certpath refers to a directory containing database files. If compiled with NSS and using SSL, you can specify 'certname' the name of the certificate to send to upsd and 'certpasswd' the password used to decrypt certificate private key. You can call linkman:upscli_add_host_cert[3] to register specific host security policy before initialize connections to them. You must call linkman:upscli_cleanup[3] when exiting application. RETURN VALUE ------------ The *upscli_init()* function returns 1 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_add_host_cert[3], linkman:upscli_cleanup[3], linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/blazer_ser.txt0000644000175000017500000000023312640444140013644 00000000000000BLAZER_SER(8) ============= :blazer_usb!: NAME ---- blazer_ser - Driver for Megatec/Q1 protocol serial based UPS equipment include::blazer-common.txt[] nut-2.7.4/docs/man/nutclient_device_forced_shutdown.30000644000175000017500000000003012665610645017647 00000000000000.so libnutclient_misc.3 nut-2.7.4/docs/man/tripplite.80000644000175000017500000000544112640476514013100 00000000000000'\" t .\" Title: tripplite .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "TRIPPLITE" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" tripplite \- Driver for Tripp\-Lite SmartPro UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the tripplite driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver should work on the SmartPro line, including the SMART700 and SMART700SER\&. It only supports SmartPro models that communicate using the serial port\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBoffdelay\fR=\fInum\fR .RS 4 Time to wait before the UPS is turned off after the kill power command is sent\&. The default value is 64 (in seconds)\&. .RE .PP \fBrebootdelay\fR=\fInum\fR .RS 4 Set the timer before the UPS is cycled after the reboot command is sent\&. The default value is 64 (in seconds)\&. .RE .PP \fBstartdelay\fR=\fInum\fR .RS 4 Set the time that the UPS waits before it turns itself back on after a reboot command\&. The default value is 60 (in seconds)\&. .RE .SH "KNOWN ISSUES AND BUGS" .sp Battery charge information may not be correct for all UPSes\&. It is tuned to be correct for a SMART700SER\&. Other models may not provide correct information\&. Information from the manufacturer would be helpful\&. .SH "AUTHORS" .sp Rickard E\&. (Rik) Faith, Nicholas Kain .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Other drivers for Tripp\-Lite hardware:" .sp \fBtripplitesu\fR(8), \fBtripplite_usb\fR(8), \fBusbhid-ups\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscli_fd.txt0000644000175000017500000000126512640443572013502 00000000000000UPSCLI_FD(3) ============ NAME ---- upscli_fd - Get file descriptor for connection SYNOPSIS -------- #include int upscli_fd(UPSCONN_t *ups); DESCRIPTION ----------- The *upscli_fd()* function takes the pointer 'ups' to a `UPSCONN_t` state structure and returns the value of the file descriptor for that connection, if any. This may be useful for determining if the connection to linkman:upsd[8] has been lost. RETURN VALUE ------------ The *upscli_fd()* function returns the file descriptor, which may be any non-negative number. It returns -1 if an error occurs. SEE ALSO -------- linkman:upscli_connect[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/upsmon.conf.50000644000175000017500000004141512640476463013332 00000000000000'\" t .\" Title: upsmon.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSMON\&.CONF" "5" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsmon.conf \- Configuration for Network UPS Tools upsmon .SH "DESCRIPTION" .sp This file\(cqs primary job is to define the systems that \fBupsmon\fR(8) will monitor and to tell it how to shut down the system when necessary\&. It will contain passwords, so keep it secure\&. Ideally, only the upsmon process should be able to read it\&. .sp Additionally, other optional configuration values can be set in this file\&. .SH "CONFIGURATION DIRECTIVES" .PP \fBDEADTIME\fR \fIseconds\fR .RS 4 upsmon allows a UPS to go missing for this many seconds before declaring it "dead"\&. The default is 15 seconds\&. .sp upsmon requires a UPS to provide status information every few seconds (see POLLFREQ and POLLFREQALERT) to keep things updated\&. If the status fetch fails, the UPS is marked stale\&. If it stays stale for more than DEADTIME seconds, the UPS is marked dead\&. .sp A dead UPS that was last known to be on battery is assumed to have changed to a low battery condition\&. This may force a shutdown if it is providing a critical amount of power to your system\&. This seems disruptive, but the alternative is barreling ahead into oblivion and crashing when you run out of power\&. .sp Note: DEADTIME should be a multiple of POLLFREQ and POLLFREQALERT\&. Otherwise, you\(cqll have "dead" UPSes simply because upsmon isn\(cqt polling them quickly enough\&. Rule of thumb: take the larger of the two POLLFREQ values, and multiply by 3\&. .RE .PP \fBFINALDELAY\fR \fIseconds\fR .RS 4 When running in master mode, upsmon waits this long after sending the NOTIFY_SHUTDOWN to warn the users\&. After the timer elapses, it then runs your SHUTDOWNCMD\&. By default this is set to 5 seconds\&. .sp If you need to let your users do something in between those events, increase this number\&. Remember, at this point your UPS battery is almost depleted, so don\(cqt make this too big\&. .sp Alternatively, you can set this very low so you don\(cqt wait around when it\(cqs time to shut down\&. Some UPSes don\(cqt give much warning for low battery and will require a value of 0 here for a safe shutdown\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br If FINALDELAY on the slave is greater than HOSTSYNC on the master, the master will give up waiting for the slave to disconnect\&. .sp .5v .RE .RE .PP \fBHOSTSYNC\fR \fIseconds\fR .RS 4 upsmon will wait up to this many seconds in master mode for the slaves to disconnect during a shutdown situation\&. By default, this is 15 seconds\&. .sp When a UPS goes critical (on battery + low battery, or "FSD": forced shutdown), the slaves are supposed to disconnect and shut down right away\&. The HOSTSYNC timer keeps the master upsmon from sitting there forever if one of the slaves gets stuck\&. .sp This value is also used to keep slave systems from getting stuck if the master fails to respond in time\&. After a UPS becomes critical, the slave will wait up to HOSTSYNC seconds for the master to set the FSD flag\&. If that timer expires, the slave will assume that the master is broken and will shut down anyway\&. .sp This keeps the slaves from shutting down during a short\-lived status change to "OB LB" that the slaves see but the master misses\&. .RE .PP \fBMINSUPPLIES\fR \fInum\fR .RS 4 Set the number of power supplies that must be receiving power to keep this system running\&. Normal computers have just one power supply, so the default value of 1 is acceptable\&. .sp Large/expensive server type systems usually have more, and can run with a few missing\&. The HP NetServer LH4 can run with 2 out of 4, for example, so you\(cqd set it to 2\&. The idea is to keep the box running as long as possible, right? .sp Obviously you have to put the redundant supplies on different UPS circuits for this to make sense! See big\-servers\&.txt in the docs subdirectory for more information and ideas on how to use this feature\&. .sp Also see the section on "power values" in \fBupsmon\fR(8)\&. .RE .PP \fBMONITOR\fR \fIsystem\fR \fIpowervalue\fR \fIusername\fR \fIpassword\fR \fItype\fR .RS 4 Each UPS that you need to be monitor should have a MONITOR line\&. Not all of these need supply power to the system that is running upsmon\&. You may monitor other systems if you want to be able to send notifications about status changes on them\&. .RE .sp You must have at least one MONITOR directive in upsmon\&.conf\&. .sp \fIsystem\fR is a UPS identifier\&. It is in this form: .sp [@[:]] .sp The default hostname is "localhost"\&. Some examples: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} "su700@mybox" means a UPS called "su700" on a system called "mybox"\&. This is the normal form\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} "fenton@bigbox:5678" is a UPS called "fenton" on a system called "bigbox" which runs \fBupsd\fR(8) on port "5678"\&. .RE .sp \fIpowervalue\fR is an integer representing the number of power supplies that the UPS feeds on this system\&. Most normal computers have one power supply, and the UPS feeds it, so this value will be 1\&. You need a very large or special system to have anything higher here\&. .sp You can set the \fIpowervalue\fR to 0 if you want to monitor a UPS that doesn\(cqt actually supply power to this system\&. This is useful when you want to have upsmon do notifications about status changes on a UPS without shutting down when it goes critical\&. .sp The \fIusername\fR and \fIpassword\fR on this line must match an entry in that system\(cqs \fBupsd.users\fR(5)\&. If your username is "monmaster" and your password is "blah", the MONITOR line might look like this: .sp MONITOR myups@bigserver 1 monmaster blah master .sp Meanwhile, the upsd\&.users on bigserver would look like this: .sp .if n \{\ .RS 4 .\} .nf [monmaster] password = blah upsmon master # (or slave) .fi .if n \{\ .RE .\} .sp The \fItype\fR refers to the relationship with \fBupsd\fR(8)\&. It can be either "master" or "slave"\&. See \fBupsmon\fR(8) for more information on the meaning of these modes\&. The mode you pick here also goes in the upsd\&.users file, as seen in the example above\&. .PP \fBNOCOMMWARNTIME\fR \fIseconds\fR .RS 4 upsmon will trigger a NOTIFY_NOCOMM after this many seconds if it can\(cqt reach any of the UPS entries in this configuration file\&. It keeps warning you until the situation is fixed\&. By default this is 300 seconds\&. .RE .PP \fBNOTIFYCMD\fR \fIcommand\fR .RS 4 upsmon calls this to send messages when things happen\&. .sp This command is called with the full text of the message as one argument\&. The environment string NOTIFYTYPE will contain the type string of whatever caused this event to happen\&. .sp If you need to use \fBupssched\fR(8), then you must make it your NOTIFYCMD by listing it here\&. .sp Note that this is only called for NOTIFY events that have EXEC set with NOTIFYFLAG\&. See NOTIFYFLAG below for more details\&. .sp Making this some sort of shell script might not be a bad idea\&. For more information and ideas, see docs/scheduling\&.txt .sp Remember, this command also needs to be one element in the configuration file, so if your command has spaces, then wrap it in quotes\&. .sp NOTIFYCMD "/path/to/script \-\-foo \-\-bar" .sp This script is run in the background\(emthat is, upsmon forks before it calls out to start it\&. This means that your NOTIFYCMD may have multiple instances running simultaneously if a lot of stuff happens all at once\&. Keep this in mind when designing complicated notifiers\&. .RE .PP \fBNOTIFYMSG\fR \fItype\fR \fImessage\fR .RS 4 upsmon comes with a set of stock messages for various events\&. You can change them if you like\&. .sp .if n \{\ .RS 4 .\} .nf NOTIFYMSG ONLINE "UPS %s is getting line power" .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf NOTIFYMSG ONBATT "Someone pulled the plug on %s" .fi .if n \{\ .RE .\} .sp Note that %s is replaced with the identifier of the UPS in question\&. .sp The message must be one element in the configuration file, so if it contains spaces, you must wrap it in quotes\&. .sp .if n \{\ .RS 4 .\} .nf NOTIFYMSG NOCOMM "Someone stole UPS %s" .fi .if n \{\ .RE .\} .sp Possible values for \fItype\fR: .PP ONLINE .RS 4 UPS is back online .RE .PP ONBATT .RS 4 UPS is on battery .RE .PP LOWBATT .RS 4 UPS is on battery and has a low battery (is critical) .RE .PP FSD .RS 4 UPS is being shutdown by the master (FSD = "Forced Shutdown") .RE .PP COMMOK .RS 4 Communications established with the UPS .RE .PP COMMBAD .RS 4 Communications lost to the UPS .RE .PP SHUTDOWN .RS 4 The system is being shutdown .RE .PP REPLBATT .RS 4 The UPS battery is bad and needs to be replaced .RE .PP NOCOMM .RS 4 A UPS is unavailable (can\(cqt be contacted for monitoring) .RE .RE .PP \fBNOTIFYFLAG\fR \fItype\fR \fIflag\fR[+\fIflag\fR][+\fIflag\fR]\&... .RS 4 By default, upsmon sends walls global messages to all logged in users) via /bin/wall and writes to the syslog when things happen\&. You can change this\&. .sp Examples: .sp .if n \{\ .RS 4 .\} .nf NOTIFYFLAG ONLINE SYSLOG NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC .fi .if n \{\ .RE .\} .sp Possible values for the flags: .PP SYSLOG .RS 4 Write the message to the syslog .RE .PP WALL .RS 4 Write the message to all users with /bin/wall .RE .PP EXEC .RS 4 Execute NOTIFYCMD (see above) with the message .RE .PP IGNORE .RS 4 Don\(cqt do anything .sp If you use IGNORE, don\(cqt use any other flags on the same line\&. .RE .RE .PP \fBPOLLFREQ\fR \fIseconds\fR .RS 4 Normally upsmon polls the \fBupsd\fR(8) server every 5 seconds\&. If this is flooding your network with activity, you can make it higher\&. You can also make it lower to get faster updates in some cases\&. .sp There are some catches\&. First, if you set the POLLFREQ too high, you may miss short\-lived power events entirely\&. You also risk triggering the DEADTIME (see above) if you use a very large number\&. .sp Second, there is a point of diminishing returns if you set it too low\&. While upsd normally has all of the data available to it instantly, most drivers only refresh the UPS status once every 2 seconds\&. Polling any more than that usually doesn\(cqt get you the information any faster\&. .RE .PP \fBPOLLFREQALERT\fR \fIseconds\fR .RS 4 This is the interval that upsmon waits between polls if any of its UPSes are on battery\&. You can use this along with POLLFREQ above to slow down polls during normal behavior, but get quicker updates when something bad happens\&. .sp This should always be equal to or lower than the POLLFREQ value\&. By default it is also set 5 seconds\&. .sp The warnings from the POLLFREQ entry about too\-high and too\-low values also apply here\&. .RE .PP \fBPOWERDOWNFLAG\fR \fIfilename\fR .RS 4 upsmon creates this file when running in master mode when the UPS needs to be powered off\&. You should check for this file in your shutdown scripts and call upsdrvctl shutdown if it exists\&. .sp This is done to forcibly reset the slaves, so they don\(cqt get stuck at the "halted" stage even if the power returns during the shutdown process\&. This usually does not work well on contact\-closure UPSes that use the genericups driver\&. .sp See the config\-notes\&.txt file in the docs subdirectory for more information\&. Refer to the section: "Configuring automatic shutdowns for low battery events", or refer to the online version\&. .RE .PP \fBRBWARNTIME\fR \fIseconds\fR .RS 4 When a UPS says that it needs to have its battery replaced, upsmon will generate a NOTIFY_REPLBATT event\&. By default, this happens every 43200 seconds (12 hours)\&. .sp If you need another value, set it here\&. .RE .PP \fBRUN_AS_USER\fR \fIusername\fR .RS 4 upsmon normally runs the bulk of the monitoring duties under another user ID after dropping root privileges\&. On most systems this means it runs as "nobody", since that\(cqs the default from compile\-time\&. .sp The catch is that "nobody" can\(cqt read your upsmon\&.conf, since by default it is installed so that only root can open it\&. This means you won\(cqt be able to reload the configuration file, since it will be unavailable\&. .sp The solution is to create a new user just for upsmon, then make it run as that user\&. I suggest "nutmon", but you can use anything that isn\(cqt already taken on your system\&. Just create a regular user with no special privileges and an impossible password\&. .sp Then, tell upsmon to run as that user, and make upsmon\&.conf readable by it\&. Your reloads will work, and your config file will stay secure\&. .sp This file should not be writable by the upsmon user, as it would be possible to exploit a hole, change the SHUTDOWNCMD to something malicious, then wait for upsmon to be restarted\&. .RE .PP \fBSHUTDOWNCMD\fR \fIcommand\fR .RS 4 upsmon runs this command when the system needs to be brought down\&. If it is a slave, it will do that immediately whenever the current overall power value drops below the MINSUPPLIES value above\&. .sp When upsmon is a master, it will allow any slaves to log out before starting the local shutdown procedure\&. .sp Note that the command needs to be one element in the config file\&. If your shutdown command includes spaces, then put it in quotes to keep it together, i\&.e\&.: .sp .if n \{\ .RS 4 .\} .nf SHUTDOWNCMD "/sbin/shutdown \-h +0" .fi .if n \{\ .RE .\} .RE .PP \fBCERTPATH\fR \fIcertificate file or database\fR .RS 4 When compiled with SSL support, you can enter the certificate path here\&. .PP With NSS: .RS 4 Certificates are stored in a dedicated database (splitted in 3 files)\&. Specify the path of the database directory\&. .RE .PP With OpenSSL: .RS 4 Directory containing CA certificates in PEM format, used to verify the server certificate presented by the upsd server\&. The files each contain one CA certificate\&. The files are looked up by the CA subject name hash value, which must hence be available\&. .RE .RE .PP \fBCERTIDENT\fR \fIcertificate name\fR \fIdatabase password\fR .RS 4 When compiled with SSL support with NSS, you can specify the certificate name to retrieve from database to authenticate itself and the password required to access certificate related private key\&. .RE .PP \fBCERTHOST\fR \fIhostname\fR \fIcertificate name\fR \fIcertverify\fR \fIforcessl\fR .RS 4 When compiled with SSL support with NSS, you can specify security directive for each server you can contact\&. .sp Each entry maps server name with the expected certificate name and flags indicating if the server certificate is verified and if the connection must be secure\&. .RE .PP \fBCERTVERIFY\fR \fI0 | 1\fR .RS 4 When compiled with SSL support, make upsmon verify all connections with certificates\&. .sp Without this, there is no guarantee that the upsd is the right host\&. Enabling this greatly reduces the risk of man\-in\-the\-middle attacks\&. This effectively forces the use of SSL, so don\(cqt use this unless all of your upsd hosts are ready for SSL and have their certificates in order\&. .sp When compiled with NSS support of SSL, can be overriden for host specified with a CERTHOST directive\&. .RE .PP \fBFORCESSL\fR \fI0 | 1\fR .RS 4 When compiled with SSL, specify that a secured connection must be used to communicate with upsd\&. .sp If you don\(cqt use \fICERTVERIFY 1\fR, then this will at least make sure that nobody can sniff your sessions without a large effort\&. Setting this will make upsmon drop connections if the remote upsd doesn\(cqt support SSL, so don\(cqt use it unless all of them have it running\&. .sp When compiled with NSS support of SSL, can be overriden for host specified with a CERTHOST directive\&. .RE .SH "SEE ALSO" .sp \fBupsmon\fR(8), \fBupsd\fR(8), \fBnutupsdrv\fR(8)\&. .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/belkin.txt0000644000175000017500000000222012640443572012766 00000000000000BELKIN(8) ========= NAME ---- belkin - Driver for Belkin serial UPS equipment NOTE ---- This man page only documents the hardware-specific features of the belkin driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The *belkin* driver is known to support the Regulator Pro 525 (F6C525-SER). Other similar models such as the 425 and 625 should also work. The Trust UPS and older Belkin units are not supported. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. BUGS ---- There are dragons lurking within the protocol to this UPS. I have one that essentially behaves like a glorified power strip due to some invasive probing on my part. Don't mess with it directly. NOTE: the driver doesn't go anywhere near these character sequences, so it won't zap your UPS. I only mention this here as yet another reminder of the perils of closed hardware. SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_execute_device_command.30000644000175000017500000000003412665610643017614 00000000000000.so libnutclient_commands.3 nut-2.7.4/docs/man/nutscan_get_serial_ports_list.30000644000175000017500000000637412665610656017224 00000000000000'\" t .\" Title: nutscan_get_serial_ports_list .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_GET_SERIAL_P" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_get_serial_ports_list \- Get a port list name from a range of port\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf char ** nutscan_get_serial_ports_list(const char *ports_range); .fi .SH "DESCRIPTION" .sp The \fBnutscan_get_serial_ports_list()\fR function returns a null terminated array of string generated from a port range\&. \fIports_range\fR may be one of: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a single character from 0 to 9 or a to z representing a serial communication port depending on the operating system\&. For instance "0" is converted to /dev/ttyS0 and /dev/ttyUSB0 on Linux; "1" is converted to COM1 on Windows; "c" is converted to /dev/ttyc on solaris\&... .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a range of character in the form "X\-Y"\&. For instance "0\-1" will be converted to /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 and /dev/ttyUSB1 on Linux; "1\-3" will be converted to COM1, COM2 and COM3 on Windows; "a\-c" will be converted to /dev/ttya, /dev/ttyb and /dev/ttyc on Solaris\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a single port name (/dev/ttyS5, COM4\&...)\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} a list of port names separated with comas : "/dev/ttyS0,/dev/ttyS2,/dev/ttyS4" or "COM1,COM3"\&... .RE .sp The returned array can be used in a call to \fBnutscan_scan_eaton_serial\fR to get the serial device on a system\&. .SH "RETURN VALUE" .sp The \fBnutscan_get_serial_ports_list()\fR function returns NULL if an error occured (invalid port range) or a pointer to a nll terminated array of string on success\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_eaton_serial\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_display_ups_conf\fR(3) nut-2.7.4/docs/man/upsc.txt0000644000175000017500000000544212640444140012475 00000000000000UPSC(8) ======= NAME ---- upsc - example lightweight UPS client SYNOPSIS -------- *upsc* -l | -L ['host'] *upsc* 'ups' ['variable'] *upsc* -c 'ups' DESCRIPTION ----------- *upsc* is provided as a quick way to poll the status of a UPS server. It can be used inside shell scripts and other programs that need UPS data but don't want to include the full interface. OPTIONS ------- *-l* 'host':: List all UPS names configured at 'host', one name per line. The hostname defaults to "localhost". You may optionally add a colon and a port number. *-L* 'host':: As above, list all UPS names configured at 'host', including their description provided by the remote upsd(8) from ups.conf(5). The hostname defaults to "localhost". You may optionally add a colon and a port number to override the default port. *-c* 'ups':: Lists each client connected on 'ups', one name per line. 'ups':: Display the status of that UPS. The format for this option is 'upsname[@hostname[:port]]'. The default hostname is "localhost". 'variable':: Display the value of this variable only. By default, upsc retrieves the list of variables from the server and then displays the value for each. This may be useful in shell scripts to save an additional pipe into grep. EXAMPLES -------- To list all variables on an UPS named "myups" on a host called "mybox", with upsd(8) running on port 1234: $ upsc myups@mybox:1234 battery.charge: 100.0 battery.voltage: 13.9 battery.voltage.nominal: 13.6 . . . To list the UPSes configured on this system, along with their descriptions: $ upsc -L apc: Back-UPS 500 ppro2: Patriot Pro II To retrieve the status for all UPSes connected to mybox, using Bourne-shell syntax: $ for UPS in `upsc -l mybox:1234`; do upsc $UPS ups.status done To list clients connected on "myups": $ upsc -c myups 127.0.0.1 ::1 192.168.1.2 SCRIPTED MODE ------------- If you run this program inside a shell script or similar to get the list of devices and variables, you should only consider using output from stdout, not stderr. DIAGNOSTICS ----------- upsc will either print a list of UPS names, a list of all supported variables and their values on the UPS, or an error message. If you receive an error, make sure you have specified a valid UPS on the command line, that linkman:upsd[8] is really running on the other host and that no firewalls are blocking you. HISTORY ------- Earlier versions of this program used the 'upsfetch' library and UDP sockets to talk to upsd. This version of upsc uses the new 'upsclient' library, which only talks TCP. This is why 'upsct' no longer exists. SEE ALSO -------- linkman:upsd[8] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/snmp-ups.txt0000644000175000017500000001174412640473702013315 00000000000000snmp-ups(8) =========== NAME ---- snmp-ups - Multi-MIB Driver for SNMP UPS equipment NOTE ---- This man page only documents the hardware-specific features of the snmp-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The snmp-ups driver automatically detects and supports a wide range of devices by loading various MIBS: *ietf*:: UPS that is RFC 1628 (UPS MIB) compliant, e.g. MGE UPS SYSTEMS, Liebert, perhaps others (default) *mge*:: MGE UPS SYSTEMS and MGE Office Protection Systems devices with SNMP cards (ref 66062, 66045, 66074 and 66244) *apcc*:: APC AP9605, AP9606, AP9617, and AP9618 APC network management cards, as well as any others supporting the APC POWERNET MIB *netvision*:: Socomec Sicon UPS with Netvision Web/SNMP management card/external box *pw*:: Powerware devices with ConnectUPS SNMP cards *pxgx_ups*:: Eaton devices with Power Xpert Gateway UPS Card *aphel_genesisII*:: Eaton Powerware ePDU Monitored *aphel_revelation*:: Eaton Powerware ePDU Managed *raritan*:: Various Raritan PDUs *baytech*:: Various BayTech PDUs *cpqpower*:: HP/Compaq AF401A management card, perhaps others *cyberpower*:: Cyberpower RMCARD201. Should also support RMCARD100 (net version), RMCARD202 and RMCARD301 *huawei*:: Huawei UPS5000-E, perhaps others EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]: *mibs*='name':: Set MIB compliance (default=auto, allowed entries: refer to SUPPORTED HARDWARE above). With "auto", the driver will try a select set of SNMP objects until it finds one that the device responds to. Note that since NUT 2.6.2, snmp-ups has a new method that uses sysObjectID (which is a pointer to the prefered MIB of the device) to detect supported devices. This renders void the use of "mibs" option. *community*='name':: Set community name (default = public). Note that a RW community name is required to change UPS settings (as for a powerdown). *snmp_version*='version':: Set SNMP version (default = v1, allowed: v2c, v3) *snmp_retries*='retries':: Specifies the number of Net-SNMP retries to be used in the requests (default=5) *snmp_timeout*='timeout':: Specifies the Net-SNMP timeout in seconds between retries (default=1) *pollfreq*='value':: Set polling frequency in seconds, to reduce network flow (default=30) *notransferoids*:: Disable the monitoring of the low and high voltage transfer OIDs in the hardware. This will remove input.transfer.low and input.transfer.high from the list of variables. This should only be used on APCC Symmetra equipment which has strangeness in the three-phase power reporting. *secLevel*='value':: Set the securityLevel used for SNMPv3 messages (default=noAuthNoPriv, allowed: authNoPriv,authPriv) *secName*='value':: Set the securityName used for authenticated SNMPv3 messages (no default) *authPassword*='value':: Set the authentication pass phrase used for authenticated SNMPv3 messages (no default) *privPassword*='value':: Set the privacy pass phrase used for encrypted SNMPv3 messages (no default) *authProtocol*='value':: Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5) *privProtocol*='value':: Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES) REQUIREMENTS ------------ You will need to install the Net-SNMP package from http://www.net-snmp.org/ before building this driver. SNMP v3 also requires OpenSSL support from http://www.openssl.org. LIMITATIONS ----------- Shutdown ~~~~~~~~ The shutdown sequence should be tested before relying on NUT to send a shutdown command to the UPS. The problem is that the host network stack may have been torn down by the time the driver is invoked to send the shutdown command. The driver attempts to send +shutdown.return+, +shutdown.reboot+, and +load.off.delay+ commands to the UPS in sequence, stopping after the first supported command. INSTALLATION ------------ This driver is only built if the Net-SNMP development files are present at configuration time. You can also force it to be built by using +configure --with-snmp=yes+ before calling make. EXAMPLES -------- The hostname of the UPS is specified with the "port" value in `ups.conf`: [snmpv1] driver = snmp-ups port = snmp-ups.example.com community = public snmp_version = v1 pollfreq = 15 desc = "Example SNMP v1 device" [snmpv3] driver = snmp-ups port = 166.99.224.132 snmp_version = v3 secLevel = authPriv secName = mysecurityname authPassword = myauthenticationpassphrase privPassword = myprivatepassphrase desc = "Example SNMP v3 device, with the highest security level" AUTHORS ------- Arnaud Quette, Dmitry Frolov SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] NUT SNMP Protocols Library ~~~~~~~~~~~~~~~~~~~~~~~~~~ Available at: http://www.networkupstools.org/protocols/snmp/ Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsclient.txt0000644000175000017500000000605012640443572013535 00000000000000UPSCLIENT(3) ============ NAME ---- upsclient - Network UPS Tools client access library DESCRIPTION ----------- The Network UPS Tools (NUT) *upsclient* library provides a number of useful functions for programs to use when communicating with linkman:upsd[8]. Many of the low-level socket and protocol details are handled automatically when using this interface. State is maintained across calls in an opaque structure called `UPSCONN_t`. Callers are expected to create one per connection. These will be provided to most of the *upsclient* functions. The format of this structure is subject to change, and client programs must not reference elements within it directly. INITIALIZATION AND CLEANUP -------------------------- Before creating any connection, and in general using any upscli function, you must call linkman:upscli_init[3] with proper parameters to initialize upscli module. To register specific host security policy, you must call linkman:upscli_add_host_cert[3] before initialize connection to it. In the same way, just before exiting, and after all upscli usage, you must call linkman:upscli_cleanup[3] to flush cache files and perform other cleanup. NETWORK FUNCTIONS ----------------- To create a new connection, use linkman:upscli_connect[3]. This will also initialize the `UPSCONN_t` structure. To verify that a connection has been established later, linkman:upscli_fd[3] can be used to return the file descriptor. Clients wishing to check for the presence and operation of SSL on a connection may call linkman:upscli_ssl[3]. The majority of clients will use linkman:upscli_get[3] to retrieve single items from the server. To retrieve a list, use linkman:upscli_list_start[3] to get it started, then call linkman:upscli_list_next[3] for each element. Raw lines of text may be sent to linkman:upsd[8] with linkman:upscli_sendline[3]. Reading raw lines is possible with linkman:upscli_readline[3]. Client programs are expected to format these lines according to the protocol, as no checking will be performed before transmission. At the end of a connection, you must call linkman:upsclient_disconnect[3] to disconnect from *upsd* and release any dynamic memory associated with the `UPSCONN_t` structure. Failure to call this function will result in memory and file descriptor leaks in your program. ERROR HANDLING -------------- In the event of an error, linkman:upscli_strerror[3] will provide human-readable details on what happened. linkman:upscli_upserror[3] may also be used to retrieve the error number. These numbers are defined in *upsclient.h* as 'UPSCLI_ERR_*'. SEE ALSO -------- linkman:libupsclient-config[1], linkman:upscli_init[3], linkman:upscli_cleanup[3], linkman:upscli_add_host_cert[3], linkman:upscli_connect[3], linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_getvar[3], linkman:upscli_list_next[3], linkman:upscli_list_start[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/powerman-pdu.txt0000644000175000017500000000352112640443572014145 00000000000000POWERMAN-PDU(8) =============== NAME ---- powerman-pdu - Driver for Powerman PDU SYNOPSIS -------- *powerman-pdu* -h *powerman-pdu* -a 'PDU_NAME' ['OPTIONS'] NOTE: This man page only documents the hardware-specific features of the powerman-pdu driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports a wide range of PDUs through the Powerman project. This includes various models from APC, Baytech, Cyclades, but also support IPMI and various blade management modules from HP, IBM and Sun. EXTRA ARGUMENTS --------------- This driver doesn't support any optional settings. INSTALLATION ------------ This driver is not built by default. You can build it by using "configure --with-powerman=yes". UPS COMMANDS ------------ The following instant commands (see linkman:upscmd[8]) are available for each outlet of the PDU, with *X* standing for the outlet number: *outlet.X.load.on*:: Power on the outlet. *outlet.X.load.off*:: Power off the outlet. *outlet.X.load.cycle*:: Cycle the outlet (power off then power on, possibly with a delay). IMPLEMENTATION -------------- The hostname of the Powerman server is specified using the "port" value in *ups.conf*, i.e.: [pdu] driver = powerman-pdu port = host.example.com:port The port used to reach 'powermand' is optional if the default port is used. KNOWN ISSUES ------------ In the current NUT version (2.4.1), ups.status is still exposed, with the value "WAIT". Some other values from the ups collection are also exposed. AUTHOR ------ Arnaud Quette SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ The PowerMan home page: http://powerman.sourceforge.net/ nut-2.7.4/docs/man/blazer_usb.80000644000175000017500000003104312640476520013206 00000000000000'\" t .\" Title: blazer_usb .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BLAZER_USB" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" blazer_usb \- Driver for Megatec/Q1 protocol USB based UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the blazer driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The blazer driver is known to work with various UPSes from Blazer, Energy Sistem, Fenton Technologies, General Electric, Mustek and many others\&. The NUT compatibility table lists all the known supported models\&. Keep in mind, however, that other models not listed there may also be supported, but haven\(cqt been tested\&. .sp All devices with a serial interface (use the \fBblazer_ser\fR driver) and many with a USB interface (use the \fBblazer_usb\fR driver) are supported\&. .SH "EXTRA ARGUMENTS" .sp You may need to override or provide defaults for some values, depending on the make and model of your UPS\&. The following are the ones that most likely will need changing (see \fBups.conf\fR(5)): .PP \fBdefault\&.battery\&.voltage\&.high =\fR \fIvalue\fR .RS 4 Maximum battery voltage that is reached after about 12 to 24 hours charging\&. If you want the driver to report a guesstimated \fBbattery\&.charge\fR, you need to specify this (see BATTERY CHARGE)\&. .RE .PP \fBdefault\&.battery\&.voltage\&.low =\fR \fIvalue\fR .RS 4 Minimum battery voltage just before the UPS automatically shuts down\&. If you want the driver to report a guesstimated \fBbattery\&.charge\fR, you need to specify this (see BATTERY CHARGE)\&. .RE .PP \fBdefault\&.battery\&.voltage\&.nominal =\fR \fIvalue\fR, \fBoverride\&.battery\&.voltage\&.nominal =\fR \fIvalue\fR .RS 4 Some devices show a wrong nominal battery voltage (or none at all), so you may need to override or set a default value\&. .RE .PP \fBoverride\&.battery\&.packs =\fR \fIvalue\fR .RS 4 Some devices report a part of the total battery voltage\&. For instance, if \fBbattery\&.voltage\&.nominal\fR is 24 V, but it reports a \fBbattery\&.voltage\fR of around 2 V, the number of \fBbattery\&.packs\fR to correct this reading would be 12\&. The driver will attempt to detect this automatically, but if this fails somehow, you may want to override this value\&. .RE .PP \fBondelay =\fR \fIvalue\fR .RS 4 Time to wait before switching on the UPS (minutes)\&. Note that a value below 3 minutes, may cause earlier firmware versions to not switch on automatically, so it defaults to 3 minutes\&. The acceptable range is 0\&.\&.9999 minutes\&. .RE .PP \fBoffdelay =\fR \fIvalue\fR .RS 4 Time to wait before shutting down the UPS (seconds)\&. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds)\&. Defaults to 30 seconds\&. The acceptable range is 12\&.\&.600 seconds\&. .RE .PP \fBnorating\fR .RS 4 Some UPSes will lock up if you attempt to read rating information from them\&. Setting this flag will make the driver skip this step\&. .RE .PP \fBnovendor\fR .RS 4 Some UPSes will lock up if you attempt to read vendor information from them\&. Setting this flag will make the driver skip this step\&. .RE .PP \fBprotocol =\fR \fIstring\fR .RS 4 Skip autodetection of the protocol to use and only use the one specified\&. Supported values \fImegatec\fR, \fImegatec/old\fR, \fImustek\fR and \fIzinto\fR\&. .RE .PP \fBruntimecal =\fR \fIvalue,value,value,value\fR .RS 4 Parameter used in the (optional) runtime estimation\&. This takes two runtimes at different loads\&. Typically, this uses the runtime at full load and the runtime at half load\&. For instance, if your UPS has a rated runtime of 240 seconds at full load and 720 seconds at half load, you would enter .sp .if n \{\ .RS 4 .\} .nf runtimecal = 240,100,720,50 .fi .if n \{\ .RE .\} .sp The first load should always be higher than the second\&. If you have values available for loads other than 100 and 50 % respectively, you can use those too, but keep them spaced apart as far as reasonably possible\&. Just don\(cqt get too close to no load (prediction of runtime depends more on idle load for the battery then)\&. .RE .PP \fBchargetime =\fR \fIvalue\fR .RS 4 The time needed to fully recharge the battery after being fully discharged\&. If not specified, the driver defaults to 43200 seconds (12 hours)\&. Only used if \fBruntimecal\fR is also specified\&. .RE .PP \fBidleload =\fR \fIvalue\fR .RS 4 Minimum battery load used by the driver to estimate the runtime\&. If not specified, the driver defaults to 10%\&. Only used if \fBruntimecal\fR is also specified\&. .RE .SS "USB INTERFACE ONLY" .PP \fBvendorid =\fR \fIregex\fR, \fBproductid =\fR \fIregex\fR, \fBvendor =\fR \fIregex\fR, \fBproduct =\fR \fIregex\fR, \fBserial =\fR \fIregex\fR .RS 4 Select a specific UPS, in case there is more than one connected via USB\&. Each option specifies an extended regular expression (see \fBregex(7)\fR) that must match the UPS\(cqs entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4\-digit hexadecimal code for vendorid and productid\&. Try \fB\-DD\fR for finding out the strings to match\&. .sp Examples: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x vendor="Foo\&.Corporation\&.*" .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x vendorid=051d* (APC) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \-x product="\&.*(Smart|Back)\-?UPS\&.*" .RE .RE .PP \fBbus =\fR \fIregex\fR .RS 4 Select a UPS on a specific USB bus or group of busses\&. The argument is a regular expression that must match the bus name where the UPS is connected (e\&.g\&. bus="002", bus="00[2\-3]")\&. .RE .PP \fBsubdriver =\fR \fIstring\fR .RS 4 Select a serial\-over\-USB subdriver to use\&. You have a choice between \fBphoenix\fR, \fBippon\fR, \fBcypress\fR, and \fBkrauler\fR\&. When using this option, it is mandatory to also specify the \fBvendorid\fR and \fBproductid\fR\&. .RE .PP \fBlangid_fix =\fR \fIvalue\fR .RS 4 Apply the language ID workaround to the krauler subdriver\&. This is mandatory for some devices to work (LDLC, Dynamix and others)\&. You must to provide \fBvalue\fR (0x409 or 0x4095), according to your device entry in NUT hardware compatibility list (HCL)\&. .RE .SH "UPS COMMANDS" .sp This driver supports some instant commands (see \fBupscmd\fR(8)): .PP \fBbeeper\&.toggle\fR .RS 4 Toggle the UPS beeper\&. (Not available on some hardware\&.) .RE .PP \fBload\&.on\fR .RS 4 Turn on the load immediately\&. .RE .PP \fBload\&.off\fR .RS 4 Turn off the load immediately (see KNOWN PROBLEMS)\&. .RE .PP \fBshutdown\&.return\fR .RS 4 Turn off the load and return when power is back\&. Uses the timers defined by \fBondelay\fR and \fBoffdelay\fR\&. .RE .PP \fBshutdown\&.stayoff\fR .RS 4 Turn off the load and remain off (see KNOWN PROBLEMS)\&. Uses the timer defined by \fBoffdelay\fR\&. .RE .PP \fBshutdown\&.stop\fR .RS 4 Stop a shutdown in progress\&. .RE .PP \fBtest\&.battery\&.start\&.deep\fR .RS 4 Perform a long battery test (Not available on some hardware\&.) .RE .PP \fBtest\&.battery\&.start\&.quick\fR .RS 4 Perform a (10 second) battery test\&. .RE .PP \fBtest\&.battery\&.start\fR \fIvalue\fR .RS 4 Perform a battery test for the duration of \fIvalue\fR minutes\&. .RE .PP \fBtest\&.battery\&.stop\fR .RS 4 Stop a running battery test (not available on some hardware\&.) .RE .SH "BATTERY CHARGE" .sp Due to popular demand, this driver will report a guesstimated \fBbattery\&.charge\fR and optionally \fBbattery\&.runtime\fR, provided you specified a couple of the EXTRA ARGUMENTS listed above\&. .sp If you specify both \fBbattery\&.voltage\&.high\fR and \fBbattery\&.voltage\&.low\fR in \fBups.conf\fR(5), but don\(cqt enter \fBruntimecal\fR, it will guesstimate the state of charge by looking at the battery voltage alone\&. This is not reliable under load, as this only gives reasonably accurate readings if you disconnect the load, let the battery rest for a couple of minutes and then measure the open cell voltage\&. This just isn\(cqt practical if the power went out and the UPS is providing power for your systems\&. .sp .if n \{\ .RS 4 .\} .nf battery\&.voltage \- battery\&.voltage\&.low battery\&.charge = \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- x 100 % battery\&.voltage\&.high \- battery\&.voltage\&.low .fi .if n \{\ .RE .\} .sp There is a way to get better readings without disconnecting the load but this requires one to keep track on how much (and how fast) current is going in\- and out of the battery\&. If you specified the \fBruntimecal\fR, the driver will attempt to do this\&. Note however, that this heavily relies on the values you enter and that the UPS must be able to report the load as well\&. There are quite a couple of devices that report 0 % (or any other fixed value) at all times, in which case this obviously doesn\(cqt work\&. .sp The driver also has no way of determining the degradation of the battery capacity over time, so you\(cqll have to deal with this yourself (by adjusting the values in \fBruntimecal\fR)\&. Also note that the driver guesses the initial state of charge based on the battery voltage, so this may be less than 100 %, even when you are certain that they are full\&. There is just no way to reliably measure this between 0 and 100 % full charge\&. .sp This is better than nothing (but not by much)\&. If any of the above calculations is giving you incorrect readings, you are the one that put in the values in \fBups.conf\fR(5), so don\(cqt complain with the author\&. If you need something better, buy a UPS that reports \fBbattery\&.charge\fR and \fBbattery\&.runtime\fR all by itself without the help of a NUT driver\&. .SH "NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS" .sp The blazer drivers having replaced the megatec ones, some configuration changes may be required by users switching to blazer\&. .sp Part of this, the following megatec options, in ups\&.conf, have to be changed: .PP \fBbattvolts\fR .RS 4 You need to use \fIdefault\&.battery\&.voltage\&.high\fR and \fIdefault\&.battery\&.voltage\&.low\fR .RE .PP \fBdtr and rts\fR .RS 4 You need to use \fIcablepower\fR .RE .PP \fBignoreoff\fR .RS 4 This parameter can simply be discarded, since it was a wrong understanding of the specification\&. .RE .SH "KNOWN PROBLEMS" .sp Some UPS commands aren\(cqt supported by all models\&. In most cases, the driver will send a message to the system log when the user tries to execute an unsupported command\&. Unfortunately, some models don\(cqt even provide a way for the driver to check for this, so the unsupported commands will silently fail\&. .sp Both the \fBload\&.off\fR and \fBshutdown\&.stayoff\fR instant commands are meant to turn the load off indefinitely\&. However, some UPS models don\(cqt allow this\&. .sp Some models report a bogus value for the beeper status (will always be \fIenabled\fR or \fIdisabled\fR)\&. So, the \fBbeeper\&.toggle\fR command may appear to have no effect in the status reported by the driver when, in fact, it is working fine\&. .sp The temperature and load value is known to be bogus in some models\&. .SH "AUTHORS" .sp Arjen de Korte , Alexander Gordeev .SH "SEE ALSO" .sp \fBblazer_ser\fR(8), \fBnutupsdrv\fR(8), \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsrw\fR(8) .SS "Internet Resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp The NUT HCL: http://www\&.networkupstools\&.org/stable\-hcl\&.html nut-2.7.4/docs/man/nutscan_display_parsable.txt0000644000175000017500000000234512640443572016603 00000000000000NUTSCAN_DISPLAY_PARSABLE(3) =========================== NAME ---- nutscan_display_parsable - Display the specified `nutscan_device_t` structure on stdout. SYNOPSIS -------- #include void nutscan_display_parsable(nutscan_device_t * device); DESCRIPTION ----------- The *nutscan_display_parsable()* function displays all NUT devices in 'device' to stdout. It displays them in a way that can be easily parsed which is: :driver="",port=""[,="",="",...] may be one of USB, SNMP, XML, NUT, IPMI or AVAHI. is the name of the driver's binary corresponding to this device. and depend on , see the corresponding driver's man page. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_cidr_to_ip[3] nut-2.7.4/docs/man/upsclient.30000644000175000017500000001032512665610630013056 00000000000000'\" t .\" Title: upsclient .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLIENT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsclient \- Network UPS Tools client access library .SH "DESCRIPTION" .sp The Network UPS Tools (NUT) \fBupsclient\fR library provides a number of useful functions for programs to use when communicating with \fBupsd\fR(8)\&. Many of the low\-level socket and protocol details are handled automatically when using this interface\&. .sp State is maintained across calls in an opaque structure called UPSCONN_t\&. Callers are expected to create one per connection\&. These will be provided to most of the \fBupsclient\fR functions\&. The format of this structure is subject to change, and client programs must not reference elements within it directly\&. .SH "INITIALIZATION AND CLEANUP" .sp Before creating any connection, and in general using any upscli function, you must call \fBupscli_init\fR(3) with proper parameters to initialize upscli module\&. .sp To register specific host security policy, you must call \fBupscli_add_host_cert\fR(3) before initialize connection to it\&. .sp In the same way, just before exiting, and after all upscli usage, you must call \fBupscli_cleanup\fR(3) to flush cache files and perform other cleanup\&. .SH "NETWORK FUNCTIONS" .sp To create a new connection, use \fBupscli_connect\fR(3)\&. This will also initialize the UPSCONN_t structure\&. To verify that a connection has been established later, \fBupscli_fd\fR(3) can be used to return the file descriptor\&. Clients wishing to check for the presence and operation of SSL on a connection may call \fBupscli_ssl\fR(3)\&. .sp The majority of clients will use \fBupscli_get\fR(3) to retrieve single items from the server\&. To retrieve a list, use \fBupscli_list_start\fR(3) to get it started, then call \fBupscli_list_next\fR(3) for each element\&. .sp Raw lines of text may be sent to \fBupsd\fR(8) with \fBupscli_sendline\fR(3)\&. Reading raw lines is possible with \fBupscli_readline\fR(3)\&. Client programs are expected to format these lines according to the protocol, as no checking will be performed before transmission\&. .sp At the end of a connection, you must call \fBupsclient_disconnect\fR(3) to disconnect from \fBupsd\fR and release any dynamic memory associated with the UPSCONN_t structure\&. Failure to call this function will result in memory and file descriptor leaks in your program\&. .SH "ERROR HANDLING" .sp In the event of an error, \fBupscli_strerror\fR(3) will provide human\-readable details on what happened\&. \fBupscli_upserror\fR(3) may also be used to retrieve the error number\&. These numbers are defined in \fBupsclient\&.h\fR as \fIUPSCLI_ERR_*\fR\&. .SH "SEE ALSO" .sp \fBlibupsclient-config\fR(1), \fBupscli_init\fR(3), \fBupscli_cleanup\fR(3), \fBupscli_add_host_cert\fR(3), \fBupscli_connect\fR(3), \fBupscli_disconnect\fR(3), \fBupscli_fd\fR(3), \fBupscli_getvar\fR(3), \fBupscli_list_next\fR(3), \fBupscli_list_start\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_splitaddr\fR(3), \fBupscli_splitname\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutscan_display_ups_conf.txt0000644000175000017500000000155212640443572016625 00000000000000NUTSCAN_DISPLAY_UPS_CONF(3) =========================== NAME ---- nutscan_display_ups_conf - Display the specified `nutscan_device_t` structure on stdout. SYNOPSIS -------- #include void nutscan_display_ups_conf(nutscan_device_t * device); DESCRIPTION ----------- The *nutscan_display_ups_conf()* function displays all NUT devices in 'device' to stdout. It displays them in a way that it can be directly copied into the ups.conf file. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_cidr_to_ip[3] nut-2.7.4/docs/man/upscli_splitaddr.30000644000175000017500000000463612665610637014434 00000000000000'\" t .\" Title: upscli_splitaddr .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_SPLITADDR" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_splitaddr \- split a listening address into its components .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_splitaddr(const char *buf, char **hostname, int *port) .fi .SH "DESCRIPTION" .sp The \fBupscli_splitaddr()\fR function takes a pointer to the raw UPS definition \fIbuf\fR and returns pointers to dynamically allocated memory in \fIupsname\fR and \fIhostname\fR\&. It also copies the port number into \fIport\fR\&. .SH "FORMATTING" .sp A listening address definition is specified according to this format: .sp .if n \{\ .RS 4 .\} .nf [:] .fi .if n \{\ .RE .\} .sp Definitions without an explicit port value receive the default value of 3493\&. .SH "MEMORY USAGE" .sp You must \fBfree\fR(3) the pointer \fIhostname\fR when you are done with it to avoid memory leaks\&. .SH "RETURN VALUE" .sp The \fBupscli_splitaddr()\fR function returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_splitname\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutdrv_atcl_usb.txt0000644000175000017500000000477312667537407014751 00000000000000NUTDRV_ATCL_USB(8) ================== NAME ---- nutdrv_atcl_usb - Driver for 'ATCL FOR UPS' equipment NOTE ---- This man page only documents the specific features of the nutdrv_atcl_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver is for UPS hardware which identifies itself as USB idVendor 0001 and idProduct 0000, and iManufacturer +ATCL FOR UPS+. Known manufacturers include Kanji and Plexus. The UPS interface seems to be a generic USB-to-serial chip, and for hardware manufactured by Kanji and Plexus, the microcontroller appears to emulate a traditional contact-closure interface. This translates into only three states in ups.status: *OL*, *OB* and *OB LB* (similar to linkman:genericups[8]), with no other dynamic status values reported. Note that these USB identifiers (including the iManufacturer string) have also been seen on devices that are supported by the `fuji` subdriver of linkman:nutdrv_qx[8]. EXTRA ARGUMENTS --------------- This driver supports the following optional setting: *vendor*='name':: In case your iManufacturer (Vendor) string does not exactly match +ATCL FOR UPS+, you may provide an alternate string here. Note that a more likely case is that your device is handled by another driver for +0001:0000+ devices, such as linkman:nutdrv_qx[8]. BUGS ---- The UPS returns the same code for "load power is off" as for "on line power". This condition will not be observed if the NUT master is powered by the UPS, but may be an issue if the UPS is monitored by a remote system. The time between the shutdown command and removal of power seems to be fixed at 30 seconds. Ensure that the NUT shutdown script is invoked as late as possible in the shutdown procedure (in case some services take longer than others to clean up). Most contact-closure UPSes will not power down the load if the line power is present. This can create a race when using slave linkman:upsmon[8] systems. See the linkman:upsmon[8] man page for more information. The solution to this problem is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command. AUTHORS ------- Charles Lepple SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] The generic serial driver: ~~~~~~~~~~~~~~~~~~~~~~~~~~ linkman:genericups[8] The Qx driver: ~~~~~~~~~~~~~~ linkman:nutdrv_qx[8] (`fuji` subdriver) Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_tcp_get_timeout.30000644000175000017500000000002712665610645016334 00000000000000.so libnutclient_tcp.3 nut-2.7.4/docs/man/ups.conf.txt0000644000175000017500000002002012640473702013251 00000000000000UPS.CONF(5) =========== NAME ---- ups.conf - UPS definitions for Network UPS Tools DESCRIPTION ----------- This file is read by the driver controller linkman:upsdrvctl[8], the UPS drivers that use the common core (see linkman:nutupsdrv[8], and linkman:upsd[8]). The file begins with global directives, and then each UPS has a section which contains a number of directives that set parameters for that UPS. A UPS section begins with the name of the UPS in brackets, and continues until the next UPS name in brackets or until EOF. The name "default" is used internally in upsd, so you can't use it in this file. You must define the 'driver' and 'port' elements for each entry. Anything after that in a section is optional. A simple example might look like this: [myups] driver = blazer_ser port = /dev/ttyS0 desc = "Web server UPS" A slightly more complicated version includes some extras for the hardware-specific part of the driver: [bigups] driver = apcsmart port = /dev/cua00 cable = 940-0095B sdtype = 2 desc = "Database server UPS" In this case, the linkman:apcsmart[8] driver will receive variables called "cable" and "sdtype" which have special meanings. See the man pages of your driver(s) to learn which variables are supported and what they do. GLOBAL DIRECTIVES ----------------- *chroot*:: Optional. The driver will chroot(2) to this directory during initialization. This can be useful when securing systems. *driverpath*:: Optional. Path name of the directory in which the UPS driver executables reside. If you don't specify this, the programs look in a built-in default directory, which is often /usr/local/ups/bin. *maxstartdelay*:: Optional. Same as the UPS field of the same name, but this is the default for UPSes that don't have the field. *maxretry*:: Optional. Specify the number of attempts to start the driver(s), in case of failure, before giving up. A delay of 'retrydelay' is inserted between each attempt. Caution should be taken when using this option, since it can impact the time taken by your system to start. + The default is 1 attempt. *retrydelay*:: Optional. Specify the delay between each restart attempt of the driver(s), as specified by 'maxretry'. Caution should be taken when using this option, since it can impact the time taken by your system to start. + The default is 5 seconds. *pollinterval*:: Optional. The status of the UPS will be refreshed after a maximum delay which is controlled by this setting. This is normally 2 seconds. This may be useful if the driver is creating too much of a load on your system or network. *synchronous*:: Optional. The driver work by default in asynchronous mode (i.e *synchronous=no*). This means that all data are pushed by the driver on the communication socket to upsd (Unix socket on Unix, Named pipe on Windows) without waiting for these data to be actually consumed. With some HW, such as ePDUs, that can produce a lot of data, asynchronous mode may cause some congestion, resulting in the socket to be full, and the driver to appear as not connected. In such case, the driver will provide the following debug message: + write XX bytes to socket Y failed + By enabling the 'synchronous' flag (value = 'yes'), the driver will wait for data to be consumed by upsd, prior to publishing more. This can be enabled either globally or per driver. + The default is 'no' (i.e. asynchronous mode) for backward compatibility of the driver behavior. *user*:: Optional. If started as root, the driver will setuid(2) to the user id associated with 'username'. UPS FIELDS ---------- *driver*:: Required. This specifies which program will be monitoring this UPS. You need to specify the one that is compatible with your hardware. See linkman:nutupsdrv[8] for more information on drivers in general and pointers to the man pages of specific drivers. *port*:: Required. This is the serial port where the UPS is connected. On a Linux system, the first serial port usually is '/dev/ttyS0'. On FreeBSD and similar systems, it probably will be '/dev/cuaa0'. *sdorder*:: Optional. When you have multiple UPSes on your system, you usually need to turn them off in a certain order. upsdrvctl shuts down all the 0s, then the 1s, 2s, and so on. To exclude a UPS from the shutdown sequence, set this to -1. + The default value for this parameter is 0. *desc*:: Optional. This allows you to set a brief description that upsd will provide to clients that ask for a list of connected equipment. *nolock*:: Optional. When you specify this, the driver skips the port locking routines every time it starts. This may allow other processes to seize the port if you start more than one accidentally. + You should only use this if your system won't work without it. + This may be needed on Mac OS X systems. *ignorelb*:: Optional. When you specify this, the driver ignores a low battery condition flag that is reported by the UPS (some devices will switch off almost immediately after setting this flag, or will report this as soons as the mains fails). Instead it will use either of the following conditions to determine when the battery is low: battery.charge < battery.charge.low battery.runtime < battery.runtime.low + The idea is to set the battery.charge.low and/or battery.runtime.low levels in *ups.conf* to a value that gives enough time to cleanly shutdown your system: override.battery.charge.low = 30 override.battery.runtime.low = 180 + In order for this to work, your UPS should be able to (reliably) report charge and/or runtime remaining on battery. Use with caution! *maxstartdelay*:: Optional. This can be set as a global variable above your first UPS definition and it can also be set in a UPS section. This value controls how long upsdrvctl will wait for the driver to finish starting. This keeps your system from getting stuck due to a broken driver or UPS. + The default is 45 seconds. *synchronous*:: Optional. Same as the global directive of the same name, but this is for a specific device. *usb_set_altinterface*[='altinterface']:: Optional. Force the USB code to call `usb_set_altinterface(0)`, as was done in NUT 2.7.2 and earlier. This should not be necessary, since the default for `bAlternateSetting` (as shown in lsusb) is zero on all USB devices seen to date. However, this redundant call to `usb_set_altinterface()` prevents certain UPSes from working on Mac OS X. If your UPS requires explicitly setting the alternate interface, include this flag, and email the nut-upsdev list with details about your UPS and operating system. *default.*:: Optional. Set a default value for which is used in case the UPS doesn't provide a value, but will be overwritten if a value is available from the UPS: default.input.voltage.nominal = 230 + The above will report the nominal input voltage to be 230, unless the UPS tells us differently. *override.*:: Optional. Set a value for that overrides any value that may be read from the UPS. Used for overriding values from the UPS that are clearly wrong (some devices report wrong values for battery voltage for instance): override.battery.voltage.nominal = 12 + Use with caution! This will only change the appearance of the variable to the outside world, internally in the UPS the original value is used. All other fields are passed through to the hardware-specific part of the driver. See those manuals for the list of what is allowed. INTEGRATION ----------- linkman:upsdrvctl[8] uses this file to start and stop the drivers. The drivers themselves also obtain configuration data from this file. Each driver looks up its section and uses that to configure itself. linkman:upsd[8] learns about which UPSes are installed on this system by reading this file. If this system is called "doghouse" and you have defined a UPS in your *ups.conf* called "snoopy", then you can monitor it from linkman:upsc[8] or similar as "snoopy@doghouse". SEE ALSO -------- linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsdrvctl[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_scan_ipmi.30000644000175000017500000000414412665610652014553 00000000000000'\" t .\" Title: nutscan_scan_ipmi .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_IPMI" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_ipmi \- Scan local IPMI devices\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_ipmi(void); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_ipmi()\fR function is not implemented yet\&. .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_ipmi()\fR function is not implemented yet\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_scan_eaton_serial\fR(3), \fBnutscan_cidr_to_ip\fR(3) nut-2.7.4/docs/man/upscli_connect.txt0000644000175000017500000000237512640443572014545 00000000000000UPSCLI_CONNECT(3) ================= NAME ---- upscli_connect - Open a connection to a NUT upsd SYNOPSIS -------- #include int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); DESCRIPTION ----------- The *upscli_connect()* function takes the pointer 'ups' to a `UPSCONN_t` state structure and opens a TCP connection to the 'host' on the given 'port'. 'flags' may be either `UPSCLI_CONN_TRYSSL` to try a SSL connection, or `UPSCLI_CONN_REQSSL` to require a SSL connection. If SSL mode is required, this function will only return successfully if it is able to establish a SSL connection with the server. Possible reasons for failure include no SSL support on the server, and if *upsclient* itself hasn't been compiled with SSL support. You must call linkman:upscli_disconnect[3] when finished with a connection, or your program will slowly leak memory and file descriptors. RETURN VALUE ------------ The *upscli_connect()* function modifies the `UPSCONN_t` structure and returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/liebert-esp2.80000644000175000017500000000407512640476504013362 00000000000000'\" t .\" Title: liebert-esp2 .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIEBERT\-ESP2" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" liebert-esp2 \- Driver for Liebert UPS, using the ESP\-II serial protocol .SH "NOTE" .sp This man page only documents the hardware\-specific features of the liebert\-esp2 driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This is an experimental driver\&. You have been warned\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in \fBups.conf\fR(5): .PP \fBbaudrate=\fR\fInum\fR .RS 4 Set the speed of the serial connection \- 1200, 2400 (default), 4800, 9600 or 19200\&. .RE .SH "AUTHOR" .sp Richard Gregory , Arjen de Korte .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_set_device_variable_values.30000644000175000017500000000003512665610646020477 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/upsrw.txt0000644000175000017500000000640312667537407012723 00000000000000UPSRW(8) ======== NAME ---- upsrw - UPS variable administration tool SYNOPSIS -------- *upsrw* 'ups' *upsrw* -h *upsrw* -s 'variable' [-u 'username'] [-p 'password'] 'ups' DESCRIPTION ----------- *upsrw* allows you to view and change the read/write variables inside your UPS. It sends commands via the server linkman:upsd[8] to your driver, which configures the hardware for you. The list of variables that allow you to change their values is based on the capabilities of your UPS equipment. Not all models support this feature. Typically, cheaper hardware does not support any of them. Run upsrw with a UPS identifier to see what will work for you. OPTIONS ------- *-h*:: Display the help message. *-s* 'variable':: Specify the variable to be changed inside the UPS. For unattended mode such as in shell scripts, use the format VAR=VALUE to specify both the variable and the value, for example: -s input.transfer.high=129 + Without this argument, upsrw will just display the list of the variables and their possible values. + Some variables are strings, and can be set to any value within the length limit. Others are enumerated types and can only be set to one of those values. Others may be within an allowed range of values. Refer to the list to know what's available in your hardware. *-u* 'username':: Set the NUT username for the connection to the server. This is optional, and you will be prompted for this when using the -s option if you don't specify -u on the command line. NUT usernames are defined in linkman:upsd.users[5], and are not linked to system usernames. *-p* 'password':: Set the password to authenticate to the server. This is also optional like -u, and you will be prompted for it if necessary. 'ups':: View or change the settings on this UPS. The format for this option is `upsname[@hostname[:port]]`. The default hostname is "localhost". UNATTENDED MODE --------------- If you run this program inside a shell script or similar to set variables, you will need to specify all of the information on the command line. This means using `-s VAR=VALUE`, `-u` and `-p`. Otherwise it will put up a prompt and your program will hang. This is not necessary when displaying the list, as the username and password are not required for read-only mode. Moreover, if you run this program inside a shell script or similar, you should only consider using output from stdout, not stderr. DIAGNOSTICS ----------- *upsrw* can't set variables on your UPS unless you provide a valid username and password. If you get "access denied" errors, make sure that your linkman:upsd.users[5] has an entry for you, and that the username you are using has permissions to SET variables. VALUE FORMAT ------------ When using *upsrw* to modify a numeric float value, that values must be given using decimal (base 10) english-based representation, so using a dot, in non-scientific notation. So hexadecimal, exponents, and comma for thousands separator are forbiden. For example: "1200.20" is valid, while "1,200.20" and "1200,20" are invalid. HISTORY ------- This program used to be called upsct2, which was ambiguous and confusing. SEE ALSO -------- linkman:upsd[8], linkman:upscmd[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_upserror.30000644000175000017500000000425112665610642014314 00000000000000'\" t .\" Title: upscli_upserror .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_UPSERROR" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_upserror \- Get current error number for connection .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_upserror(UPSCONN_t *ups); .fi .SH "DESCRIPTION" .sp The \fBupscli_upserror\fR() function takes the pointer \fIups\fR to a UPSCONN_t state structure and returns the value of the internal error number, if any\&. .sp This is typically used to check for certain error values like UPSCLI_ERR_UNKCOMMAND\&. That specific error can be used for detecting older versions of \fBupsd\fR(8) which might not support a given command\&. .sp Some error messages have additional meanings, so you should use \fBupscli_strerror\fR(3) to obtain readable error messages\&. .SH "RETURN VALUE" .sp The \fBupscli_upserror\fR() function returns one of the UPSCLI_ERR_* values from upsclient\&.h, or 0 if no error has occurred\&. .SH "SEE ALSO" .sp \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_strerror\fR(3) nut-2.7.4/docs/man/isbmex.txt0000644000175000017500000000126512640443572013021 00000000000000ISBMEX(8) ========= NAME ---- isbmex - Driver for ISBMEX UPS equipment NOTE ---- This man page only documents the hardware-specific features of the isbmex driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports SOLA/BASIC Mexico ISBMEX protocol UPS equipment. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHOR ------ Edscott Wilson Garcia SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsset.conf.50000644000175000017500000000636212665610625013333 00000000000000'\" t .\" Title: upsset.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSSET\&.CONF" "5" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsset.conf \- Configuration for Network UPS Tools upsset\&.cgi .SH "DESCRIPTION" .sp This file only does one job\(emit lets you convince \fBupsset.cgi\fR(8) that your system\(cqs CGI directory is secure\&. The program will not run until this file has been properly defined\&. .SH "SECURITY REQUIREMENTS" .sp \fBupsset.cgi\fR(8) allows you to try login name and password combinations\&. There is no rate limiting, as the program shuts down between every request\&. Such is the nature of CGI programs\&. .sp Normally, attackers would not be able to access your \fBupsd\fR(8) server directly as it would be protected by the LISTEN directives in your \fBupsd.conf\fR(5) file, tcp\-wrappers (if available when NUT was built), and hopefully local firewall settings in your OS\&. .sp \fBupsset\fR runs on your web server, so upsd will see it as a connection from a host on an internal network\&. It doesn\(cqt know that the connection is actually coming from someone on the outside\&. This is why you must secure it\&. .sp On Apache, you can use the \&.htaccess file or put the directives in your httpd\&.conf\&. It looks something like this, assuming the \&.htaccess method: .sp .if n \{\ .RS 4 .\} .nf deny from all allow from your\&.network\&.addresses .fi .if n \{\ .RE .\} .sp You will probably have to set "AllowOverride Limit" for this directory in your server\-level configuration file as well\&. .sp If this doesn\(cqt make sense, then stop reading and leave this program alone\&. It\(cqs not something you absolutely need to have anyway\&. .sp Assuming you have all this done, and it actually works (test it!), then you may add the following directive to this file: .sp .if n \{\ .RS 4 .\} .nf I_HAVE_SECURED_MY_CGI_DIRECTORY .fi .if n \{\ .RE .\} .sp If you lie to the program and someone beats on your upsd through your web server, don\(cqt blame me\&. .SH "SEE ALSO" .sp \fBupsset.cgi\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/metasys.80000644000175000017500000000623112640476505012547 00000000000000'\" t .\" Title: metasys .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "METASYS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" metasys \- Driver for Meta System UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the metasys driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The \fBmetasys\fR driver was written with the "Meta System UPS Protocol Rev\&.1\&.12" kindly supplied from Meta System\&. .sp The driver should support all the common features of the ups models: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} HF Line (/2) 1\-8 boards .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} HF Millennium (810, 820) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} HF TOP Line (910, 920, 930, 940, 950, 970, 980) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ECO Network (750, 1000, 1050, 1500, 1800, 2000, 2100, 2500, 3000) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ECO (305, 308, 311, 511, 516, 519, 522, SX, SXI) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} ally HF (800, 1000, 1250, 1600, 2000, 2500) .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Megaline (1250, 2500, 3750, 5000, 6250, 7500, 8750, 10000) .RE .SH "CABLING" .sp The needed cable is a standard pin\-to\-pin serial cable with at least pins 2, 3, and 5 (on DB9 connector) connected\&. .SH "EXTRA ARGUMENTS" .sp This driver supports no extra arguments from \fBups.conf\fR(5)\&. .SH "BUGS" .sp This driver has been tested on Meta System HF Millennium 820 and ally HF 1000 only\&. .sp Any information about the use of the driver with the other listed UPS are really welcome\&. .SH "AUTHOR" .sp Fabio Di Niro .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_get_serial_ports_list.txt0000644000175000017500000000332612640444140017655 00000000000000NUTSCAN_GET_SERIAL_PORTS_LIST(3) ================================ NAME ---- nutscan_get_serial_ports_list - Get a port list name from a range of port. SYNOPSIS -------- #include char ** nutscan_get_serial_ports_list(const char *ports_range); DESCRIPTION ----------- The *nutscan_get_serial_ports_list()* function returns a null terminated array of string generated from a port range. 'ports_range' may be one of: - a single character from 0 to 9 or a to z representing a serial communication port depending on the operating system. For instance "0" is converted to /dev/ttyS0 and /dev/ttyUSB0 on Linux; "1" is converted to COM1 on Windows; "c" is converted to /dev/ttyc on solaris... - a range of character in the form "X-Y". For instance "0-1" will be converted to /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 and /dev/ttyUSB1 on Linux; "1-3" will be converted to COM1, COM2 and COM3 on Windows; "a-c" will be converted to /dev/ttya, /dev/ttyb and /dev/ttyc on Solaris. - a single port name (/dev/ttyS5, COM4...). - a list of port names separated with comas : "/dev/ttyS0,/dev/ttyS2,/dev/ttyS4" or "COM1,COM3"... The returned array can be used in a call to *nutscan_scan_eaton_serial* to get the serial device on a system. RETURN VALUE ------------ The *nutscan_get_serial_ports_list()* function returns NULL if an error occured (invalid port range) or a pointer to a nll terminated array of string on success. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_eaton_serial[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3] nut-2.7.4/docs/man/hosts.conf.txt0000644000175000017500000000201412640443572013607 00000000000000HOSTS.CONF(5) ============= NAME ---- hosts.conf - Access control for Network UPS Tools CGI programs DESCRIPTION ----------- The CGI programs (linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8]) use this file to determine if they are allowed to talk to a host. This keeps random visitors from using your web server to annoy others by creating outgoing connections. DIRECTIVES ---------- *MONITOR* 'ups' 'description':: The 'ups' element is in the form `upsname[@hostname[:port]]`. To allow connections to a UPS called "snoopy" on a system called "doghouse" that runs upsd on port 7877, it would look like this: MONITOR snoopy@doghouse:7877 "Joe Cool" + The description must be one element, so if it has spaces, then it must be wrapped with quotes as shown above. The default hostname is "localhost". SEE ALSO -------- linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upsmon.conf.txt0000644000175000017500000003415212640473702013776 00000000000000UPSMON.CONF(5) ============== NAME ---- upsmon.conf - Configuration for Network UPS Tools upsmon DESCRIPTION ----------- This file's primary job is to define the systems that linkman:upsmon[8] will monitor and to tell it how to shut down the system when necessary. It will contain passwords, so keep it secure. Ideally, only the upsmon process should be able to read it. Additionally, other optional configuration values can be set in this file. CONFIGURATION DIRECTIVES ------------------------ *DEADTIME* 'seconds':: upsmon allows a UPS to go missing for this many seconds before declaring it "dead". The default is 15 seconds. + upsmon requires a UPS to provide status information every few seconds (see POLLFREQ and POLLFREQALERT) to keep things updated. If the status fetch fails, the UPS is marked stale. If it stays stale for more than DEADTIME seconds, the UPS is marked dead. + A dead UPS that was last known to be on battery is assumed to have changed to a low battery condition. This may force a shutdown if it is providing a critical amount of power to your system. This seems disruptive, but the alternative is barreling ahead into oblivion and crashing when you run out of power. + Note: DEADTIME should be a multiple of POLLFREQ and POLLFREQALERT. Otherwise, you'll have "dead" UPSes simply because upsmon isn't polling them quickly enough. Rule of thumb: take the larger of the two POLLFREQ values, and multiply by 3. *FINALDELAY* 'seconds':: When running in master mode, upsmon waits this long after sending the NOTIFY_SHUTDOWN to warn the users. After the timer elapses, it then runs your SHUTDOWNCMD. By default this is set to 5 seconds. + If you need to let your users do something in between those events, increase this number. Remember, at this point your UPS battery is almost depleted, so don't make this too big. + Alternatively, you can set this very low so you don't wait around when it's time to shut down. Some UPSes don't give much warning for low battery and will require a value of 0 here for a safe shutdown. + NOTE: If FINALDELAY on the slave is greater than HOSTSYNC on the master, the master will give up waiting for the slave to disconnect. *HOSTSYNC* 'seconds':: upsmon will wait up to this many seconds in master mode for the slaves to disconnect during a shutdown situation. By default, this is 15 seconds. + When a UPS goes critical (on battery + low battery, or "FSD": forced shutdown), the slaves are supposed to disconnect and shut down right away. The HOSTSYNC timer keeps the master upsmon from sitting there forever if one of the slaves gets stuck. + This value is also used to keep slave systems from getting stuck if the master fails to respond in time. After a UPS becomes critical, the slave will wait up to HOSTSYNC seconds for the master to set the FSD flag. If that timer expires, the slave will assume that the master is broken and will shut down anyway. + This keeps the slaves from shutting down during a short-lived status change to "OB LB" that the slaves see but the master misses. *MINSUPPLIES* 'num':: Set the number of power supplies that must be receiving power to keep this system running. Normal computers have just one power supply, so the default value of 1 is acceptable. + Large/expensive server type systems usually have more, and can run with a few missing. The HP NetServer LH4 can run with 2 out of 4, for example, so you'd set it to 2. The idea is to keep the box running as long as possible, right? + Obviously you have to put the redundant supplies on different UPS circuits for this to make sense! See big-servers.txt in the docs subdirectory for more information and ideas on how to use this feature. + Also see the section on "power values" in linkman:upsmon[8]. *MONITOR* 'system' 'powervalue' 'username' 'password' 'type':: Each UPS that you need to be monitor should have a MONITOR line. Not all of these need supply power to the system that is running upsmon. You may monitor other systems if you want to be able to send notifications about status changes on them. You must have at least one MONITOR directive in `upsmon.conf`. 'system' is a UPS identifier. It is in this form: +[@[:]]+ The default hostname is "localhost". Some examples: - "su700@mybox" means a UPS called "su700" on a system called "mybox". This is the normal form. - "fenton@bigbox:5678" is a UPS called "fenton" on a system called "bigbox" which runs linkman:upsd[8] on port "5678". 'powervalue' is an integer representing the number of power supplies that the UPS feeds on this system. Most normal computers have one power supply, and the UPS feeds it, so this value will be 1. You need a very large or special system to have anything higher here. You can set the 'powervalue' to 0 if you want to monitor a UPS that doesn't actually supply power to this system. This is useful when you want to have upsmon do notifications about status changes on a UPS without shutting down when it goes critical. The 'username' and 'password' on this line must match an entry in that system's linkman:upsd.users[5]. If your username is "monmaster" and your password is "blah", the MONITOR line might look like this: +MONITOR myups@bigserver 1 monmaster blah master+ Meanwhile, the `upsd.users` on `bigserver` would look like this: [monmaster] password = blah upsmon master # (or slave) The 'type' refers to the relationship with linkman:upsd[8]. It can be either "master" or "slave". See linkman:upsmon[8] for more information on the meaning of these modes. The mode you pick here also goes in the `upsd.users` file, as seen in the example above. *NOCOMMWARNTIME* 'seconds':: upsmon will trigger a NOTIFY_NOCOMM after this many seconds if it can't reach any of the UPS entries in this configuration file. It keeps warning you until the situation is fixed. By default this is 300 seconds. *NOTIFYCMD* 'command':: upsmon calls this to send messages when things happen. + This command is called with the full text of the message as one argument. The environment string NOTIFYTYPE will contain the type string of whatever caused this event to happen. + If you need to use linkman:upssched[8], then you must make it your NOTIFYCMD by listing it here. + Note that this is only called for NOTIFY events that have EXEC set with NOTIFYFLAG. See NOTIFYFLAG below for more details. + Making this some sort of shell script might not be a bad idea. For more information and ideas, see docs/scheduling.txt + Remember, this command also needs to be one element in the configuration file, so if your command has spaces, then wrap it in quotes. + +NOTIFYCMD "/path/to/script --foo --bar"+ + This script is run in the background--that is, upsmon forks before it calls out to start it. This means that your NOTIFYCMD may have multiple instances running simultaneously if a lot of stuff happens all at once. Keep this in mind when designing complicated notifiers. *NOTIFYMSG* 'type' 'message':: upsmon comes with a set of stock messages for various events. You can change them if you like. NOTIFYMSG ONLINE "UPS %s is getting line power" NOTIFYMSG ONBATT "Someone pulled the plug on %s" + Note that +%s+ is replaced with the identifier of the UPS in question. + The message must be one element in the configuration file, so if it contains spaces, you must wrap it in quotes. NOTIFYMSG NOCOMM "Someone stole UPS %s" + Possible values for 'type': ONLINE;; UPS is back online ONBATT;; UPS is on battery LOWBATT;; UPS is on battery and has a low battery (is critical) FSD;; UPS is being shutdown by the master (FSD = "Forced Shutdown") COMMOK;; Communications established with the UPS COMMBAD;; Communications lost to the UPS SHUTDOWN;; The system is being shutdown REPLBATT;; The UPS battery is bad and needs to be replaced NOCOMM;; A UPS is unavailable (can't be contacted for monitoring) *NOTIFYFLAG* 'type' 'flag'[\+'flag'][+'flag']...:: By default, upsmon sends walls global messages to all logged in users) via /bin/wall and writes to the syslog when things happen. You can change this. + Examples: + NOTIFYFLAG ONLINE SYSLOG NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC + Possible values for the flags: + SYSLOG;; Write the message to the syslog WALL;; Write the message to all users with /bin/wall EXEC;; Execute NOTIFYCMD (see above) with the message IGNORE;; Don't do anything + If you use IGNORE, don't use any other flags on the same line. *POLLFREQ* 'seconds':: Normally upsmon polls the linkman:upsd[8] server every 5 seconds. If this is flooding your network with activity, you can make it higher. You can also make it lower to get faster updates in some cases. + There are some catches. First, if you set the POLLFREQ too high, you may miss short-lived power events entirely. You also risk triggering the DEADTIME (see above) if you use a very large number. + Second, there is a point of diminishing returns if you set it too low. While upsd normally has all of the data available to it instantly, most drivers only refresh the UPS status once every 2 seconds. Polling any more than that usually doesn't get you the information any faster. *POLLFREQALERT* 'seconds':: This is the interval that upsmon waits between polls if any of its UPSes are on battery. You can use this along with POLLFREQ above to slow down polls during normal behavior, but get quicker updates when something bad happens. + This should always be equal to or lower than the POLLFREQ value. By default it is also set 5 seconds. + The warnings from the POLLFREQ entry about too-high and too-low values also apply here. *POWERDOWNFLAG* 'filename':: upsmon creates this file when running in master mode when the UPS needs to be powered off. You should check for this file in your shutdown scripts and call `upsdrvctl shutdown` if it exists. + This is done to forcibly reset the slaves, so they don't get stuck at the "halted" stage even if the power returns during the shutdown process. This usually does not work well on contact-closure UPSes that use the genericups driver. + See the config-notes.txt file in the docs subdirectory for more information. Refer to the section: [[UPS_shutdown]] "Configuring automatic shutdowns for low battery events", or refer to the online version. *RBWARNTIME* 'seconds':: When a UPS says that it needs to have its battery replaced, upsmon will generate a NOTIFY_REPLBATT event. By default, this happens every 43200 seconds (12 hours). + If you need another value, set it here. *RUN_AS_USER* 'username':: upsmon normally runs the bulk of the monitoring duties under another user ID after dropping root privileges. On most systems this means it runs as "nobody", since that's the default from compile-time. + The catch is that "nobody" can't read your upsmon.conf, since by default it is installed so that only root can open it. This means you won't be able to reload the configuration file, since it will be unavailable. + The solution is to create a new user just for upsmon, then make it run as that user. I suggest "nutmon", but you can use anything that isn't already taken on your system. Just create a regular user with no special privileges and an impossible password. + Then, tell upsmon to run as that user, and make `upsmon.conf` readable by it. Your reloads will work, and your config file will stay secure. + This file should not be writable by the upsmon user, as it would be possible to exploit a hole, change the SHUTDOWNCMD to something malicious, then wait for upsmon to be restarted. *SHUTDOWNCMD* 'command':: upsmon runs this command when the system needs to be brought down. If it is a slave, it will do that immediately whenever the current overall power value drops below the MINSUPPLIES value above. + When upsmon is a master, it will allow any slaves to log out before starting the local shutdown procedure. + Note that the command needs to be one element in the config file. If your shutdown command includes spaces, then put it in quotes to keep it together, i.e.: SHUTDOWNCMD "/sbin/shutdown -h +0" *CERTPATH* 'certificate file or database':: When compiled with SSL support, you can enter the certificate path here. + With NSS:;; Certificates are stored in a dedicated database (splitted in 3 files). Specify the path of the database directory. With OpenSSL:;; Directory containing CA certificates in PEM format, used to verify the server certificate presented by the upsd server. The files each contain one CA certificate. The files are looked up by the CA subject name hash value, which must hence be available. *CERTIDENT* 'certificate name' 'database password':: When compiled with SSL support with NSS, you can specify the certificate name to retrieve from database to authenticate itself and the password required to access certificate related private key. *CERTHOST* 'hostname' 'certificate name' 'certverify' 'forcessl':: When compiled with SSL support with NSS, you can specify security directive for each server you can contact. + Each entry maps server name with the expected certificate name and flags indicating if the server certificate is verified and if the connection must be secure. *CERTVERIFY* '0 | 1':: When compiled with SSL support, make upsmon verify all connections with certificates. + Without this, there is no guarantee that the upsd is the right host. Enabling this greatly reduces the risk of man-in-the-middle attacks. This effectively forces the use of SSL, so don't use this unless all of your upsd hosts are ready for SSL and have their certificates in order. + When compiled with NSS support of SSL, can be overriden for host specified with a CERTHOST directive. *FORCESSL* '0 | 1':: When compiled with SSL, specify that a secured connection must be used to communicate with upsd. + If you don't use 'CERTVERIFY 1', then this will at least make sure that nobody can sniff your sessions without a large effort. Setting this will make upsmon drop connections if the remote upsd doesn't support SSL, so don't use it unless all of them have it running. + When compiled with NSS support of SSL, can be overriden for host specified with a CERTHOST directive. SEE ALSO -------- linkman:upsmon[8], linkman:upsd[8], linkman:nutupsdrv[8]. Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/solis.80000644000175000017500000000643412640476514012220 00000000000000'\" t .\" Title: solis .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "SOLIS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" solis \- Driver for Brazilian Microsol SOLIS UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the solis driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver has been tested with : .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Solis 1000 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Solis 1500 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Solis 2000 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Solis 3000 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Back\-UPS BZ1200\-BR .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Back\-UPS BZ2200BI\-BR .RE .sp All Solis models are sinusoidal on\-line\&. .sp In 2009, Schneider Electric acquired Microsol Technologies, and by 2012, the entire Microsol line of equipment was being sold under the APC brand\&. .SH "EXTRA ARGUMENTS" .sp This driver support the following extra optional settings in the \fBups.conf\fR(5)\&. .sp \fBbattext = n\fR \- (default = 0, no extra battery, where n = Ampere*hour ) .sp \fBprgshut = 1\fR \- (default = 0, no programable shutdown ) .SH "COMMANDS" .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.return \- Shut down in \&.3 minutes and restart in \&.3 minutes .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.stayoff \- Shut down in \&.3 minutes and do not return .RE .SH "ISSUES" .sp The APC version of the Microsol protocol is slightly incompatible with the \fBsolis\fR driver\&. As of version 0\&.62 of the \fBsolis\fR driver, the driver will connect to the UPS, but some values are read incorrectly\&. .SH "AUTHOR" .sp Silvino B\&. Magalhães .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_new_device.txt0000644000175000017500000000150012640443572015365 00000000000000NUTSCAN_NEW_DEVICE(3) ====================== NAME ---- nutscan_new_device - Create a new nutscan_device_t structure. SYNOPSIS -------- #include nutscan_device_t * nutscan_new_device(); DESCRIPTION ----------- The *nutscan_new_device()* function allocates a new `nutscan_device_type_t` structure. RETURN VALUE ------------ The *nutscan_new_device()* function returns the newly allocated `nutscan_device_type_t` structure SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3] linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3] linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3] linkman:nutscan_add_device_to_device[3] nut-2.7.4/docs/man/nutclient_tcp_set_timeout.30000644000175000017500000000002712665610645016350 00000000000000.so libnutclient_tcp.3 nut-2.7.4/docs/man/nutscan_init.30000644000175000017500000000520012665610657013553 00000000000000'\" t .\" Title: nutscan_init .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_INIT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_init \- Initialize the nutscan library\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf void nutscan_init(); .fi .SH "DESCRIPTION" .sp The \fBnutscan_init()\fR function must be called at least once before using any other function of the nutscan library\&. .sp It updates the following global variables which can be used by nutscan library user to know which scan methods are available at run\-time\&. This depends on the libraries installed on the system: .sp .if n \{\ .RS 4 .\} .nf nutscan_avail_avahi = 1 : AVAHI scan is available nutscan_avail_ipmi = 1 : IPMI scan is available nutscan_avail_nut = 1 : Old NUT method is available nutscan_avail_snmp = 1 : SNMP method is available nutscan_avail_usb = 1 : USB method is available nutscan_avail_xml_http = 1 : XML HTTP method is available .fi .if n \{\ .RE .\} .sp Note that if a method is reported as unavailable by those variables, the call to the corresponding nutscan_scan_* function will always return NULL\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3) nut-2.7.4/docs/man/upscli_upserror.txt0000644000175000017500000000170112640443572014765 00000000000000UPSCLI_UPSERROR(3) ================== NAME ---- upscli_upserror - Get current error number for connection SYNOPSIS -------- #include int upscli_upserror(UPSCONN_t *ups); DESCRIPTION ----------- The *upscli_upserror*() function takes the pointer 'ups' to a `UPSCONN_t` state structure and returns the value of the internal error number, if any. This is typically used to check for certain error values like `UPSCLI_ERR_UNKCOMMAND`. That specific error can be used for detecting older versions of linkman:upsd[8] which might not support a given command. Some error messages have additional meanings, so you should use linkman:upscli_strerror[3] to obtain readable error messages. RETURN VALUE ------------ The *upscli_upserror*() function returns one of the `UPSCLI_ERR_*` values from `upsclient.h`, or 0 if no error has occurred. SEE ALSO -------- linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_strerror[3] nut-2.7.4/docs/man/upscode2.80000644000175000017500000001127612640476515012614 00000000000000'\" t .\" Title: upscode2 .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCODE2" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscode2 \- Driver for UPScode II compatible UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the upscode2 driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports UPS equipment which can be controlled via the UPScode II protocol\&. This is mainly Fiskars, Powerware equipment, but also some (probably OEM\(cqed) products from Compaq\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBmanufacturer\fR=\fIvalue\fR .RS 4 Autodetection of this parameter is not possible yet (and it probably never will be)\&. Therefore, this user\-defined string accepts any name\&. The default is \fIunknown\fR\&. .RE .PP \fBinput_timeout\fR=\fIvalue\fR .RS 4 The timeout waiting for a response from the UPS\&. Some UPS models have shown to be rather slow, resulting in frequent messages about empty responses from the UPS\&. If you see this, try increasing this value\&. .RE .PP \fBoutput_pace\fR=\fIvalue\fR .RS 4 Delay between characters sent to the UPS\&. This was added for completeness with the above parameter\&. It has not shown to be needed yet\&. .RE .PP \fBbaudrate\fR=\fIvalue\fR .RS 4 The default baudrate is 1200, which is the standard for the UPScode II protocol\&. .RE .PP \fBfull_update_timer\fR=\fIvalue\fR .RS 4 Number of seconds between collection of normative values\&. .RE .PP \fBuse_crlf\fR .RS 4 Flag to set if commands towards to UPS need to be terminated with CR\-LF, and not just CR\&. .RE .PP \fBuse_pre_lf\fR .RS 4 Flag to set if commands towards to UPS need to be introduced with an LF\&. A Compaq T1500h is known to need this\&. .RE .SH "COMMANDS" .sp The driver supports the following commands for those UPSen that support them\&. The available commands are autodetected during initialization, so you should check availability with \fIupscmd \-l\fR\&. .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.panel\&.start \- Start UPS self test .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} test\&.battery\&.start \- Start battery self test .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} beeper\&.enable \- Enable UPS beeper .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} beeper\&.disable \- Disable UPS beeper .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.return \- Shut down in 1 second and wait for power to return .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.stayoff \- Shut down in 1 seconds .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.reboot \- Shut down in 1 seconds and reboot after 1 minute .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.reboot\&.graceful \- Shut down in 20 seconds and reboot after 1 minute .RE .SH "NOTES" .sp The Powerware UPS models that this driver has been tested against until now have not returned a value for \fIbattery\&.charge\fR\&. Therefore, the driver will guestimate a value based on the nominal battery min/max and the current battery voltage\&. .SH "AUTHOR" .sp HÃ¥vard Lygre , Niels Baggesen .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/solis.txt0000644000175000017500000000274412640473702012664 00000000000000SOLIS(8) ======== NAME ---- solis - Driver for Brazilian Microsol SOLIS UPS equipment NOTE ---- This man page only documents the hardware-specific features of the solis driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver has been tested with : * Solis 1000 VA * Solis 1500 VA * Solis 2000 VA * Solis 3000 VA * Back-UPS BZ1200-BR * Back-UPS BZ2200BI-BR All Solis models are sinusoidal on-line. In 2009, Schneider Electric acquired Microsol Technologies, and by 2012, the entire Microsol line of equipment was being sold under the APC brand. EXTRA ARGUMENTS --------------- This driver support the following extra optional settings in the linkman:ups.conf[5]. *battext = n* - (default = 0, no extra battery, where n = Ampere*hour ) *prgshut = 1* - (default = 0, no programable shutdown ) COMMANDS -------- * shutdown.return - Shut down in .3 minutes and restart in .3 minutes * shutdown.stayoff - Shut down in .3 minutes and do not return ISSUES ------ The APC version of the Microsol protocol is slightly incompatible with the *solis* driver. As of version 0.62 of the *solis* driver, the driver will connect to the UPS, but some values are read incorrectly. AUTHOR ------ Silvino B. Magalhães SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nut.conf.50000644000175000017500000001211112640476461012604 00000000000000'\" t .\" Title: nut.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUT\&.CONF" "5" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nut.conf \- UPS definitions for Network UPS Tools .SH "DESCRIPTION" .sp This file attempts to standardize the various files being found in different installations, like /etc/default/nut on Debian based systems and /etc/sysconfig/ups on RedHat based systems\&. .sp Distribution\(cqs init script should source this file in order to determine which components have to be started\&. .sp Blank lines are ignored\&. Lines with a hash (\fI#\fR) character at the 1st position of the line are ignored, too\&. They can be used to add comments\&. .SH "IMPORTANT NOTE" .sp This file is intended to be sourced by shell scripts\&. You MUST NOT use spaces around the equal sign! .sp Refer to the EXAMPLE section for illustrations\&. .SH "DIRECTIVES" .PP \fBMODE\fR .RS 4 Required\&. Recognized values are \fInone\fR, \fIstandalone\fR, \fInetserver\fR and \fInetclient\fR\&. Defaults to \fInone\fR\&. .PP none .RS 4 Indicates that NUT should not get started automatically, possibly because it is not configured or that an Integrated Power Management or some external system, is used to startup the NUT components\&. .RE .PP standalone .RS 4 Addresses a local only configuration, with 1 UPS protecting the local system\&. This implies to start the 3 NUT layers (driver, upsd and upsmon), with the related configuration files\&. This mode can also address UPS redundancy\&. .RE .PP netserver .RS 4 Like the standalone configuration, but also possibly need one or more specific LISTEN directive(s) in upsd\&.conf\&. Since this MODE is open to the network, a special care should be applied to security concerns\&. .RE .PP netclient .RS 4 When only upsmon is required, possibly because there are other hosts that are more closely attached to the UPS, the MODE should be set to netclient\&. .RE .RE .PP \fBUPSD_OPTIONS\fR .RS 4 Optional\&. Set upsd specific options\&. See \fBupsd\fR(8) for more details\&. It is ignored when \fIMODE\fR above indicates that no upsd should be running\&. .RE .PP \fBUPSMON_OPTIONS\fR .RS 4 Optional\&. Set upsmon specific options\&. See \fBupsmon\fR(8) for more details\&. It is ignored when \fIMODE\fR above indicates that no upsmon should be running\&. .RE .PP \fBPOWEROFF_WAIT\fR .RS 4 Optional\&. At the end of an emergency system halt, the upsmon master will signal the UPS to switch off\&. This may fail for a number of reasons\&. Most notably is the case that mains power returns during the shutdown process\&. See the section "Power races" in /usr/share/doc/nut/FAQ\&.txt\&.gz\&. The system will wait this long for the UPS to cut power, and then reboot\&. It should be long enough to exhaust the batteries, in case line power continues to be unavailable\&. On the other hand, it should not be so long that the system remains offline for an unreasonable amount of time if line power has returned\&. See sleep(1) for compatible time syntax\&. If you specify the time in seconds, use the "s" suffix\&. .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBWarning\fR .ps -1 .br .sp this workaround might be dangerous under some circumstances\&. Please read http://bugs\&.debian\&.org/358696 for more details\&. .sp .5v .RE .SH "EXAMPLE" .sp .if n \{\ .RS 4 .\} .nf # /etc/nut/nut\&.conf\&. See nut\&.conf(5) .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf MODE=none .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf UPSD_OPTIONS="" .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf UPSMON_OPTIONS="" .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf # POWEROFF_WAIT=15m .fi .if n \{\ .RE .\} .SH "INTEGRATION" .sp An init script, such as /etc/init\&.d/nut, is expected to source this file in order to determine which component(s) has to be started\&. .SH "SEE ALSO" .sp \fBups.conf\fR(5), \fBupsd.conf\fR(5), \fBupsd.users\fR(5), \fBupsmon.conf\fR(5) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_display_ups_conf.30000644000175000017500000000410012665610653016143 00000000000000'\" t .\" Title: nutscan_display_ups_conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_DISPLAY_UPS_" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_display_ups_conf \- Display the specified `nutscan_device_t` structure on stdout\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf void nutscan_display_ups_conf(nutscan_device_t * device); .fi .SH "DESCRIPTION" .sp The \fBnutscan_display_ups_conf()\fR function displays all NUT devices in \fIdevice\fR to stdout\&. It displays them in a way that it can be directly copied into the ups\&.conf file\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_cidr_to_ip\fR(3) nut-2.7.4/docs/man/libnutclient_tcp.txt0000644000175000017500000000344412640444140015065 00000000000000LIBNUTCLIENT_TCP(3) =================== NAME ---- libnutclient_tcp, nutclient_tcp_create_client, nutclient_tcp_is_connected, nutclient_tcp_disconnect, nutclient_tcp_reconnect, nutclient_tcp_set_timeout, nutclient_tcp_get_timeout - TCP protocol related function for Network UPS Tools high-level client access library SYNOPSIS -------- #include typedef NUTCLIENT_t NUTCLIENT_TCP_t; NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port); int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client); void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client); int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); DESCRIPTION ----------- These functions allow to manage connections to linkman:upsd[8] using NUT TCP protocol. The *nutclient_tcp_create_client()* function create the 'NUTCLIENT_TCP_t' context and intend to connect to upsd at 'host' and 'port'. The context must be freed by 'nutclient_destroy()' 'host' can be a sever name or a valid IPv4 or IPv6 adress like "localhost", "127.0.0.1" or "::1". 'port' is a valid TCP port, genrally 3493. The *nutclient_tcp_is_connected()* function test if the connection is valid. The *nutclient_tcp_disconnect()* function force to disconnect the specified connection. The *nutclient_tcp_reconnect()* function force to reconnect a connection, disconnecting it if needed. The *nutclient_tcp_set_timeout()* function set the timeout duration for I/O operations. The *nutclient_tcp_get_timeout()* function retrieve the timeout duration for I/O operations. 'timeout' values are specified in seconds, negatives values for blocking. SEE ALSO -------- linkman:libnutclient[3] linkman:libnutclient_general[3] nut-2.7.4/docs/man/nutclient_tcp_reconnect.30000644000175000017500000000002712665610645015767 00000000000000.so libnutclient_tcp.3 nut-2.7.4/docs/man/upsimage.cgi.80000644000175000017500000000470612665610627013444 00000000000000'\" t .\" Title: upsimage.cgi .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSIMAGE\&.CGI" "8" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsimage.cgi \- Image\-generating helper for upsstats\&.cgi .SH "SYNOPSIS" .sp \fBupsimage\&.cgi\fR .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp As a CGI program, this should be invoked through your web server\&. If you run it from the command line, it will either complain about unauthorized access or spew a PNG at you\&. .sp .5v .RE .SH "DESCRIPTION" .sp \fBupsimage\&.cgi\fR generates the graphical bars that make up the right side of the page generated by \fBupsstats.cgi\fR(8)\&. These represent the current battery charge, utility voltage, and UPS load where available\&. .sp The images are in PNG format, and are created by linking to Boutell\(cqs excellent gd library\&. .SH "ACCESS CONTROL" .sp upsstats will only talk to \fBupsd\fR(8) servers that have been defined in your \fBhosts.conf\fR(5)\&. If it complains about "Access to that host is not authorized", check that file first\&. .SH "FILES" .sp \fBhosts.conf\fR(5) .SH "SEE ALSO" .sp \fBupsd\fR(8), \fBupsstats.cgi\fR(8) .SS "Internet resources:" .sp The gd home page: http://libgd\&.bitbucket\&.org .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upscmd.80000644000175000017500000001027112640476465012361 00000000000000'\" t .\" Title: upscmd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCMD" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscmd \- UPS administration program for instant commands .SH "SYNOPSIS" .sp \fBupscmd\fR \-h .sp \fBupscmd\fR \-l \fIups\fR .sp \fBupscmd\fR [\-u \fIusername\fR] [\-p \fIpassword\fR] \fIups\fR \fIcommand\fR .SH "DESCRIPTION" .sp \fBupscmd\fR allows you to invoke "instant commands" in your UPS hardware\&. Not all hardware supports this, so check the list with \-l to see if anything will work on your equipment\&. .sp On hardware that supports it, you can use this program to start and stop battery tests, invoke a front panel test (beep!), turn the load on or off, and more\&. .SH "OPTIONS" .PP \fB\-h\fR .RS 4 Display the help message\&. .RE .PP \fB\-l\fR \fIups\fR .RS 4 Show the list of supported instant commands on that UPS\&. Some hardware may not support any of them\&. .RE .PP \fB\-u\fR \fIusername\fR .RS 4 Set the username for the connection to the server\&. This is optional, and you will be prompted for this when invoking a command if \-u is not used\&. .RE .PP \fB\-p\fR \fIpassword\fR .RS 4 Set the password to authenticate to the server\&. This is also optional like \-u, and you will be prompted for it if necessary\&. .RE .PP \fIups\fR .RS 4 Connect to this UPS\&. The format is upsname[@hostname[:port]]\&. The default hostname is "localhost"\&. .RE .SH "UNATTENDED MODE" .sp If you run this program inside a shell script or similar to invoke a command, you will need to specify all of the information on the command line\&. This means using \-u and \-p\&. Otherwise it will put up a prompt and your program will hang\&. .sp This is not necessary when displaying the list, as the username and password are not required for read\-only mode\&. .sp Moreover, if you run this program inside a shell script or similar, you should only consider using output from stdout, not stderr\&. .SH "DANGEROUS COMMANDS" .sp Some drivers like \fBapcsmart\fR(8) have built\-in paranoia for the dangerous commands like load\&.off\&. To make them actually turn off the load, you will have to send the command twice within a short window\&. That is, you will have to send it once, then send it again after 3 seconds elapse but before 15 seconds pass\&. .sp This paranoia is entirely defined within the driver\&. upsd and upscmd have no control over the timing\&. .SH "DIAGNOSTICS" .sp upscmd won\(cqt work unless you provide a valid username and password\&. If you get "access denied" errors, make sure that your \fBupsd.users\fR(5) has an entry for you, and that the username you are using has permissions to SET variables\&. .SH "BUGS" .sp There is currently no way to tell the user when the driver requires confirmation to invoke a command such as load\&.off\&. Similarly, there is not yet a way to tell the user if a command succeeds or fails\&. .sp This is on the list of things to fix in the future, so don\(cqt despair\&. It involves magic cookies\&. .SH "SEE ALSO" .sp \fBupsd\fR(8), \fBupsrw\fR(8) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_set_device_variable_value.30000644000175000017500000000003512665610646020314 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/mge-utalk.80000644000175000017500000000740712640476506012757 00000000000000'\" t .\" Title: mge-utalk .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "MGE\-UTALK" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" mge-utalk \- Driver for MGE UPS SYSTEMS UTalk protocol equipment .SH "SYNOPSIS" .sp \fBmge\-utalk\fR \-h .sp \fBmge\-utalk\fR \-a \fIUPS_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the mge\-utalk driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp mge\-utalk supports the following legacy units, using the MGE UTalk protocol: .sp .if n \{\ .RS 4 .\} .nf Pulsar ESV+, Pulsar ES+, Pulsar EL, Pulsar EX, Pulsar EXtreme, Comet EXtreme, Comet (Utalk Serial Card, ref 66060), Galaxy (Utalk Serial Card, ref 66060)\&. .fi .if n \{\ .RE .\} .sp This driver also support some newer models with backward UTalk compatibility, such as Pulsar Evolution and Pulsar EXtreme C\&. As these models also support the SHUT protocol, prefer mge\-shut for serial communication, or use the USB port, if available, with the usbhid\-ups driver\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBlowbatt\fR=\fInum\fR .RS 4 Set the low battery warning threshold at which shutdown is initiated by \fBupsmon\fR(8)\&. .sp The factory default value is 30 (in percent), and can be settable depending on the exact model\&. .RE .PP \fBoffdelay\fR=\fInum\fR .RS 4 Set the timer before the UPS is turned off after the kill power command is sent (via the \fB\-k\fR switch)\&. .sp The default value is 20 (in seconds)\&. .RE .PP \fBondelay\fR=\fInum\fR .RS 4 Set the delay before the UPS is turned on, after the power returns\&. .sp The default value is 1 (in minutes)\&. .RE .PP \fBoldmac\fR .RS 4 Set this flag if you are running Linux on an Oldworld Macintosh box (all beige Apple Macintosh)\&. This might also be needed for other OSs (like *BSD) running on PowerMac\&. .RE .SH "KNOWN ISSUES" .SS "Repetitive timeout and staleness" .sp Older models, such as ES/ESV ones, might report repetitive "data stale" errors\&. This is due to the fact that these models don\(cqt support too much polling\&. To solve this problem, add "pollinterval=20" in ups\&.conf, and change the value of MAXAGE to 25 in upsd\&.conf, and DEADTIME to 25 in upsmon\&.conf\&. .SH "AUTHOR" .sp Hans Ekkehard Plesser, Arnaud Quette, Martin Loyer, Patrick Agrain, Nicholas Reilly, Dave Abbott, Marek Kralewski .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_tcp_is_connected.30000644000175000017500000000002712665610645016444 00000000000000.so libnutclient_tcp.3 nut-2.7.4/docs/man/bcmxcp_usb.80000644000175000017500000001010412640476517013204 00000000000000'\" t .\" Title: bcmxcp_usb .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BCMXCP_USB" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" bcmxcp_usb \- Experimental driver for UPSes supporting the BCM/XCP protocol over USB .SH "NOTE" .sp This man page only documents the hardware\-specific features of the bcmxcp_usb driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. This driver is a variant of the serial driver bcmxcp and uses the same core code\&. .SH "SUPPORTED HARDWARE" .sp This driver should recognize all BCM/XCP\-compatible UPSes that are connected via USB\&. It has been developed and tested on Powerware PW3501 hardware\&. It also has been tested on PW5110 hardware\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5)\&. .PP \fBshutdown_delay=\fR\fIdelay\fR .RS 4 The number of seconds that the UPS should wait between receiving the shutdown command and actually shutting off\&. .RE .SH "DEFAULT VALUES FOR THE EXTRA ARGUMENTS" .sp \fBshutdown_delay =\fR\fI120\fR .SH "INSTANT COMMANDS" .sp This driver supports the following Instant Commands: .PP \fBshutdown\&.return\fR .RS 4 Turn off the load and return when power is back\&. .RE .PP \fBshutdown\&.stayoff\fR .RS 4 Turn off the load and remain off\&. .RE .PP \fBtest\&.battery\&.start\fR .RS 4 Start a battery test\&. .RE .SH "TODO LIST" .PP \fBReport UPS alarm status\fR .RS 4 BCM/XCP supports reporting a wide range of UPS alarm conditions\&. .RE .PP \fBReport UPS statistics informations\fR .RS 4 BCM/XCP supports reporting of UPS statistics data\&. .RE .SH "EXPERIMENTAL DRIVER" .sp This driver has been tagged experimental, even if it has been reported to be stable\&. Thus it is not suitable for production systems and it is not built by default\&. This is mainly due to the fact that it is a new driver\&. .SH "INSTALLATION" .sp This driver is not built by default\&. You can build it by using "configure \-\-with\-usb=yes"\&. Note that it will also install other USB drivers\&. .sp You also need to install manually the hotplug files (libhidups and libhid\&.usermap), generally in etc/hotplug/usb/, to address the permission settings problem\&. Lastly note that the libhidups file must have execution flag set (ie using chmod +x \&...)\&. .SH "IMPLEMENTATION" .sp bcmxcp_usb only supports 1 UPS at this time\&. You can put the "auto" value for port in ups\&.conf, i\&.e\&.: .sp .if n \{\ .RS 4 .\} .nf [pw3105] driver = bcmxcp_usb port = auto .fi .if n \{\ .RE .\} .SH "KNOWN ISSUES AND BUGS" .SS ""Got EPERM: Operation not permitted upon driver startup"" .sp You have forgotten to install the hotplug files, as explained in the INSTALLATION section above\&. Don\(cqt forget to restart hotplug so that it applies these changes\&. .SH "AUTHOR" .sp Tore Ørpetveit , Wolfgang Ocker .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/belkin.80000644000175000017500000000473012640476474012335 00000000000000'\" t .\" Title: belkin .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BELKIN" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" belkin \- Driver for Belkin serial UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the belkin driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp The \fBbelkin\fR driver is known to support the Regulator Pro 525 (F6C525\-SER)\&. Other similar models such as the 425 and 625 should also work\&. .sp The Trust UPS and older Belkin units are not supported\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "BUGS" .sp There are dragons lurking within the protocol to this UPS\&. I have one that essentially behaves like a glorified power strip due to some invasive probing on my part\&. Don\(cqt mess with it directly\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp the driver doesn\(cqt go anywhere near these character sequences, so it won\(cqt zap your UPS\&. I only mention this here as yet another reminder of the perils of closed hardware\&. .sp .5v .RE .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/etapro.txt0000644000175000017500000000127112640443572013021 00000000000000ETAPRO(8) ========= NAME ---- etapro - Driver for ETA UPS equipment NOTE ---- This man page only documents the hardware-specific features of the etapro driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports ETA UPS equipment with the "PRO" option for smart mode. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHOR ------ Marek Michalkiewicz SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscmd.txt0000644000175000017500000000547212640444140013021 00000000000000UPSCMD(8) ========= NAME ---- upscmd - UPS administration program for instant commands SYNOPSIS -------- *upscmd* -h *upscmd* -l 'ups' *upscmd* [-u 'username'] [-p 'password'] 'ups' 'command' DESCRIPTION ----------- *upscmd* allows you to invoke "instant commands" in your UPS hardware. Not all hardware supports this, so check the list with -l to see if anything will work on your equipment. On hardware that supports it, you can use this program to start and stop battery tests, invoke a front panel test (beep!), turn the load on or off, and more. OPTIONS ------- *-h*:: Display the help message. *-l* 'ups':: Show the list of supported instant commands on that UPS. Some hardware may not support any of them. *-u* 'username':: Set the username for the connection to the server. This is optional, and you will be prompted for this when invoking a command if -u is not used. *-p* 'password':: Set the password to authenticate to the server. This is also optional like -u, and you will be prompted for it if necessary. 'ups':: Connect to this UPS. The format is `upsname[@hostname[:port]]`. The default hostname is "localhost". UNATTENDED MODE --------------- If you run this program inside a shell script or similar to invoke a command, you will need to specify all of the information on the command line. This means using -u and -p. Otherwise it will put up a prompt and your program will hang. This is not necessary when displaying the list, as the username and password are not required for read-only mode. Moreover, if you run this program inside a shell script or similar, you should only consider using output from stdout, not stderr. DANGEROUS COMMANDS ------------------ Some drivers like linkman:apcsmart[8] have built-in paranoia for the dangerous commands like `load.off`. To make them actually turn off the load, you will have to send the command twice within a short window. That is, you will have to send it once, then send it again after 3 seconds elapse but before 15 seconds pass. This paranoia is entirely defined within the driver. upsd and upscmd have no control over the timing. DIAGNOSTICS ----------- upscmd won't work unless you provide a valid username and password. If you get "access denied" errors, make sure that your linkman:upsd.users[5] has an entry for you, and that the username you are using has permissions to SET variables. BUGS ---- There is currently no way to tell the user when the driver requires confirmation to invoke a command such as `load.off`. Similarly, there is not yet a way to tell the user if a command succeeds or fails. This is on the list of things to fix in the future, so don't despair. It involves magic cookies. SEE ALSO -------- linkman:upsd[8], linkman:upsrw[8] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/upscli_sendline.30000644000175000017500000000404212665610637014236 00000000000000'\" t .\" Title: upscli_sendline .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_SENDLINE" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_sendline \- send a single command to a UPS .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); .fi .SH "DESCRIPTION" .sp The \fBupscli_sendline()\fR function takes the pointer \fIups\fR to a UPSCONN_t state structure and transmits a buffer \fIbuf\fR of size \fIbuflen\fR to the server\&. .sp The data in \fIbuf\fR must be a fully formatted protocol command as no parsing of the buffer occurs within this function\&. .SH "RETURN VALUE" .sp The \fBupscli_sendline()\fR function returns 0 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/upscli_splitaddr.txt0000644000175000017500000000214212640443572015072 00000000000000UPSCLI_SPLITADDR(3) =================== NAME ---- upscli_splitaddr - split a listening address into its components SYNOPSIS -------- #include int upscli_splitaddr(const char *buf, char **hostname, int *port) DESCRIPTION ----------- The *upscli_splitaddr()* function takes a pointer to the raw UPS definition 'buf' and returns pointers to dynamically allocated memory in 'upsname' and 'hostname'. It also copies the port number into 'port'. FORMATTING ---------- A listening address definition is specified according to this format: [:] Definitions without an explicit port value receive the default value of 3493. MEMORY USAGE ------------ You must *free*(3) the pointer 'hostname' when you are done with it to avoid memory leaks. RETURN VALUE ------------ The *upscli_splitaddr()* function returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_splitname[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/microdowell.txt0000644000175000017500000000137412640443572014053 00000000000000MICRODOWELL(8) ============== NAME ---- microdowell - Driver for Microdowell Enterprise UPS series NOTE ---- This man page only documents the hardware-specific features of the Microdowell driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver was developed for the Enterprise Nxx and Bxx models. Other Microdowell models may work, too. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHOR ------ Elio Corbolante SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_tcp_disconnect.30000644000175000017500000000002712665610645016140 00000000000000.so libnutclient_tcp.3 nut-2.7.4/docs/man/upscli_init.30000644000175000017500000000525612665610634013405 00000000000000'\" t .\" Title: upscli_init .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_INIT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_init \- Initialize upsclient module specifying security properties\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_init(int certverify, const char *certpath, const char *certname, const char *certpasswd); .fi .SH "DESCRIPTION" .sp The \fBupscli_init()\fR function initialize upsclient module and set many SSL\-related properties: \fIcertverify\fR to 1 makes certificate verification required for all SSL connections and \fIcertpath\fR is the location of certificate database\&. .sp If compiled with OpenSSL, certpath refers to a \&.pem file containing certificates and if compiled with NSS, certpath refers to a directory containing database files\&. .sp If compiled with NSS and using SSL, you can specify \fIcertname\fR the name of the certificate to send to upsd and \fIcertpasswd\fR the password used to decrypt certificate private key\&. .sp You can call \fBupscli_add_host_cert\fR(3) to register specific host security policy before initialize connections to them\&. .sp You must call \fBupscli_cleanup\fR(3) when exiting application\&. .SH "RETURN VALUE" .sp The \fBupscli_init()\fR function returns 1 on success, or \-1 if an error occurs\&. .SH "SEE ALSO" .sp \fBupscli_add_host_cert\fR(3), \fBupscli_cleanup\fR(3), \fBupscli_disconnect\fR(3), \fBupscli_fd\fR(3), \fBupscli_splitaddr\fR(3), \fBupscli_splitname\fR(3), \fBupscli_ssl\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutclient_has_device_variable.30000644000175000017500000000003512665610646017100 00000000000000.so libnutclient_variables.3 nut-2.7.4/docs/man/libnutclient_commands.30000644000175000017500000000541112665610643015431 00000000000000'\" t .\" Title: libnutclient_commands .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT_COMMAND" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient_commands, nutclient_get_device_commands, nutclient_has_device_command, nutclient_get_device_command_description, nutclient_execute_device_command \- Instant command related functions in Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf typedef void* NUTCLIENT_t; .fi .sp .nf strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev); int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd); void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); .fi .SH "DESCRIPTION" .sp These functions allow to manage instant commands of devices\&. .sp The \fBnutclient_get_device_commands()\fR function retrieve the list of command names for a device\&. The returned strarr must be freed by \fIstrarr_free\fR\&. .sp The \fBnutclient_has_device_command\fR function test if the specified command is supported by the device\&. Return 1 is supported and 0 if not\&. .sp The \fBnutclient_get_device_command_description\fR function retrieve the command description, if any\&. The resturned string must be freed\&. .sp The \fBnutclient_execute_device_command\fR intend to execute the instant command\&. .sp \fIdev\fR is the device name\&. .sp \fIcmd\fR is the instant command name\&. .SH "SEE ALSO" .sp \fBlibnutclient\fR(3) \fBlibnutclient_devices\fR(3) \fBlibnutclient_general\fR(3) nut-2.7.4/docs/man/upsd.80000644000175000017500000001533112640476466012044 00000000000000'\" t .\" Title: upsd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSD" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsd \- UPS information server .SH "SYNOPSIS" .sp \fBupsd\fR \-h .sp \fBupsd\fR [\fIOPTIONS\fR] .SH "DESCRIPTION" .sp \fBupsd\fR is responsible for serving the data from the drivers to the clients\&. It connects to each driver and maintains a local cache of the current state\&. Queries from the clients are served from this cache, so delays are minimal\&. .sp It also conveys administrative messages from the clients back to the drivers, such as starting tests, or setting values\&. .sp Communication between \fBupsd\fR and clients is handled on a TCP port\&. Configuration details for this port are described in \fBupsd.conf\fR(8)\&. .sp This program is essential, and must be running at all times to actually make any use out of the drivers and clients\&. .sp Controls in the configuration files allow you to limit access to the server, but you should also use a firewall for extra protection\&. Client processes such as \fBupsmon\fR(8) trust \fBupsd\fR for status information about the UPS hardware, so keep it secure\&. .SH "OPTIONS" .PP \fB\-c\fR \fIcommand\fR .RS 4 Send \fIcommand\fR to the background process as a signal\&. Valid commands are: .PP \fBreload\fR .RS 4 reread configuration files .RE .PP \fBstop\fR .RS 4 stop process and exit .RE .RE .PP \fB\-D\fR .RS 4 Raise the debug level\&. Use this multiple times for additional details\&. .RE .PP \fB\-h\fR .RS 4 Display the help text\&. .RE .PP \fB\-r\fR \fIdirectory\fR .RS 4 upsd will \fBchroot\fR(2) to \fIdirectory\fR shortly after startup and before parsing any configuration files with this option set\&. You can use this to create a "jail" for greater security\&. .sp You must coordinate this with your drivers, as upsd must be able to find the state path within \fIdirectory\fR\&. See \fBupsdrvctl\fR(8) and \fBnutupsdrv\fR(8)\&. .RE .PP \fB\-u\fR \fIuser\fR .RS 4 Switch to user \fIuser\fR after startup if started as root\&. This overrides whatever you may have compiled in with configure \-\-with\-user\&. .RE .PP \fB\-V\fR .RS 4 Display the version of the program\&. .RE .SH "RELOADING" .sp upsd can reload its configuration files without shutting down the process if you send it a SIGHUP or start it again with \-c reload\&. This only works if the background process is able to read those files\&. .sp If you think that upsd can\(cqt reload, check your syslogs for error messages\&. If it\(cqs complaining about not being able to read the files, then you need to adjust your system to make it possible\&. Either change the permissions on the files, or run upsd as another user that will be able to read them\&. .sp DO NOT make your upsd\&.conf or upsd\&.users world\-readable, as those files hold important authentication information\&. In the wrong hands, it could be used by some evil person to spoof your master upsmon and command your systems to shut down\&. .SH "DIAGNOSTICS" .sp upsd expects the drivers to either update their status regularly or at least answer periodic queries, called pings\&. If a driver doesn\(cqt answer, upsd will declare it "stale" and no more information will be provided to the clients\&. .sp If upsd complains about staleness when you start it, then either your driver or configuration files are probably broken\&. Be sure that the driver is actually running, and that the UPS definition in \fBups.conf\fR(5) is correct\&. Also make sure that you start your driver(s) before starting upsd\&. .sp Data can also be marked stale if the driver can no longer communicate with the UPS\&. In this case, the driver should also provide diagnostic information in the syslog\&. If this happens, check the serial or USB cabling, or inspect the network path in the case of a SNMP UPS\&. .SH "ACCESS CONTROL" .sp If the server is build with tcp\-wrappers support enabled, it will check if the NUT username is allowed to connect from the client address through the /etc/hosts\&.allow and /etc/hosts\&.deny files\&. Note that this will only be done for commands that require to be logged into the server\&. Further details are described in \fBhosts_access\fR(5)\&. .SH "FILES" .sp The general upsd configuration file is \fBupsd.conf\fR(5)\&. The administrative functions like SET and INSTCMD for users are defined and controlled in \fBupsd.users\fR(5)\&. UPS definitions are found in \fBups.conf\fR(5)\&. .SH "ENVIRONMENT VARIABLES" .sp \fBNUT_CONFPATH\fR is the path name of the directory that contains upsd\&.conf and other configuration files\&. If this variable is not set, \fBupsd\fR uses a built\-in default, which is often /usr/local/ups/etc\&. .sp \fBNUT_STATEPATH\fR is the path name of the directory in which \fBupsd\fR keeps state information\&. If this variable is not set, \fBupsd\fR uses a built\-in default, which is often /var/state/ups\&. The \fBSTATEPATH\fR directive in \fBupsd.conf\fR(5) overrides this variable\&. .SH "SEE ALSO" .SS "Clients:" .sp \fBupsc\fR(8), \fBupscmd\fR(8), \fBupsrw\fR(8), \fBupslog\fR(8), \fBupsmon\fR(8) .SS "CGI programs:" .sp \fBupsset.cgi\fR(8), \fBupsstats.cgi\fR(8), \fBupsimage.cgi\fR(8) .SS "Drivers:" .sp \fBnutupsdrv\fR(8), \fBapcsmart\fR(8), \fBbelkin\fR(8), \fBbelkinunv\fR(8), \fBbestuferrups\fR(8), \fBbestups\fR(8), \fBcyberpower\fR(8), \fBenergizerups\fR(8), \fBetapro\fR(8), \fBeverups\fR(8), \fBgenericups\fR(8), \fBisbmex\fR(8), \fBliebert\fR(8), \fBmasterguard\fR(8), \fBmge-shut\fR(8), \fBmge-utalk\fR(8), \fBoneac\fR(8), \fBpowercom\fR(8), \fBsafenet\fR(8), \fBsnmp-ups\fR(8), \fBtripplite\fR(8), \fBtripplitesu\fR(8), \fBvictronups\fR(8), .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/upsd.conf.txt0000644000175000017500000000714512640443572013434 00000000000000UPSD.CONF(5) ============ NAME ---- upsd.conf - Configuration for Network UPS Tools upsd DESCRIPTION ----------- upsd uses this file to control access to the server and set some other miscellaneous configuration values. This file contains details on access controls, so keep it secure. Ideally, only the upsd process should be able to read it. CONFIGURATION DIRECTIVES ------------------------ "MAXAGE 'seconds'":: upsd usually allows a driver to stop responding for up to 15 seconds before declaring the data "stale". If your driver takes a very long time to process updates but is otherwise operational, you can use MAXAGE to make upsd wait longer. + Most users should leave this at the default value. "STATEPATH 'path'":: Tell upsd to look for the driver state sockets in 'path' rather than the default that was compiled into the program. "LISTEN 'interface' 'port'":: Bind a listening port to the interface specified by its Internet address. This may be useful on hosts with multiple interfaces. You should not rely exclusively on this for security, as it can be subverted on many systems. + Listen on TCP port 'port' instead of the default value which was compiled into the code. This overrides any value you may have set with 'configure --with-port'. If you don't change it with configure or this value, upsd will listen on port 3493 for this interface. + Multiple LISTEN addresses may be specified. The default is to bind to 127.0.0.1 if no LISTEN addresses are specified (and ::1 if IPv6 support is compiled in). LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 + This parameter will only be read at startup. You'll need to restart (rather than reload) upsd to apply any changes made here. "MAXCONN 'connections'":: This defaults to maximum number allowed on your system. Each UPS, each LISTEN address and each client count as one connection. If the server runs out of connections, it will no longer accept new incoming client connections. Only set this if you know exactly what you're doing. "CERTFILE 'certificate file'":: When compiled with SSL support with OpenSSL backend, you can enter the certificate file here. The certificates must be in PEM format and must be sorted starting with the subject's certificate (server certificate), followed by intermediate CA certificates (if applicable_ and the highest level (root) CA. It should end with the server key. See 'docs/security.txt' or the Security chapter of NUT user manual for more information on the SSL support in NUT. "CERTPATH 'certificate database'":: When compiled with SSL support with NSS backend, you can enter the certificate path here. Certificates are stored in a dedicated database (splitted in 3 files). Specify the path of the database directory. "CERTIDENT 'certificate name' 'database password'":: When compiled with SSL support with NSS backend, you can specify the certificate name to retrieve from database to authenticate itself and the password required to access certificate related private key. "CERTREQUEST 'certificate request level'":: When compiled with SSL support with NSS backend and client certificate validation (disabled by default, see 'docs/security.txt'), you can specify if upsd requests or requires client's' certificates. Possible values are : - '0' to not request to clients to provide any certificate - '1' to require to all clients a certificate - '2' to require to all clients a valid certificate SEE ALSO -------- linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsd.users[5] INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/tripplite_usb.txt0000644000175000017500000001374612640473702014424 00000000000000TRIPPLITE_USB(8) ================ NAME ---- tripplite_usb - Driver for older Tripp Lite USB UPSes (not PDC HID) SYNOPSIS -------- *tripplite_usb* -h *tripplite_usb* -a 'UPS_NAME' ['OPTIONS'] SUPPORTED HARDWARE ------------------ This driver should work with older Tripp Lite UPSes which are detected as USB HID-class devices, but are not true HID Power-Device Class devices. So far, the devices supported by tripplite_usb have product ID 0001, and the newer units (such as those with "LCD" in the model name) with product ID 2001 require the linkman:usbhid-ups[8] driver instead. Please report success or failure to the nut-upsuser mailing list. A key piece of information is the protocol number, returned in `ups.debug.0`. Also, be sure to turn on debugging ('-DDD') for more informative log messages. If your Tripp Lite UPS uses a serial port, you may wish to investigate the linkman:tripplite[8] or linkman:tripplite_su[8] driver. This driver has been tested with the following models: * INTERNETOFFICE700 * OMNIVS1000 * OMNIVS1500XL (some warnings) * SMART700USB * SMART1500RM2U * SMART2200RMXL2U * SMART3000RM2U If you have used Tripp Lite's PowerAlert software to connect to your UPS, there is a good chance that 'tripplite_usb' will work if it uses one of the following protocols: * Protocol 0004 * Protocol 1001 * Protocol 2001 * Protocol 3003 On the other hand, if the web page for your UPS on the Tripp-Lite website says "HID-compliant USB port also enables direct integration with built-in power management and auto-shutdown features of Windows and MAC OS X", then you should use the linkman:usbhid-ups[8] driver instead. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5] file (or with '-x' on the command line): *offdelay*:: This setting controls the delay between receiving the "kill" command ('-k') and actually cutting power to the computer. *battery_min*, *battery_max*:: These floating-point values correspond to the "empty" (10%) and "full" (100%) voltages of the battery. They are used for an approximation of the battery state-of-charge. The calculated battery.charge value will be clamped to the range of 10% through 100%, so the resting voltage of the charged battery can be used for *battery_max*, and the higher float charge voltage should not cause problems. *bus*:: This regular expression is used to match the USB bus (as seen in `/proc/bus/usb/devices` or lsusb(8); including leading zeroes). *product*:: A regular expression to match the product string for the UPS. This would be useful if you have two different Tripp Lite UPS models connected to the system, and you want to be sure that you shut them down in the correct order. NOTE: This regex is matched against the full USB product string as seen in lsusb(8). The `ups.model` in the linkman:upsc[1] output only lists the name after `TRIPP LITE`, so to match a SMART2200RMXL2U, you could use the regex `.*SMART2200.*`. *productid*:: The productid is a regular expression which matches the UPS PID as four hexadecimal digits. So far, the only devices that work with this driver have PID `0001`. *serial*:: It does not appear that these particular Tripp Lite UPSes use the `iSerial` descriptor field to return a serial number. However, in case your unit does, you may specify it here. For more information on regular expressions, see regex(7) RUNTIME VARIABLES ----------------- *ups.delay.shutdown*:: This variable is the same as the 'offdelay' setting, but it can be changed at runtime by linkman:upsrw[8]. *ups.id*:: Some SMARTPRO models feature an ID that can be set and retrieved. If your UPS supports this feature, this variable will be listed in the output of linkman:upsrw[8]. *outlet.1.switch*:: Some Tripp Lite units have a switchable outlet (usually outlet #1) which can be turned on and off by writing '1' or '0', respectively, to `outlet.1.switch` with linkman:upsrw[8]. If your unit has multiple switchable outlets, substitute the outlet number for '1' in the variable name. Be sure to test this first - there is no other way to be certain that the number used by the driver matches the label on the unit. KNOWN ISSUES AND BUGS --------------------- The driver was not developed with any official documentation from Tripp Lite, so certain events may confuse the driver. If you observe any strange behavior, please re-run the driver with `-DDD` to increase the verbosity. So far, the Tripp Lite UPSes do not seem to have a serial number or other globally unique identifier accessible through USB. Thus, when monitoring several Tripp Lite USB UPSes, you should use either the 'bus' or 'product' configuration options to uniquely specify which UPS a given driver instance should control. For instance, you can easily monitor an OMNIVS1000 and a SMART1500RM2U at the same time, since they have different USB Product ID strings. If you have two SMART1500RM2U units, you would have to find which USB bus number each unit is on (via lsusb(8)), which may result in ambiguities if the available USB ports are on the same bus. Some of the SMART*2U models have an ID number, but because this ID is not exposed as a USB string descriptor, there is no easy way to use this ID to distinguish between multiple UPS units on a single machine. The UPS would need to be claimed by the driver in order to read this ID. AUTHOR ------ Written by Charles Lepple, based on the linkman:tripplite[8] driver by Rickard E. (Rik) Faith and Nicholas Kain. Please do not email the authors directly - use the nut-upsdev mailing list. A Tripp Lite OMNIVS1000 was graciously donated to the NUT project by Bradley Feldman (http://www.bradleyloritheo.com) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ linkman:tripplite[8], linkman:tripplitesu[8], linkman:usbhid-ups[8] Other tools: ~~~~~~~~~~~~ regex(7), lsusb(8) INTERNET RESOURCES ------------------ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_scan_usb.txt0000644000175000017500000000176012640444140015052 00000000000000NUTSCAN_SCAN_USB(3) ==================== NAME ---- nutscan_scan_usb - Scan NUT compatible USB devices. SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_usb(); DESCRIPTION ----------- The *nutscan_scan_usb()* function try to detect NUT compatible USB devices. You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ The *nutscan_scan_usb()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] nut-2.7.4/docs/man/liebert.txt0000644000175000017500000000232612640443572013157 00000000000000LIEBERT(8) ========== NAME ---- liebert - Driver for Liebert contact-closure UPS equipment NOTE ---- This man page only documents the hardware-specific features of the liebert driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver supports some Liebert UPS equipment with a contact-closure interface. This includes the UPStation GXT2 with their contact-closure cable. The smart mode ("Multilink") cable is not supported by this driver. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. BUGS ---- This driver does not yet support shutdowns by raising DTR. Be aware that shutdowns are not possible with the stock contact-closure cable. You may have to build another cable with DTR connected through to the UPS for it to work. There is no way for this driver to detect the hardware or cable. It will start up successfully even if no UPS is present. This is a fundamental limitation of any contact-closure driver. SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/al175.txt0000644000175000017500000000312412640473702012355 00000000000000AL175(8) ======== NAME ---- al175 - Driver for Eltek UPS models with AL175 alarm module NOTE ---- This man page only documents the hardware-specific features of the *al175* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The *al175* driver is known to work with the following UPSes: Eltek MPSU4000 with AL175 alarm module It may also work with other UPSes equiped with AL175 alarm module, but they have not been tested. AL175 have to be configured to operate in COMLI mode. See documentation supplied with your hardware on how to do it. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. INSTANT COMMANDS ---------------- This driver supports some extra commands (see linkman:upscmd[8]): *test.battery.start*:: Start a battery test. *test.battery.stop*:: Stop a battery test. VARIABLES --------- Besides status, this driver reads UPS state into following variables: - *ups.test.result* - *output.voltage.nominal* - *output.current* - *battery.voltage.nominal* - *battery.current* - *battery.temperature* - *input.transfer.boost.low* KNOWN ISSUES AND BUGS --------------------- * Shutdown is not supported. FIXME * The driver was reworked to meet the project code quality criteria, without testing on real hardware. Some bugs may have crept in. AUTHOR ------ Kirill Smelkov SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_init.txt0000644000175000017500000000257212640443572014232 00000000000000NUTSCAN_INIT(3) =============== NAME ---- nutscan_init - Initialize the nutscan library. SYNOPSIS -------- #include void nutscan_init(); DESCRIPTION ----------- The *nutscan_init()* function must be called at least once before using any other function of the nutscan library. It updates the following global variables which can be used by nutscan library user to know which scan methods are available at run-time. This depends on the libraries installed on the system: nutscan_avail_avahi = 1 : AVAHI scan is available nutscan_avail_ipmi = 1 : IPMI scan is available nutscan_avail_nut = 1 : Old NUT method is available nutscan_avail_snmp = 1 : SNMP method is available nutscan_avail_usb = 1 : USB method is available nutscan_avail_xml_http = 1 : XML HTTP method is available Note that if a method is reported as unavailable by those variables, the call to the corresponding nutscan_scan_* function will always return NULL. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3] nut-2.7.4/docs/man/powerman-pdu.80000644000175000017500000000644712640476523013511 00000000000000'\" t .\" Title: powerman-pdu .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "POWERMAN\-PDU" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" powerman-pdu \- Driver for Powerman PDU .SH "SYNOPSIS" .sp \fBpowerman\-pdu\fR \-h .sp \fBpowerman\-pdu\fR \-a \fIPDU_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the powerman\-pdu driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp This driver supports a wide range of PDUs through the Powerman project\&. .sp This includes various models from APC, Baytech, Cyclades, but also support IPMI and various blade management modules from HP, IBM and Sun\&. .SH "EXTRA ARGUMENTS" .sp This driver doesn\(cqt support any optional settings\&. .SH "INSTALLATION" .sp This driver is not built by default\&. You can build it by using "configure \-\-with\-powerman=yes"\&. .SH "UPS COMMANDS" .sp The following instant commands (see \fBupscmd\fR(8)) are available for each outlet of the PDU, with \fBX\fR standing for the outlet number: .PP \fBoutlet\&.X\&.load\&.on\fR .RS 4 Power on the outlet\&. .RE .PP \fBoutlet\&.X\&.load\&.off\fR .RS 4 Power off the outlet\&. .RE .PP \fBoutlet\&.X\&.load\&.cycle\fR .RS 4 Cycle the outlet (power off then power on, possibly with a delay)\&. .RE .SH "IMPLEMENTATION" .sp The hostname of the Powerman server is specified using the "port" value in \fBups\&.conf\fR, i\&.e\&.: .sp .if n \{\ .RS 4 .\} .nf [pdu] driver = powerman\-pdu port = host\&.example\&.com:port .fi .if n \{\ .RE .\} .sp The port used to reach \fIpowermand\fR is optional if the default port is used\&. .SH "KNOWN ISSUES" .sp In the current NUT version (2\&.4\&.1), ups\&.status is still exposed, with the value "WAIT"\&. Some other values from the ups collection are also exposed\&. .SH "AUTHOR" .sp Arnaud Quette .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp The PowerMan home page: http://powerman\&.sourceforge\&.net/ nut-2.7.4/docs/man/Makefile.am0000644000175000017500000003331012640473702013017 00000000000000# Network UPS Tools: man # # Notes: # - sources (.txt) and groff formats are both distributed, # - only sources are versioned ; groff files are generated at worst # during 'make dist' # - HTML files are built upon request, if AsciiDoc is available, # - groff update will only happen if AsciiDoc is available too, # - all this can probably (and hopefully) be improved, but I've not # found a way to do pattern replacement on the fly for target deps! # FIXME: investigate an autogen.sh hook # - Ref: http://www.gnu.org/software/hello/manual/automake/Man-pages.html # Base configuration and client manpages, always installed SRC_CONF_PAGES = \ nut.conf.txt \ ups.conf.txt \ upsd.conf.txt \ upsd.users.txt \ upsmon.conf.txt \ upssched.conf.txt MAN_CONF_PAGES = \ nut.conf.5 \ ups.conf.5 \ upsd.conf.5 \ upsd.users.5 \ upsmon.conf.5 \ upssched.conf.5 man5_MANS = $(MAN_CONF_PAGES) HTML_CONF_MANS = \ nut.conf.html \ ups.conf.html \ upsd.conf.html \ upsd.users.html \ upsmon.conf.html \ upssched.conf.html SRC_CLIENT_PAGES = \ nutupsdrv.txt \ upsc.txt \ upscmd.txt \ upsd.txt \ upsdrvctl.txt \ upslog.txt \ upsmon.txt \ upsrw.txt \ upssched.txt MAN_CLIENT_PAGES = \ nutupsdrv.8 \ upsc.8 \ upscmd.8 \ upsd.8 \ upsdrvctl.8 \ upslog.8 \ upsmon.8 \ upsrw.8 \ upssched.8 man8_MANS = $(MAN_CLIENT_PAGES) HTML_CLIENT_MANS = \ nutupsdrv.html \ upsc.html \ upscmd.html \ upsd.html \ upsdrvctl.html \ upslog.html \ upsmon.html \ upsrw.html \ upssched.html SRC_TOOL_PAGES = nut-scanner.txt nut-recorder.txt MAN_TOOL_PAGES = nut-scanner.8 nut-recorder.8 man8_MANS += $(MAN_TOOL_PAGES) HTML_TOOL_MANS = nut-scanner.html nut-recorder.html # CGI (--with-cgi) related manpages SRC_CGI_PAGES = \ hosts.conf.txt \ upsset.conf.txt \ upsstats.html.txt \ upsset.cgi.txt \ upsstats.cgi.txt \ upsimage.cgi.txt MAN5_CGI_PAGES = \ hosts.conf.5 \ upsset.conf.5 \ upsstats.html.5 MAN8_CGI_PAGES = \ upsset.cgi.8 \ upsstats.cgi.8 \ upsimage.cgi.8 if WITH_CGI man5_MANS += $(MAN5_CGI_PAGES) man8_MANS += $(MAN8_CGI_PAGES) endif HTML_CGI_MANS = \ hosts.conf.html \ upsset.conf.html \ upsstats.html.html \ upsset.cgi.html \ upsstats.cgi.html \ upsimage.cgi.html # Development (--with-dev) related manpages SRC_DEV_PAGES = \ upsclient.txt \ upscli_add_host_cert.txt \ upscli_cleanup.txt \ upscli_connect.txt \ upscli_disconnect.txt \ upscli_fd.txt \ upscli_get.txt \ upscli_init.txt \ upscli_list_next.txt \ upscli_list_start.txt \ upscli_readline.txt \ upscli_sendline.txt \ upscli_splitaddr.txt \ upscli_splitname.txt \ upscli_ssl.txt \ upscli_strerror.txt \ upscli_upserror.txt \ libnutclient.txt \ libnutclient_commands.txt \ libnutclient_devices.txt \ libnutclient_general.txt \ libnutclient_misc.txt \ libnutclient_tcp.txt \ libnutclient_variables.txt \ nutscan.txt \ nutscan_scan_snmp.txt \ nutscan_scan_usb.txt \ nutscan_scan_xml_http.txt \ nutscan_scan_nut.txt \ nutscan_scan_avahi.txt \ nutscan_scan_ipmi.txt \ nutscan_scan_eaton_serial.txt \ nutscan_display_ups_conf.txt \ nutscan_display_parsable.txt \ nutscan_cidr_to_ip.txt \ nutscan_new_device.txt \ nutscan_free_device.txt \ nutscan_add_option_to_device.txt \ nutscan_add_device_to_device.txt \ nutscan_init.txt \ nutscan_get_serial_ports_list.txt \ libupsclient-config.txt \ skel.txt # NOTE: nutclient_*.3 has no source counterpart (libnutclient_*.txt) MAN3_DEV_PAGES = \ upsclient.3 \ upscli_add_host_cert.3 \ upscli_cleanup.3 \ upscli_connect.3 \ upscli_disconnect.3 \ upscli_fd.3 \ upscli_get.3 \ upscli_init.3 \ upscli_list_next.3 \ upscli_list_start.3 \ upscli_readline.3 \ upscli_sendline.3 \ upscli_splitaddr.3 \ upscli_splitname.3 \ upscli_ssl.3 \ upscli_strerror.3 \ upscli_upserror.3 \ libnutclient.3 \ libnutclient_commands.3 \ libnutclient_devices.3 \ libnutclient_general.3 \ libnutclient_misc.3 \ libnutclient_tcp.3 \ libnutclient_variables.3 \ nutclient_authenticate.3 \ nutclient_destroy.3 \ nutclient_device_forced_shutdown.3 \ nutclient_device_login.3 \ nutclient_device_master.3 \ nutclient_execute_device_command.3 \ nutclient_get_device_command_description.3 \ nutclient_get_device_commands.3 \ nutclient_get_device_description.3 \ nutclient_get_device_num_logins.3 \ nutclient_get_device_rw_variables.3 \ nutclient_get_devices.3 \ nutclient_get_device_variable_description.3 \ nutclient_get_device_variables.3 \ nutclient_get_device_variable_values.3 \ nutclient_has_device.3 \ nutclient_has_device_command.3 \ nutclient_has_device_variable.3 \ nutclient_logout.3 \ nutclient_set_device_variable_value.3 \ nutclient_set_device_variable_values.3 \ nutclient_tcp_create_client.3 \ nutclient_tcp_disconnect.3 \ nutclient_tcp_get_timeout.3 \ nutclient_tcp_is_connected.3 \ nutclient_tcp_reconnect.3 \ nutclient_tcp_set_timeout.3 \ nutscan.3 \ nutscan_scan_snmp.3 \ nutscan_scan_usb.3 \ nutscan_scan_xml_http.3 \ nutscan_scan_nut.3 \ nutscan_scan_avahi.3 \ nutscan_scan_ipmi.3 \ nutscan_scan_eaton_serial.3 \ nutscan_display_ups_conf.3 \ nutscan_display_parsable.3 \ nutscan_cidr_to_ip.3 \ nutscan_new_device.3 \ nutscan_free_device.3 \ nutscan_add_option_to_device.3 \ nutscan_add_device_to_device.3 \ nutscan_get_serial_ports_list.3 \ nutscan_init.3 MAN1_DEV_PAGES = \ libupsclient-config.1 if WITH_DEV man3_MANS = $(MAN3_DEV_PAGES) if !WITH_PKG_CONFIG man1_MANS = $(MAN1_DEV_PAGES) endif # WITH_DEV endif HTML_DEV_MANS = \ upsclient.html \ upscli_add_host_cert.html \ upscli_cleanup.html \ upscli_connect.html \ upscli_disconnect.html \ upscli_fd.html \ upscli_get.html \ upscli_init.html \ upscli_list_next.html \ upscli_list_start.html \ upscli_readline.html \ upscli_sendline.html \ upscli_splitaddr.html \ upscli_splitname.html \ upscli_ssl.html \ upscli_strerror.html \ upscli_upserror.html \ libnutclient.html \ libnutclient_commands.html \ libnutclient_devices.html \ libnutclient_general.html \ libnutclient_misc.html \ libnutclient_tcp.html \ libnutclient_variables.html \ nutscan.html \ nutscan_scan_snmp.html \ nutscan_scan_usb.html \ nutscan_scan_xml_http.html \ nutscan_scan_nut.html \ nutscan_scan_avahi.html \ nutscan_scan_ipmi.html \ nutscan_scan_eaton_serial.html \ nutscan_display_ups_conf.html \ nutscan_display_parsable.html \ nutscan_cidr_to_ip.html \ nutscan_new_device.html \ nutscan_free_device.html \ nutscan_add_option_to_device.html \ nutscan_add_device_to_device.html \ nutscan_get_serial_ports_list.html \ nutscan_init.html \ libupsclient-config.html \ skel.html # Drivers related manpages # (--with-drivers=...) if SOME_DRIVERS man8_MANS += $(DRIVER_MAN_LIST) else # (--with-serial) SRC_SERIAL_PAGES = \ al175.txt \ apcsmart.txt \ apcsmart-old.txt \ bcmxcp.txt \ belkin.txt \ belkinunv.txt \ bestfortress.txt \ bestuferrups.txt \ bestups.txt \ bestfcom.txt \ blazer-common.txt \ blazer_ser.txt \ clone.txt \ dummy-ups.txt \ etapro.txt \ everups.txt \ gamatronic.txt \ genericups.txt \ isbmex.txt \ ivtscd.txt \ liebert.txt \ liebert-esp2.txt \ masterguard.txt \ metasys.txt \ mge-shut.txt \ mge-utalk.txt \ oneac.txt \ microdowell.txt \ nutdrv_qx.txt \ optiups.txt \ powercom.txt \ powerpanel.txt \ rhino.txt \ riello_ser.txt \ safenet.txt \ solis.txt \ tripplite.txt \ tripplitesu.txt \ upscode2.txt \ victronups.txt \ apcupsd-ups.txt MAN_SERIAL_PAGES = \ al175.8 \ apcsmart.8 \ apcsmart-old.8 \ bcmxcp.8 \ belkin.8 \ belkinunv.8 \ bestfortress.8 \ bestuferrups.8 \ bestups.8 \ bestfcom.8 \ blazer_ser.8 \ clone.8 \ dummy-ups.8 \ etapro.8 \ everups.8 \ gamatronic.8 \ genericups.8 \ isbmex.8 \ ivtscd.8 \ liebert.8 \ liebert-esp2.8 \ masterguard.8 \ metasys.8 \ mge-shut.8 \ mge-utalk.8 \ nutdrv_qx.8 \ oneac.8 \ microdowell.8 \ optiups.8 \ powercom.8 \ powerpanel.8 \ rhino.8 \ riello_ser.8 \ safenet.8 \ solis.8 \ tripplite.8 \ tripplitesu.8 \ upscode2.8 \ victronups.8 \ apcupsd-ups.8 if WITH_SERIAL man8_MANS += $(MAN_SERIAL_PAGES) endif HTML_SERIAL_MANS = \ al175.html \ apcsmart.html \ apcsmart-old.html \ bcmxcp.html \ belkin.html \ belkinunv.html \ bestfortress.html \ bestuferrups.html \ bestups.html \ bestfcom.html \ blazer_ser.html \ clone.html \ dummy-ups.html \ etapro.html \ everups.html \ gamatronic.html \ genericups.html \ isbmex.html \ ivtscd.html \ liebert.html \ liebert-esp2.html \ masterguard.html \ metasys.html \ mge-shut.html \ mge-utalk.html \ nutdrv_qx.html \ oneac.html \ microdowell.html \ optiups.html \ powercom.html \ powerpanel.html \ rhino.html \ riello_ser.html \ safenet.html \ solis.html \ tripplite.html \ tripplitesu.html \ upscode2.html \ victronups.html \ apcupsd-ups.html # (--with-snmp) SRC_SNMP_PAGES = snmp-ups.txt MAN_SNMP_PAGES = snmp-ups.8 if WITH_SNMP man8_MANS += $(MAN_SNMP_PAGES) endif HTML_SNMP_MANS = snmp-ups.html # (--with-usb) SRC_USB_LIBUSB_PAGES = \ bcmxcp_usb.txt \ blazer-common.txt \ blazer_usb.txt \ nutdrv_atcl_usb.txt \ nutdrv_qx.txt \ richcomm_usb.txt \ riello_usb.txt \ tripplite_usb.txt \ usbhid-ups.txt MAN_USB_LIBUSB_PAGES = \ bcmxcp_usb.8 \ blazer_usb.8 \ nutdrv_atcl_usb.8 \ nutdrv_qx.8 \ richcomm_usb.8 \ riello_usb.8 \ tripplite_usb.8 \ usbhid-ups.8 if WITH_USB man8_MANS += $(MAN_USB_LIBUSB_PAGES) endif HTML_USB_LIBUSB_MANS = \ bcmxcp_usb.html \ blazer_usb.html \ nutdrv_qx.html \ nutdrv_atcl_usb.html \ richcomm_usb.html \ riello_usb.html \ tripplite_usb.html \ usbhid-ups.html # (--with-neon) SRC_NETXML_PAGES = netxml-ups.txt MAN_NETXML_PAGES = netxml-ups.8 if WITH_NEON man8_MANS += $(MAN_NETXML_PAGES) endif HTML_NETXML_MANS = netxml-ups.html # (--with-powerman) SRC_POWERMAN_PAGES = powerman-pdu.txt MAN_POWERMAN_PAGES = powerman-pdu.8 if WITH_LIBPOWERMAN man8_MANS += $(MAN_POWERMAN_PAGES) endif HTML_POWERMAN_MANS = powerman-pdu.html # (--with-ipmi) SRC_IPMIPSU_PAGES = nut-ipmipsu.txt MAN_IPMIPSU_PAGES = nut-ipmipsu.8 if WITH_IPMI man8_MANS += $(MAN_IPMIPSU_PAGES) endif HTML_IPMIPSU_MANS = nut-ipmipsu.html SRC_MACOSX_PAGES = macosx-ups.txt MAN_MACOSX_PAGES = macosx-ups.8 if WITH_MACOSX man8_MANS += $(MAN_MACOSX_PAGES) endif HTML_MACOSX_MANS = macosx-ups.html SRC_LINUX_I2C_PAGES = asem.txt MAN_LINUX_I2C_PAGES = asem.8 if WITH_LINUX_I2C man8_MANS += $(LINUX_I2C_PAGES) endif HTML_LINUX_I2C_MANS = asem.html # SOME_DRIVERS endif MAN_MANS = \ $(MAN_CONF_PAGES) \ $(MAN_CLIENT_PAGES) \ $(MAN_TOOL_PAGES) \ $(MAN5_CGI_PAGES) \ $(MAN8_CGI_PAGES) \ $(MAN1_DEV_PAGES) \ $(MAN3_DEV_PAGES) \ $(MAN_SERIAL_PAGES) \ $(MAN_SNMP_PAGES) \ $(MAN_USB_LIBUSB_PAGES) \ $(MAN_NETXML_PAGES) \ $(MAN_POWERMAN_PAGES) \ $(MAN_IPMIPSU_PAGES) \ $(MAN_MACOSX_PAGES) \ $(MAN_LINUX_I2C_PAGES) # distribute everything, even those not installed by default # Note that 'dist' target requires AsciiDoc! EXTRA_DIST = \ $(SRC_CONF_PAGES) \ $(SRC_CLIENT_PAGES) \ $(SRC_TOOL_PAGES) \ $(SRC_CGI_PAGES) \ $(SRC_DEV_PAGES) \ $(SRC_SERIAL_PAGES) \ $(SRC_SNMP_PAGES) \ $(SRC_USB_LIBUSB_PAGES) \ $(SRC_NETXML_PAGES) \ $(SRC_POWERMAN_PAGES) \ $(SRC_IPMIPSU_PAGES) \ $(SRC_MACOSX_PAGES) \ $(SRC_LINUX_I2C_PAGES) \ $(MAN_MANS) \ asciidoc.conf HTML_MANS = \ $(HTML_CONF_MANS) \ $(HTML_CLIENT_MANS) \ $(HTML_TOOL_MANS) \ $(HTML_CGI_MANS) \ $(HTML_DEV_MANS) \ $(HTML_SERIAL_MANS) \ $(HTML_SNMP_MANS) \ $(HTML_USB_LIBUSB_MANS) \ $(HTML_NETXML_MANS) \ $(HTML_POWERMAN_MANS) \ $(HTML_IPMIPSU_MANS) \ $(HTML_MACOSX_MANS) \ $(HTML_LINUX_I2C_MANS) all: html-man: $(HTML_MANS) index.html CLEANFILES = *.xml *.html SUFFIXES = .txt .html .1 .3 .5 .8 if HAVE_ASCIIDOC .txt.html: $(ASCIIDOC) --backend=xhtml11 \ --attribute localdate=`TZ=UTC date +%Y-%m-%d` \ --attribute localtime=`TZ=UTC date +%H:%M:%S` \ --attribute nutversion="@PACKAGE_VERSION@" \ -o $@ $< ### Prior to Asciidoc ~8.6.8, the --destination-dir flag didn't seem to affect the location of the intermediate .xml file. A2X_MANPAGE_OPTS = --doctype manpage --format manpage \ --xsltproc-opts "--nonet" \ --attribute mansource="Network UPS Tools" \ --attribute manversion="@PACKAGE_VERSION@" \ --attribute manmanual="NUT Manual" \ --destination-dir=. .txt.1: $(A2X) $(A2X_MANPAGE_OPTS) $< .txt.3: $(A2X) $(A2X_MANPAGE_OPTS) $< .txt.5: $(A2X) $(A2X_MANPAGE_OPTS) $< .txt.8: $(A2X) $(A2X_MANPAGE_OPTS) $< else !HAVE_ASCIIDOC .txt.html: @if [ -r "$@" ]; then \ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ else \ echo "Could not find prebuilt $@ manual page." ; \ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ exit 1; \ fi .txt.1: @if [ -r "$@" ]; then \ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ else \ echo "Could not find prebuilt $@ manual page." ; \ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ exit 1; \ fi .txt.3: @if [ -r "$@" ]; then \ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ else \ echo "Could not find prebuilt $@ manual page." ; \ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ exit 1; \ fi .txt.5: @if [ -r "$@" ]; then \ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ else \ echo "Could not find prebuilt $@ manual page." ; \ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ exit 1; \ fi .txt.8: @if [ -r "$@" ]; then \ echo "Not (re)building $@ manual page, since 'asciidoc', 'xmllint' or 'xsltproc' were not found." ; \ else \ echo "Could not find prebuilt $@ manual page." ; \ echo "If you are building from Git, do you have all of the asciidoc/a2x tools installed?"; \ exit 1; \ fi endif !HAVE_ASCIIDOC nut-2.7.4/docs/man/nutscan_add_option_to_device.txt0000644000175000017500000000252112640443572017422 00000000000000NUTSCAN_ADD_OPTION_TO_DEVICE(3) =============================== NAME ---- nutscan_add_option_to_device - Add option data to the specified device. SYNOPSIS -------- #include void nutscan_add_option_to_device(nutscan_device_t * device,char * option_name, char * value); DESCRIPTION ----------- The `nutscan_device_t` contains the following variables: nutscan_device_type_t type; char * driver; char * port; nutscan_options_t opt; struct nutscan_device * prev; struct nutscan_device * next; This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. The *nutscan_add_option_to_device()* adds an optional data in the given devcie. Optional data are made of an 'option_name' and an associated 'value'. Copies of 'option_name' and 'value' are stored in the device, so the caller can safely free both of them. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_device_to_device[3] nut-2.7.4/docs/man/safenet.80000644000175000017500000001003212640476513012500 00000000000000'\" t .\" Title: safenet .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "SAFENET" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" safenet \- Driver for SafeNet compatible UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the safenet driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports UPS equipment which can be controlled via SafeNet v1\&.0 for Windows (serial interface only)\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5) file: .PP \fBmanufacturer=\fR\fIvalue\fR .RS 4 Autodetection of this parameter is not possible yet (and it probably never will be)\&. Therefore, this user\-defined string accepts any name\&. The default is \fIunknown\fR\&. .RE .PP \fBmodelname=\fR\fIvalue\fR .RS 4 Like manufacturer above\&. The default is \fIunknown\fR\&. .RE .PP \fBserialnumber=\fR\fIvalue\fR .RS 4 Like manufacturer above\&. The default is \fIunknown\fR\&. .RE .PP \fBondelay=\fR\fIvalue\fR .RS 4 Time to wait before switching on the UPS (minutes)\&. Defaults to 1 minute\&. .RE .PP \fBoffdelay=\fR\fIvalue\fR .RS 4 Time to wait before shutting down the UPS (seconds)\&. Defaults to 30 seconds\&. .RE .SH "UPSCMD" .sp This driver supports some instant commands (see \fBupscmd\fR(8)): .PP \fBtest\&.battery\&.start\fR .RS 4 Start UPS self test .RE .PP \fBtest\&.battery\&.stop\fR .RS 4 Cancel UPS self test .RE .PP \fBtest\&.failure\&.start\fR .RS 4 Start simulated power failure .RE .PP \fBtest\&.failure\&.stop\fR .RS 4 Cancel simulated power failure .RE .PP \fBbeeper\&.enable\fR .RS 4 Enable the UPS beeper .RE .PP \fBbeeper\&.mute\fR .RS 4 Temporarily mute the UPS beeper .RE .PP \fBbeeper\&.toggle\fR .RS 4 Toggle the UPS beeper .RE .PP \fBshutdown\&.return\fR .RS 4 Turn off the load and wait for the power to return\&. Uses the timer defined by \fBoffdelay\fR\&. .RE .PP \fBshutdown\&.reboot\fR .RS 4 Turn off the load and return\&. Uses the timers defined by \fBoffdelay\fR and \fBondelay\fR\&. .RE .SH "KNOWN PROBLEMS" .sp If you run the \fBshutdown\&.return\fR command with mains present, the output may stay on or switch off and not back on again\&. The \fBshutdown\&.reboot\fR command will unconditionally switch on the load again (with or without mains present)\&. .sp If the driver is called with the \fI\-k\fR option (or through \fBupsdrvctl shutdown\fR) it tries to detect which command should be used in an attempt to stay off until mains is present again or to cycle the output if the power returned in the mean time\&. This isn\(cqt bullet\-proof, and you should be prepared that the power will either not be shutdown, or that it doesn\(cqt return when the power comes back\&. .SH "AUTHOR" .sp Arjen de Korte .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_scan_avahi.txt0000644000175000017500000000237412640444140015353 00000000000000NUTSCAN_SCAN_AVAHI(3) ===================== NAME ---- nutscan_scan_avahi - Scan network for NUT services via mDNS SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_avahi(long usec_timeout); DESCRIPTION ----------- The *nutscan_scan_avahi()* function tries to detect the NUT service via mDNS, and its associated devices. It uses the Avahi library to do so. You MUST call linkman:nutscan_init[3] before using this function. This function waits up to 'usec_timeout' microseconds before considering an IP address to be unresponsive. RETURN VALUE ------------ The *nutscan_scan_avahi()* function returns a pointer to a `nutscan_device_t` structure containing all found devices. It returns NULL if an error occurs, or if no device is found. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_cidr_to_ip[3], linkman:nutscan_scan_eaton_serial[3], http://avahi.org/ nut-2.7.4/docs/man/apcupsd-ups.80000644000175000017500000001150212640476516013325 00000000000000'\" t .\" Title: apcupsd-ups .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "APCUPSD\-UPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" apcupsd-ups \- Driver for apcupsd client access .SH "NOTE" .sp This man page only documents the specific features of the \fBapcupsd\-ups\fR driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "DESCRIPTION" .sp This driver is a client to \fBapcupsd\fR\&. .sp \fBapcupsd\-ups\fR acts as an \fBapcupsd\fR client, simply forwarding data\&. This can be useful in cases where both protocols are required in a network, or in case apcupsd has a required UPS access mode missing from NUT\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in \fBnut.conf\fR(5): .PP \fBport\fR=[:] .RS 4 This is the name of a remote host running apcupsd (plus an optional port)\&. .RE .sp For instance: .sp .if n \{\ .RS 4 .\} .nf [apcupsd] driver = apcupsd\-ups port = localhost desc = "apcupsd client" .fi .if n \{\ .RE .\} .SH "BACKGROUND" .sp This driver was originally written in one evening to allow interworking with \fBapcupsd\fR\&. .SH "SUPPORTED VARIABLES" .sp The following variables are translated from \fBapcupsd\fR to NUT\&. All times should be converted to seconds (please file a bug if you notice a mismatch in units)\&. .TS allbox tab(:); ltB ltB. T{ apcupsd variable T}:T{ NUT variable(s) T} .T& lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp BCHARGE T}:T{ .sp battery\&.charge T} T{ .sp MBATTCHG T}:T{ .sp battery\&.charge\&.low T} T{ .sp RETPCT T}:T{ .sp battery\&.charge\&.restart T} T{ .sp BATTDATE T}:T{ .sp battery\&.date T} T{ .sp TIMELEFT T}:T{ .sp battery\&.runtime T} T{ .sp MINTIMEL T}:T{ .sp battery\&.runtime\&.low T} T{ .sp BATTV T}:T{ .sp battery\&.voltage T} T{ .sp NOMBATTV T}:T{ .sp battery\&.voltage\&.nominal T} T{ .sp LINEFREQ T}:T{ .sp input\&.frequency T} T{ .sp SENSE T}:T{ .sp input\&.sensitivity T} T{ .sp HITRANS T}:T{ .sp input\&.transfer\&.high T} T{ .sp LOTRANS T}:T{ .sp input\&.transfer\&.low T} T{ .sp LASTXFER T}:T{ .sp input\&.transfer\&.reason T} T{ .sp LINEV T}:T{ .sp input\&.voltage T} T{ .sp MAXLINEV T}:T{ .sp input\&.voltage\&.maximum T} T{ .sp MINLINEV T}:T{ .sp input\&.voltage\&.minimum T} T{ .sp NOMINV T}:T{ .sp input\&.voltage\&.nominal T} T{ .sp LINEFREQ T}:T{ .sp output\&.frequency T} T{ .sp OUTPUTV T}:T{ .sp output\&.voltage T} T{ .sp NOMOUTV T}:T{ .sp output\&.voltage\&.nominal T} T{ .sp DATE T}:T{ .sp ups\&.date, ups\&.time T} T{ .sp DSHUTD T}:T{ .sp ups\&.delay\&.shutdown T} T{ .sp DWAKE T}:T{ .sp ups\&.delay\&.start T} T{ .sp FIRMWARE T}:T{ .sp ups\&.firmware, ups\&.firmware\&.aux T} T{ .sp UPSNAME T}:T{ .sp ups\&.id T} T{ .sp LOADPCT T}:T{ .sp ups\&.load T} T{ .sp MANDATE T}:T{ .sp ups\&.mfr\&.date T} T{ .sp NOMPOWER T}:T{ .sp ups\&.realpower\&.nominal T} T{ .sp SERIALNO T}:T{ .sp ups\&.serial T} T{ .sp STATUS T}:T{ .sp ups\&.status T} T{ .sp ITEMP T}:T{ .sp ups\&.temperature T} T{ .sp STESTI T}:T{ .sp ups\&.test\&.interval T} T{ .sp SELFTEST T}:T{ .sp ups\&.test\&.result T} .TE .sp 1 .SH "LIMITATIONS" .sp Access to \fBapcupsd\fR is strictly read only: no commands can be issued\&. This stems from the design of \fBapcupsd\fR, where the settings are changed in \fBapctest\fR\&. In order to run \fBapctest\fR, \fBapcupsd\fR must be stopped (and \fBapcupsd\fR exposes the UPS to the network)\&. .SH "AUTHOR" .sp Andreas Steinmetz .SH "SEE ALSO" .sp \fBups.conf\fR(5), \fBnutupsdrv\fR(8) .SS "Internet Resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp The apcupsd home page: http://www\&.apcupsd\&.org/ nut-2.7.4/docs/man/nutscan_scan_usb.30000644000175000017500000000430112665610650014377 00000000000000'\" t .\" Title: nutscan_scan_usb .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_USB" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_usb \- Scan NUT compatible USB devices\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_usb(); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_usb()\fR function try to detect NUT compatible USB devices\&. .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_usb()\fR function returns a pointer to a nutscan_device_t structure containing all found devices or NULL if an error occurs or no device is found\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_scan_eaton_serial\fR(3) nut-2.7.4/docs/man/nutscan_add_option_to_device.30000644000175000017500000000507312665610655016757 00000000000000'\" t .\" Title: nutscan_add_option_to_device .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_ADD_OPTION_T" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_add_option_to_device \- Add option data to the specified device\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf void nutscan_add_option_to_device(nutscan_device_t * device,char * option_name, char * value); .fi .SH "DESCRIPTION" .sp The nutscan_device_t contains the following variables: .sp .if n \{\ .RS 4 .\} .nf nutscan_device_type_t type; char * driver; char * port; nutscan_options_t opt; struct nutscan_device * prev; struct nutscan_device * next; .fi .if n \{\ .RE .\} .sp This is a double linked list of device\&. Each device is described by its type, its driver name, its port and any number of optional data\&. .sp The \fBnutscan_add_option_to_device()\fR adds an optional data in the given devcie\&. Optional data are made of an \fIoption_name\fR and an associated \fIvalue\fR\&. Copies of \fIoption_name\fR and \fIvalue\fR are stored in the device, so the caller can safely free both of them\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_device_to_device\fR(3) nut-2.7.4/docs/man/nut-ipmipsu.80000644000175000017500000001177712640476523013367 00000000000000'\" t .\" Title: nut-ipmipsu .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUT\-IPMIPSU" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nut-ipmipsu \- Driver for IPMI Power Supply Units (PSU) .SH "SYNOPSIS" .sp \fBnut\-ipmipsu\fR \-h .sp \fBnut\-ipmipsu\fR \-a \fIPSU_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This driver is experimental, and still a work\-in\-progress\&. Feedback is encouraged\&. .sp .5v .RE .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the nut\-ipmipsu driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp This driver should support a wide range of PSUs through local IPMI interface\&. .sp nut\-ipmipsu currently use the GNU FreeIPMI project, for IPMI implementation\&. .SH "EXTRA ARGUMENTS" .sp This driver doesn\(cqt support any optional settings\&. .SH "INSTALLATION" .sp This driver is not built by default\&. You can build it by using "configure \-\-with\-ipmi=yes"\&. .sp You also need to give proper permissions on the local IPMI device file (/dev/ipmi0 for example) to allow the NUT user to access it\&. .sp An udev rules file (nut\-ipmipsu\&.rules) is provided and automatically installed on udev enabled system\&. This file is generally installed in /etc/udev/rules\&.d/ or /lib/udev/rules\&.d/ on newer systems, to address the permission settings problem\&. For more information, refer to nut/scripts/udev/README\&. .SH "INSTANT COMMANDS" .sp This driver doesn\(cqt support any instant commands\&. .SH "IMPLEMENTATION" .sp The "port" value is used to identify the PSU\&. For instance, to target FRU 0x2, use the following in \fBups\&.conf\fR: .sp .if n \{\ .RS 4 .\} .nf [pdu] driver = nut\-ipmipsu port = id2 .fi .if n \{\ .RE .\} .sp This driver will report various information related to a PSU, including: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} manufacturer, model, serial and part numbers, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} nominal voltage and frequency, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} actual current and voltage, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} status of the PSU: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fIOL\fR means that the PSU is present and providing power, .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fIOFF\fR means that the PSU is present but not providing power (power cable removed), .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fIstale\fR (no data) means that the PSU is not present (ie physically removed)\&. .RE .RE .sp Here is an example output for a Dell r610 server: .sp .if n \{\ .RS 4 .\} .nf device\&.mfr: DELL device\&.mfr\&.date: 01/05/11 \- 08:51:00 device\&.model: PWR SPLY,717W,RDNT device\&.part: 0RN442A01 device\&.serial: CN179721130031 device\&.type: psu driver\&.name: nut\-ipmipsu driver\&.parameter\&.pollinterval: 2 driver\&.parameter\&.port: id2 driver\&.version: 2\&.6\&.1\-3139M driver\&.version\&.data: IPMI PSU driver driver\&.version\&.internal: 0\&.01 input\&.current: 0\&.20 input\&.frequency\&.high: 63 input\&.frequency\&.low: 47 input\&.voltage: 232\&.00 input\&.voltage\&.maximum: 264 input\&.voltage\&.minimum: 90 ups\&.id: 2 ups\&.realpower\&.nominal: 717 ups\&.status: OL ups\&.voltage: 12 .fi .if n \{\ .RE .\} .SH "AUTHOR" .sp Arnaud Quette .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ .sp GNU FreeIPMI home page: http://www\&.gnu\&.org/software/freeipmi/ nut-2.7.4/docs/man/nutscan_free_device.txt0000644000175000017500000000144612640443572015526 00000000000000NUTSCAN_FREE_DEVICE(3) ====================== NAME ---- nutscan_free_device - Free a nutscan_device_t structure created by nutscan_new_device. SYNOPSIS -------- #include void nutscan_free_device(nutscan_device_t * device); DESCRIPTION ----------- The *nutscan_free_device()* function free a `nutscan_device_type_t` structure. Doing so, it free the whole linked list, not only the given device. SEE ALSO -------- linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3] nut-2.7.4/docs/man/everups.80000644000175000017500000000403212640476501012544 00000000000000'\" t .\" Title: everups .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "EVERUPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" everups \- Driver for Ever UPS models .SH "NOTE" .sp This man page only documents the hardware\-specific features of the everups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver should recognize the NET *\-DPC and AP *\-PRO models\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "BUGS" .sp This UPS can only switch off the load if it\(cqs running on battery\&. This means you may be vulnerable to power races if your shutdown scripts don\(cqt sleep and force a reboot\&. .SH "AUTHOR" .sp Bartek Szady .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nut-recorder.80000644000175000017500000000545612640476472013506 00000000000000'\" t .\" Title: nut-recorder .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUT\-RECORDER" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nut-recorder \- utility to record device status and values changes .SH "SYNOPSIS" .sp \fBnut\-recorder\fR \fIdevice\-name\fR [output\-file] [interval] .SH "DESCRIPTION" .sp \fBnut\-recorder\fR is an utility to record sequences from running devices (such as power failures, or any other value changes) from upsd, and dump it in a \&.seq format\&. .sp The \&.seq file can then be used by the \fBdummy-ups\fR(8) driver to replay the sequence\&. .SH "OPTIONS" .PP \fIdevice\-name\fR .RS 4 Record the changes of this device\&. The format for this option is \fIdevname[@hostname[:port]]\fR\&. The default hostname is "localhost"\&. .RE .PP \fIoutput\-file\fR .RS 4 Optional\&. Data will be saved to this file\&. The default is \fIdummy\-device\&.seq\fR\&. .RE .PP \fIinterval\fR .RS 4 Optional\&. The status of the device will be checked every \fIinterval\fR\&. The default is 5 seconds\&. .RE .SH "EXAMPLES" .sp To record data from \fIups1@host1\fR every 10 seconds: .sp .if n \{\ .RS 4 .\} .nf $ nut\-recorder ups1@host1\*(Aq ups1\-output\&.seq 10 \&. \&. \&. battery\&.charge: 100\&.0 battery\&.voltage: 13\&.9 battery\&.voltage\&.nominal: 13\&.6 ups\&.status: OL \&. \&. \&. battery\&.charge: 90\&.0 ups\&.status: OB \&. \&. \&. .fi .if n \{\ .RE .\} .sp You can then define a dummy device in \fBups.conf\fR(5): .sp .if n \{\ .RS 4 .\} .nf [ups\-test] driver = dummy\-ups port = ups1\-output\&.seq .fi .if n \{\ .RE .\} .SH "AUTHOR" .sp Arnaud Quette .SH "SEE ALSO" .sp \fBdummy-ups\fR(8) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/dummy-ups.txt0000644000175000017500000001126512640473702013471 00000000000000DUMMY-UPS(8) ============ NAME ---- dummy-ups - Driver for multi-purpose UPS emulation NOTE ---- This man page only documents the specific features of the dummy-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- This program is a multi-purpose UPS emulation tool. Its behavior depends on the running mode: Dummy Mode ~~~~~~~~~~ *dummy-ups* looks like a standard device driver to linkman:upsd[8] and allows one to change any value for testing purposes. It is both interactive, controllable through the linkman:upsrw[1] and linkman:upscmd[1] commands (or equivalent graphical tool), and batchable through script files. It can be configured, launched and used as any other real driver. This mode is mostly useful for development and testing purposes. Repeater Mode ~~~~~~~~~~~~~ *dummy-ups* acts as a NUT client, simply forwarding data. This can be useful for supervision purposes. This can also allow some load sharing between several UPS instances, using a point-to-point communication with the UPS. IMPLEMENTATION -------------- The port specification depends on the running mode, and allows the driver to select the right mode. Dummy Mode ~~~~~~~~~~ Port is a definition file name for *dummy-ups*. This can either be an absolute or a relative path name. In the latter case the NUT sysconfig directory (ie /etc/nut, /usr/local/ups/etc, ...) is prepended. For instance: [dummy] driver = dummy-ups port = evolution500.dev desc = "dummy-ups in dummy mode" This file is generally named "something.dev". It contains a list of all valid data and associated values, and has the same format as an linkman:upsc[8] dump (: ). So you can easily create definition files from an existing UPS using "upsc > file.dev". It can also be empty, in which case only a basic set of data is available: device.*, driver.*, ups.mfr, ups.model, ups.status Samples definition files are available in the "data" directory of the nut source tree, and generally in the sysconfig directory of your system distribution. Since *dummy-ups* will loop on reading this file, you can dynamically modify it to interact with the driver. This will avoid message spam into your system log files, if you are using NUT default configuration. You can also use the "TIMER " instruction to create scheduled events sequences. For example, the following sequence will loop on switching ups.status between "OL", "OB" and "OB LB" every minute: ups.status: OL TIMER 60 ups.status: OB TIMER 60 ups.status: LB TIMER 60 It is wise to end the script with a TIMER. Otherwise dummy-ups will directly go back to the beginning of the file. Repeater Mode ~~~~~~~~~~~~~ Port is the name of a remote UPS, using the NUT form, ie: @[:] For instance: [repeater] driver = dummy-ups port = ups@hostname desc = "dummy-ups in repeater mode" Unlike UPS specifications in the rest of NUT, the `@hostname` portion is not optional - it is the `@` character which enables Repeater Mode. To refer to an UPS on the same host as *dummy-ups*, use `port = upsname@localhost`. INTERACTION ----------- Once the driver is loaded in dummy mode, you can change any variables, except those of the driver.* and server.* collections. You can do this by either editing the definition file, or use the linkman:upsrw[1] and linkman:upscmd[1] commands. Note that in simulation mode, new variables can be added on the fly, by adding these to the definition file. Conversely, if you need to remove variable (such as transient ones, like ups.alarm), simply update these by setting an empty value. As a result, they will get removed from the data. In repeater mode, the driver acts according to the capabilities of the UPS, and so support the same instant commands and settable values. BACKGROUND ---------- Dummy Mode was originally written in one evening to replace the previous dummycons testing driver, which was too limited, and required a terminal for interaction. *dummy-ups* is useful for NUT client development, and other testing purposes. It also helps the NUT Quality Assurance effort, by automating some tests on the NUT framework. It now offers a repeater mode. This will help in building the Meta UPS approach, which allows one to build a virtual device, composed of several other devices (either UPS, PDUs). BUGS ---- Instant commands are not yet supported in Dummy Mode, and data need name/value checking enforcement, as well as boundaries or enumeration definition. AUTHOR ------ Arnaud Quette SEE ALSO -------- linkman:upscmd[1], linkman:upsrw[1], linkman:ups.conf[5], linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_destroy.30000644000175000017500000000003312665610644014626 00000000000000.so libnutclient_general.3 nut-2.7.4/docs/man/nutupsdrv.txt0000644000175000017500000001355612640473702013610 00000000000000NUTUPSDRV(8) ============ NAME ---- nutupsdrv - generic manual for unified NUT drivers SYNOPSIS -------- *nutupsdrv* '-h' *nutupsdrv* [OPTIONS] DESCRIPTION ----------- *nutupsdrv* is not actually a driver. This is a combined man page for the shared code that is the core of many drivers within the Network UPS Tools package. For information on the specific drivers, see their individual man pages. UPS drivers provide a communication channel between the physical UPS hardware and the linkman:upsd[8] server. The driver is responsible for translating the native protocol of the UPS to the common format used by the rest of this package. The core has two modes of operation which are determined by the command line switches. In the normal mode, the driver will periodically poll the UPS for its state and parameters. The results of this command is presented to upsd. The driver will also handle setting variables and instant commands if available. The driver can also instruct the UPS to shut down the load, possibly after some delay. This mode of operation is intended for cases when it is known that the UPS is running out of battery power and the systems attached must be turned off to ensure a proper reboot when power returns. NOTE: You probably don't want to use any of these options directly. You should use linkman:upsdrvctl[8] to control your drivers, and linkman:ups.conf[5] to configure them. The rest of this manual describes options and parameters that generally are not needed by normal users. OPTIONS ------- *-h*:: Display a help message without doing anything else. This will also list possible values for '-x' in that driver, and other help text that the driver's author may have provided. *-a* 'id':: Autoconfigure this driver using the 'id' section of linkman:ups.conf[5]. *This argument is mandatory when calling the driver directly.* *-D*:: Raise the debugging level. Use this multiple times to see more details. Running a driver in debug mode will prevent it from backgrounding after startup. It will keep on logging information to the console until it receives a SIGINT (usually Ctrl-C) or SIGTERM signal. + The level of debugging needed depends both on the driver and the problem you're trying to diagnose. Therefore, first explain the problem you have with a driver to a developer/maintainer, before sending them debugging output. More often than not, if you just pick a level, the output may be either too limited or too verbose to be of any use. *-q*:: Raise log level threshold. Use this multiple times to log more details. + The debugging comment above also applies here. *-i* 'interval':: Set the poll interval for the device. *-V*:: Print only version information, then exit. *-L*:: Print a parseable list of driver variables. Mostly useful for configuration wizard programs. *-k*:: ("Kill" power) Forced shutdown mode. The UPS will power off the attached load, if possible. + You should use +upsdrvctl shutdown+ whenever possible instead of calling this directly. *-r* 'directory':: The driver will chroot(2) to 'directory' during initialization. This can be useful when securing systems. + In addition to the state path, many systems will require /dev/null to exist within 'directory' for this to work. The serial ports are opened before the chroot call, so you do not need to create them inside the jail. In fact, it is somewhat safer if you do not. *-u* 'username':: If started as root, the driver will setuid(2) to the user id associated with 'username'. + If you do not specify this value and start it as root, the driver will switch to the default value that was compiled into the code. This is typically 'nobody', and is far from ideal. *-x* 'var'='val':: Define a variable called 'var' with the value of 'var' in the driver. This varies from driver to driver - see the specific man pages for more information. + This is like setting 'var'='val' in linkman:ups.conf[5], but *-x* overrides any settings from that file. DIAGNOSTICS ----------- Information about the startup process is printed to stdout. Additional messages after that point are available in the syslog. After linkman:upsd[8] starts, the UPS clients such as linkman:upsc[8] can be used to query the status of an UPS. PROGRAM CONTROL --------------- You should always use linkman:upsdrvctl[8] to control the drivers. While drivers can be started by hand for testing purposes, it is not recommended for production use. FILES ----- ups.conf:: Required configuration file. This contains all details on which drivers to start and where the hardware is attached. BUGS ---- Some of the drivers may have bugs. See their manuals for more information. SEE ALSO -------- Server: linkman:upsd[8] Clients: linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] CGI programs: linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Driver control: linkman:upsdrvctl[8] Drivers: linkman:al175[8] linkman:apcsmart[8], linkman:bcmxcp[8], linkman:bcmxcp_usb[8], linkman:belkin[8], linkman:belkinunv[8], linkman:bestfcom[8], linkman:bestuferrups[8], linkman:bestups[8], linkman:blazer_ser[8], linkman:blazer_usb[8], linkman:cyberpower[8], linkman:dummy-ups[8], linkman:etapro[8], linkman:everups[8], linkman:gamatronic[8], linkman:genericups[8], linkman:isbmex[8], linkman:liebert[8], linkman:masterguard[8], linkman:metasys[8], linkman:mge-shut[8], linkman:mge-utalk[8], linkman:mge-xml[8], linkman:newmge-shut[8], linkman:nitram[8], linkman:nutdrv_qx[8], linkman:oneac[8], linkman:optiups[8], linkman:powercom[8], linkman:powerman-pdu[8], linkman:powerpanel[8], linkman:rhino[8], linkman:richcomm_usb[8], linkman:safenet[8], linkman:snmp-ups[8], linkman:solis[8], linkman:tripplite[8], linkman:tripplitesu[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8], linkman:upscode2[8], linkman:victronups[8] Internet resources: The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/ups.conf.50000644000175000017500000002414512640476461012617 00000000000000'\" t .\" Title: ups.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPS\&.CONF" "5" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ups.conf \- UPS definitions for Network UPS Tools .SH "DESCRIPTION" .sp This file is read by the driver controller \fBupsdrvctl\fR(8), the UPS drivers that use the common core (see \fBnutupsdrv\fR(8), and \fBupsd\fR(8))\&. The file begins with global directives, and then each UPS has a section which contains a number of directives that set parameters for that UPS\&. .sp A UPS section begins with the name of the UPS in brackets, and continues until the next UPS name in brackets or until EOF\&. The name "default" is used internally in upsd, so you can\(cqt use it in this file\&. .sp You must define the \fIdriver\fR and \fIport\fR elements for each entry\&. Anything after that in a section is optional\&. A simple example might look like this: .sp .if n \{\ .RS 4 .\} .nf [myups] driver = blazer_ser port = /dev/ttyS0 desc = "Web server UPS" .fi .if n \{\ .RE .\} .sp A slightly more complicated version includes some extras for the hardware\-specific part of the driver: .sp .if n \{\ .RS 4 .\} .nf [bigups] driver = apcsmart port = /dev/cua00 cable = 940\-0095B sdtype = 2 desc = "Database server UPS" .fi .if n \{\ .RE .\} .sp In this case, the \fBapcsmart\fR(8) driver will receive variables called "cable" and "sdtype" which have special meanings\&. See the man pages of your driver(s) to learn which variables are supported and what they do\&. .SH "GLOBAL DIRECTIVES" .PP \fBchroot\fR .RS 4 Optional\&. The driver will chroot(2) to this directory during initialization\&. This can be useful when securing systems\&. .RE .PP \fBdriverpath\fR .RS 4 Optional\&. Path name of the directory in which the UPS driver executables reside\&. If you don\(cqt specify this, the programs look in a built\-in default directory, which is often /usr/local/ups/bin\&. .RE .PP \fBmaxstartdelay\fR .RS 4 Optional\&. Same as the UPS field of the same name, but this is the default for UPSes that don\(cqt have the field\&. .RE .PP \fBmaxretry\fR .RS 4 Optional\&. Specify the number of attempts to start the driver(s), in case of failure, before giving up\&. A delay of \fIretrydelay\fR is inserted between each attempt\&. Caution should be taken when using this option, since it can impact the time taken by your system to start\&. .sp The default is 1 attempt\&. .RE .PP \fBretrydelay\fR .RS 4 Optional\&. Specify the delay between each restart attempt of the driver(s), as specified by \fImaxretry\fR\&. Caution should be taken when using this option, since it can impact the time taken by your system to start\&. .sp The default is 5 seconds\&. .RE .PP \fBpollinterval\fR .RS 4 Optional\&. The status of the UPS will be refreshed after a maximum delay which is controlled by this setting\&. This is normally 2 seconds\&. This may be useful if the driver is creating too much of a load on your system or network\&. .RE .PP \fBsynchronous\fR .RS 4 Optional\&. The driver work by default in asynchronous mode (i\&.e \fBsynchronous=no\fR)\&. This means that all data are pushed by the driver on the communication socket to upsd (Unix socket on Unix, Named pipe on Windows) without waiting for these data to be actually consumed\&. With some HW, such as ePDUs, that can produce a lot of data, asynchronous mode may cause some congestion, resulting in the socket to be full, and the driver to appear as not connected\&. In such case, the driver will provide the following debug message: .sp .if n \{\ .RS 4 .\} .nf write XX bytes to socket Y failed .fi .if n \{\ .RE .\} .sp By enabling the \fIsynchronous\fR flag (value = \fIyes\fR), the driver will wait for data to be consumed by upsd, prior to publishing more\&. This can be enabled either globally or per driver\&. .sp The default is \fIno\fR (i\&.e\&. asynchronous mode) for backward compatibility of the driver behavior\&. .RE .PP \fBuser\fR .RS 4 Optional\&. If started as root, the driver will setuid(2) to the user id associated with \fIusername\fR\&. .RE .SH "UPS FIELDS" .PP \fBdriver\fR .RS 4 Required\&. This specifies which program will be monitoring this UPS\&. You need to specify the one that is compatible with your hardware\&. See \fBnutupsdrv\fR(8) for more information on drivers in general and pointers to the man pages of specific drivers\&. .RE .PP \fBport\fR .RS 4 Required\&. This is the serial port where the UPS is connected\&. On a Linux system, the first serial port usually is \fI/dev/ttyS0\fR\&. On FreeBSD and similar systems, it probably will be \fI/dev/cuaa0\fR\&. .RE .PP \fBsdorder\fR .RS 4 Optional\&. When you have multiple UPSes on your system, you usually need to turn them off in a certain order\&. upsdrvctl shuts down all the 0s, then the 1s, 2s, and so on\&. To exclude a UPS from the shutdown sequence, set this to \-1\&. .sp The default value for this parameter is 0\&. .RE .PP \fBdesc\fR .RS 4 Optional\&. This allows you to set a brief description that upsd will provide to clients that ask for a list of connected equipment\&. .RE .PP \fBnolock\fR .RS 4 Optional\&. When you specify this, the driver skips the port locking routines every time it starts\&. This may allow other processes to seize the port if you start more than one accidentally\&. .sp You should only use this if your system won\(cqt work without it\&. .sp This may be needed on Mac OS X systems\&. .RE .PP \fBignorelb\fR .RS 4 Optional\&. When you specify this, the driver ignores a low battery condition flag that is reported by the UPS (some devices will switch off almost immediately after setting this flag, or will report this as soons as the mains fails)\&. Instead it will use either of the following conditions to determine when the battery is low: .sp .if n \{\ .RS 4 .\} .nf battery\&.charge < battery\&.charge\&.low battery\&.runtime < battery\&.runtime\&.low .fi .if n \{\ .RE .\} .sp The idea is to set the battery\&.charge\&.low and/or battery\&.runtime\&.low levels in \fBups\&.conf\fR to a value that gives enough time to cleanly shutdown your system: .sp .if n \{\ .RS 4 .\} .nf override\&.battery\&.charge\&.low = 30 override\&.battery\&.runtime\&.low = 180 .fi .if n \{\ .RE .\} .sp In order for this to work, your UPS should be able to (reliably) report charge and/or runtime remaining on battery\&. Use with caution! .RE .PP \fBmaxstartdelay\fR .RS 4 Optional\&. This can be set as a global variable above your first UPS definition and it can also be set in a UPS section\&. This value controls how long upsdrvctl will wait for the driver to finish starting\&. This keeps your system from getting stuck due to a broken driver or UPS\&. .sp The default is 45 seconds\&. .RE .PP \fBsynchronous\fR .RS 4 Optional\&. Same as the global directive of the same name, but this is for a specific device\&. .RE .PP \fBusb_set_altinterface\fR[=\fIaltinterface\fR] .RS 4 Optional\&. Force the USB code to call usb_set_altinterface(0), as was done in NUT 2\&.7\&.2 and earlier\&. This should not be necessary, since the default for bAlternateSetting (as shown in lsusb) is zero on all USB devices seen to date\&. However, this redundant call to usb_set_altinterface() prevents certain UPSes from working on Mac OS X\&. If your UPS requires explicitly setting the alternate interface, include this flag, and email the nut\-upsdev list with details about your UPS and operating system\&. .RE .PP \fBdefault\&.\fR .RS 4 Optional\&. Set a default value for which is used in case the UPS doesn\(cqt provide a value, but will be overwritten if a value is available from the UPS: .sp .if n \{\ .RS 4 .\} .nf default\&.input\&.voltage\&.nominal = 230 .fi .if n \{\ .RE .\} .sp The above will report the nominal input voltage to be 230, unless the UPS tells us differently\&. .RE .PP \fBoverride\&.\fR .RS 4 Optional\&. Set a value for that overrides any value that may be read from the UPS\&. Used for overriding values from the UPS that are clearly wrong (some devices report wrong values for battery voltage for instance): .sp .if n \{\ .RS 4 .\} .nf override\&.battery\&.voltage\&.nominal = 12 .fi .if n \{\ .RE .\} .sp Use with caution! This will only change the appearance of the variable to the outside world, internally in the UPS the original value is used\&. .RE .sp All other fields are passed through to the hardware\-specific part of the driver\&. See those manuals for the list of what is allowed\&. .SH "INTEGRATION" .sp \fBupsdrvctl\fR(8) uses this file to start and stop the drivers\&. .sp The drivers themselves also obtain configuration data from this file\&. Each driver looks up its section and uses that to configure itself\&. .sp \fBupsd\fR(8) learns about which UPSes are installed on this system by reading this file\&. If this system is called "doghouse" and you have defined a UPS in your \fBups\&.conf\fR called "snoopy", then you can monitor it from \fBupsc\fR(8) or similar as "snoopy@doghouse"\&. .SH "SEE ALSO" .sp \fBupsd\fR(8), \fBnutupsdrv\fR(8), \fBupsdrvctl\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/usbhid-ups.txt0000644000175000017500000001653212640473702013616 00000000000000USBHID-UPS(8) ============= NAME ---- usbhid-ups - Driver for USB/HID UPS equipment NOTE ---- This man page only documents the hardware-specific features of the usbhid-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. This driver, formerly called 'newhidups', replaces the legacy 'hidups' driver, which only supported Linux systems. SUPPORTED HARDWARE ------------------ *usbhid-ups* brings USB/HID UPS monitoring to NUT on all platform supporting USB through libusb. It should detect any UPS that uses the HID power device class, but the amount of data will vary depending on the manufacturer and model. At the present time, usbhid-ups supports: - the newer Eaton USB models, - all MGE USB models, - all Dell USB models, - some APC models, - some Belkin models, - some Cyber Power Systems models, - some Powercom models, - some TrippLite models. For a more complete list, refer to the NUT hardware compatibility list, available in the source distribution as data/drivers.list, or on the NUT website. You may use the "explore" driver option to gather information from HID UPSes which are not yet supported; see below for details. This driver is known to work on: - most Linux systems, - FreeBSD (beta stage) and maybe other *BSD, - Darwin / Mac OS X, - Solaris 10. EXTRA ARGUMENTS --------------- This driver also supports the following optional settings: *offdelay*='num':: Set the timer before the UPS is turned off after the kill power command is sent (via the *-k* switch). + The default value is 20 (in seconds). Usually this *must be lower* than 'ondelay', but the driver will *not* warn you upon startup if it isn't. *ondelay*='num':: Set the timer for the UPS to switch on in case the power returns after the kill power command had been sent but before the actual switch off. This ensures the machines connected to the UPS are, in all cases, rebooted after a power failure. + The default value is 30 (in seconds). Usually this *must be greater* than offdelay, but the driver will *not* warn you upon startup if it isn't. Some UPS'es will restart no matter what, even if the power is (still) out at the moment this timer elapses. In that case, you could try if setting 'ondelay = -1' in *ups.conf* helps. *pollfreq*='num':: Set polling frequency, in seconds, to reduce the USB data flow. Between two polling requests, the driver will wait for interrupts (aka UPS notifications), which are data changes returned by the UPS by itself. This mechanism allow to avoid or reduce staleness message, due to the UPS being temporarily overloaded with too much polling requests. The default value is 30 (in seconds). *pollonly*:: If this flag is set, the driver will ignore interrupts it receives from the UPS (not recommended, but needed if these reports are broken on your UPS). *vendor*='regex':: *product*='regex':: *serial*='regex':: *vendorid*='regex':: *productid*='regex':: Select a specific UPS, in case there is more than one connected via USB Each option specifies an extended regular expression (see regex(7)) that must match the UPS's entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4-digit hexadecimal code for vendorid and productid. Try *-DD* for finding out the strings to match. + Examples: - `-x vendor="Foo.Corporation.*"` - `-x vendorid=051d*` (APC) - `-x product=".*(Smart|Back)-?UPS.*"` *bus*='regex':: Select a UPS on a specific USB bus or group of busses. The argument is a regular expression that must match the bus name where the UPS is connected (e.g. bus="002", bus="00[2-3]"). *explore*:: With this option, the driver will connect to any device, including ones that are not yet supported. This must always be combined with the "vendorid" option. In this mode, the driver will not do anything useful except for printing debugging information (typically used with -DD). *maxreport*:: With this option, the driver activates a tweak to workaround buggy firmware returning invalid HID report length. Some APC Back-UPS units are known to have this bug. *interruptonly*:: If this flag is set, the driver will not poll UPS. This also implies using of INPUT flagged objects. Some Powercom units need this option. *interruptsize*='num':: Limit the number of bytes to read from interrupt pipe. For some Powercom units this option should be equal to 8. INSTALLATION ------------ This driver is not built by default. You can build it by using "configure --with-usb=yes". Note that it will also install other USB drivers. You also need to install manually the legacy hotplug files (libhidups and libhid.usermap, generally in /etc/hotplug/usb/), or the udev file (nut-usbups.rules, generally in /etc/udev/rules.d/) to address the permission settings problem. For more information, refer to the README file in nut/scripts/hotplug or nut/scripts/udev. On Linux with MGE equipment, you will need at least a 2.4.25 or 2.6.2 kernel as well as libusb-0.1.8 or later to disable hiddev support and avoid conflict. IMPLEMENTATION -------------- The driver ignores the "port" value in *ups.conf*. Unlike previous versions of this driver, it is now possible to control multiple UPS units simultaneously with this driver, provided they can be distinguished by setting some combination of the "vendor", "product", "serial", "vendorid", and "productid" options. For instance: [mge] driver = usbhid-ups port = auto vendorid = 0463 [tripplite] driver = usbhid-ups port = auto vendorid = 09ae KNOWN ISSUES AND BUGS --------------------- Repetitive timeout and staleness ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some models tends to be unresponsive with the default polling frequency. The result is that your system log will have lots of messages like: usb 2-1: control timeout on ep0in usb 2-1: usbfs: USBDEVFS_CONTROL failed cmd usbhid-ups rqt 128 rq 6 len 256 ret -110 In this case, simply modify the general parameter "pollinterval" to a higher value (like 10 for 10 seconds). This should solve the issue. Got EPERM: Operation not permitted upon driver startup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You have forgotten to install the hotplug files, as explained in the INSTALLATION section above. Don't forget to restart hotplug so that it applies these changes. Unattended shutdowns ~~~~~~~~~~~~~~~~~~~~ The hardware which was used for development of this driver is almost certainly different from what you have, and not all manufacturers follow the USB HID Power Device Class specifications to the letter. You don't want to find out that yours has issues here when a power failure hits your server room and you're not around to manually restart your servers. If you rely on the UPS to shutdown your systems in case of mains failure and to restart them when the power returns, you *must* test this. You can do so by running 'upsmon -c fsd'. With the mains present, this should bring your systems down and then cycle the power to restart them again. If you do the same without mains present, it should do the same, but in this case, the outputs shall remain off until mains power is applied again. AUTHORS ------- Originally sponsored by MGE UPS SYSTEMS. Now sponsored by Eaton Arnaud Quette, Peter Selinger, Arjen de Korte SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutclient_logout.30000644000175000017500000000003012665610645014444 00000000000000.so libnutclient_misc.3 nut-2.7.4/docs/man/libnutclient_tcp.30000644000175000017500000000616012665610645014422 00000000000000'\" t .\" Title: libnutclient_tcp .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "LIBNUTCLIENT_TCP" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" libnutclient_tcp, nutclient_tcp_create_client, nutclient_tcp_is_connected, nutclient_tcp_disconnect, nutclient_tcp_reconnect, nutclient_tcp_set_timeout, nutclient_tcp_get_timeout \- TCP protocol related function for Network UPS Tools high\-level client access library .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf typedef NUTCLIENT_t NUTCLIENT_TCP_t; .fi .sp .nf NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port); int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client); void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client); int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); .fi .SH "DESCRIPTION" .sp These functions allow to manage connections to \fBupsd\fR(8) using NUT TCP protocol\&. .sp The \fBnutclient_tcp_create_client()\fR function create the \fINUTCLIENT_TCP_t\fR context and intend to connect to upsd at \fIhost\fR and \fIport\fR\&. The context must be freed by \fInutclient_destroy()\fR .sp \fIhost\fR can be a sever name or a valid IPv4 or IPv6 adress like "localhost", "127\&.0\&.0\&.1" or "::1"\&. .sp \fIport\fR is a valid TCP port, genrally 3493\&. .sp The \fBnutclient_tcp_is_connected()\fR function test if the connection is valid\&. .sp The \fBnutclient_tcp_disconnect()\fR function force to disconnect the specified connection\&. .sp The \fBnutclient_tcp_reconnect()\fR function force to reconnect a connection, disconnecting it if needed\&. .sp The \fBnutclient_tcp_set_timeout()\fR function set the timeout duration for I/O operations\&. .sp The \fBnutclient_tcp_get_timeout()\fR function retrieve the timeout duration for I/O operations\&. .sp \fItimeout\fR values are specified in seconds, negatives values for blocking\&. .SH "SEE ALSO" .sp \fBlibnutclient\fR(3) \fBlibnutclient_general\fR(3) nut-2.7.4/docs/man/nutdrv_atcl_usb.80000644000175000017500000000727112667757216014277 00000000000000'\" t .\" Title: nutdrv_atcl_usb .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/09/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTDRV_ATCL_USB" "8" "03/09/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutdrv_atcl_usb \- Driver for \*(AqATCL FOR UPS\*(Aq equipment .SH "NOTE" .sp This man page only documents the specific features of the nutdrv_atcl_usb driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver is for UPS hardware which identifies itself as USB idVendor 0001 and idProduct 0000, and iManufacturer ATCL FOR UPS\&. Known manufacturers include Kanji and Plexus\&. The UPS interface seems to be a generic USB\-to\-serial chip, and for hardware manufactured by Kanji and Plexus, the microcontroller appears to emulate a traditional contact\-closure interface\&. This translates into only three states in ups\&.status: \fBOL\fR, \fBOB\fR and \fBOB LB\fR (similar to \fBgenericups\fR(8)), with no other dynamic status values reported\&. Note that these USB identifiers (including the iManufacturer string) have also been seen on devices that are supported by the fuji subdriver of \fBnutdrv_qx\fR(8)\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional setting: .PP \fBvendor\fR=\fIname\fR .RS 4 In case your iManufacturer (Vendor) string does not exactly match ATCL FOR UPS, you may provide an alternate string here\&. Note that a more likely case is that your device is handled by another driver for 0001:0000 devices, such as \fBnutdrv_qx\fR(8)\&. .RE .SH "BUGS" .sp The UPS returns the same code for "load power is off" as for "on line power"\&. This condition will not be observed if the NUT master is powered by the UPS, but may be an issue if the UPS is monitored by a remote system\&. .sp The time between the shutdown command and removal of power seems to be fixed at 30 seconds\&. Ensure that the NUT shutdown script is invoked as late as possible in the shutdown procedure (in case some services take longer than others to clean up)\&. .sp Most contact\-closure UPSes will not power down the load if the line power is present\&. This can create a race when using slave \fBupsmon\fR(8) systems\&. See the \fBupsmon\fR(8) man page for more information\&. The solution to this problem is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command\&. .SH "AUTHORS" .sp Charles Lepple .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "The generic serial driver:" .sp \fBgenericups\fR(8) .SS "The Qx driver:" .sp \fBnutdrv_qx\fR(8) (fuji subdriver) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutdrv_qx.txt0000644000175000017500000007576512667537407013616 00000000000000NUTDRV_QX(8) ============ NAME ---- nutdrv_qx - Driver for Q* protocol serial and USB based UPS equipment NOTE ---- This man page only documents the hardware-specific features of the *nutdrv_qx* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ The *nutdrv_qx* driver is known to work with various UPSes from 'Blazer', 'Energy Sistem', 'Fenton Technologies', 'General Electric', 'Mustek', 'Voltronic Power' (rebranded by many, many - have I said many? - others.. Long story short: if your UPS came with a software called 'Viewpower', chances are high that it works with this driver with one of the <<_extra_arguments,'voltronic*' protocols or with the 'mecer' one>>) and many others. The <<_internet_resources,NUT compatibility table>> lists all the known supported models. Keep in mind, however, that other models not listed there may also be supported, but haven't been tested. All devices with a serial interface and many with a USB interface are supported. EXTRA ARGUMENTS --------------- You may need to override or provide defaults for some values, depending on the make and model of your UPS. The following are the ones that most likely will need changing (see linkman:ups.conf[5]): *ondelay =* 'value':: Time to wait before switching on the UPS (seconds). This value is truncated to units of 60 seconds. + Note that a value below 3 minutes, may cause earlier firmware versions to not switch on automatically, so it defaults to 3 minutes (i.e. 180 seconds). + This option provides a default value for *ups.delay.start* that will then be used by the driver in the automatic shutdown sequence (i.e. calling the driver with the *-k* option, calling linkman:upsdrvctl[8] with the *shutdown* option or when the +FSD+ flag is set and linkman:upsmon[8] enters its shutdown sequence): however you can change this value `on the fly' for the actual session, only for the use with instant commands, setting *ups.delay.start* with linkman:upsrw[8]. *offdelay =* 'value':: Time to wait before shutting down the UPS (seconds). This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds). Defaults to 30 seconds. + This option provides a default value for *ups.delay.shutdown* that will then be used by the driver in the automatic shutdown sequence (i.e. calling the driver with the *-k* option, calling linkman:upsdrvctl[8] with the *shutdown* option or when the +FSD+ flag is set and linkman:upsmon[8] enters its shutdown sequence): however you can change this value `on the fly' for the actual session, only for the use with instant commands, setting *ups.delay.shutdown* with linkman:upsrw[8]. *stayoff*:: If you set stayoff in linkman:ups.conf[5] when FSD arises the UPS will call a *shutdown.stayoff* shutting down after *ups.delay.shutdown* seconds and won't return (see <<_known_problems,KNOWN PROBLEMS>>), otherwise (standard behaviour) the UPS will call *shutdown.return* shutting down after *ups.delay.shutdown* seconds and then turn on after *ups.delay.start* seconds (if mains meanwhile returned). *protocol =* 'string':: Skip autodetection of the protocol to use and only use the one specified. Supported values: 'bestups', 'mecer', 'megatec', 'megatec/old', 'mustek', 'q1', 'voltronic', 'voltronic-qs', 'voltronic-qs-hex' and 'zinto'. + Note that if you end up using the 'q1' protocol, you may want to give a try to the 'mecer', 'megatec' and 'zinto' ones setting the <> (only one, or both). *pollfreq =* 'value':: Set polling frequency, in seconds, to reduce the data flow. Between two polling requests the driver will do `quick polls' dealing just with ups.status. The default value is 30 (in seconds). If your UPS doesn't report either *battery.charge* or *battery.runtime* you may want to add the following ones in order to have guesstimated values: *default.battery.voltage.high =* 'value':: Maximum battery voltage that is reached after about 12 to 24 hours charging. If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge,BATTERY CHARGE>>). *default.battery.voltage.low =* 'value':: Minimum battery voltage just before the UPS automatically shuts down. If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge,BATTERY CHARGE>>). *default.battery.voltage.nominal =* 'value':: *override.battery.voltage.nominal =* 'value':: Some devices show a wrong nominal battery voltage (or none at all), so you may need to override or set a default value. *override.battery.packs =* 'value':: Some devices report a part of the total battery voltage. For instance, if *battery.voltage.nominal* is 24 V, but it reports a *battery.voltage* of around 2 V, the number of *battery.packs* to correct this reading would be 12. The driver will attempt to detect this automatically, but if this fails somehow, you may want to override this value. *runtimecal =* 'value,value,value,value':: Parameter used in the (optional) runtime estimation. This takes two runtimes at different loads. Typically, this uses the runtime at full load and the runtime at half load. For instance, if your UPS has a rated runtime of 240 seconds at full load and 720 seconds at half load, you would enter + runtimecal = 240,100,720,50 + The first load should always be higher than the second. If you have values available for loads other than 100 and 50 % respectively, you can use those too, but keep them spaced apart as far as reasonably possible. Just don't get too close to no load (prediction of runtime depends more on idle load for the battery then). *chargetime =* 'value':: The time needed to fully recharge the battery after being fully discharged. If not specified, the driver defaults to 43200 seconds (12 hours). Only used if *runtimecal* is also specified. *idleload =* 'value':: Minimum battery load used by the driver to estimate the runtime. If not specified, the driver defaults to 10%. Only used if *runtimecal* is also specified. BESTUPS, MECER, MEGATAEC, MEGATEC/OLD, MUSTEK, Q1, VOLTRONIC-QS, VOLTRONIC-QS-HEX, ZINTO PROTOCOLS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *ignoresab*:: Some UPSes incorrectly report the `Shutdown Active' bit as always on, consequently making the driver believe the UPS is nearing a shutdown (and, as a result, ups.status always contains +FSD+... and you know what this means). Setting this flag will make the driver ignore the `Shutdown Active' bit. [[old-blazer-protocols-options]] MECER, MEGATAEC, MEGATEC/OLD, MUSTEK, ZINTO PROTOCOLS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *ondelay*:: The acceptable range is +0..599940+ seconds. *offdelay*:: The acceptable range is +12..600+ seconds. *norating*:: Some UPSes will lock up if you attempt to read rating information from them. Setting this flag will make the driver skip this step. *novendor*:: Some UPSes will lock up if you attempt to read vendor information from them. Setting this flag will make the driver skip this step. BESTUPS PROTOCOL ~~~~~~~~~~~~~~~~ *ondelay*:: The acceptable range is +60..599940+ seconds. *offdelay*:: The acceptable range is +12..5940+ seconds. *pins_shutdown_mode =* 'value':: Set http://www.networkupstools.org/protocols/sola.html#_shutdown_set_command[shutdown mode functionality of Pin 1 and Pin 7] on the UPS DB9 communication port (Per Best Power’s EPS-0059) to 'value' [+0..6+]. Q1 PROTOCOL ~~~~~~~~~~~ *ondelay*:: The acceptable range is +0..599940+ seconds. *offdelay*:: The acceptable range is +12..600+ seconds. VOLTRONIC-QS, VOLTRONIC-QS-HEX PROTOCOLS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *ondelay*:: The acceptable range is +60..599940+ seconds. *offdelay*:: The acceptable range is +12..540+ seconds. VOLTRONIC PROTOCOL ~~~~~~~~~~~~~~~~~~ The following options are supported only by the 'voltronic' protocol. Not all of them are available on all the UPSes supported by this protocol. *ondelay*:: The acceptable range is +0..599940+ seconds. *offdelay*:: The acceptable range is +12..5940+ seconds. *battery_number =* 'value':: Set number of batteries that make a pack to 'value' [+1..9+]. This setting will change the charge and runtime estimation reported by the UPS. *output_phase_angle =* 'value':: Changes output phase angle to the provided value [+000+, +120+, +180+, +240+]°. UPS CAPABILITY SETTINGS ^^^^^^^^^^^^^^^^^^^^^^^ *reset_to_default*:: Reset capability options and their voltage and frequency limits to safe default values. (*Doable only when the UPS is in Standby Mode*) + Note that setting this option will reset also *ups.start.auto*, *battery.protection*, *battery.energysave*, *ups.start.battery*, *outlet.0.switchable*, *input.transfer.high*, *input.transfer.low*, *input.frequency.high* and *input.frequency.low*. These UPSes can be fine-tuned to suit your needs enabling or disabling the following options (the driver should tell you which one the UPS is capable of on startup: the settable ones will be reported either ar 'enabled' or 'disabled' in the logs): *alarm_control =* 'string':: Enable or disable alarm (BEEP!) [+enabled+/+disabled+]. Settable also `on the fly' with *beeper.enable* and *beeper.disable* instant commands. *bypass_alarm =* 'string':: Enable or disable alarm (BEEP!) at Bypass Mode [+enabled+/+disabled+]. *battery_alarm =* 'string':: Enable or disable alarm (BEEP!) at Battery Mode [+enabled+/+disabled+]. *bypass_when_off =* 'string':: Enable or disable bypass when the UPS is Off [+enabled+/+disabled+]. If enabled, AC will directly provide power to connected devices when the UPS is off. *bypass_forbidding =* 'string':: Enable or disable Bypass Forbidding [+enabled+/+disabled+]. If enabled, the UPS will not transfer to bypass mode under any condition. *converter_mode =* 'string':: Enable or disable Converter Mode [+enabled+/+disabled+]. When input frequency is within 40 Hz to 70 Hz, the UPS can be set at a constant output frequency, 50 Hz or 60 Hz. The UPS will still charge battery under this mode. *eco_mode =* 'string':: Enable or disable ECO Mode [+enabled+/+disabled+]. When input voltage/frequency are within acceptable range, the UPS will bypass voltage to output for energy saving. PFC and INVERTER are still active at this mode. Settable also `on the fly' with *bypass.start* and *bypass.stop* instant commands. *advanced_eco_mode =* 'string':: Enable or disable Advanced ECO Mode [+enabled+/+disabled+]. When input voltage/frequency are within acceptable range, the UPS will bypass voltage to output for energy saving. PFC and INVERTER are off at this mode. *battery_open_status_check =* 'string':: Enable or disable Battery Open Status Check [+enabled+/+disabled+]. If enabled, when the UPS is turned on, it will check if the battery is connected or not. *site_fault_detection =* 'string':: Enable or disable site fault detection [+enabled+/+disabled+]. If enabled, the UPS will beep when the input neutral and hot wires are reversed. *constant_phase_angle =* 'string':: Enable or disable Constant Phase Angle Function (output and input phase angles are not equal) [+enabled+/+disabled+]. *limited_runtime_on_battery =* 'string':: Enable or disable limited runtime on battery mode [+enabled+/+disabled+]. BYPASS MODE VOLTAGE/FREQUENCY LIMITS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Variables to fine-tune voltage and frequency limits for Bypass mode. These limits are reset to safe default values by *reset_to_default*. If AC voltage and frequency are within acceptable range, Bypass mode will be used (If the UPS is capable of and it's enabled). Since these values are device-specific, if your UPS support them, you will get their settable limits printed in the logs on startup. *max_bypass_volt =* 'value':: Maximum voltage for Bypass Mode (V). *min_bypass_volt =* 'value':: Minimum voltage for Bypass Mode (V). *max_bypass_freq =* 'value':: Maximum frequency for Bypass Mode (Hz). *min_bypass_freq =* 'value':: Minimum frequency for Bypass Mode (Hz). OPTIONS SPECIFIC FOR P31 UPSES ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following options are available only on P31 UPSes. *work_range_type =* 'string':: Device grid working range for P31 UPSes [+Appliance+/+UPS+]. TESTING ^^^^^^^ This protocol comes with a couple of functions that are not enabled by default because of the lack of knowledge of some part of the communication protocol used by these UPSes by your friendly neighborhood developer. Since these functions are supposed to be queries to the UPS for some kind of informations, they _should_ not make your UPS go boom. So if you are brave enough to risk your UPS and attached devices' life to help the developers, this will be very appreciated.. *Do it at your own risk*. *testing*:: If invoked the driver will exec also commands that still need testing. SERIAL INTERFACE ONLY ~~~~~~~~~~~~~~~~~~~~~ *cablepower =* 'string':: By default the driver will set DTR and clear RTS ('normal'). If you find that your UPS isn't detected or the communication with the UPS is unreliable, you may try if clear DTR and set RTS ('reverse'), set DTR and RTS ('both') or clear DTR and RTS ('none') improves this situation. USB INTERFACE ONLY ~~~~~~~~~~~~~~~~~~ *port =* 'string':: You must set 'value' to *auto*. *vendorid =* 'regex':: *productid =* 'regex':: *vendor =* 'regex':: *product =* 'regex':: *serial =* 'regex':: Select a specific UPS, in case there is more than one connected via USB. Each option specifies an extended regular expression (see *regex(7)*) that must match the UPS's entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4-digit hexadecimal code for vendorid and productid. Try *-DD* for finding out the strings to match. + Examples: - `-x vendor="Foo.Corporation.*"` - `-x vendorid=051d*` (APC) - `-x product=".*(Smart|Back)-?UPS.*"` *bus =* 'regex':: Select a UPS on a specific USB bus or group of busses. The argument is a regular expression that must match the bus name where the UPS is connected (e.g. +bus="002"+, +bus="00[2-3]"+). *subdriver =* 'string':: Select a serial-over-USB subdriver to use. You have a choice between *cypress*, *fabula*, *fuji*, *ippon*, *krauler*, *phoenix* and *sgs*. When using this option, it is mandatory to also specify the *vendorid* and *productid*. *langid_fix =* 'value':: Apply the language ID workaround to the *krauler* subdriver. This is mandatory for some devices to work (LDLC, Dynamix and others). You must provide *value* (+0x409+ or +0x4095+), according to your device entry in NUT hardware compatibility list (HCL). IMPLEMENTATION NOTES ^^^^^^^^^^^^^^^^^^^^ *'fabula' subdriver*:: This subdriver, meant to be used with the 'megatec' protocol, does *not* support the various *test.battery* commands. Plus, the *shutdown.return* command ignores the values set in 'ups.delay.start'/*ondelay* and makes the UPS turn on the load as soon as power is back. *'fuji' subdriver*:: This subdriver, meant to be used with the 'megatec' protocol, does *not* support the *shutdown.stayoff* and *load.off* commands. Plus, the *shutdown.return* command ignores the values set in 'ups.delay.start'/*ondelay* and makes the UPS turn on the load as soon as power is back. *'krauler' subdriver*:: This subdriver, meant to be used with the 'megatec' protocol, does *not* support the shutdown commands, i.e.: *shutdown.return*, *shutdown.stayoff* and *load.off*. UPS COMMANDS ------------ This driver supports some instant commands (see linkman:upscmd[8]): *beeper.toggle*:: Toggle the UPS beeper. (Not available on some hardware) *load.on*:: Turn on the load immediately. (Not available on some hardware) *load.off*:: Turn off the load immediately (see <<_known_problems,KNOWN PROBLEMS>>). *shutdown.return*:: Turn off the load and return when power is back. Uses the timers defined by *ups.delay.start* and *ups.delay.shutdown*. *shutdown.stayoff*:: Turn off the load and remain off (see <<_known_problems,KNOWN PROBLEMS>>). Uses the timer defined by *ups.delay.shutdown*. *shutdown.stop*:: Stop a shutdown in progress. *test.battery.start.deep*:: Perform a long battery test. (Not available on some hardware) *test.battery.start.quick*:: Perform a quick (10 second) battery test. *test.battery.stop*:: Stop a running battery test. (Not available on some hardware) BESTUPS, MECER, MEGATEC, MEGATEC/OLD, MUSTEK, Q1, ZINTO PROTOCOLS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *test.battery.start* 'value':: Perform a battery test for the duration of 'value' seconds (truncated to 60 seconds) [+60..5940+]. VOLTRONIC POWER P98 UNITS (WITH MECER PROTOCOL) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *test.battery.start* 'value':: Perform a battery test for the duration of 'value' seconds (truncated to 60 seconds) [+12..5940+]. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds). VOLTRONIC PROTOCOL ~~~~~~~~~~~~~~~~~~ The following instant commands are available for the 'voltronic' protocol. Not all of them are available on all the UPSes supported by this protocol. *beeper.enable*:: Enable the UPS beeper. *beeper.disable*:: Disable the UPS beeper. *test.battery.start* 'value':: Perform a battery test for the duration of 'value' seconds [+12..5940+]. This value is truncated to units of 6 seconds (less than 60 seconds) or 60 seconds (more than 60 seconds). *outlet.1.load.off*:: Turn off outlet 1 load immediately. *outlet.1.load.on*:: Turn on outlet 1 load immediately. *outlet.2.load.off*:: Turn off outlet 2 load immediately. *outlet.2.load.on*:: Turn on outlet 2 load immediately. *outlet.3.load.off*:: Turn off outlet 3 load immediately. *outlet.3.load.on*:: Turn on outlet 3 load immediately. *outlet.4.load.off*:: Turn off outlet 4 load immediately. *outlet.4.load.on*:: Turn on outlet 4 load immediately. *bypass.start*:: Put the UPS in ECO Mode. *bypass.stop*:: Take the UPS out of ECO Mode. BATTERY CHARGE -------------- Due to popular demand, this driver will report a guesstimated *battery.charge* and optionally *battery.runtime*, provided you specified a couple of the <<_extra_arguments,EXTRA ARGUMENTS>> listed above. If you specify both *battery.voltage.high* and *battery.voltage.low* in linkman:ups.conf[5], but don't enter *runtimecal*, it will guesstimate the state of charge by looking at the battery voltage alone. This is not reliable under load, as this only gives reasonably accurate readings if you disconnect the load, let the battery rest for a couple of minutes and then measure the open cell voltage. This just isn't practical if the power went out and the UPS is providing power for your systems. battery.voltage - battery.voltage.low battery.charge = ------------------------------------------ x 100 % battery.voltage.high - battery.voltage.low There is a way to get better readings without disconnecting the load but this requires one to keep track on how much (and how fast) current is going in and out of the battery. If you specified the *runtimecal*, the driver will attempt to do this. Note however, that this heavily relies on the values you enter and that the UPS must be able to report the load as well. There are quite a couple of devices that report 0 % (or any other fixed value) at all times, in which case this obviously doesn't work. The driver also has no way of determining the degradation of the battery capacity over time, so you'll have to deal with this yourself (by adjusting the values in *runtimecal*). Also note that the driver guesses the initial state of charge based on the battery voltage, so this may be less than 100 %, even when you are certain that they are full. There is just no way to reliably measure this between 0 and 100 % full charge. This is better than nothing (but not by much). If any of the above calculations is giving you incorrect readings, you are the one that put in the values in linkman:ups.conf[5], so don't complain with the author. If you need something better, buy a UPS that reports *battery.charge* and *battery.runtime* all by itself without the help of a NUT driver. NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS ---------------------------------------------- The *nutdrv_qx* driver having replaced the megatec ones, some configuration changes may be required by users switching to *nutdrv_qx*. Part of this, the following megatec options, in linkman:ups.conf[5], have to be changed: *battvolts*:: You need to use 'default.battery.voltage.high' and 'default.battery.voltage.low' *dtr* and *rts*:: You need to use 'cablepower' *ignoreoff*:: This parameter can simply be discarded, since it was a wrong understanding of the specification. NOTES FOR THE PREVIOUS USER OF BLAZER DRIVERS --------------------------------------------- The *nutdrv_qx* driver having replaced the blazer ones, some configuration changes may be required by users switching to *nutdrv_qx*. Part of this, the following blazer options, in linkman:ups.conf[5], have to be changed: *ondelay*:: While the previous blazer drivers expected minutes, the new *nutdrv_qx* driver wants seconds. The following instant command has also been changed: *test.battery.start* 'value':: While the old blazer drivers expected a 'value' in minutes, the *nutdrv_qx* driver wants a 'value' in seconds. NOTES FOR THE PREVIOUS USER OF BESTUPS DRIVER --------------------------------------------- The *nutdrv_qx* driver having replaced the bestups one, some configuration changes may be required by users switching to *nutdrv_qx*. Part of this, the following bestups options, in linkman:ups.conf[5], are no longer supported by this driver: *nombattvolt*:: *battvoltmult*:: See <<_battery_charge,BATTERY CHARGE>>. *ID*:: Discarded. NOTES FOR THE PREVIOUS USER OF VOLTRONIC DRIVERS ------------------------------------------------ The *nutdrv_qx* driver having replaced the voltronic ones, some configuration changes may be required by users switching to *nutdrv_qx*. Part of this, the following voltronic options, in linkman:ups.conf[5], have to be changed: *ondelay*:: While the previous voltronic drivers expected minutes, the new *nutdrv_qx* driver wants seconds. It no longer defaults to 0 minutes but to 3 minutes (i.e. 180 seconds) for compatibility with the users switching from the old blazer drivers. *battnumb*:: This option has been renamed to *battery_number*. The following options are no longer supported by this driver, you can now change them more conveniently `on the fly' calling linkman:upsrw[8] with the appropriate NUT variable - provided that your UPS supports them. [horizontal] *battpacks*:: -> *battery.packs* + Set number of battery packs in parallel [+1..99+]. This setting will change the charge and runtime estimation reported by the UPS. *battlow*:: -> *battery.voltage.low* + Set minimum battery voltage just before the UPS automatically shuts down. This setting will change the charge and runtime estimation reported by the UPS. *auto_reboot*:: -> *ups.start.auto* + Enable or disable auto reboot [+enabled+/+disabled+]. If enabled, the UPS will auto recover when AC power returns. *battery_protection*:: -> *battery.protection* + Enable or disable battery deep discharge protection [+enabled+/+disabled+]. *energy_saving*:: -> *battery.energysave* + Enable or disable Green power function [+enabled+/+disabled+]. If enabled, for energy saving, the UPS will auto off when there is no load. *cold_start*:: -> *ups.start.battery* + Enable or disable Cold Start [+enabled+/+disabled+]. If enabled, the UPS can be turned on also if AC is not connected to the UPS. *outlet_control*:: -> *outlet.0.switchable* + Enable or disable programmable outlets control at battery mode [+enabled+/+disabled+]. If enabled, the UPS will cut off programmable outlets after backup time (set through *outlet.*{*1*,*2*,*3*,*4*}**.delay.shutdown**) arrives. If disabled, the UPS will provide continuous power to programmable outlets until the battery is running out. *max_eco_volt*:: -> *input.transfer.high* + Maximum voltage for ECO Mode (V). If AC voltage is within acceptable range, ECO mode will be used (If the UPS is capable of and it’s enabled). *min_eco_volt*:: -> *input.transfer.low* + Minimum voltage for ECO Mode (V). If AC voltage is within acceptable range, ECO mode will be used (If the UPS is capable of and it’s enabled). *max_eco_freq*:: -> *input.frequency.high* + Maximum frequency for ECO Mode (Hz). If AC frequency is within acceptable range, ECO mode will be used (If the UPS is capable of and it’s enabled). *min_eco_freq*:: -> *input.frequency.low* + Minimum frequency for ECO Mode (Hz). If AC frequency is within acceptable range, ECO mode will be used (If the UPS is capable of and it’s enabled). *outlet1_delay*:: -> *outlet.1.delay.shutdown* + Delay time before programmable outlet 1 shuts down the load when on battery mode [+0..59940+] (seconds). *outlet2_delay*:: -> *outlet.2.delay.shutdown* + Delay time before programmable outlet 2 shuts down the load when on battery mode [+0..59940+] (seconds). *outlet3_delay*:: -> *outlet.3.delay.shutdown* + Delay time before programmable outlet 3 shuts down the load when on battery mode [+0..59940+] (seconds). *outlet4_delay*:: -> *outlet.4.delay.shutdown* + Delay time before programmable outlet 4 shuts down the load when on battery mode [+0..59940+] (seconds). *batt_type*:: -> *battery.type* + Battery type (for P31 UPSes only) [+Li+/+Flooded+/+AGM+]. KNOWN PROBLEMS -------------- Some UPS commands aren't supported by all models. In most cases, the driver will send a message to the system log when the user tries to execute an unsupported command. Unfortunately, some models don't even provide a way for the driver to check for this, so the unsupported commands will silently fail. Both the *load.off* and *shutdown.stayoff* instant commands are meant to turn the load off indefinitely. However, some UPS models don't allow this. Some models report a bogus value for the beeper status (will always be 'enabled' or 'disabled'). So, the *beeper.toggle* command may appear to have no effect in the status reported by the driver when, in fact, it is working fine. The temperature and load value is known to be bogus in some models. VOLTRONIC-QS UNITS ~~~~~~~~~~~~~~~~~~ Both *load.off* and *shutdown.stayoff* instant commands are known to work as expected (i.e. turn the load off indefinitely) only if mains is present, otherwise, as soon as mains returns the load will be powered. After issuing a *shutdown.return* instant command, the UPS won't wait *ondelay* before powering on the load, provided the following conditions are met: - if the load has been previously (no matter how long before) powered off through *load.off*/*shutdown.stayoff* _and_ powered on through *load.on*/*shutdown.stop* _and_ - if AC wasn't cut after issuing the *load.off*/*shutdown.stayoff* (i.e. the UPS didn't turn itself off) _and_ - if there's a power outage after issuing the *shutdown.return* command In this case, as soon as mains returns the load will be powered. VOLTRONIC-QS-HEX UNITS ~~~~~~~~~~~~~~~~~~~~~~ *shutdown.return*, *load.off*, and *shutdown.stayoff* instant commands are known to work as expected only if mains is present, otherwise, as soon as mains returns the load will be powered. UPS WARNINGS (VOLTRONIC PROTOCOL) --------------------------------- The UPSes supported by 'voltronic' protocol report warnings through a 64bit flag (bit1bit2...bit63bit64) where 1 means that a warning arose, while 0 means no warning. Since more than one warning at a time can be signaled, and because of the limited space in the ups.alarm variable, if the length of the warnings exceeds that of ups.alarms variable, they will be reported as bits. If you want to know the explanation of that bit you can either watch the log or see the next table (unlisted bits equal to unknown warnings). .UPS Warnings for 'voltronic' UPSes [cols="5>,95",options="autowidth,header",frame="topbot",grid="rows",align="center",caption=""] |==== |# |Corresponding Warning |1 |Battery disconnected |2 |Neutral not connected |3 |Site fault |4 |Phase sequence incorrect |5 |Phase sequence incorrect in bypass |6 |Input frequency unstable in bypass |7 |Battery overcharged |8 |Low battery |9 |Overload alarm |10 |Fan alarm |11 |EPO enabled |12 |Unable to turn on UPS |13 |Over temperature alarm |14 |Charger alarm |15 |Remote auto shutdown |16 |L1 input fuse not working |17 |L2 input fuse not working |18 |L3 input fuse not working |19 |Positive PFC abnormal in L1 |20 |Negative PFC abnormal in L1 |21 |Positive PFC abnormal in L2 |22 |Negative PFC abnormal in L2 |23 |Positive PFC abnormal in L3 |24 |Negative PFC abnormal in L3 |25 |Abnormal in CAN-bus communication |26 |Abnormal in synchronous signal circuit |27 |Abnormal in synchronous pulse signal circuit |28 |Abnormal in host signal circuit |29 |Male connector of parallel cable not connected well |30 |Female connector of parallel cable not connected well |31 |Parallel cable not connected well |32 |Battery connection not consistent in parallel systems |33 |AC connection not consistent in parallel systems |34 |Bypass connection not consistent in parallel systems |35 |UPS model types not consistent in parallel systems |36 |Capacity of UPSs not consistent in parallel systems |37 |Auto restart setting not consistent in parallel systems |38 |Battery cell over charge |39 |Battery protection setting not consistent in parallel systems |40 |Battery detection setting not consistent in parallel systems |41 |Bypass not allowed setting not consistent in parallel systems |42 |Converter setting not consistent in parallel systems |43 |High loss point for frequency in bypass mode not consistent in parallel systems |44 |Low loss point for frequency in bypass mode not consistent in parallel systems |45 |High loss point for voltage in bypass mode not consistent in parallel systems |46 |Low loss point for voltage in bypass mode not consistent in parallel systems |47 |High loss point for frequency in AC mode not consistent in parallel systems |48 |Low loss point for frequency in AC mode not consistent in parallel systems |49 |High loss point for voltage in AC mode not consistent in parallel systems |50 |Low loss point for voltage in AC mode not consistent in parallel systems |51 |Warning for locking in bypass mode after 3 consecutive overloads within 30 min |52 |Warning for three-phase AC input current unbalance |53 |Warning for a three-phase input current unbalance detected in battery mode |54 |Warning for Inverter inter-current unbalance |55 |Programmable outlets cut off pre-alarm |56 |Warning for Battery replace |57 |Abnormal warning on input phase angle |58 |Warning!! Cover of maintain switch is open |62 |EEPROM operation error |==== AUTHORS ------- Daniele Pezzini , Arnaud Quette , John Stamp , Peter Selinger , Arjen de Korte , Alexander Gordeev SEE ALSO -------- linkman:blazer_ser[8], linkman:blazer_usb[8], linkman:nutupsdrv[8], linkman:ups.conf[5], linkman:upsc[8], linkman:upscmd[8], linkman:upsdrvctl[8], linkman:upsmon[8], linkman:upsrw[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ The NUT HCL: http://www.networkupstools.org/stable-hcl.html nut-2.7.4/docs/man/upsd.users.50000644000175000017500000000744412640476463013204 00000000000000'\" t .\" Title: upsd.users .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSD\&.USERS" "5" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upsd.users \- Administrative user definitions for NUT upsd .SH "DESCRIPTION" .sp Administrative commands such as setting variables and the instant commands are powerful, and access to them needs to be restricted\&. This file defines who may access them, and what is available\&. .sp Each user gets its own section\&. The fields in that section set the parameters associated with that user\(cqs privileges\&. The section begins with the name of the user in brackets, and continues until the next user name in brackets or EOF\&. These users are independent of /etc/passwd\&. .sp Here are some examples to get you started: .sp .if n \{\ .RS 4 .\} .nf [admin] password = mypass actions = set actions = fsd instcmds = all .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf [pfy] password = duh instcmds = test\&.panel\&.start instcmds = test\&.panel\&.stop .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf [monmaster] password = blah upsmon master .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf [monslave] password = abcd upsmon slave .fi .if n \{\ .RE .\} .SH "FIELDS" .PP \fBpassword\fR .RS 4 Set the password for this user\&. .RE .PP \fBactions\fR .RS 4 Allow the user to do certain things with upsd\&. To specify multiple actions, use multiple instances of the \fBactions\fR field\&. Valid actions are: .PP SET .RS 4 change the value of certain variables in the UPS .RE .PP FSD .RS 4 set the forced shutdown flag in the UPS\&. This is equivalent to an "on battery + low battery" situation for the purposes of monitoring\&. .RE .RE .sp The list of actions is expected to grow in the future\&. .PP \fBinstcmds\fR .RS 4 Let a user initiate specific instant commands\&. Use "ALL" to grant all commands automatically\&. To specify multiple commands, use multiple instances of the \fBinstcmds\fR field\&. For the full list of what your UPS supports, use "upscmd \-l"\&. .sp The cmdvartab file supplied with the distribution contains a list of most of the known command names\&. .RE .PP \fBupsmon\fR .RS 4 Add the necessary actions for a upsmon process to work\&. This is either set to "master" or "slave"\&. .sp Do not attempt to assign actions to upsmon by hand, as you may miss something important\&. This method of designating a "upsmon user" was created so internal capabilities could be changed later on without breaking existing installations\&. .RE .SH "SEE ALSO" .sp \fBupsd\fR(8), \fBupsd.conf\fR(5) .SH "INTERNET RESOURCES" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutscan_display_parsable.30000644000175000017500000000471312665610653016132 00000000000000'\" t .\" Title: nutscan_display_parsable .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_DISPLAY_PARS" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_display_parsable \- Display the specified `nutscan_device_t` structure on stdout\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf void nutscan_display_parsable(nutscan_device_t * device); .fi .SH "DESCRIPTION" .sp The \fBnutscan_display_parsable()\fR function displays all NUT devices in \fIdevice\fR to stdout\&. It displays them in a way that can be easily parsed which is: .sp :driver="",port=""[,="",="",\&...] .sp may be one of USB, SNMP, XML, NUT, IPMI or AVAHI\&. is the name of the driver\(cqs binary corresponding to this device\&. and depend on , see the corresponding driver\(cqs man page\&. .SH "SEE ALSO" .sp \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_nut\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_cidr_to_ip\fR(3) nut-2.7.4/docs/man/riello_usb.txt0000644000175000017500000000127012640444140013655 00000000000000RIELLO_USB(8) =========== NAME ---- riello_usb - Driver for Riello UPS Protocol UPS equipment via USB SYNOPSIS -------- *riello_usb* -h *riello_usb* -a 'UPS_NAME' ['OPTIONS'] NOTE: This man page only documents the hardware-specific features of the riello_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ riello_usb supports all recent Riello UPS with USB. Older Riello UPS products are not supported. AUTHOR ------ Massimo Zampieri SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/bcmxcp.txt0000644000175000017500000000424112640443572013003 00000000000000BCMXCP(8) ========= NAME ---- bcmxcp - Driver for UPSes supporting the serial BCM/XCP protocol NOTE ---- This man page only documents the hardware-specific features of the bcmxcp driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ This driver should recognize all serial BCM/XCP-compatible UPSes. It has been developed and tested on Powerware PW5115 and PW9120 hardware. If your UPS has a USB connection, you may also consult the linkman:bcmxcp_usb[8] driver documentation. EXTRA ARGUMENTS --------------- This driver supports the following optional settings in the linkman:ups.conf[5]. *shutdown_delay=*'delay':: The number of seconds that the UPS should wait between receiving the shutdown command (`upsdrvctl shutdown`) and actually shutting off. *baud_rate=*'rate':: Communication speed for the UPS. If this is set to 9600, it tries to connect to the UPS at 9600bps. If it fails to communicate, it will go into baud-hunting. It starts at 1200 and goes up to 19200. If it succeeds, it tell you the speed it connected with. If not included in the config, it defaults to baud-hunting. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- - *shutdown_delay =* '120' - *baud_rate =* 'none' INSTANT COMMANDS ---------------- This driver supports the following Instant Commands: *shutdown.return*:: Turn off the load and return when power is back. *shutdown.stayoff*:: Turn off the load and remain off. *test.battery.start*:: Start a battery test. *outlet.n.shutdown.return*:: Turn off the load on outlet 'n' and return when power is back. ('n' is the outlet number reported by the upsc command) TODO LIST --------- Report UPS statistics information:: BCM/XCP supports reporting of UPS statistics data. Change settings:: Access the config register to change settings. BUGS ---- None known. AUTHOR ------ Tore Ørpetveit SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] The USB BCM/XCP driver: ~~~~~~~~~~~~~~~~~~~~~~~ linkman:bcmxcp_usb[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/libnutclient_devices.txt0000644000175000017500000000215612640444140015720 00000000000000LIBNUTCLIENT_DEVICES(3) ======================= NAME ---- libnutclient_devices, nutclient_get_devices, nutclient_has_device, nutclient_get_device_description - Device related functions in Network UPS Tools high-level client access library SYNOPSIS -------- #include typedef void* NUTCLIENT_t; strarr nutclient_get_devices(NUTCLIENT_t client); int nutclient_has_device(NUTCLIENT_t client, const char* dev); char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev); DESCRIPTION ----------- These functions allow to manage devices. The *nutclient_get_devices()* function retrieve the list of devices monitored by a client. The returned strarr must be freed by 'strarr_free'. The *nutclient_has_device()* function test if a device is monitored by a client. The *nutclient_get_device_description()* function retrieve the device description. The returned description string must be freed. 'dev' is the device name. SEE ALSO -------- linkman:libnutclient[3] linkman:libnutclient_commands[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] linkman:libnutclient_variables[3] nut-2.7.4/docs/man/upscli_ssl.30000644000175000017500000000371112665610641013233 00000000000000'\" t .\" Title: upscli_ssl .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "UPSCLI_SSL" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" upscli_ssl \- Check SSL mode for current connection .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf int upscli_ssl(UPSCONN_t *ups); .fi .SH "DESCRIPTION" .sp The \fBupscli_ssl\fR() function takes the pointer \fIups\fR to a UPSCONN_t state structure\&. It only returns 1 if SSL support has been compiled into the \fBupsclient\fR(3) library, and if it was successfully enabled for this connection\&. .SH "RETURN VALUE" .sp The \fBupscli_ssl\fR() function returns 1 if SSL is running, and 0 if not\&. It returns \-1 in the event of an error\&. .SH "SEE ALSO" .sp \fBupscli_fd\fR(3), \fBupscli_get\fR(3), \fBupscli_readline\fR(3), \fBupscli_sendline\fR(3), \fBupscli_strerror\fR(3), \fBupscli_upserror\fR(3) nut-2.7.4/docs/man/nutscan_scan_eaton_serial.30000644000175000017500000000476712665610652016275 00000000000000'\" t .\" Title: nutscan_scan_eaton_serial .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_EATON_S" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_eaton_serial \- Scan serial ports for Eaton devices (XCP, SHUT and Q1)\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_list); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_eaton_serial()\fR function tries to detect NUT devices which are compatible with Eaton\(cqs serial device protocols (SHUT, XCP and Q1 (aka blazer))\&. \fIports_list\fR is a NULL terminated array of pointers to strings containing serial device name (/dev/ttyS0, COM1, /dev/ttya\&...) .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_eaton_serial()\fR function returns a pointer to a nutscan_device_t structure containing all found devices or NULL if an error occurs or no device is found\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_get_serial_ports_list\fR(3) nut-2.7.4/docs/man/rhino.80000644000175000017500000000573212640476512012204 00000000000000'\" t .\" Title: rhino .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "RHINO" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" rhino \- Driver for Brazilian Microsol RHINO UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the rhino driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver has been tested with : .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Rhino 6000 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Rhino 7500 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Rhino 10000 VA .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Rhino 20000 VA .RE .sp All Rhino models are sinusoidal on\-line\&. .SH "EXTRA ARGUMENTS" .sp This driver support the following extra optional settings in \fBups.conf\fR(5): .sp \fBbattext = n\fR \- (default = 0, no extra battery, where n = Ampere*hour ) .SH "COMMANDS" .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} load\&.on \- Turn on the load immediately .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} load\&.off \- Turn off the load immediately .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} bypass\&.start \- Put the UPS in bypass mode .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} bypass\&.stop \- Put the UPS out of bypass mode .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} shutdown\&.stayoff \- Shut down in 3 minutes and do not return .RE .SH "AUTHOR" .sp Silvino B\&. Magalhães .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/nutclient_get_device_description.30000644000175000017500000000003312665610643017635 00000000000000.so libnutclient_devices.3 nut-2.7.4/docs/man/gamatronic.txt0000644000175000017500000000137312640443572013656 00000000000000GAMATRONIC(8) ============= NAME ---- gamatronic - Driver for Gamatronic UPS equipment NOTE ---- This man page only documents the hardware-specific features of the gamatronic driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ Various - Rebuilt to work with Gamatronic UPS Units, but should recognize any UPS that speaks the SEC protocol at 1200-19200 bps. EXTRA ARGUMENTS --------------- This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHOR ------ Nadav Moskovitch SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~ linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ nut-2.7.4/docs/man/nutscan_scan_nut.30000644000175000017500000000536012665610651014423 00000000000000'\" t .\" Title: nutscan_scan_nut .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/02/2016 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "NUTSCAN_SCAN_NUT" "3" "03/02/2016" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" nutscan_scan_nut \- Scan network for available NUT services\&. .SH "SYNOPSIS" .sp .nf #include .fi .sp .nf nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, long usec_timeout); .fi .SH "DESCRIPTION" .sp The \fBnutscan_scan_nut()\fR function try to detect available NUT services and their associated devices\&. It issues a NUT request on every IP ranging from \fIstartIP\fR to \fIstopIP\fR\&. \fIstartIP\fR is mandatory, \fIstopIP\fR is optional\&. Those IP may be either IPv4 or IPv6 addresses or host names\&. .sp You MUST call \fBnutscan_init\fR(3) before using this function\&. .sp A specific \fIport\fR number may be passed, or NULL to use the default NUT port\&. .sp This function waits up to \fIusec_timeout\fR microseconds before considering an IP address does not respond to NUT queries\&. .SH "RETURN VALUE" .sp The \fBnutscan_scan_nut()\fR function returns a pointer to a nutscan_device_t structure containing all found devices or NULL if an error occurs or no device is found\&. .SH "SEE ALSO" .sp \fBnutscan_init\fR(3), \fBnutscan_scan_usb\fR(3), \fBnutscan_scan_xml_http\fR(3), \fBnutscan_scan_snmp\fR(3), \fBnutscan_scan_avahi\fR(3), \fBnutscan_scan_ipmi\fR(3), \fBnutscan_display_ups_conf\fR(3), \fBnutscan_display_parsable\fR(3), \fBnutscan_new_device\fR(3), \fBnutscan_free_device\fR(3), \fBnutscan_add_option_to_device\fR(3), \fBnutscan_add_device_to_device\fR(3), \fBnutscan_scan_eaton_serial\fR(3), \fBnutscan_cidr_to_ip\fR(3) nut-2.7.4/docs/man/nutscan_scan_ipmi.txt0000644000175000017500000000162212640444140015214 00000000000000NUTSCAN_SCAN_IPMI(3) ==================== NAME ---- nutscan_scan_ipmi - Scan local IPMI devices. SYNOPSIS -------- #include nutscan_device_t * nutscan_scan_ipmi(void); DESCRIPTION ----------- The *nutscan_scan_ipmi()* function is not implemented yet. You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ The *nutscan_scan_ipmi()* function is not implemented yet. SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3], linkman:nutscan_cidr_to_ip[3] nut-2.7.4/docs/man/libnutclient_general.txt0000644000175000017500000000217012640473702015715 00000000000000LIBNUTCLIENT_GENERAL(3) ======================= NAME ---- libnutclient_general, nutclient_destroy, strarr_alloc, strarr_free - General and utility functions in Network UPS Tools high-level client access library SYNOPSIS -------- #include typedef void* NUTCLIENT_t; void nutclient_destroy(NUTCLIENT_t client); typedef char** strarr; strarr strarr_alloc(unsigned short count); void strarr_free(strarr arr); DESCRIPTION ----------- The *nutclient_destroy()* function destroys a 'NUTCLIENT_t' or derived (like 'NUTCLIENT_TCP_t') connection object, and frees allocated memory. The *strarr* type represents an array of C strings (array of char pointer). The array must always be terminated by a NULL pointer. Pointed strings must be allocated by (x)calloc or (x)strdup. The *strarr_alloc()* function allocates a 'strarr' array with the specified number of (non-initialized) string pointers. Another additional pointer set to 0 is added at the end of the array. The *strarr_free* function frees a 'strarr' array. It also frees all pointed strings. 'dev' is the device name. SEE ALSO -------- linkman:libnutclient[3] nut-2.7.4/docs/man/upscli_sendline.txt0000644000175000017500000000146412640443572014713 00000000000000UPSCLI_SENDLINE(3) ================== NAME ---- upscli_sendline - send a single command to a UPS SYNOPSIS -------- #include int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); DESCRIPTION ----------- The *upscli_sendline()* function takes the pointer 'ups' to a `UPSCONN_t` state structure and transmits a buffer 'buf' of size 'buflen' to the server. The data in 'buf' must be a fully formatted protocol command as no parsing of the buffer occurs within this function. RETURN VALUE ------------ The *upscli_sendline()* function returns 0 on success, or -1 if an error occurs. SEE ALSO -------- linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] nut-2.7.4/docs/man/etapro.80000644000175000017500000000353612640476501012355 00000000000000'\" t .\" Title: etapro .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "ETAPRO" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" etapro \- Driver for ETA UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the etapro driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp This driver supports ETA UPS equipment with the "PRO" option for smart mode\&. .SH "EXTRA ARGUMENTS" .sp This driver does not support any extra settings in the \fBups.conf\fR(5)\&. .SH "AUTHOR" .sp Marek Michalkiewicz .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/mge-shut.80000644000175000017500000001040312640476506012610 00000000000000'\" t .\" Title: mge-shut .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "MGE\-SHUT" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" mge-shut \- Driver for SHUT Protocol UPS equipment .SH "SYNOPSIS" .sp \fBmge\-shut\fR \-h .sp \fBmge\-shut\fR \-a \fIUPS_NAME\fR [\fIOPTIONS\fR] .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBNote\fR .ps -1 .br .sp This man page only documents the hardware\-specific features of the mge\-shut driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .sp .5v .RE .SH "SUPPORTED HARDWARE" .sp mge\-shut supports all recent Eaton, MGE and Dell UPS models which use the SHUT (Serial HID UPS Transfer) protocol\&. .sp Older MGE models, such as Pulsar ESV+, Pulsar EX and Pulsar ES+, use the U\-Talk protocol and should use the mge\-utalk driver\&. .SH "EXTRA ARGUMENTS" .sp This driver also supports the following optional settings: .PP \fBlowbatt\fR=\fInum\fR .RS 4 Set the low battery warning threshold at which shutdown is initiated by \fBupsmon\fR(8)\&. .sp The factory default value is 30 (in percent), and can be settable depending on the exact model\&. .RE .PP \fBoffdelay\fR=\fInum\fR .RS 4 Set the timer before the UPS is turned off after the kill power command is sent (via the \fB\-k\fR switch)\&. .sp The default value is 20 (in seconds)\&. Usually this \fBmust be lower\fR than \fIondelay\fR, but the driver will \fBnot\fR warn you upon startup if it isn\(cqt\&. .RE .PP \fBondelay\fR=\fInum\fR .RS 4 Set the timer for the UPS to switch on in case the power returns after the kill power command had been sent but before the actual switch off\&. This ensures the machines connected to the UPS are, in all cases, rebooted after a power failure\&. .sp The default value is 30 (in seconds)\&. Usually this \fBmust be greater\fR than offdelay, but the driver will \fBnot\fR warn you upon startup if it isn\(cqt\&. Some UPS\(cqes will restart no matter what, even if the power is (still) out at the moment this timer elapses\&. In that case, you could try if setting \fIondelay = \-1\fR in \fBups\&.conf\fR helps\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBWarning\fR .ps -1 .br ondelay parameter was set in ten seconds unit in the legacy mge\-shut driver ( 3 for 30 seconds) \&. It is now set in seconds ( 30 for 30 seconds)\&. Make sure you use the correct unit in your configuration\&. .sp .5v .RE .RE .PP \fBnotification\fR=\fInum\fR .RS 4 Set notification type to 1 (no), 2 (light) or 3 (yes)\&. .sp This argument is ignored\&. It is only here for backward compatibility\&. .RE .SH "KNOWN ISSUES" .SS "Repetitive timeout and staleness" .sp Some models tends to be unresponsive with the default polling frequency\&. The result is that you have some "data stale" errors in your system log\&. .sp In this case, simply modify the general parameter "pollinterval" to a higher value (like 10 for 10 seconds)\&. This should solve the issue\&. .sp Using \fInotification=3\fR might also help\&. .SH "AUTHOR" .sp Arnaud Quette .SH "SEE ALSO" .SS "The core driver" .sp \fBnutupsdrv\fR(8) .SS "Internet resources" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/man/bestups.80000644000175000017500000001236212640476476012560 00000000000000'\" t .\" Title: bestups .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 12/29/2015 .\" Manual: NUT Manual .\" Source: Network UPS Tools 2.7.3.1 .\" Language: English .\" .TH "BESTUPS" "8" "12/29/2015" "Network UPS Tools 2\&.7\&.3\&." "NUT Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" bestups \- Driver for Best Power / SOLA (Phoenixtec protocol) UPS equipment .SH "NOTE" .sp This man page only documents the hardware\-specific features of the bestups driver\&. For information about the core driver, see \fBnutupsdrv\fR(8)\&. .SH "SUPPORTED HARDWARE" .sp \fBbestups\fR was designed to monitor Best Power UPS hardware like the Fortress, Fortress Telecom, Axxium Rackmount and Patriot Pro\&. It also recognizes and supports SOLA units such as the 325, 520 and 620\&. In addition, the Best 610 is supported using the \(oqID\(cq option\&. .sp Other UPS hardware using the Phoenixtec protocol should also work, but they will generate a warning since their battery information is not known\&. .sp This driver does not support some older Best/SOLA units\&. .SH "EXTRA ARGUMENTS" .sp This driver supports the following optional settings in the \fBups.conf\fR(5): .PP \fBnombattvolt=\fR\fInum\fR .RS 4 Override the battery float voltage which is normally determined by asking the hardware\&. This is useful if your UPS constantly reports battery\&.charge values just below 100% even when it\(cqs completely charged\&. .sp If you have this problem, set this to whatever battery\&.voltage reports when the UPS is known to be completely charged with a good battery\&. .sp The author\(cqs Best Fortress 750 uses nombattvolt=27\&.4\&. .RE .PP \fBbattvoltmult=\fR\fInum\fR .RS 4 Multiply the reported battery voltage by this number\&. Some devices report only a fraction of the total battery voltage\&. .sp For example, the SOLA 610 700VA UPS (with a 24V battery) reports the single cell voltage (about 2\&.27V when fully charged)\&. In this particular case you can set battvoltmult = 12 in \fBups.conf\fR(8) to fix this\&. .RE .PP \fBID=\fR\fIstring\fR .RS 4 Set the Identification response string\&. This should only be used with hardware that supports the Phoenixtec protocol status inquiry commands, but not the "ID" command, such as the Best/SOLA 610\&. Format of the ID string is: AAA,BBBB,CCC,DDD,EE\&.E,FF\&.F .sp AAA is the three\-character identification for the UPS model\&. .sp BBBB is the output power in VA (volt amperes)\&. B is an integer number ranging from 0 to 9\&. .sp CCC is the Nominal Input Voltage\&. C is an integer number ranging from 0 to 9\&. The unit is Volts AC\&. .sp DDD is the Nominal Output Voltage\&. D is an integer number ranging from 0 to 9\&. The unit is Volts AC\&. .sp EE\&.E is the Battery Voltage that will cause the UPS to shut itself off\&. E is an integer number ranging from 0 to 9\&. Then unit is Volts DC and a decimal point is present\&. .sp FF\&.F or FFF\&.F is the Battery Voltage at full charge\&. F is an integer number ranging from 0 to 9\&. Then unit is Volts DC\&. Typically, for 700VA, 1KVA and 1\&.5KVA units, the format is FF\&.F\&. For 2KVA and 3KVA units, the format is FFF\&.F\&. .sp Example: a Best 610 1\&.5KVA unit would use the string "610,1500,120,120,10\&.0,48\&.0"\&. .RE .SH "BUGS" .sp The battery charge percentage value (in battery\&.charge) is derived from the voltage data that the UPS returns, since the UPS doesn\(cqt return that value directly\&. On some hardware, the charge will remain at 100% for a long time and then drops quickly shortly before the battery runs out\&. You can confirm from the battery\&.voltage readings that this is a problem with the UPS and not this driver\&. .sp Similarly, the float from the charger in some models forces the battery charge percentage back up to 100% immedately after the UPS goes back on\-line, so you can\(cqt tell when it is really recharged\&. .sp Finally, some models give one value for the battery\(cqs nominal voltage and yet actually have a nominal voltage slightly below that\&. This leads to things such as the perpetual 98\&.7% charge on the author\(cqs Fortress 750, even when it\(cqs been charging for weeks\&. You can use nombattvolt= in \fBups.conf\fR(8) to fix this\&. .SH "AUTHOR" .sp Russell Kroll, Jason White .SH "SEE ALSO" .SS "The core driver:" .sp \fBnutupsdrv\fR(8) .SS "Internet resources:" .sp The NUT (Network UPS Tools) home page: http://www\&.networkupstools\&.org/ nut-2.7.4/docs/nutdrv_qx-subdrivers.txt0000644000175000017500000007452112667753764015224 00000000000000How to make a new subdriver to support another Q* UPS ----------------------------------------------------- Overall concept ~~~~~~~~~~~~~~~ The NUT "*nutdrv_qx*" driver is a meta-driver that handles Q* UPS devices. It consists of a core driver that handles most of the work of talking to the hardware, and several sub-drivers to handle specific UPS manufacturers. Adding support for a new UPS device is easy, because it requires only the creation of a new sub-driver. Creating a subdriver ~~~~~~~~~~~~~~~~~~~~ In order to develop a new subdriver for a specific UPS you have to know the idiom spoken by that device. This kind of devices speaks idioms that can be summed up as follows: - We send the UPS a query for one or more information * If the query is supported by the device, we'll get a reply that is mostly of a fixed length, therefore, in most cases, each information starts and ends always at the same indexes - We send the UPS a command * If the command is supported by the device, the UPS will either take action without any reply or reply us with a device-specific answer signaling that the command has been accepted (e.g. +ACK+) - If the query/command isn't supported by the device we'll get either the query/command echoed back or a device-specific reply signaling that it has been rejected (e.g. +NAK+) To be supported by this driver the idiom spoken by the UPS must comply to these conditions. Writing a subdriver ~~~~~~~~~~~~~~~~~~~ You have to fill the +subdriver_t+ structure: ---- typedef struct { const char *name; int (*claim)(void); item_t *qx2nut; void (*initups)(void); void (*initinfo)(void); void (*makevartable)(void); const char *accepted; const char *rejected; #ifdef TESTING testing_t *testing; #endif /* TESTING */ } subdriver_t; ---- Where: *+name+*:: Name of this subdriver: name of the +protocol+ that will need to be set in +ups.conf+ to use this subdriver plus the internal version of it separated by a space (e.g. "++Megatec 0.01++"). *+claim+*:: This function allows the subdriver to "claim" a device: return +1+ if the device is supported by this subdriver, else +0+. *+qx2nut+*:: Main table of vars and instcmds: an array of +item_t+ mapping a UPS idiom to NUT. *+initups+* (optional):: Subdriver-specific +upsdrv_initups+. This function will be called at the end of nutdrv_qx's own +upsdrv_initups+. *+initinfo+* (optional):: Subdriver-specific +upsdrv_initinfo+. This function will be called at the end of nutdrv_qx's own +upsdrv_initinfo+. *+makevartable+* (optional):: Function to add subdriver-specific +ups.conf+ vars and flags. Make sure not to collide with other subdrivers' vars and flags. *+accepted+* (optional):: String to match if the driver is expecting a reply from the UPS on instcmd/setvar in case of success. This comparison is done after the answer we got back from the UPS has been processed to get the value we are searching, so you don't have to include the trailing carriage return (+\r+) and you can decide at which index of the answer the value should start or end setting the appropriate +from+ and +to+ in the +item_t+ (see <<_mapping_an_idiom_to_nut,Mapping an idiom to NUT>>). *+rejected+* (optional):: String to match if the driver is expecting a reply from the UPS in case of error. Note that this comparison is done on the answer we got back from the UPS before it has been processed, so include also the trailing carriage return (+\r+) and whatever character is expected. *+testing+*:: Testing table (an array of +testing_t+) that will hold the commands and the replies used for testing the subdriver. + -- +testing_t+: ---- typedef struct { const char *cmd; const char answer[SMALLBUF]; const int answer_len; } testing_t; ---- Where: *+cmd+*:: Command to match. *+answer+*:: Answer for that command. + NOTE: If +answer+ contains inner ++\0++s, in order to preserve them, +answer_len+ as well as an +item_t+'s +preprocess_answer()+ function must be set. *+answer_len+*:: Answer length: + - if set to +-1+ -> auto calculate answer length (treat +answer+ as a null-terminated string), - otherwise -> use the provided length (if reasonable) and preserve inner ++\0++s (treat +answer+ as a sequence of bytes till the +item_t+'s +preprocess_answer()+ function gets called). For more informations, see <<_mapping_an_idiom_to_nut,Mapping an idiom to NUT>>. -- Mapping an idiom to NUT ~~~~~~~~~~~~~~~~~~~~~~~ If you understand the idiom spoken by your device, you can easily map it to NUT variables and instant commands, filling +qx2nut+ with an array of +item_t+ data structure: ---- typedef struct item_t { const char *info_type; const int info_flags; info_rw_t *info_rw; const char *command; char answer[SMALLBUF]; const int answer_len; const char leading; char value[SMALLBUF]; const int from; const int to; const char *dfl; unsigned long qxflags; int (*preprocess_command)(struct item_t *item, char *command, const size_t commandlen); int (*preprocess_answer)(struct item_t *item, const int len); int (*preprocess)(struct item_t *item, char *value, const size_t valuelen); } item_t; ---- Where: *+info_type+*:: NUT variable name, otherwise, if +QX_FLAG_NONUT+ is set, name to print to logs and if both +QX_FLAG_NONUT+ and +QX_FLAG_SETVAR+ are set, name of the var to retrieve from +ups.conf+. *+info_flags+*:: NUT flags (+ST_FLAG_*+ values to set in +dstate_addinfo+). *+info_rw+*:: + -- An array of +info_rw_t+ to handle r/w variables: - If +ST_FLAG_STRING+ is set in +info_flags+ it'll be used to set the length of the string (in +dstate_setaux+) - If +QX_FLAG_ENUM+ is set in +qxflags+ it'll be used to set enumerated values (in +dstate_addenum+) - If +QX_FLAG_RANGE+ is set in +qxflags+ it'll be used to set range boundaries (in +dstate_addrange+) NOTE: If +QX_FLAG_SETVAR+ is set the value given by the user will be checked against these infos. +info_rw_t+: ---- typedef struct { char value[SMALLBUF]; int (*preprocess)(char *value, const size_t len); } info_rw_t; ---- Where: *+value+*:: Value for enum/range, or length for +ST_FLAG_STRING+. *+preprocess(value, len)+*:: Optional function to preprocess range/enum +value+. + This function will be given +value+ and its +size_t+ and must return either +0+ if +value+ is supported or +-1+ if not supported. -- *+command+*:: Command sent to the UPS to get answer/to execute a instant command/to set a variable. *+answer+*:: Answer from the UPS, filled at runtime. + NOTE: If you expect a non-valid C string (e.g.: inner ++\0++s) or need to perform actions before the answer is used (and treated as a null-terminated string), you should set a +preprocess_answer()+ function. *+answer_len+*:: Expected minimum length of the answer. Set it to +0+ if there's no minimum length to look after. *+leading+*:: Expected leading character of the answer (optional), e.g. +#+, +(+ ... *+value+*:: Value from the answer, filled at runtime (i.e. +answer+ in the interval [+from+ to +to+]). *+from+*:: Position of the starting character of the info we're after in the answer. *+to+*:: Position of the ending character of the info we're after in the answer: use +0+ if all the remaining of the line is needed. *+dfl+*:: printf format to store value from the UPS in NUT variables. Set it either to +%s+ for strings or to a floating point specifier (e.g. +%.1f+) for numbers. + -- Otherwise: - If +QX_FLAG_ABSENT+ -> default value - If +QX_FLAG_CMD+ -> default command value -- + *+qxflags+*:: Driver's own flags. + -- [cols="m,",options="autowidth",frame="topbot",grid="rows"] |==== |QX_FLAG_STATIC |Retrieve this variable only once. |QX_FLAG_SEMI_STATIC |Retrieve this info smartly, i.e. only when a command/setvar is executed and we expect that data could have been changed. |QX_FLAG_ABSENT |Data is absent in the device, use default value. |QX_FLAG_QUICK_POLL |Mandatory vars. |QX_FLAG_CMD |Instant command. |QX_FLAG_SETVAR |The var is settable and the actual item stores info on how to set it. |QX_FLAG_TRIM |This var's value need to be trimmed of leading/trailing spaces/hashes. |QX_FLAG_ENUM |Enum values exist. |QX_FLAG_RANGE |Ranges for this var are available. |QX_FLAG_NONUT |This var doesn't have a corresponding var in NUT. |QX_FLAG_SKIP |Skip this var: this item won't be processed. |==== [NOTE] ==== The driver will run a so-called +QX_WALKMODE_INIT+ in +initinfo+ walking through all the items in +qx2nut+, adding instant commands and the like. From then on it'll run a so-called +QX_WALKMODE_QUICK_UPDATE+ just to see if the UPS is still there and then it'll do a so-called +QX_WALKMODE_FULL_UPDATE+ to update all the vars. If there's a problem with a var in +QX_WALKMODE_INIT+, the driver will automagically set +QX_FLAG_SKIP+ on it and then it'll skip that item in +QX_WALKMODE_QUICK_UPDATE+/+QX_WALKMODE_FULL_UPDATE+, provided that the item has not the flag +QX_FLAG_QUICK_POLL+ set, in that case the driver will set +datastale+. ==== -- *+preprocess_command(item, command, commandlen)+*:: Last chance to preprocess the command to be sent to the UPS (e.g. to add CRC, ...). This function is given the currently processed item (+item+), the command to be sent to the UPS (+command+) and its size_t (+commandlen+). Return +-1+ in case of errors, else +0+. +command+ must be filled with the actual command to be sent to the UPS. *+preprocess_answer(item, len)+*:: Function to preprocess the answer we got from the UPS before we do anything else (e.g. for CRC, decoding, ...). This function is given the currently processed item (+item+) with the answer we got from the UPS unmolested and already stored in +item+'s +answer+ and the length of that answer (+len+). Return +-1+ in case of errors, else the length of the newly allocated +item+'s +answer+ (from now on, treated as a null-terminated string). *+preprocess(item, value, valuelen)+*:: Function to preprocess the data from/to the UPS: you are given the currently processed item (+item+), a char array (+value+) and its +size_t+ (+valuelen+). Return +-1+ in case of errors, else +0+. + -- - If +QX_FLAG_SETVAR+/+QX_FLAG_CMD+ is set then the item is processed before the command is sent to the UPS so that you can fill it with the value provided by the user. + NOTE: In this case +value+ must be filled with the command to be sent to the UPS. - Otherwise the function will be used to process the value we got from the answer of the UPS before it'll get stored in a NUT variable. + NOTE: In this case +value+ must be filled with the processed value already compliant to NUT standards. -- IMPORTANT: You must provide an +item_t+ with +QX_FLAG_SETVAR+ and its boundaries set for both +ups.delay.start+ and +ups.delay.shutdown+ to map the driver variables +ondelay+ and +offdelay+, as they will be used in the shutdown sequence. TIP: In order to keep the data flow at minimum you should keep together the items in +qx2nut+ that need data from the same query (i.e. +command+): doing so the driver will send the query only once and then every +item_t+ processed after the one that got the answer, provided that it's filled with the same +command+ and that the answer wasn't +NULL+, will get that +answer+. Examples ~~~~~~~~ The following examples are from the +voltronic+ subdriver. Simple vars ^^^^^^^^^^^ We know that when the UPS is queried for status with +QGS\r+, it replies with something like +(234.9 50.0 229.8 50.0 000.0 000 369.1 ---.- 026.5 ---.- 018.8 100000000001\r+ and we want to access the output voltage (the third token, in this case +229.8+). ---- > [QGS\r] < [(234.9 50.0 229.8 50.0 000.0 000 369.1 ---.- 026.5 ---.- 018.8 100000000001\r] 0123456789012345678901234567890123456789012345678901234567890123456789012345 0 1 2 3 4 5 6 7 ---- Here's the +item_t+: ---- { "output.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 12, 16, "%.1f", 0, NULL, NULL, NULL }, ---- [horizontal] +info_type+:: +output.voltage+ +info_flags+:: +0+ +info_rw+:: +NULL+ +command+:: +QGS\r+ +answer+:: Filled at runtime +answer_len+:: +76+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +12+ -> the index at which the info (i.e. +value+) starts +to+:: +16+ -> the index at which the info (i.e. +value+) ends +dfl+:: +%.1f+ + We are expecting a number, so at first the core driver will check if it's made up entirely of digits/points/spaces, then it'll convert it into a double. Because of that we need to provide a floating point specifier. +qxflags+:: +0+ +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +NULL+ Mandatory vars ^^^^^^^^^^^^^^ Also from +QGS\r+, we want to process the 9th status bit +10000000+*`0`*+001+ that tells us whether the UPS is shutting down or not. ---- > [QGS\r] < [(234.9 50.0 229.8 50.0 000.0 000 369.1 ---.- 026.5 ---.- 018.8 100000000001\r] 0123456789012345678901234567890123456789012345678901234567890123456789012345 0 1 2 3 4 5 6 7 ---- Here's the +item_t+: ---- { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 71, 71, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, ---- [horizontal] +info_type+:: +ups.status+ +info_flags+:: +0+ +info_rw+:: +NULL+ +command+:: +QGS\r+ +answer+:: Filled at runtime +answer_len+:: +76+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +71+ -> the index at which the info (i.e. +value+) starts +to+:: +71+ -> the index at which the info (i.e. +value+) ends +dfl+:: +%s+ + Since a +preprocess+ function is defined for this item, this could have been +NULL+, however, if we want - like here -, we can use it in our +preprocess+ function. +qxflags+:: +QX_FLAG_QUICK_POLL+ -> this item will be polled every time the driver will check for updates. Since this item is mandatory to run the driver, if a problem arises in +QX_WALKMODE_INIT+ the driver won't skip it an it'll set +datastale+. +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_status+ + This function will be called *after* the +command+ has been sent to the UPS and we got back the +answer+ and stored the +value+ in order to process it to NUT standards: in this case we will convert the binary +value+ to a NUT status. Settable vars ^^^^^^^^^^^^^ So your UPS reports its battery type when queried for +QBT\r+; we are expecting an answer like +(01\r+ and we know that the values can be mapped as follows: +00+ -> "Li", +01+ -> "Flooded" and +02+ -> "AGM". ---- > [QBT\r] < [(01\r] <- 00="Li", 01="Flooded" or 02="AGM" 0123 0 ---- Here's the +item_t+: ---- { "battery.type", ST_FLAG_RW, voltronic_e_batt_type, "QBT\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_p31b }, ---- [horizontal] +info_type+:: +battery.type+ +info_flags+:: +ST_FLAG_RW+ -> this is a r/w var +info_rw+:: +voltronic_e_batt_type+ + The values stored here will be added to the NUT variable, setting its boundaries: in this case +Li+, +Flooded+ and +AGM+ will be added as enumerated values. +command+:: +QBT\r+ +answer+:: Filled at runtime +answer_len+:: +4+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +1+ -> the index at which the info (i.e. +value+) starts +to+:: +2+ -> the index at which the info (i.e. +value+) ends +dfl+:: +%s+ + Since a +preprocess+ function is defined for this item, this could have been +NULL+, however, if we want - like here -, we can use it in our +preprocess+ function. +qxflags+:: +QX_FLAG_SEMI_STATIC+ -> this item changes - and will therefore updated - only when we send a command/setvar to the UPS + +QX_FLAG_ENUM+ -> this r/w variable is of the enumerated type and the enumerated values are listed in the +info_rw+ structure (i.e. +voltronic_e_batt_type+) +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_p31b+ + This function will be called *after* the +command+ has been sent to the UPS and we got back the +answer+ and stored the +value+ in order to process it to NUT standards: in this case we will check if the value is in the range and then publish the human readable form of it (i.e. +Li+, +Flooded+ or +AGM+). We also know that we can change battery type with the +PBTnn\r+ command; we are expecting either +(ACK\r+ if the command succeeded or +(NAK\r+ if the command is rejected. ---- > [PBTnn\r] nn = 00/01/02 < [(ACK\r] 01234 0 ---- Here's the +item_t+: ---- { "battery.type", 0, voltronic_e_batt_type, "PBT%02.0f\r", "", 5, '(', "", 1, 4, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM, NULL, NULL, voltronic_p31b_set }, ---- [horizontal] +info_type+:: +battery.type+ +info_flags+:: +0+ +info_rw+:: +voltronic_e_batt_type+ + The value provided by the user will be automagically checked by the core nutdrv_qx driver against the enumerated values already set by the non setvar item (i.e. +Li+, +Flooded+ or +AGM+), so this could have been +NULL+, however if we want - like here - we can use it in our +preprocess+ function. +command+:: +PBT%02.0f\r+ +answer+:: Filled at runtime +answer_len+:: +5+ <- either +(NAK\r+ or +(ACK\r+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +1+ -> the index at which the info (i.e. +value+) starts +to+:: +3+ -> the index at which the info (i.e. +value+) ends +dfl+:: Not used for +QX_FLAG_SETVAR+ +qxflags+:: +QX_FLAG_SETVAR+ -> this item is used to set the variable +info_type+ (i.e. +battery.type+) + +QX_FLAG_ENUM+ -> this r/w variable is of the enumerated type and the enumerated values are listed in the +info_rw+ structure (i.e. +voltronic_e_batt_type+) +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_p31b_set+ + This function will be called *before* the +command+ is sent to the UPS so that we can fill +command+ with the value provided by the user: in this case the function will simply translate the human readable form of battery type (i.e. +Li+, +Flooded+ or +AGM+) to the UPS compliant type (i.e. +00+, +01+ and +02+) and then fill +value+ (the second argument passed to the +preprocess+ function). Instant commands ^^^^^^^^^^^^^^^^ We know that we have to send to the UPS +Tnn\r+ or +T.n\r+ in order to start a battery test lasting +nn+ minutes or +.n+ minutes: we are expecting either +(ACK\r+ on success or +(NAK\r+ if the command is rejected. ---- > [Tnn\r] < [(ACK\r] 01234 0 ---- Here's the +item_t+: ---- { "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 4, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, ---- [horizontal] +info_type+:: +test.battery.start+ +info_flags+:: +0+ +info_rw+:: +NULL+ +command+:: +T%s\r+ +answer+:: Filled at runtime +answer_len+:: +5+ <- either +(NAK\r+ or +(ACK\r+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +1+ -> the index at which the info (i.e. +value+) starts +to+:: +3+ -> the index at which the info (i.e. +value+) ends +dfl+:: Not used for +QX_FLAG_CMD+ +qxflags+:: +QX_FLAG_CMD+ -> this item is an instant command that will be fired when +info_type+ (i.e. +test.battery.start+) is called +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_process_command+ + This function will be called *before* the +command+ is sent to the UPS so that we can fill +command+ with the value provided by the user: in this case the function will check if the value is in the accepted range and then fill +value+ (the second argument passed to the +preprocess+ function) with +command+ and the given value. Informations absent in the device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order to set the server-side var +ups.delay.start+, that will be then used by the driver, we have to provide the following +item_t+: ---- { "ups.delay.start", ST_FLAG_RW, voltronic_r_ondelay, NULL, "", 0, 0, "", 0, 0, "180", QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, ---- [horizontal] +info_type+:: +ups.delay.start+ +info_flags+:: +ST_FLAG_RW+ -> this is a r/w var +info_rw+:: +voltronic_r_ondelay+ + The values stored here will be added to the NUT variable, setting its boundaries: in this case +0+ and +599940+ will be set as the minimum and maximum value of the variable's range. Those values will then be used by the driver to check the user provided value. +command+:: Not used for +QX_FLAG_ABSENT+ +answer+:: Not used for +QX_FLAG_ABSENT+ +answer_len+:: Not used for +QX_FLAG_ABSENT+ +leading+:: Not used for +QX_FLAG_ABSENT+ +value+:: Not used for +QX_FLAG_ABSENT+ +from+:: Not used for +QX_FLAG_ABSENT+ +to+:: Not used for +QX_FLAG_ABSENT+ +dfl+:: +180+ <- the default value that will be set for this variable +qxflags+:: +QX_FLAG_ABSENT+ -> this item isn't available in the device + +QX_FLAG_SETVAR+ -> this item is used to set the variable +info_type+ (i.e. +ups.delay.start+) + +QX_FLAG_RANGE+ -> this r/w variable has a settable range and its boundaries are listed in the +info_rw+ structure (i.e. +voltronic_r_ondelay+) +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_process_setvar+ + This function will be called, in setvar, before the driver stores the value in the NUT var: here it's used to truncate the user-provided value to the nearest settable interval. Informations not yet available in NUT ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If your UPS reports some informations that are not yet available as NUT variables and you need to process them, you can add them in +item_t+ data structure adding the +QX_FLAG_NONUT+ flag to its +qxflags+: the info will then be printed to the logs. So we know that the UPS reports actual input/output phase angles when queried for +QPD\r+: ---- > [QPD\r] < [(000 120\r] <- Input Phase Angle - Output Phase Angle 012345678 0 ---- Here's the +item_t+ for input phase angle: ---- { "input_phase_angle", 0, NULL, "QPD\r", "", 9, '(', "", 1, 3, "%03.0f", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_phase }, ---- [horizontal] +info_type+:: +input_phase_angle+ + This information will be used to print the value we got back from the UPS in the logs. +info_flags+:: +0+ +info_rw+:: +NULL+ +command+:: +QPD\r+ +answer+:: Filled at runtime +answer_len+:: +9+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +1+ -> the index at which the info (i.e. +value+) starts +to+:: +3+ -> the index at which the info (i.e. +value+) ends +dfl+:: +%03.0f+ + If there's no +preprocess+ function, the format is used to print the value to the logs. Here instead it's used by the +preprocess+ function. +qxflags+:: +QX_FLAG_STATIC+ -> this item doesn't change + +QX_FLAG_NONUT+ -> this item doesn't have yet a NUT variable +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_phase+ + This function will be called *after* the +command+ has been sent to the UPS so that we can parse the value we got back and check it. Here's the +item_t+ for output phase angle: ---- { "output_phase_angle", ST_FLAG_RW, voltronic_e_phase, "QPD\r", "", 9, '(', "", 5, 7, "%03.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_NONUT, NULL, NULL, voltronic_phase }, ---- [horizontal] +info_type+:: +output_phase_angle+ + This information will be used to print the value we got back from the UPS in the logs. +info_flags+:: +ST_FLAG_RW+ + This could also be +0+ (it's not really used by the driver), but it's set to +ST_FLAG_RW+ for cohesion with other rw vars - also, if ever a NUT variable would become available for this item, it'll be easier to change this item and its +QX_FLAG_SETVAR+ counterpart to use it. +info_rw+:: +voltronic_e_phase+ + Enumerated list of available value (here: +000+, +120+, +240+ and +360+). Since +QX_FLAG_NONUT+ is set the driver will print those values to the logs, plus you could use it in the +preprocess+ function to check the value we got back from the UPS (as done here). +command+:: +QPD\r+ +answer+:: Filled at runtime +answer_len+:: +9+ +leading+:: +(+ +value+:: Filled at runtime +from+ :: +5+ -> the index at which the info (i.e. +value+) starts +to+:: +7+ -> the index at which the info (i.e. +value+) ends +dfl+:: +%03.0f+ + If there's no +preprocess+ function, the format is used to print the value to the logs. Here instead it's used by the +preprocess+ function. +qxflags+:: +QX_FLAG_SEMI_STATIC+ -> this item changes - and will therefore updated - only when we send a command/setvar to the UPS + +QX_FLAG_ENUM+ -> this r/w variable is of the enumerated type and the enumerated values are listed in the +info_rw+ structure (i.e. +voltronic_e_phase+). + +QX_FLAG_NONUT+ -> this item doesn't have yet a NUT variable +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_phase+ + This function will be called *after* the +command+ has been sent to the UPS so that we can parse the value we got back and check it. Here it's used also to store a var that will then be used to check the value in setvar's preprocess function. If you need also to change some values in the UPS you can add a +ups.conf+ var/flag in the subdriver's own +makevartable+ and then process it adding to its +qxflags+ both +QX_FLAG_NONUT+ and +QX_FLAG_SETVAR+: this item will be processed only once in +QX_WALKMODE_INIT+. The driver will check if the var/flag is defined in +ups.conf+: if so, it'll then call +setvar+ passing to this item the defined value, if any, and then it'll print the results in the logs. We know we can set output phase angle sending +PPDnnn\r+ to the UPS: ---- > [PPDn\r] n = (000, 120, 180 or 240) < [(ACK\r] 01234 0 ---- Here's the +item_t+ ---- { "output_phase_angle", 0, voltronic_e_phase, "PPD%03.0f\r", "", 5, '(', "", 1, 4, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT, NULL, NULL, voltronic_phase_set }, ---- [horizontal] +info_type+:: +output_phase_angle+ + This information will be used to print the value we got back from the UPS in the logs and to retrieve the user-provided value in +ups.conf+. So, name it after the variable you created to use in +ups.conf+ in the subdriver's own +makevartable+. +info_flags+:: +0+ +info_rw+:: +voltronic_e_phase+ + Enumerated list of available values (here: +000+, +120+, +240+ and +360+). The value provided by the user will be automagically checked by the core nutdrv_qx driver against the enumerated values stored here. +command+:: +PPD%03.0f\r+ +answer+:: Filled at runtime +answer_len+:: +5+ <- either +(NAK\r+ or +(ACK\r+ +leading+:: +(+ +value+:: Filled at runtime +from+:: +1+ -> the index at which the info (i.e. +value+) starts +to+:: +3+ -> the index at which the info (i.e. +value+) ends +dfl+:: Not used for +QX_FLAG_SETVAR+ +qxflags+:: +QX_FLAG_SETVAR+ -> this item is used to set the variable +info_type+ (i.e. +output_phase_angle+) + +QX_FLAG_ENUM+ -> this r/w variable is of the enumerated type and the enumerated values are listed in the +info_rw+ structure (i.e. +voltronic_e_phase+). + +QX_FLAG_NONUT+ -> this item doesn't have yet a NUT variable +preprocess_command+:: +NULL+ +preprocess_answer+:: +NULL+ +preprocess+:: +voltronic_phase_set+ + This function will be called *before* the +command+ is sent to the UPS so that we can check user-provided value and fill +command+ with it and then fill +value+ (the second argument passed to the +preprocess+ function). Support functions ~~~~~~~~~~~~~~~~~ You are already given the following functions: *+int instcmd(const char *cmdname, const char *extradata)+*:: Execute an instant command. In detail: + -- - look up the given +cmdname+ in the qx2nut data structure (if not found, try to fallback to commonly known commands); - if +cmdname+ is found, call its preprocess function, passing to it +extradata+, if any, otherwise its +dfl+ value, if any; - send the command to the device and check the reply. -- + Return +STAT_INSTCMD_INVALID+ if the command is invalid, +STAT_INSTCMD_FAILED+ if it failed, +STAT_INSTCMD_HANDLED+ on success. *+int setvar(const char *varname, const char *val)+*:: Set r/w variable to a value after it has been checked against its +info_rw+ structure. Return +STAT_SET_HANDLED+ on success, otherwise +STAT_SET_UNKNOWN+. *+item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag)+*:: Find an item of +item_t+ type in +qx2nut+ data structure by its +info_type+, optionally filtered by its +qxflags+, and return it if found, otherwise return +NULL+. - +flag+: flags that have to be set in the item, i.e. if one of the flags is absent in the item it won't be returned. - +noflag+: flags that have to be absent in the item, i.e. if at least one of the flags is set in the item it won't be returned. *+int qx_process(item_t *item, const char *command)+*:: Send +command+ (a null-terminated byte string) or, if it is +NULL+, send the command stored in the +item+ to the UPS and process the reply, saving it in +item+'s +answer+. Return +-1+ on errors, +0+ on success. *+int ups_infoval_set(item_t *item)+*:: Process the value we got back from the UPS (set status bits and set the value of other parameters), calling the +item+-specific +preprocess+ function, if any, otherwise executing the standard preprocessing (including trimming if +QX_FLAG_TRIM+ is set). Return +-1+ on failure, +0+ for a status update and +1+ in all other cases. *+int qx_status(void)+*:: Return the currently processed status so that it can be checked with one of the +status_bit_t+ passed to the +STATUS()+ macro (see +nutdrv_qx.h+). *+void update_status(const char *nutvalue)+*:: If you need to edit the current status call this function with one of the NUT status (all but +OB+ are supported, simply set it as not +OL+); preceed them with an exclamation mark if you want to clear them from the status (e.g. +!OL+). Notes ~~~~~ You must put the generated files into the +drivers/+ subdirectory, with the name of your subdriver preceded by +nutdrv_qx_+, and update +nutdrv_qx.c+ by adding the appropriate +#include+ line and by updating the definition of +subdriver_list+. Please, make sure to add your driver in that list in a smart way: if your device supports also the basic commands used by the other subdrivers to claim a device, add something that is unique (i.e. not supported by the other subdrivers) to your device in your claim function and then add it on top of the slighty supported ones in that list. You must also add the subdriver to +NUTDRV_QX_SUBDRIVERS+ in +drivers/Makefile.am+ and call "++autoreconf++" and/or "++./configure++" from the top level NUT directory. You can then recompile +nutdrv_qx+, and start experimenting with the new subdriver. For more informations, have a look at the currently available subdrivers: - +nutdrv_qx_bestups.+{+c+,+h+} - +nutdrv_qx_mecer.+{+c+,+h+} - +nutdrv_qx_megatec.+{+c+,+h+} - +nutdrv_qx_megatec-old.+{+c+,+h+} - +nutdrv_qx_mustek.+{+c+,+h+} - +nutdrv_qx_q1.+{+c+,+h+} - +nutdrv_qx_voltronic.+{+c+,+h+} - +nutdrv_qx_voltronic-qs.+{+c+,+h+} - +nutdrv_qx_voltronic-qs-hex.+{+c+,+h+} - +nutdrv_qx_zinto.+{+c+,+h+} nut-2.7.4/docs/docinfo.xml.in0000644000175000017500000000110612640473702012756 00000000000000 @PACKAGE_VERSION@ @now@ Current release of Network UPS Tools (NUT). 2.6.0 2011-01-14 First release of AsciiDoc documentation for Network UPS Tools (NUT). nut-2.7.4/docs/history.txt0000644000175000017500000005077112640473702012464 00000000000000ifdef::website[] Project history =============== endif::website[] This page is an attempt to document how everything came together. The Network UPS Tools team would like to warmly thank Russell Kroll. Russell initially started this project, maintaining and improving it for over 8 years (1996 - mid 2005). Prototypes and experiments -------------------------- May 1996: early status hacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ APC's Powerchute was running on kadets.d20.co.edu (a BSD/OS box) with SCO binary emulation. Early test versions ran in cron, pulled status from the log files and wrote them to a .plan file. You could see the results by fingering `pwrchute@kadets.d20.co.edu` while it lasted: ------------------------------------------------------------------------ Last login Sat May 11 21:33 (MDT) on ttyp0 from intrepid.rmi.net Plan: Welcome to the UPS monitor service at kadets.d20.co.edu. The Smart-UPS attached to kadets generated a report at 14:24:01 on 05/17/96. During the measured period, the following data points were taken: Voltage ranged from 115.0 VAC to 116.3 VAC. The UPS generated 116.3 VAC at 60.00 Hz. The battery level was at 27.60 volts. The load placed on the UPS was 024.9 percent. UPS temperature was measured at 045.0 degrees Celsius. Measurements are taken every 10 minutes by the upsd daemon. This report is generated by a script written by Russell Kroll. Modified for compatibility with the BSD/OS cron daemon by Neil Schroeder ------------------------------------------------------------------------ This same status data could also be seen with a web browser, since we had rigged up a CGI wrapper script which called finger. January 1997: initial protocol tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Initial tests with a freestanding non-daemon program provided a few basic status registers from the UPS. The 940-0024C cable was not yet understood, so this happened over the [attachment:apcevilhack.jpg evil two-wire serial hack]. ------------------------------------------------------------------------ Communicating with SMART-UPS 700 S/N WS9643050926 [10/17/96] Input voltage range: 117.6 VAC - 118.9 VAC Load is 010.9% of capacity, battery is charged to 100.0% of capacity ------------------------------------------------------------------------ Note that today's apcsmart driver still displays the serial number when it starts, since it is derived from this original code. September 1997: first client/server code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The first split daemon/client code was written. upsd spoke directly to the UPS (APC Smart models only) and communicated with upsc by sending binary structures in UDP datagrams. The first CGI interface existed, but it was all implemented with shell scripts. The main script would call upsc to retrieve status values. Then it would cat a template file through sed to plug them into the page. image::images/old-cgi.png[Old CGI screenshot] upsstats actually has since returned to using templates, despite having a period in the middle when it used hardcoded HTML. The images were also created with shell scripts. Each script would call upsc to get the right value (utility, upsload, battcap). It then took the value, plugged it into a command file with sed, and passed that into 'fly', a program which used an interpreted language to create images. fly actually uses gd, just like upsimage does today. This code later evolved into Smart UPS Tools 0.10. Smart UPS Tools --------------- March 1998: first public release ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Version 0.10 was released on March 10, 1998. It used the same design as the pre-release prototype. This made expansion difficult as the binary structure used for network communications would break any time a new variable was added. Due to byte-ordering and struct alignment issues, the code usually couldn't talk over the network to a system with a different architecture. It was also hopelessly bound to one type of UPS hardware. Five more releases followed with this design followed. The last was 0.34, released October 27, 1998. June 1999: Redesigned, rewritten ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Following a long period of inactivity and two months of prerelease testing versions, 0.40.0 was released on June 5, 1999. It featured a complete redesign and rewrite of all of the code. The layering was now in three pieces, with the single driver (smartups) separate from the server (upsd). Clients remained separate as before and still used UDP to talk to the server, but they now used a text-based protocol instead of the brittle binary structs. A typical request like "REQ UTILITY" would be answered with "ANS UTILITY 120.0". The ups-trust425-625 driver appeared shortly after the release of 0.40.0, marking the first expansion beyond APC hardware. Over the months that followed, the backupspro driver would be forked from the smartups driver to handle the APC Back-UPS Pro line. Then the backups driver was written to handle the APC Back-UPS contact-closure models. These drivers would later be renamed and recombined, with smartups and backupspro becoming apcsmart, and backups became genericups. The drivers stored status data in an array. At first, they passed this data to upsd by saving it to a file. upsd would reread this file every few seconds to keep a copy for itself. This was later expanded to allow shared memory mode, where only a stub would remain on the disk. The drivers and server then passed data through the shared memory space. upsd picked up the ability to monitor multiple drivers on the system, and the "upsname@hostname" scheme was born. Access controls were added, and then the network code was expanded to allow TCP communications, which at this point were on port 3305. Network UPS Tools ----------------- September 1999: new name, new URL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Several visitors to the web page and subscribers to the mailing lists provided suggestions to rename the project. The old name no longer accurately described it, and it was perilously close to APC's "Smart-UPS" trademark. Rather than risk problems in the future, the name was changed. Kern Sibbald provided the winner: Network UPS Tools, which captures the essence of the project and makes for great short tarball filenames: nut-x.y.z.tar.gz. The new name was first applied to 0.42.0, released October 31, 1999. This is also when the web pages moved from the old `http://www.exploits.org/~rkroll/smartupstools/` URL to the replacement at `http://www.exploits.org/nut/` to coincide with the name change. More drivers were written and the hardware support continued to grow. upsmon picked up the concepts of "master" and "slave", and could now handle environments where multiple systems get power from a single UPS. Manager mode was added to allow changing the value of read/write variables in certain UPS models. June 2001: common driver core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Up to this point, all of the drivers compiled into freestanding programs, each providing their own implementation of main(). This meant they all had to check the incoming arguments and act uniformly. Unfortunately, not all of the programs behaved the same way, and it was hard to document and use consistently. It also meant that startup scripts had to be edited depending on what kind of hardware was attached. Starting in 0.45.0, released June 11, 2001, there was a new common core for all drivers called main.c. It provided the main function and called back to the upsdrv_* functions provided by the hardware-specific part of the drivers. This allowed driver authors to focus on the UPS hardware without worrying about the housekeeping stuff that needs to happen. This new design provided an obvious way to configure drivers from one file, and ups.conf was born. This eventually spawned upsdrvctl, and now all drivers based on this common core could be started or stopped with one command. Startup scripts now could contain "upsdrvctl start", and it didn't matter what kind of hardware or how many UPSes you had on one system. Interestingly, at the end of this month, Arnaud Quette entered the UPS world, as a subcontractor of the now defunct MGE UPS SYSTEMS. This marks the start of a future successful collaboration. May 2002: casting off old drivers, IANA port, towards 1.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ During the 0.45.x series, both the old standalone drivers and the ones which had been converted to the common core were released together. Before the release of 0.50.0 on May 24, 2002, all of the old drivers were removed. While this shrank the list of supported hardware, it set the precedent for removing code which isn't receiving regular maintenance. The assumption is that the code will be brought back up to date by someone if they actually need it. Otherwise, it's just dead weight in the tree. This change meant that all drivers could be controlled with upsdrvctl and ups.conf, allowing the documentation to be greatly simplified. There was no longer any reason to say "do this, unless you have this driver, then do this". IANA granted an official port number to the project, and the network code switched to port 3493. It had previously been on 3305 which is assigned to odette-ftp. 3305 was probably picked in 1997 because it was the fifth project to spawn from some common UDP server code. After 0.50.1, the 0.99 tree was created to provide a tree which would receive nothing but bug fixes in preparation for the release of 1.0. As it turned out, very few things required fixing, and there were only three releases in this tree. Leaving 0.x territory --------------------- August 2002: first stable tree: NUT 1.0.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After nearly 5 years of having a 0.x version number, 1.0.0 was released on August 19, 2002. This milestone meant that all of the base features that you would expect to find were intact: good hardware support, a network server with security controls, and system shutdowns that worked. The design was showing signs of wear from the rapid expansion, but this was intentionally ignored for the moment. The focus was on getting a good version out that would provide a reasonable base while the design issues could be addressed in the future, and I'm confident that we succeeded. November 2002: second stable tree: NUT 1.2.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ One day after the release of 1.0.0, 1.1.0 started the new development tree. During that development cycle, the CGI programs were rewritten to use templates instead of hard-coded HTML, thus bringing back the flexibility of the original unreleased prototype from 5 years before. multimon was removed from the tree, as the new upsstats could do both jobs by loading different templates. A new client library called upsclient was created, and it replaced upsfetch. This new library only supported TCP connections, and used an opaque context struct to keep state for each connection. As a result, client programs could now do things that used multiple connections without any conflicts. This was done primarily to allow OpenSSL support, but there were other benefits from the redesign. upsd and the clients could now use OpenSSL for basic authentication and encryption, but this was not included by default. This was provided as a bonus feature for those users who cared to read about it and enable the option, as the initial setup was complex. After the 1.1 tree was frozen and deemed complete, it became the second stable tree with the release of 1.2.0 on November 5, 2002. April 2003: new naming scheme, better driver glue, and an overhauled protocol ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Following an extended period with no development tree, 1.3.0 got things moving again on April 13, 2003. The focus of this tree was to rewrite the driver-server communication layer and replace the static naming scheme for variables and commands. Up to this point, all variables had names like STATUS, UTILITY, and OUTVOLT. They had been created as drivers were added to the tree, and there was little consistency. For example, it probably should have been INVOLT and OUTVOLT, but there was no OUTVOLT originally, so UTILITY was all we had. This same pattern repeated with ACFREQ - is it incoming or outgoing? - and many more. To solve this problem, all variables and commands were renamed to a hierarchical scheme that had obvious grouping. STATUS became ups.status. UTILITY turned into input.voltage, and OUTVOLT is output.voltage. ACFREQ is input.frequency, and the new output.frequency is also now supported. Every other variable or command was renamed in this fashion. These variables had been shared between the drivers and upsd as values. That is, for each name like STATUS, there was a #define somewhere in the tree with an INFO_ prefix that gave it a number. INFO_STATUS was 0x0006, INFO_UTILITY was 0x0004, and so on, with each name having a matching number. This number was stored in an int within a structure which was part of the array that was either written to disk or shared memory. That structure had several restrictions on expansion and was dropped as the data sharing method between the drivers and the server. It was replaced by a new system of text-based messages over Unix domain sockets. Drivers now accepted a short list of commands from upsd, and would push out updates asynchronously. upsd no longer had to poll the state files or shared memory. It could just select all of the driver and client fds and act on events. At the same time, the network protocol on port 3493 was overhauled to take advantage of the new naming scheme. The existing "REQ STATUS@su700", "ANS STATUS@su700 OL" scheme was showing signs of age, and it really only supported the UPS name (@su700) as an afterthought. The new protocol would now use commands like GET and LIST, leading to exchanges like "GET VAR su700 ups.status" and "VAR su700 ups.status OL". The responses contain enough data to stand alone, so clients can now handle them asynchronously. July 2003: third stable tree: NUT 1.4.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On July 25, 2003, 1.4.0 was released. It contained support for both the old "REQ" style protocol (with names like STATUS), and the new "GET" style protocol (with names like ups.status). This tree is provided to bridge the gap between all of the old releases and the upcoming 2.0. 2.0 will be released without support for the old REQ/STATUS protocol. The hope is that client authors and those who have implemented their own monitoring software will use the 1.4 cycle to change to the new protocol. The 1.4 releases contain a lot of compatibility code to make sure both work at the same time. July 2003: pushing towards 2.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1.5.0 forked from 1.4.0 and was released on July 29, 2003. The first changes were to throw out anything which was providing compatibility with the older versions of the software. This means that 1.5 and the eventual 2.0 will not talk to anything older than 1.4. This tree continues to evolve with new serial routines for the drivers which are intended to replace the aging upscommon code which dates back to the early 0.x releases. The original routines would call alarm and read in a tight loop while fetching characters. The new functions are much cleaner, and wait for data with select. This makes for much cleaner code and easier strace/ktrace logs, since the number of syscalls has been greatly reduced. There has also been a push to make sure the data from the UPS is well-formed and is actually usable before sending updates out to upsd. This started during 1.3 as drivers were adapted to use the dstate functions and the new variable/command names. Some drivers which were not converted to the new naming scheme or didn't do sanity checks on the incoming UPS data from the serial port were dropped from the tree. This tree was released as 2.0.0. networkupstools.org ------------------- November 2003: a new URL ~~~~~~~~~~~~~~~~~~~~~~~~ The bandwidth demands of a project like this have slowly been forcing me to offload certain parts to other servers. The download links have pointed offsite for many months, and other large things like certain UPS protocols have followed. As the traffic grows, it's clear that having the project attached to exploits.org is not going to work. The solution was to register a new domain and set up mirrors. There are two initial web servers, with more on the way. The main project URL has changed from `http://www.exploits.org/nut/` to http://www.networkupstools.org. The actual content is hosted on various mirrors which are updated regularly with rsync, so the days of dribbling bits through my DSL should be over. This is also when all of the web pages were redesigned to have a simpler look with fewer links on the left side. The old web pages used to have 30 or more links on the top page, and most of them vanished when you dropped down one level. The links are now constant on the entire site, and the old links now live in their own groups in separate directories. Second major version -------------------- March 2004: NUT 2.0.0 ~~~~~~~~~~~~~~~~~~~~~ NUT 2.0.0 arrived on March 23, 2004. The jump to version 2 shows the difference in the protocols and naming that happened during the 1.3 and 1.5 development series. 2.0 no longer ships with backwards compatibility code, so it's smaller and cleaner than 1.4. The change of leadership ------------------------ February 2005: NUT 2.0.1 ~~~~~~~~~~~~~~~~~~~~~~~~ The year 2004 was marked by a release slowdown, since Russell was busy with personal subjects. But the patches queue was still growing quickly. At that time, the development process was still centralized. There was no revision control system (like the current Subversion repository), nor trackers to interact with NUT development. Russell was receiving all the patches and requests, and doing all the work on his own, including releases. Russell was more and more thinking about giving the project leadership to Arnaud Quette, which finally happened with the 2.0.1 release in February 2005. This marked a new era for NUT... First, Arnaud aimed at opening up the development by creating a project on the http://www.debian.org/[Debian] http://alioth.debian.org/projects/nut/[Alioth Forge]. This allowed to build the team of hackers that Russell dreamed about. It also allows to ensure NUT's continuation, whatever happens to the leader. And that would most of all boost the projects contributions. //////////////////////////////////////////////////////////////////////// *sandbox* TO BE COMPLETED + ADD A LINK TO IDEA / FUTURE link on the team + history mention: - MGE supporting officially, - USB / SNMP / XML drivers (first non serial drivers), - Power Management (HAL) bridging, - PDU support, - perl / python support... - client history and evolution (wmnut: 01/01/2002) release history
  • July 7, 2008: Client activity: new client NUT-Monitor
  • June 27, 2008: Client activity: new Python class PyNUT
  • June 19, 2008: Client activity: KNutClient 0.9.4
  • May 7, 2008: 2.2.2 released
  • December 21, 2007: 2.2.1 released
  • August 31, 2007: Client activity: UPS Monitor link update
  • August 31, 2007: Client activity: Windows NUT client 1.5.0
  • July 5, 2007: 2.2.0 released
  • June 5, 2007: Client activity: collectd NUT plugin
  • April 10, 2007: Client activity: KNutClient 0.9.3
  • January 15, 2007: 2.0.5 released
  • January 8, 2007: Client activity: Windows NUT client PC DB9-M <--------> DB9-F Pin 1 <-----------> Pin 3 Pin 2 <-----------> Pin 8 Pin 4 <-----------> Pin 4 Pin 5 <-----------> Pin 1 Shells Grounded Both Ends nut-2.7.4/docs/cables/apc.txt0000644000175000017500000003735612640443572012765 00000000000000Desc: APC UPS cables File: apc.txt Date: 12 February 2010 Auth: Arjen de Korte , Martin Edlman This document was constructed from a mail from Martin. He figured out the pinouts to make the Back-UPS RS 500 work with a normal serial port. Here are the details: --- UPS side PC side RJ45 (8 pins) DB9F 1 orange/white 2,8,9 2 orange 1 3 green/white chassis/ground 4 blue - 5 blue/white - 6 green 5 7 brown/white 4,6 8 brown - +---+ +-----+ +-----+ | | +---------------+ 8 7 6 5 4 3 2 1 view from back (cable entry) --- Note this results in OL=-CTS, LB=DCD, SD=ST, CP=RTS, so it should work with the existing genericups type 20. nut-2.7.4/docs/common.xsl0000644000175000017500000001035712640473702012236 00000000000000 1 0 images/icons/ 0 0 #E0E0E0 images/icons/ margin-left: 0; margin-right: 10%; article toc,title book toc,title,figure,table,example,equation chapter toc,title part toc,title preface toc,title qandadiv toc qandaset toc reference toc,title sect1 toc sect2 toc sect3 toc sect4 toc sect5 toc section toc set toc,title article nop book nop nut-2.7.4/docs/asciidoc.conf0000644000175000017500000000345112640443572012642 00000000000000## NUT macros: linkman, linkdoc # # Usage: linkman:command[manpage-section] # This macro allows to handle variable manpage location, depending on the # document type # # Note, {0} is the manpage section, while {target} is the command. # # Example: linkman:ups.conf[5] # # Show NUT link as: (
    ); if section is defined, else just show # the command. # ################################################################################ # # Usage: linkdoc:document[display title,[anchor]] # This macro allows to handle variable NUT documentation location, depending # on the document type # # Note, {1} is the display title, {2} is the optional anchor name, # {0} is the whole set of args ({1}...{n}) and {target} is the # base document name. # Example: # linkdoc:user-manual[user manual,NUT_Security] # linkdoc:developer-guide[developer guide,_status_data] [macros] (?su)[\\]?(?Plinkman):(?P\S*?)\[(?P.*?)\]= (?su)[\\]?(?Plinkdoc):(?P\S*?)\[(?P.*?)\]= ifdef::basebackend-docbook[] ifdef::xhtml11_format[] [linkman-inlinemacro] {target}{0?({0})} [linkdoc-inlinemacro] {1} endif::xhtml11_format[] # FIXME: linkdoc does not support 'anchor' ifdef::chunked_format[] [linkman-inlinemacro] {target}{0?({0})} [linkdoc-inlinemacro] {1} endif::chunked_format[] # PDF output points online versions # FIXME: linkdoc does not support 'anchor' ifdef::pdf_format[] [linkman-inlinemacro] {target}{0?({0})} [linkdoc-inlinemacro] {1} endif::pdf_format[] endif::basebackend-docbook[] nut-2.7.4/docs/hid-subdrivers.txt0000644000175000017500000002312012667753305013712 00000000000000How to make a new subdriver to support another USB/HID UPS ---------------------------------------------------------- Overall concept ~~~~~~~~~~~~~~~ USB (Universal Serial Port) devices can be divided into several different classes (audio, imaging, mass storage etc). Almost all UPS devices belong to the "HID" class, which means "Human Interface Device", and also includes things like keyboards and mice. What HID devices have in common is a particular (and very flexible) interface for reading and writing information (such as x/y coordinates and button states, in case of a mouse, or voltages and status information, in case of a UPS). The NUT "usbhid-ups" driver is a meta-driver that handles all HID UPS devices. It consists of a core driver that handles most of the work of talking to the USB hardware, and several sub-drivers to handle specific UPS manufacturers (MGE, APC, and Belkin are currently supported). Adding support for a new HID UPS device is easy, because it requires only the creation of a new sub-driver. There are a few USB UPS devices that are not HID devices. These devices typically implement some version of the manufacturer's serial protocol over USB (which is a really dumb idea, by the way). An example is the Tripplite USB. Such devices are *not* supported by the usbhid-ups driver, and are not covered in this document. If you need to add support for such a device, read new-drivers.txt and see the tripplite_usb driver for inspiration. HID Usage Tree ~~~~~~~~~~~~~~ From the point of view of writing a HID subdriver, a HID device consists of a bunch of variables. Some variables (such as the current input voltage) are read-only, whereas other variables (such as the beeper enabled/disabled/muted status) can be read and written. These variables are usually grouped together and arranged in a hierarchical tree shape, similar to directories in a file system. This tree is called the "usage" tree. For example, here is part of the usage tree for a typical APC device. Variable components are separated by ".". Typical values for each variable are also shown for illustrative purposes. [width="35%"] |================================================ |UPS.Battery.Voltage | 11.4 V |UPS.Battery.ConfigVoltage | 12 V |UPS.Input.Voltage | 117 V |UPS.Input.ConfigVoltage | 120 V |UPS.AudibleAlarmControl | 2 (=enabled) |UPS.PresentStatus.Charging | 1 (=yes) |UPS.PresentStatus.Discharging | 0 (=no) |UPS.PresentStatus.ACPresent | 1 (=yes) |================================================ As you can see, variables that describe the battery status might be grouped together under "Battery", variables that describe the input power might be grouped together under "Input", and variables that describe the current UPS status might be grouped together under "PresentStatus". All of these variables are grouped together under "UPS". This hierarchical organization of data has the advantage of being very flexible; for example, if some device has more than one battery, then similar information about each battery could be grouped under "Battery1", "Battery2" and so forth. If your UPS can also be used as a toaster, then information about the toaster function might be grouped under "Toaster", rather than "UPS". However, the disadvantage is that each manufacturer will have their own idea about how the usage tree should be organized, and usbhid-ups needs to know about all of them. This is why manufacturer specific subdrivers are needed. To make matters more complicated, usage tree components (such as "UPS", "Battery", or "Voltage") are internally represented not as strings, but as numbers (called "usages" in HID terminology). These numbers are defined in the "HID Usage Tables", available from http://www.usb.org/developers/hidpage/. The standard usages for UPS devices are defined in a document called "Usage Tables for HID Power Devices" (the Power Device Class [PDC] specification). For example: -------------------------------------------------------------------------------- 0x00840010 = UPS 0x00840012 = Battery 0x00840030 = Voltage 0x00840040 = ConfigVoltage 0x0084001a = Input 0x0084005a = AudibleAlarmControl 0x00840002 = PresentStatus 0x00850044 = Charging 0x00850045 = Discharging 0x008500d0 = ACPresent -------------------------------------------------------------------------------- Thus, the above usage tree is internally represented as: -------------------------------------------------------------------------------- 00840010.00840012.00840030 00840010.00840012.00840040 00840010.0084001a.00840030 00840010.0084001a.00840040 00840010.0084005a 00840010.00840002.00850044 00840010.00840002.00850045 00840010.00840002.008500d0 -------------------------------------------------------------------------------- To make matters worse, most manufacturers define their own additional usages, even in cases where standard usages could have been used. for example Belkin defines `00860040` = ConfigVoltage (which is incidentally a violation of the USB PDC specification, as `00860040` is reserved for future use). Thus, subdrivers generally need to provide: - manufacturer-specific usage definitions, - a mapping of HID variables to NUT variables. Moreover, subdrivers might have to provide additional functionality, such as custom implementations of specific instant commands (load.off, shutdown.restart), and conversions of manufacturer specific data formats. Writing a subdriver ~~~~~~~~~~~~~~~~~~~ In preparation for writing a subdriver for a device that is currently unsupported, run usbhid-ups with the following command line: drivers/usbhid-ups -DD -u root -x explore -x vendorid=XXXX auto (substitute your device's 4-digit VendorID instead of "XXXX"). This will produce a bunch of debugging information, including a number of lines starting with "Path:" that describe the device's usage tree. This information forms the initial basis for a new subdriver. You should save this information to a file, e.g. drivers/usbhid-ups -DD -u root -x explore -x vendorid=XXXX auto >& /tmp/info You can create an initial "stub" subdriver for your device by using script scripts/subdriver/gen-usbhid-subdriver.sh. Note: this only creates a "stub" and needs to be futher customized to be useful (see CUSTOMIZATION below). Use the script as follows: scripts/subdriver/gen-usbhid-subdriver.sh < /tmp/info where /tmp/info is the file where you previously saved the debugging information. This script prompts you for a name for the subdriver; use only letters and digits, and use natural capitalization such as "Belkin" (not "belkin" or "BELKIN"). The script may prompt you for additional information. You should put the generated files into the drivers/ subdirectory, and update usbhid-ups.c by adding the appropriate #include line and by updating the definition of subdriver_list in usbhid-ups.c. You must also add the subdriver to USBHID_UPS_SUBDRIVERS in drivers/Makefile.am and call "autoreconf" and/or "./configure" from the top level NUT directory. You can then recompile usbhid-ups, and start experimenting with the new subdriver. CUSTOMIZATION: The initially generated subdriver code is only a stub, and will not implement any useful functionality (in particular, it will be unable to shut down the UPS). In the beginning, it simply attempts to monitor some UPS variables. To make this driver useful, you must examine the NUT variables of the form "unmapped.*" in the hid_info_t data structure, and map them to actual NUT variables and instant commands. There are currently no step-by-step instructions for how to do this. Please look at the files to see how the currently implemented subdrivers are written.: - apc-hid.c/h - belkin-hid.c/h - cps-hid.c/h - explore-hid.c/h - libhid.c/h - liebert-hid.c/h - mge-hid.c/h - powercom-hid.c/h - tripplite-hid.c/h Shutting down the UPS ~~~~~~~~~~~~~~~~~~~~~ It is desireable to support shutting down the UPS. Usually (for devices that follow the HID Power Device Class specification), this requires sending the UPS two commands. One for shutting down the UPS (with an 'offdelay') and one for restarting it (with an 'ondelay'), where offdelay < ondelay. The two NUT commands for which this is relevant, are 'shutdown.return' and 'shutdown.stayoff'. Since the one-to-one mapping above doesn't allow sending two HID commands to the UPS in response to sending one NUT command to the driver, this is handled by the driver. In order to make this work, you need to define the following four NUT values: ups.delay.start (variable, R/W) ups.delay.shutdown (variable, R/W) load.off.delay (command) load.on.delay (command) If the UPS supports it, the following variables can be used to show the countdown to start/shutdown: ups.timer.start (variable, R/O) ups.timer.shutdown (variable, R/O) The `load.on` and `load.off` commands will be defined implicitly by the driver (using a delay value of '0'). Define these commands yourself, if your UPS requires a different value to switch on/off the load without delay. Note that the driver expects the `load.off.delay` and `load.on.delay` to follow the HID Power Device Class specification, which means that the `load.on.delay` command should NOT switch on the load in the absence of mains power. If your UPS switches on the load regardless of the mains status, DO NOT define this command. You probably want to define the `shutdown.return` and/or `shutdown.stayoff` commands in that case. Commands defined in the subdriver will take precedence over the ones that are composed in the driver. When running the driver with the '-k' flag, it will first attempt to send a `shutdown.return` command and if that fails, will fallback to `shutdown.reboot`. nut-2.7.4/docs/developer-guide.txt0000644000175000017500000000751212640443572014040 00000000000000:titles.underlines: "__","==","--","~~","^^" Network UPS Tools Developer Guide _________________________________ :Author: Russell_Kroll,_Arnaud_Quette,_Charles_Lepple_and_Peter_Selinger :Author Initials: RK, AQ, CL & PS Introduction ============ NUT is both a powerful toolkit and framework that provides support for Power Devices, such as Uninterruptible Power Supplies, Power Distribution Units and Solar Controllers. This document intend to describe how NUT is designed, and the way to develop new device drivers and client applications. [[design]] include::design.txt[] [[developers]] include::developers.txt[] [[new-drivers]] include::new-drivers.txt[] [[sock-protocol]] include::sock-protocol.txt[] [[augeas]] include::../scripts/augeas/README[] [[devscan]] include::../tools/nut-scanner/README[] [[new-clients]] include::new-clients.txt[] [[net-protocol]] include::net-protocol.txt[] [[dev-tools]] NUT developers tools ==================== NUT provides several tools for clients and core developers, and QA people. [[dev-simu]] Device simulation ----------------- The dummy-ups driver propose a simulation mode, also known as 'Dummy Mode'. This mode allows to simulate any kind of devices, even non existing ones. Using this method, you can either replay a real life sequence, <>, or directly interact through upsrw or by editing the device file, to modify the variables values. Here is an example to setup a device simulation: - install NUT as usual, if not already done - get a simulation file (.dev) or sequence (.seq), or generate one using the <>. Sample files are provided in the 'data' directory of the NUT source. You can also download these from the development repository, such as the link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2778&view=co[evolution500.seq]. - copy the simulation file to your sysconfig directory, like /etc/nut or /etc/ups - configure NUT for simulation (linkman:ups.conf[5]): + [dummy] driver = dummy-ups port = evolution500.dev desc = "dummy-ups in dummy mode" + - now start NUT, at least dummy-ups and upsd: + $ upsdrvctl start dummy $ upsd + - and check the data: + $ upsc dummy ... + - you can also use upsrw to modify the data: + $ upsrw -s ups.status="OB LB" -u user -p password dummy + - or directly edit /etc/nut/evolution500.seq. In this case, modification will only apply according to the TIMER events and the current position in the sequence. For more information, refer to linkman:dummy-ups[8] manual page. [[dev-recording]] Device recording ---------------- To complete dummy-ups, NUT provides a device recorder script called 'nut-recorder.sh' and located in the 'tools/' directory of the NUT source tree. This script uses 'upsc' to record device information, and stores these in a differential fashion every 5 seconds (by default). Its usage is the following: Usage: dummy-recorder.sh [output-file] [interval] For example, to record information from the device 'myups' every 10 seconds: nut-recorder.sh myups@localhost myups.seq 10 During the recording, you will want to generate power events, such as power failure and restoration. These will be tracked in the simulation files, and be eventually be replayed by the <> driver. NUT core development and maintenance ==================================== This section is intended to people who want to develop new core features, or to do some maintenance. include::macros.txt[] [[roadmap]] include::../TODO[] [[nut-names]] Appendix A: NUT command and variable naming scheme ================================================== include::nut-names.txt[] [[lib-info]] Appendix B: NUT libraries complementary information =================================================== include::../lib/README[] nut-2.7.4/docs/security.txt0000644000175000017500000005742112640473702012631 00000000000000Notes on securing NUT ===================== The NUT Team is very interested in providing the highest security level to its users. Many internal and external mechanisms exist to secure NUT. And several steps are needed to ensure that your NUT setup meets your security requirements. This chapter will present you these mechanisms, by increasing order of security level. This means that the more security you need, the more mechanisms you will have to apply. NOTE: you may want to have a look at NUT Quality Assurance, since some topics are related to NUT security and reliability. [[verifySourceSig]] How to verify the NUT source code signature ------------------------------------------- In order to verify the NUT source code signature for releases, perform the following steps: - Retrieve the link:http://www.networkupstools.org/download.html[NUT source code] (nut-X.Y.Z.tar.gz) and the matching signature (nut-X.Y.Z.tar.gz.sig) - Retrieve the link:http://www.networkupstools.org/source/nut-key.gpg[NUT maintainer's signature]: $ gpg --fetch-keys http://www.networkupstools.org/source/nut-key.gpg NOTE: As of NUT 2.7.3, a new release key is used. In order to verify previous release, please use link:http://www.networkupstools.org/source/nut-old-key.gpg[NUT old maintainer's signature] - Launch the GPG checking using the following command: $ gpg --verify nut-X.Y.Z.tar.gz.sig - You should see a message mentioning a "Good signature", like: gpg: Signature made Wed Apr 15 15:55:30 2015 CEST using RSA key ID 55CA5976 gpg: Good signature from "Arnaud Quette ..." ... NOTE: the previously used maintainer's signature would output: + gpg: Signature made Thu Jul 5 16:15:05 2007 CEST using DSA key ID 204DDF1B gpg: Good signature from "Arnaud Quette ..." ... System level privileges and ownership ------------------------------------- All configuration files should be protected so that the world can't read them. Use the following commands to accomplish this: chown root:nut /etc/nut/* chmod 640 /etc/nut/* Finally, the <> directory, which holds the communication between the driver(s) and upsd, should also be secured. chown root:nut /var/state/ups chmod 0770 /var/state/ups NUT level user privileges ------------------------- Administrative commands such as setting variables and the instant commands are powerful, and access to them needs to be restricted. NUT provides an internal mechanism to do so, through linkman:upsd.users[5]. This file defines who may access instant commands and settings, and what is available. During the initial <>, we have created a monitoring user for upsmon. You can also create an 'administrator' user with full power using: [administrator] password = mypass actions = set instcmds = all For more information on how to restrict actions and instant commands, refer to linkman:upsd.users[5] manual page. NOTE: NUT administrative user definitions should be used in conjunction with <>. Network access control ---------------------- If you are not using NUT on a standalone setup, you will need to enforce network access to upsd. There are various ways to do so. NUT LISTEN directive ~~~~~~~~~~~~~~~~~~~~ linkman:upsd.conf[5]. LISTEN interface port Bind a listening port to the interface specified by its Internet address. This may be useful on hosts with multiple interfaces. You should not rely exclusively on this for security, as it can be subverted on many systems. Listen on TCP port `port` instead of the default value which was compiled into the code. This overrides any value you may have set with `configure --with-port`. If you don't change it with configure or this value, `upsd` will listen on port 3493 for this interface. Multiple LISTEN addresses may be specified. The default is to bind to 127.0.0.1 if no LISTEN addresses are specified (and ::1 if IPv6 support is compiled in). LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 This parameter will only be read at startup. You'll need to restart (rather than reload) `upsd` to apply any changes made here. Firewall ~~~~~~~~ NUT has its own official IANA port: 3493/tcp. The `upsmon` process on slave systems, as well as any other NUT client (such as `upsc`, `upscmd`, `upsrw`, NUT-Monitor, ...) connects to the `upsd` process on the master system via this TCP port. The `upsd` process does not connect out. You should use this to restrict network access. [[UFW]] include::../scripts/ufw/README[] [[TCP_Wrappers]] TCP Wrappers ~~~~~~~~~~~~ If the server is build with tcp-wrappers support enabled, it will check if the NUT username is allowed to connect from the client address through the `/etc/hosts.allow` and `/etc/hosts.deny` files. NOTE: this will only be done for commands that require the user to be logged into the server. `hosts.allow`: ups : admin@127.0.0.1/32 ups : monslave@127.0.0.1/32 monslave@192.168.1.0/24 `hosts.deny`: upsd : ALL Further details are described in hosts_access(5). Configuring SSL --------------- SSL is available as a build option (`--with-ssl`). It encrypts sessions between upsd and clients, and can also be used to authenticate servers. This means that stealing port 3493 from upsd will no longer net you interesting passwords. Several things must happen before this will work, however. This chapter will present these steps. SSL is available via two back-end libraries : NSS and OpenSSL (historically). You can choose to use one of them by specifying it with a build option (`--with-nss` or `--with-openssl`). If neither is specified, the configure script will try to detect one of them, with a precedence for OpenSSL. OpenSSL backend usage ~~~~~~~~~~~~~~~~~~~~~ This section describes how to enable NUT SSL support using link:http://www.openssl.org[OpenSSL]. Install OpenSSL ^^^^^^^^^^^^^^^ Install link:http://www.openssl.org[OpenSSL] as usual, either from source or binary packages. If using binary packages, be sure to include the developer libraries. Recompile and install NUT ^^^^^^^^^^^^^^^^^^^^^^^^^ Recompile NUT from source, starting with `configure --with-openssl`. Then install everything as usual. Create a certificate and key for upsd ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ openssl (the program) should be in your PATH, unless you installed it from source yourself, in which case it may be in /usr/local/ssl/bin. Use the following command to create the certificate: openssl req -new -x509 -nodes -out upsd.crt -keyout upsd.key You can also put a `-days nnn` in there to set the expiration. If you skip this, it may default to 30 days. This is probably not what you want. It will ask several questions. What you put in there doesn't matter a whole lot, since nobody is going to see it for now. Future versions of the clients may present data from it, so you might use this opportunity to identify each server somehow. Figure out the hash for the key ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use the following command to determine the hash of the certificate: openssl x509 -hash -noout -in upsd.crt You'll get back a single line with 8 hex characters. This is the hash of the certificate, which is used for naming the client-side certificate. For the purposes of this example the hash is *0123abcd*. Install the client-side certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use the following commands to install the client-side certificate: mkdir chmod 0755 cp upsd.crt /.0 Example: mkdir /usr/local/ups/etc/certs chmod 0755 /usr/local/ups/etc/certs cp upsd.crt /usr/local/ups/etc/certs/0123abcd.0 If you already have a file with that name in there, increment the 0 until you get a unique filename that works. If you have multiple client systems (like upsmon slaves), be sure to install this file on them as well. We recommend making a directory under your existing confpath to keep everything in the same place. Remember the path you created, since you will need to put it in upsmon.conf later. It must not be writable by unprivileged users, since someone could insert a new client certificate and fool upsmon into trusting a fake upsd. Create the combined file for upsd ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To do so, use the below commands: cat upsd.crt upsd.key > upsd.pem chown root:nut upsd.pem chmod 0640 upsd.pem This file must be kept secure, since anyone possessing it could pretend to be upsd and harvest authentication data if they get a hold of port 3493. Having it be owned by 'root' and readable by group 'nut' allows upsd to read the file without being able to change the contents. This is done to minimize the impact if someone should break into upsd. NUT reads the key and certificate files after dropping privileges and forking. Note on certification authorities (CAs) and signed keys ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ There are probably other ways to handle this, involving keys which have been signed by a CA you recognize. Contact your local SSL guru. Install the server-side certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Install the certificate with the following command: mv upsd.pem Example: mv upsd.pem /usr/local/ups/etc/upsd.pem After that, edit your upsd.conf and tell it where to find it: CERTFILE /usr/local/ups/etc/upsd.pem Clean up the temporary files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rm -f upsd.crt upsd.key Restart upsd ^^^^^^^^^^^^ It should come back up without any complaints. If it says something about keys or certificates, then you probably missed a step. If you run upsd as a separate user id (like nutsrv), make sure that user can read the upsd.pem file. Point upsmon at the certificates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit your upsmon.conf, and tell it where the CERTPATH is: CERTPATH Example: CERTPATH /usr/local/ups/etc/certs Recommended: make upsmon verify all connections with certificates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Put this in upsmon.conf: CERTVERIFY 1 Without this, there is no guarantee that the upsd is the right host. Enabling this greatly reduces the risk of man in the middle attacks. This effectively forces the use of SSL, so don't use this unless all of your upsd hosts are ready for SSL and have their certificates in order. Recommended: force upsmon to use SSL ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Again in upsmon.conf: FORCESSL 1 If you don't use `CERTVERIFY 1`, then this will at least make sure that nobody can sniff your sessions without a large effort. Setting this will make upsmon drop connections if the remote upsd doesn't support SSL, so don't use it unless all of them have it running. NSS backend usage ~~~~~~~~~~~~~~~~~ This section describes how to enable NUT SSL support using link:http://www.mozilla.org/projects/security/pki/nss/[Mozilla NSS]. Install NSS ^^^^^^^^^^^ Install link:http://www.mozilla.org/projects/security/pki/nss/[Mozilla NSS] as usual, either from source or binary packages. If using binary packages, be sure to include the developer libraries, and nss-tools (for `certutil`). Recompile and install NUT ^^^^^^^^^^^^^^^^^^^^^^^^^ Recompile NUT from source, starting with `configure --with-nss`. Then install everything as usual. Create certificate and key for the host ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ NSS (package generally called libnss3-tools) will install a tool called `certutil`. It will be used to generate certificates and manage certificate database. Certificates should be signed by a certification authorities (CAs). Following commands are typical samples, contact your SSL guru or security officer to follow your company procedures. .Generate a server certificate for upsd: - Create a directory where store the certificate database: `mkdir cert_db` - Create the certificate database : `certutil -N -d cert_db` - Import the CA certificate: `certutil -A -d cert_db -n "My Root CA" -t "TC,," -a -i rootca.crt` - Create a server certificate request (here called 'My nut server'): `certutil -R -d cert_db -s "CN=My nut server,O=MyCompany,ST=MyState,C=US" -a -o server.req` - Make your CA sign the certificate (produces server.crt) - Import the signed certificate into server database: `certutil -A -d cert_db -n "My nut server" -a -i server.crt -t ",,"` - Display the content of certificate server: `certutil -L -d cert_db` Clients and servers in the same host could share the same certificate to authenticate them or use different ones in same or different databases. The same operation can be done in same or different databases to generate other certificates. Create a self-signed CA certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ NSS provides a way to create self-signed certificate which can acting as CA certificate, and to sign other certificates with this CA certificate. This method can be used to provide a CA certification chain without using an "official" certificate authority. .Generate a self-signed CA certificate: - Create a directory where store the CA certificate database: `mkdir CA_db` - Create the certificate database: `certutil -N -d CA_db` - Generate a certificate for CA: `certutil -S -d CA_db -n "My Root CA" -s "CN=My CA,O=MyCompany,ST=MyState,C=US" -t "CT,," -x -2` (Do not forget to answer 'Yes' to the question 'Is this a CA certificate [y/N]?') - Extract the CA certificate to be able to import it in upsd (or upsmon) certificate database: `certutil -L -d CA_db -n "My Root CA" -a -o rootca.crt` - Sign a certificate request with the CA certificate (simulate a real CA signature): `certutil -C -d CA_db -c "My Root CA" -a -i server.req -o server.crt -2 -6` Install the server-side certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Just copy the database directory (just the directory and included 3 database .db files) to the right place, such as `/usr/local/ups/etc/`: mv cert_db /usr/local/ups/etc/ upsd (required): certificate database and self certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit the upsd.conf to tell where find the certificate database: CERTPATH /usr/local/ups/etc/cert_db Also tell which is the certificate to send to clients to authenticate itself and the password to decrypt private key associated to certificate: CERTIDENT 'certificate name' 'database password' NOTE: Generally, the certificate name is the server domain name, but is not a hard rule. The certificate can be named as useful. upsd (optional): client authentication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ NOTE: This functionality is disabled by default. To activate it, recompile NUT with `WITH_CLIENT_CERTIFICATE_VALIDATION` defined: make CFLAGS="-DWITH_CLIENT_CERTIFICATE_VALIDATION" UPSD can accept three levels of client authentication. Just specify it with the directive `CERTREQUEST` with the corresponding value in the upsd.conf file: - NO: no client authentication. - REQUEST: a certificate is request to the client but it is not strictly validated. If the client does not send any certificate, the connection is closed. - REQUIRE: a certificate is requested to the client and if it is not valid (no validation chain) the connection is closed. Like CA certificates, you can add many 'trusted' client and CA certificates in server's certificate databases. upsmon (required): upsd authentication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order for upsmon to securely connect to upsd, it must authenticate it. You must associate an upsd host name to security rules in upsmon.conf with the directive 'CERTHOST'. 'CERTHOST' associates a hostname to a certificate name. It also determines whether a SSL connection is mandatory, and if the server certificate must be validated. CERTHOST 'hostname' 'certificate name' 'certverify' 'forcessl' If the flag `forcessl` is set to `1`, and upsd answers that it can not connect with SSL, the connection closes. If the flag `certverify` is set to `1` and the connection is done in SSL, upsd's certificate is verified and its name must be the specified 'certificate name'. To prevent security leaks, you should set all `certverify` and `forcessl` flags to `1` (force SSL connection and validate all certificates for all peers). You can specify `CERTVERIFY` and `FORCESSL` directive (to `1` or `0`) to define a default security rule to apply to all host not specified with a dedicated `CERTHOST` directive. If a host is not specified in a `CERTHOST` directive, its expected certificate name is its hostname. upsmon (optional): certificate database and self certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Like upsd, upsmon may need to authenticate itself (upsd's `CERTREQUEST` directive set to `REQUEST` or `REQUIRE`). It must access to a certificate (and its private key) in a certificate database configuring `CERTPATH` and `CERTIDENT` in upsmon.conf in the same way than upsd. CERTPATH /usr/local/ups/etc/cert_db CERTIDENT 'certificate name' 'database password' Restart upsd ~~~~~~~~~~~~ It should come back up without any complaints. If it says something about keys or certificates, then you probably missed a step. If you run upsd as a separate user ID (like nutsrv), make sure that user can read files in the certificate directory. NUT reads the keys and certificates after forking and dropping privileges. Restart upsmon ~~~~~~~~~~~~~~ You should see something like this in the syslog from upsd: foo upsd[1234]: Client mon@localhost logged in to UPS [myups] (SSL) If upsd or upsmon give any error messages, or the `(SSL)` is missing, then something isn't right. If in doubt about upsmon, start it with -D so it will stay in the foreground and print debug messages. It should print something like this every couple of seconds: polling ups: myups@localhost [SSL] Obviously, if the `[SSL]` isn't there, something's broken. Recommended: sniff the connection to see it for yourself ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using tcpdump, Wireshark (Ethereal), or another network sniffer tool, tell it to monitor port 3493/tcp and see what happens. You should only see `STARTTLS` go out, `OK STARTTLS` come back, and the rest will be certificate data and then seemingly random characters. If you see any plaintext besides that (USERNAME, PASSWORD, etc.) then something is not working. Potential problems ~~~~~~~~~~~~~~~~~~ If you specify a certificate expiration date, you will eventually see things like this in your syslog: Oct 29 07:27:25 rktoy upsmon[3789]: Poll UPS [for750@rktoy] failed - SSL error: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE: certificate verify failed You can verify that it is expired by using openssl to display the date: openssl x509 -enddate -noout -in It'll display a date like this: notAfter=Oct 28 20:05:32 2002 GMT If that's after the current date, you need to generate another cert/key pair using the procedure above. Conclusion ~~~~~~~~~~ SSL support should be considered stable but purposely under-documented since various bits of the implementation or configuration may change in the future. In other words, if you use this and it stops working after an upgrade, come back to this file to find out what changed. This is why the other documentation doesn't mention any of these directives yet. SSL support is a treat for those of you that RTFM. There are also potential licensing issues for people who ship binary packages since NUT is GPL and OpenSSL is not compatible with it. You can still build and use it yourself, but you can't distribute the results of it. Or maybe you can. It depends on what you consider "essential system software", and some other legal junk that we're not going to touch. Other packages have solved this by explicitly stating that an exception has been granted. That is (purposely) impossible here, since NUT is the combined effort of many people, and all of them would have to agree to a license change. This is actually a feature, since it means nobody can unilaterally run off with the source - not even the NUT team. Note that the replacement of OpenSSL by Mozilla Network Security Services (NSS) should avoid the above licensing issues. chrooting and other forms of paranoia ------------------------------------- It has been possible to run the drivers and upsd in a chrooted jail for some time, but it involved a number of evil hacks. From the 1.3 series, a much saner chroot behavior exists, using BIND 9 as an inspiration. The old way involved creating an entire tree, complete with libraries, a shell (!), and many auxiliary files. This was hard to maintain and could have become an interesting playground for an intruder. The new way is minimal, and leaves little in the way of usable materials within the jail. This document assumes that you already have created at least one user account for the software to use. If you're still letting it fall back on "nobody", stop right here and go figure that out first. It also assumes that you have everything else configured and running happily all by itself. Generalities ~~~~~~~~~~~~ Essentially, you need to create your configuration directory and state path in their own little world, plus a special device or two. For the purposes of this example, the chroot jail is /chroot/nut. The programs have been built with the default prefix, so they are using /usr/local/ups. First, create the confpath and bring over a few files. mkdir -p /chroot/nut/usr/local/ups/etc cd /chroot/nut/usr/local/ups/etc cp -a /usr/local/ups/etc/upsd.users . cp -a /usr/local/ups/etc/upsd.conf . cp -a /usr/local/ups/etc/ups.conf . We're using 'cp -a' to maintain the permissions on those files. Now bring over your state path, maintaining the same permissions as before. mkdir -p /chroot/nut/var/state cp -a /var/state/ups /chroot/nut/var/state Next we must put /etc/localtime inside the jail, or you may get very strange readings in your syslog. You'll know you have this problem if upsd shows up as UTC in the syslog while the rest of the system doesn't. mkdir -p /chroot/nut/etc cp /etc/localtime /chroot/nut/etc Note that this is not "cp -a", since we want to copy the *content*, not the symlink that it may be on some systems. Finally, create a tiny bit of /dev so the programs can enter the background properly - they redirect fds into the bit bucket to make sure nothing else grabs 0-2. mkdir -p /chroot/nut/dev cp -a /dev/null /chroot/nut/dev Try to start your driver(s) and make sure everything fires up as before. upsdrvctl -r /chroot/nut -u nutdev start Once your drivers are running properly, try starting upsd. upsd -r /chroot/nut -u nutsrv Check your syslog. If nothing is complaining, try running clients like upsc and upsmon. If they seem happy, then you're done. symlinks ~~~~~~~~ After you do this, you will have two copies of many things, like the confpath and the state path. I recommend deleting the 'real' /var/state/ups, replacing it with a symlink to /chroot/nut/var/state/ups. That will let other programs reference the .pid files without a lot of hassle. You can also do this with your confpath and point /usr/local/ups/etc at /chroot/nut/usr/local/ups/etc unless you're worried about something hurting the files inside that directory. In that case, you should maintain a 'master' copy and push it into the chroot path after making changes. upsdrvctl itself does not chroot, so the ups.conf still needs to be in the usual confpath. upsmon ~~~~~~ This has not yet been applied to upsmon, since it can be quite complicated when there are notifiers that need to be run. One possibility would be for upsmon to have three instances: - privileged root parent that listens for a shutdown command - unprivileged child that listens for notify events - unprivileged chrooted child that does network I/O This one is messy, and may not happen for some time, if ever. Config files ~~~~~~~~~~~~ You may now set chroot= and user= in the global section of ups.conf. upsd chroots before opening any config files, so there is no way to add support for that in upsd.conf at the present time. nut-2.7.4/docs/nut.dict0000644000175000017500000000057012640443572011667 00000000000000personal_ws-1.1 en 54 upsstats powerman PowerMan HCL PDC USB upsmon config TCP Javascript apcsmart dev upsset PDU pdu localhost gnuplot usr BigServers snmp OUTVOLT gd upscmd Fenton upsclient zlib SRC IP upsc upsd Megatec html ENUM Powerchute genericups upsimage IMG REQ upsdrvctl hostname SCD upslog ttyS linkdoc libpng usbhid Sistem Mustek IPMI cgi conf morbo upsrw LOADPCT nut-2.7.4/docs/config-notes.txt0000644000175000017500000006611412667537407013370 00000000000000Configuration notes =================== This chapter describe most of the configuration and use aspects of NUT, including establishing communication with the device and configuring safe shutdowns when the UPS battery runs out of power. There are many programs and <> in this package. You should check out the <> and other accompanying documentation to see how it all works. NOTE: NUT does not currently provide proper graphical configuration tools. However, there is now support for linkdoc:developer-guide[Augeas,augeas_user], which will enable the easier creation of configuration tools. Moreover, linkman:nut-scanner[8] is available to discover supported devices (USB, SNMP, Eaton XML/HTTP and IPMI) and NUT servers (using Avahi or the classic connection method). Details about the configuration files ------------------------------------- Generalities ~~~~~~~~~~~~ All configuration files within this package are parsed with a common state machine, which means they all can use a number of extras described here. First, most of the programs use an uppercase word to declare a configuration directive. This may be something like MONITOR, NOTIFYCMD, or ACCESS. The case does matter here. "monitor" won't be recognized. Next, the parser does not care about whitespace between words. If you like to indent things with tabs or spaces, feel free to do it here. If you need to set a value to something containing spaces, it has to be contained within "quotes" to keep the parser from splitting up the line. That is, you want to use something like this: SHUTDOWNCMD "/sbin/shutdown -h +0" Without the quotes, it would only see the first word on the line. OK, so let's say you really need to embed that kind of quote within your configuration directive for some reason. You can do that too. NOTIFYCMD "/bin/notifyme -foo -bar \"hi there\" -baz" In other words, `\` can be used to escape the `"`. Finally, for the situation where you need to put the `\` character into your string, you just escape it. NOTIFYCMD "/bin/notifyme c:\\dos\\style\\path" The `\` can actually be used to escape any character, but you only really need it for `\`, `"`, and `#` as they have special meanings to the parser. When using file names with space characters, you may end up having tricky things since you need to write them inside `""` which must be escaped: NOTIFYCMD "\"c:\\path with space\\notifyme\" \"c:\\path with space\\name\"" `#` is the comment character. Anything after an unescaped `#` is ignored. Something like this... identity = my#1ups will actually turn into `identity = my`, since the `#` stops the parsing. If you really need to have a `#` in your configuration, then escape it. identity = my\#1ups Much better. The `=` character should be used with care too. There should be only one "simple" `=` character in a line: between the parameter name and its value. All other `=` characters should be either escaped or within "quotes". password = 123=123 is incorrect. You should use: password = 123\=123 or: password = "123=123" Line spanning ~~~~~~~~~~~~~ You can put a backslash at the end of the line to join it to the next one. This creates one virtual line that is composed of more than one physical line. Also, if you leave the `""` quote container open before a newline, it will keep scanning until it reaches another one. If you see bizarre behavior in your configuration files, check for an unintentional instance of quotes spanning multiple lines. Basic configuration ------------------- This chapter describe the base configuration to establish communication with the device. This will be sufficient for PDU. But for UPS and SCD, you will also need to configure <>. image:images/simple.png[] [[Driver_configuration]] Driver configuration ~~~~~~~~~~~~~~~~~~~~ Create one section per UPS in /usr/local/ups/etc/ups.conf To find out which driver to use, check the <>, or data/driver.list. Once you have picked a driver, create a section for your UPS in ups.conf. You must supply values for "driver" and "port". Some drivers may require other flags or settings. The "desc" value is optional, but is recommended to provide a better description of what your UPS is supporting. A typical device without any extra settings looks like this: [mydevice] driver = mydriver port = /dev/ttyS1 desc = "Workstation" NOTE: USB drivers (usbhid-ups, bcmxcp_usb, tripplite_usb, blazer_usb and richcomm_usb) are special cases and ignore the 'port' value. You must still set this value, but it does not matter what you set it to; a common and good practice is to set 'port' to *auto*, but you can put whatever you like. If you only own one UBS UPS, the driver will find it automatically. If you own more than one, refer to the driver's manual page for more information on matching a specific device. References: linkman:ups.conf[5], linkman:nutupsdrv[8], linkman:bcmxcp_usb[8], linkman:blazer[8], linkman:richcomm_usb[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] [[Starting_drivers]] Starting the driver(s) ~~~~~~~~~~~~~~~~~~~~~~ Start the driver(s) for your hardware: /usr/local/ups/sbin/upsdrvctl start Make sure the driver doesn't report any errors. It should show a few details about the hardware and then enter the background. You should get back to the command prompt a few seconds later. For reference, a successful start of the `usbhid-ups` driver looks like this: # /usr/local/ups/sbin/upsdrvctl start Network UPS Tools - Generic HID driver 0.34 (2.4.1) USB communication driver 0.31 Using subdriver: MGE HID 1.12 Detected EATON - Ellipse MAX 1100 [ADKK22008] If the driver doesn't start cleanly, make sure you have picked the right one for your hardware. You might need to try other drivers by changing the "driver=" value in ups.conf. Be sure to check the driver's man page to see if it needs any extra settings in `ups.conf` to detect your hardware. If it says `can't bind /var/state/ups/...` or similar, then your state path probably isn't writable by the driver. Check the <>. After making changes, try the <> step again. References: man pages: linkman:nutupsdrv[8], linkman:upsdrvctl[8] Data server configuration (upsd) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configure upsd, which serves data from the drivers to the clients. First, edit upsd.conf to allow access to your client systems. By default, upsd will only listen to localhost port 3493/tcp. If you want to connect to it from other machines, you must specify each interface you want upsd to listen on for connections, optionally with a port number. LISTEN 127.0.0.1 3493 LISTEN ::1 3493 NOTE: Refer to the NUT user manual <> for information on how to access and secure upsd clients connections. Next, create upsd.users. For now, this can be an empty file. You can come back and add more to it later when it's time to configure upsmon or run one of the management tools. Do not make either file world-readable, since they both hold access control data and passwords. They just need to be readable by the user you created in the preparation process. The suggested configuration is to chown it to root, chgrp it to the group you created, then make it readable by the group. chown root:nut upsd.conf upsd.users chmod 0640 upsd.conf upsd.users References: man pages: linkman:upsd.conf[5], linkman:upsd.users[5], linkman:upsd[8] [[Starting_upsd]] Starting the data server ~~~~~~~~~~~~~~~~~~~~~~~~ Start the network data server: /usr/local/ups/sbin/upsd Make sure it is able to connect to the driver(s) on your system. A successful run looks like this: # /usr/local/ups/sbin/upsd Network UPS Tools upsd 2.4.1 listening on 127.0.0.1 port 3493 listening on ::1 port 3493 Connected to UPS [eaton]: usbhid-ups-eaton upsd prints dots while it waits for the driver to respond. Your system may print more or less depending on how many drivers you have and how fast they are. NOTE: if upsd says that it can't connect to a UPS or that the data is stale, then your ups.conf is not configured correctly, or you have a driver that isn't working properly. You must fix this before going on to the next step. Reference: man page: linkman:upsd[8] Check the UPS data ~~~~~~~~~~~~~~~~~~ Status data ^^^^^^^^^^^ Make sure that the UPS is providing good status data. /usr/local/ups/bin/upsc myupsname@localhost ups.status You should see just one line in response: OL OL means your system is running on line power. If it says something else (like OB - on battery, or LB - low battery), your driver was probably misconfigured during the <> step. If you reconfigure the driver, use `upsdrvctl stop` to stop it, then start it again as shown in the <> step. Reference: man page: linkman:upsc[8] All data ^^^^^^^^ Look at all of the status data which is being monitored. /usr/local/ups/bin/upsc myupsname@localhost What happens now depends on the kind of device and driver you have. In the list, you should see ups.status with the same value you got above. A sample run on a UPS (Eaton Ellipse MAX 1100) looks like this: battery.charge: 100 battery.charge.low: 20 battery.runtime: 2525 battery.type: PbAc device.mfr: EATON device.model: Ellipse MAX 1100 device.serial: ADKK22008 device.type: ups driver.name: usbhid-ups driver.parameter.pollfreq: 30 driver.parameter.pollinterval: 2 driver.parameter.port: auto driver.version: 2.4.1-1988:1990M driver.version.data: MGE HID 1.12 driver.version.internal: 0.34 input.sensitivity: normal input.transfer.boost.low: 185 input.transfer.high: 285 input.transfer.low: 165 input.transfer.trim.high: 265 input.voltage.extended: no outlet.1.desc: PowerShare Outlet 1 outlet.1.id: 2 outlet.1.status: on outlet.1.switchable: no outlet.desc: Main Outlet outlet.id: 1 outlet.switchable: no output.frequency.nominal: 50 output.voltage: 230.0 output.voltage.nominal: 230 ups.beeper.status: enabled ups.delay.shutdown: 20 ups.delay.start: 30 ups.firmware: 5102AH ups.load: 0 ups.mfr: EATON ups.model: Ellipse MAX 1100 ups.power.nominal: 1100 ups.productid: ffff ups.serial: ADKK22008 ups.status: OL CHRG ups.timer.shutdown: -1 ups.timer.start: -1 ups.vendorid: 0463 Reference: man page: linkman:upsc[8], <> Startup scripts ~~~~~~~~~~~~~~~ NOTE: This step is not necessary if you installed from packages. Edit your startup scripts, and make sure upsdrvctl and upsd are run every time your system starts. [[UPS_shutdown]] Configuring automatic shutdowns for low battery events ------------------------------------------------------ The whole point of UPS software is to bring down the OS cleanly when you run out of battery power. Everything else is roughly eye candy. To make sure your system shuts down properly, you will need to perform some additional configuration and run upsmon. Here are the basics. [[Shutdown_design]] Shutdown design ~~~~~~~~~~~~~~~ When your UPS batteries get low, the operating system needs to be brought down cleanly. Also, the UPS load should be turned off so that all devices that are attached to it are forcibly rebooted. Here are the steps that occur when a critical power event happens: 1. The UPS goes on battery 2. The UPS reaches low battery (a "critical" UPS), that is to say upsc displays: + ups.status: OB LB + The exact behavior depends on the specific device, and is related to: - battery.charge and battery.charge.low - battery.runtime and battery.runtime.low 3. The upsmon master notices and sets "FSD" - the "forced shutdown" flag to tell all slave systems that it will soon power down the load. + (If you have no slaves, skip to step 6) 4. upsmon slave systems see "FSD" and: - generate a NOTIFY_SHUTDOWN event - wait FINALDELAY seconds - typically 5 - call their SHUTDOWNCMD - disconnect from upsd 5. The upsmon master system waits up to HOSTSYNC seconds (typically 15) for the slaves to disconnect from upsd. If any are connected after this time, upsmon stops waiting and proceeds with the shutdown process. 6. The upsmon master: - generates a NOTIFY_SHUTDOWN event - waits FINALDELAY seconds - typically 5 - creates the POWERDOWNFLAG file - usually /etc/killpower - calls the SHUTDOWNCMD 7. On most systems, init takes over, kills your processes, syncs and unmounts some filesystems, and remounts some read-only. 8. init then runs your shutdown script. This checks for the POWERDOWNFLAG, finds it, and tells the UPS driver(s) to power off the load. 9. The system loses power. 10. Time passes. The power returns, and the UPS switches back on. 11. All systems reboot and go back to work. How you set it up ~~~~~~~~~~~~~~~~~ [[NUT_user_creation]] NUT user creation ^^^^^^^^^^^^^^^^^ Create a upsd user for upsmon to use while monitoring this UPS. Edit upsd.users and create a new section. upsmon will connect to upsd and use this user name (in brackets) and password to authenticate. This example is for a user called "monuser": [monuser] password = mypass upsmon master # or upsmon slave References: linkman:upsd[8], linkman:upsd.users[5] Reloading the data server ^^^^^^^^^^^^^^^^^^^^^^^^^ Reload upsd. Depending on your configuration, you may be able to do this without stopping upsd: /usr/local/ups/sbin/upsd -c reload If that doesn't work (check the syslog), just restart it: /usr/local/ups/sbin/upsd -c stop /usr/local/ups/sbin/upsd NOTE: if you want to make reloading work later, see the entry in the link:FAQ.html[FAQ] about starting upsd as a different user. Power Off flag file ^^^^^^^^^^^^^^^^^^^ Set the POWERDOWNFLAG location for upsmon. In upsmon.conf, add a POWERDOWNFLAG directive with a filename. upsmon will create this file when the UPS needs to be powered off during a power failure when low battery is reached. We will test for the presence of this file in a later step. POWERDOWNFLAG /etc/killpower References: man pages: linkman:upsmon[8], linkman:upsmon.conf[5] Securing upsmon.conf ^^^^^^^^^^^^^^^^^^^^ The recommended setting is to have it owned by root:nut, then make it readable by the group and not world. This file contains passwords that could be used by an attacker to start a shutdown, so keep it secure. chown root:nut upsmon.conf chmod 0640 upsmon.conf This step has been placed early in the process so you secure this file before adding sensitive data in the next step. Create a MONITOR directive for upsmon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit upsmon.conf and create a MONITOR line with the UPS definition (@), username and password from the <> step, and the master or slave setting. If it's the master (i.e., it's connected to this UPS directly): MONITOR myupsname@mybox 1 monuser mypass master If it's just monitoring this UPS over the network, and some other system is the master: MONITOR myupsname@mybox 1 monuser mypass slave The number "1" here is the power value. This should always be set to 1 unless you have a very special (read: expensive) system with redundant power supplies. In such cases, refer to the User Manual: - <>, - <>. References: linkman:upsmon[8], linkman:upsmon.conf[5] Define a SHUTDOWNCMD for upsmon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Still in upsmon.conf, add a directive that tells upsmon how to shut down your system. This example seems to work on most systems: SHUTDOWNCMD "/sbin/shutdown -h +0" Notice the presence of "quotes" here to keep it together. If your system has special needs, you may want to set this to a script which does local shutdown tasks before calling init. Start upsmon ^^^^^^^^^^^^ /usr/local/ups/sbin/upsmon If it complains about something, then check your configuration. Checking upsmon ^^^^^^^^^^^^^^^ Look for messages in the syslog to indicate success. It should look something like this: May 29 01:11:27 mybox upsmon[102]: Startup successful May 29 01:11:28 mybox upsd[100]: Client monuser@192.168.50.1 logged into UPS [myupsname] Any errors seen here are probably due to an error in the config files of either `upsmon` or `upsd`. You should fix them before continuing. Startup scripts ^^^^^^^^^^^^^^^ NOTE: This step is not need if you installed from packages. Edit your startup scripts, and add a call to `upsmon`. Make sure `upsmon` starts when your system comes up. Do it after `upsdrvctl` and `upsd`, or it will complain about not being able to contact the server. You may delete the POWERDOWNFLAG in the startup scripts, but it is not necessary. `upsmon` will clear that file for you when it starts. NOTE: Init script examples are provide in the 'scripts' directory of the NUT source tree, and in the various <<_binary_packages,packages>> that exist. Shutdown scripts ^^^^^^^^^^^^^^^^ NOTE: This step is not need if you installed from packages. Edit your shutdown scripts, and add `upsdrvctl shutdown`. You should configure your system to power down the UPS after the filesystems are remounted read-only. Have it look for the presence of the POWERDOWNFLAG (from linkman:upsmon.conf[5]), using this as an example: -------------------------------------------------------------------------------- if (test -f /etc/killpower) then echo "Killing the power, bye!" /usr/local/ups/bin/upsdrvctl shutdown sleep 120 # uh oh... the UPS power-off failed # you probably want to reboot here so you don't get stuck! # *** see also the section on power races in the FAQ! *** fi -------------------------------------------------------------------------------- [WARNING] ================================================================================ - Be careful that upsdrvctl command will probably power off your machine. Don't use it unless your system is ready to be halted by force. If you run RAID, read the <<_raid_warning,RAID warning>> below! - Make sure the filesystem(s) containing upsdrvctl, ups.conf and your UPS driver(s) are mounted (possibly in read-only mode) when the system gets to this point. Otherwise it won't be able to figure out what to do. ================================================================================ [[Testing_shutdowns]] Testing shutdowns ^^^^^^^^^^^^^^^^^ UPS equipment varies from manufacturer to manufacturer and even within model lines. You should test the <> on your systems before leaving them unattended. A successful sequence is one where the OS halts before the battery runs out, and the system restarts when power returns. The first step is to see how upsdrvctl will behave without actually turning off power. To do so, use the '-t' argument: /usr/local/ups/bin/upsdrvctl -t shutdown It will display the sequence without actually calling the drivers. You can finally test a forced shutdown sequence (FSD) using: /usr/local/ups/sbin/upsmon -c fsd This will execute a full shutdown sequence, as presented in <>, starting from the 3rd step. If everything works correctly, the computer will be forcibly powered off, may remain off for a few seconds to a few minutes (depending on the driver and UPS type), then will power on again. If your UPS just sits there and never resets the load, you are vulnerable to a power race and should add the "reboot after timeout" hack at the very least. Also refer to the section on power races in the link:FAQ.html[FAQ]. Using suspend to disk ~~~~~~~~~~~~~~~~~~~~~ Support for suspend to RAM and suspend to disk has been available in the Linux kernel for a while now. For obvious reasons, suspending to RAM isn't particularly useful when the UPS battery is getting low, but suspend to disk may be an interesting concept. This approach minimizes the amount of disruption which would be caused by an extended outage. The UPS goes on battery, then reaches low battery, and the system takes a snapshot of itself and halts. Then it is turned off and waits for the power to return. Once the power is back, the system reboots, pulls the snapshot back in, and keeps going from there. If the user happened to be away when it happened, they may return and have no idea that their system actually shut down completely in the middle. In order for this to work, you need to shutdown NUT (UPS driver, upsd server and upsmon client) in the suspend script and start them again in the resume script. Don't try to keep them running. The upsd server will latch the FSD state (so it won't be usable after resuming) and so will the upsmon client. Some drivers may work after resuming, but many don't and some UPSs will require re-initialization, so it's best not to keep this running either. After stopping driver, server and client you'll have to send the UPS the command to shutdown only if the POWERDOWNFLAG is present. Note that most likely you'll have to allow for a grace period after sending 'upsdrvctl shutdown' since the system will still have to take a snapshot of itself after that. Not all drivers support this, so before going down this road, make sure that the one you're using does. RAID warning ~~~~~~~~~~~~ If you run any sort of RAID equipment, make sure your arrays are either halted (if possible) or switched to "read-only" mode. Otherwise you may suffer a long resync once the system comes back up. The kernel may not ever run its final shutdown procedure, so you must take care of all array shutdowns in userspace before upsdrvctl runs. If you use software RAID (md) on Linux, get mdadm and try using 'mdadm --readonly' to put your arrays in a safe state. This has to happen after your shutdown scripts have remounted the filesystems. On hardware RAID or other kernels, you have to do some detective work. It may be necessary to contact the vendor or the author of your driver to find out how to put the array in a state where a power loss won't leave it "dirty". Our understanding is that most if not all RAID devices on Linux will be fine unless there are pending writes. Make sure your filesystems are remounted read-only and you should be covered. [[DataRoom]] Typical setups for enterprise networks and data rooms ----------------------------------------------------- The split nature of this UPS monitoring software allows a wide variety of power connections. This chapter will help you identify how things should be configured using some general descriptions. There are two main elements: 1. There's a UPS attached to a communication (serial, USB or network) port on this system. 2. This system depends on a UPS for power. You can play "mix and match" with those two to arrive at these descriptions for individual hosts: - A: 1 but not 2 - B: 2 but not 1 - C: 1 and 2 A small to medium sized data room usually has one C and a bunch of Bs. This means that there's a system (type C) hooked to the UPS which depends on it for power. There are also some other systems in there (type B) which depend on that same UPS for power, but aren't directly connected to it. Larger data rooms or those with multiple UPSes may have several "clusters" of the "single C, many Bs" depending on how it's all wired. Finally, there's a special case. Type A systems are connected to a UPS's serial port, but don't depend on it for power. This usually happens when a UPS is physically close to a box and can reach the serial port, but the wiring is such that it doesn't actually feed it. Once you identify a system's type, use this list to decide which of the programs need to be run for monitoring: - A: driver and upsd - B: upsmon (as slave) - C: driver, upsd, and upsmon (as master) To further complicate things, you can have a system that is hooked to multiple UPSes, but only depends on one for power. This particular situation makes it an "A" relative to one UPS, and a "C" relative to the other. The software can handle this - you just have to tell it what to do. NOTE: NUT can also serve as a data proxy to increase the number of clients, or share the communication load between several upsd instances. image:images/advanced.png[] If you are running large server-class systems that have more than one power feed, see the next section for information on how to handle it properly. [[BigServers]] Typical setups for big servers with UPS redundancy -------------------------------------------------- By using multiple MONITOR statements in upsmon.conf, you can configure an environment where a large machine with redundant power monitors multiple separate UPSes. image:images/bigbox.png[] Example configuration ~~~~~~~~~~~~~~~~~~~~~ For the examples in this section, we will use a server with four power supplies installed. Two UPS, 'Alpha' and 'Beta', are each driving two of the power supplies. This means that either 'Alpha' *or* 'Beta' can totally shut down and the server will be able to keep running. The upsmon.conf configuration that reflect this is the following: MONITOR ups-alpha@myhost 2 monuser mypass master MONITOR ups-beta@myhost 2 monuser mypass master MINSUPPLIES 2 With that configuration, upsmon will only shut down when both UPS reaches a critical (on battery + low battery) condition, since 'Alpha' and 'Beta' provide the same power value. As an added bonus, this means you can move a running server from one UPS to another (for maintenance purpose for example) without bringing it down since the minimum power will be provided at all times. The MINSUPPLIES line tells upsmon that we need at least 2 power supplies to be receiving power from a good UPS (on line or on battery, just not on battery and low battery). NOTE: we could have used a 'Power Value' of 1 for both UPS, and MINSUPPLIES set to 1 too. These values are purely arbitrary, so you are free to use your own rules. Here, we have linked these values to the number of power supplies that each UPS is feeding (2). Multiple UPS shutdowns ordering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have multiple UPSes connected to your system, chances are that you need to shut them down in a specific order. The goal is to shut down everything but the one keeping upsmon alive at first, then you do that one last. To set the order in which your UPSes receive the shutdown commands, define the 'sdorder' value in your ups.conf. [bigone] driver = usbhid-ups port = auto sdorder = 2 [littleguy] driver = mge-shut port = /dev/ttyS0 sdorder = 1 [misc] driver = blazer_ser port = /dev/ttyS1 sdorder = 0 The order runs from 0 to the highest number available. So, for this configuration, the order of shutdowns would be 'misc', 'littleguy', and then 'bigone'. NOTE: If you have a UPS that shouldn't be shutdown when running 'upsdrvctl shutdown', set the *sdorder* to *-1*. Other redundancy configurations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are a lot of ways to handle redundancy and they all come down to how many power supplies, power cords and independent UPS connections you have. A system with a 1:1 cord:supply ratio has more wires stuffed behind it, but it's much easier to move things around since any given UPS drives a smaller percentage of the overall power. More information can be found in the linkdoc:user-manual[NUT user manual], and the various link:man/index.html[user manual pages]. nut-2.7.4/docs/Makefile.in0000644000175000017500000006341612667762001012271 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = docs DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/docinfo.xml.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = docinfo.xml CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ IMAGE_FILES = images/asciidoc.png \ images/hostedby.png \ images/nut_layering.png \ images/nut-logo.png \ images/note.png \ images/warning.png \ images/blue-arrow.png \ images/simple.png \ images/advanced.png \ images/bigbox.png \ images/bizarre.png \ images/old-cgi.png # Only track here the local deps SHARED_DEPS = nut-names.txt asciidoc.conf USER_MANUAL_DEPS = acknowledgements.txt cables.txt config-notes.txt \ configure.txt download.txt documentation.txt features.txt history.txt \ outlets.txt scheduling.txt security.txt support.txt user-manual.txt DEVELOPER_GUIDE_DEPS = contact-closure.txt design.txt developers.txt \ developer-guide.txt hid-subdrivers.txt macros.txt new-clients.txt \ new-drivers.txt net-protocol.txt nutdrv_qx-subdrivers.txt \ snmp-subdrivers.txt sock-protocol.txt CABLES_DEPS = cables/apc-rs500-serial.txt \ cables/apc.txt cables/ge-imv-victron.txt cables/imv.txt \ cables/mgeups.txt cables/powerware.txt cables/repotec.txt \ cables/sms.txt CABLES_IMAGES = images/cables/73-0724.png images/cables/940-0024C.jpg \ images/cables/belkin-f6cx-rkm-xu-cable.jpg images/cables/Lansafecable.jpg \ images/cables/mac-940-0024C.png images/cables/mge-66049.png \ images/cables/mge-db9-rj12.jpg images/cables/mge-db9-rj45.jpg \ images/cables/mge-usb-rj45.jpg \ images/cables/SOLA-330.png ALL_TXT_SRC = nut-names.txt $(USER_MANUAL_DEPS) $(DEVELOPER_GUIDE_DEPS) \ $(CABLES_DEPS) FAQ.txt nut-qa.txt packager-guide.txt snmp.txt NUT_SPELL_DICT = nut.dict EXTRA_DIST = $(ALL_TXT_SRC) $(SHARED_DEPS) $(IMAGE_FILES) \ $(CABLES_IMAGES) $(NUT_SPELL_DICT) \ common.xsl xhtml.xsl chunked.xsl ASCIIDOC_HTML_SINGLE = user-manual.html \ developer-guide.html \ packager-guide.html \ FAQ.html ASCIIDOC_HTML_CHUNKED = user-manual.chunked \ developer-guide.chunked \ packager-guide.chunked \ FAQ.html ASCIIDOC_PDF = user-manual.pdf \ developer-guide.pdf \ packager-guide.pdf \ cables.pdf \ FAQ.pdf SUBDIRS = man SUFFIXES = .txt .html .pdf ### TODO: automatic dependency generation # Add other directory deps (not for local EXTRA_DIST) and generated contents FULL_USER_MANUAL_DEPS = $(USER_MANUAL_DEPS) $(SHARED_DEPS) ../README \ ../INSTALL.nut ../UPGRADING ../TODO ../scripts/ufw/README FULL_DEVELOPER_GUIDE_DEPS = $(DEVELOPER_GUIDE_DEPS) $(SHARED_DEPS) \ ../scripts/augeas/README ../TODO ../lib/README \ ../tools/nut-scanner/README # Note: without the "-v", asciidoc (circa 8.6.2) sometimes hangs when # generating the chunked HTML. In this case, export the environment # variable ASCIIDOC_VERBOSE to "-v", ie: # $ ASCIIDOC_VERBOSE=-v make A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) --attribute icons \ --xsltproc-opts "--nonet" \ --xsltproc-opts "--stringparam nut.localdate \"`TZ=UTC date +%Y-%m-%d`\"" \ --xsltproc-opts "--stringparam nut.localtime \"`TZ=UTC date +%H:%M:%S`\"" \ --xsltproc-opts "--stringparam nut.nutversion \"@PACKAGE_VERSION@\"" \ --attribute iconsdir=$(srcdir)/images \ --attribute=badges \ --attribute=external_title \ --attribute tree_version=@TREE_VERSION@ \ -a toc -a numbered --destination-dir=. # Non-interactively spell check all documentation source files. # This is useful for Buildbot and automated QA processing # FIXME: how to present output (std{out,err}, single file or per target)? @HAVE_ASPELL_TRUE@SPELLCHECK_SRC = $(ALL_TXT_SRC) ../README ../INSTALL.nut ../UPGRADING ../NEWS \ @HAVE_ASPELL_TRUE@ ../TODO ../scripts/ufw/README ../scripts/augeas/README ../lib/README \ @HAVE_ASPELL_TRUE@ ../tools/nut-scanner/README all: all-recursive .SUFFIXES: .SUFFIXES: .txt .html .pdf .chunked $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu docs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu docs/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): docinfo.xml: $(top_builddir)/config.status $(srcdir)/docinfo.xml.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool clean-local \ cscopelist-am ctags ctags-am distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am all: doc doc: @DOC_BUILD_LIST@ pdf: $(ASCIIDOC_PDF) # also build the HTML manpages with these targets html-single: $(ASCIIDOC_HTML_SINGLE) html-chunked: $(ASCIIDOC_HTML_CHUNKED) clean-local: rm -rf *.pdf *.html *.chunked docbook-xsl.css *.bak user-manual.html user-manual.chunked user-manual.pdf: $(FULL_USER_MANUAL_DEPS) developer-guide.html developer-guide.chunked developer-guide.pdf: $(FULL_DEVELOPER_GUIDE_DEPS) packager-guide.html packager-guide.chunked packager-guide.pdf: packager-guide.txt asciidoc.conf .txt.html: common.xsl xhtml.xsl $(A2X) $(A2X_COMMON_OPTS) --attribute=xhtml11_format --format=xhtml --xsl-file=$(srcdir)/xhtml.xsl $< .txt.chunked: common.xsl chunked.xsl $(A2X) $(A2X_COMMON_OPTS) --attribute=chunked_format --format=chunked --xsl-file=$(srcdir)/chunked.xsl $< .txt.pdf: docinfo.xml $(A2X) $(A2X_COMMON_OPTS) --attribute=pdf_format --format=pdf -a docinfo1 $< @HAVE_ASPELL_TRUE@spellcheck: @HAVE_ASPELL_TRUE@ @for docsrc in $(SPELLCHECK_SRC); do \ @HAVE_ASPELL_TRUE@ echo "Spell checking on $$docsrc"; \ @HAVE_ASPELL_TRUE@ LANG=C $(ASPELL) -a -t -p $(NUT_SPELL_DICT) < $$docsrc | grep [^*]; \ @HAVE_ASPELL_TRUE@ done # Interactively spell check all documentation source files @HAVE_ASPELL_TRUE@spellcheck-interactive: @HAVE_ASPELL_TRUE@ @for docsrc in $(SPELLCHECK_SRC); do\ @HAVE_ASPELL_TRUE@ echo "Spell checking on $$docsrc"; \ @HAVE_ASPELL_TRUE@ LANG=C $(ASPELL) check -p $(NUT_SPELL_DICT) $$docsrc; \ @HAVE_ASPELL_TRUE@ done @HAVE_ASPELL_FALSE@spellcheck: @HAVE_ASPELL_FALSE@ @echo "Documentation spell check not available since 'aspell' was not found." @HAVE_ASPELL_FALSE@spellcheck-interactive: @HAVE_ASPELL_FALSE@ @echo "Documentation spell check not available since 'aspell' was not found." .PHONY: html html-single pdf # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/docs/xhtml.xsl0000644000175000017500000000121112640473702012067 00000000000000 nut-2.7.4/docs/packager-guide.txt0000644000175000017500000002231012667573713013633 00000000000000NUT Packager and Integrators Guide ================================== Arnaud Quette WARNING: this is a Work In Progress document. Abstract -------- The aim of this document is to describe the best way to package the Network UPS Tools, and the best practices across the various packaging implementation of NUT. So as to these can be spread on all supported platforms as a standard, and as a foundation block to build upon. /////////////////////////////////////////////////////////////////////// *sandbox* that have been done to help those improving, and give advice on what's the best way to package NUT for the remaining "not yet packaged" platform (Sun, Aix, Mac, ...). The ultimate aim is to have NUT well packaged (all NUT features available) on all supported platforms. /////////////////////////////////////////////////////////////////////// Introduction ------------ Packaging is a final aim for software. It eases and completes the software integration into an OS, and allows users to have an easy software installation and support out of the box. NOTE: making NUT packaging more uniform should help its documentation, support and maintenance across the supported OSes. ------------------------------------------------------------------------ *sandbox* This document assumes that you have read the other NUT document such as INSTALL.nut, FAQ, config-notes.txt, ... Facts about NUT packaging ========================= NUT has so much evolved those two last years (with USB and SNMP support, the premises of libraries, ...) that the simple "1, 2 or 3 package(s)" approach is no more suitable. This fact has reached a high level since NUT 1.4. Actually, doing this would result in either being forced to install hosts of unneeded dependencies (net-snmp, gd, ... as seen on SuSE), to have a partially broken package [1] or not being able to use all NUT features [2]. Let's now have an overview on how NUT is currently packaged: 1) Debian: http://packages.qa.debian.org/n/nut.html nut, nut-dev, nut-usb, nut-snmp, nut-xml, nut-cgi, nut-doc 2) Mandriva http://cvs.mandriva.com/cgi-bin/cvsweb.cgi/SPECS/nut/ nut-server nut nut-cgi 3) SuSE / Novell nut 4) RedHat 5) PLD http://cvs.pld-linux.org/cgi-bin/cvsweb/SPECS/nut.spec ... (FreeBSD, Gentoo Linux, IRIX, NetBSD, OpenBSD) This shows how much the packages name split is now scattered. The result is: - that a user of several systems will be lost, and will waste time - there is a big waste of energy - this makes things hard to create standard configuration wizards [1] NUT build on Debian GNU/Linux m68k and Hurd was once broken due to hiddev dependencies, and usb support still included in the core package. [2] - snmp-ups driver is not available under Mandrake GNU/Linux, but its man is present. See http://rpms.mandrakeclub.com/rpms/mandrake/9.1/i586/Mandrake/RPMS/nut-server-1.2.1-4mdk.i586.html - secured ssh network mode not available (due to deps and/or non free) - some systems don't provide libupsclient lib/header/.pc so as to client application (such as wmnut) can't be built - the logger function is not (well) used, same goes for the syslog (triple redundancy in Mandriva) - the solution is partial in every system: lost of tests case / feedback could be shared ... ------------------------------------------------------------------------ Packagers involved ------------------ The following packagers are working on this subject: - Debian (and derivatives): Arnaud Quette - SuSE/Novell: Stanislav Brabec NOTE: the people below should be contacted to (re)launch discussions! The following packagers should be interested in working on this subject: - FreeBSD: Thierry Thomas? <> - Mandriva: Oden Erikson? <> - RedHat / Fedora Core: <> - Gentoo: <> - NetBSD: <> - OpenBSD: <> - PLD: Andrzej Zawadzki - E-Smith: Charlie Brady - openSolaris: <> - Windows: check with WinNUT author?! - MacOS: <> => Charles Lepple? <> - HP-UX: <> - IBM AIX: <> Possible use cases ------------------ - standalone (1 system + 1-n UPS) - network server (same as standalone, but serving data to network clients) - network monitoring client - network supervision client TO BE COMPLETED... Optimised packaging proposal ---------------------------- NOTE: The below proposed packages split is subject to discussion. The aim of this is to: - rationalise split according to the above use cases, - share ressources (descriptions, i18n, ...) - find the best compromise between available features and dependencies, - standardize nut packages name, - create the foundation for the upcoming and underway improvements, - improve nut integration - ease and improve user experience. This standard was created by: - capitalizing the experience of existing packages, - using and improving the use of all nut features - considering upcoming nut changes and improvements - working closely with packagers. Overview of the package tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FIXME: make a dependency graph - <> - <> - <> - <> - <> - <> - <> - <> - <> - <> (or nut-control-center or Ultimate NUT Tool...) - <> Detailed view of the package tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [NOTE] ======================================================================== - The *Desc* field represent the package's description, as exposed by the packaging system. Each package's description is composed of a paragraph common to all NUT packages, and a part that is specific to the package. The common part (further referenced by *COMMON DESC*) is: ________________________________________________________________________ Network UPS Tools (NUT) is a client/server monitoring system that allows computers to share uninterruptible power supply (UPS) and power distribution unit (PDU) hardware. Clients access the hardware through the server, and are notified whenever the power status changes. ________________________________________________________________________ - The *Files* field lists the content of the package. - The mentioned *Size* is a rough estimation of packaged and installed size. This may varies across the systems and architecture, and is based upon the Debian x86 packages. - The *Deps* field lists the dependencies of the packages. The exact name may vary across the various systems. - The *Comment* field is used to place comment for points subject to discussion. ======================================================================== [[pkg-nut]] nut ^^^ - Desc: - Files: dummy/serial/USB drivers + upsd + upslog - Size: - Deps: [[pkg-libupsclient1]] libupsclient1 ^^^^^^^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-libupsclient1-dev]] libupsclient1-dev ^^^^^^^^^^^^^^^^^ - Desc: - Files: - Size: - Deps: NOTE: the "-dev" suffix is to be replaced by "-devel" on RPM based platforms. [[pkg-nut-cgi]] nut-cgi ^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-nut-powerman-pdu]] nut-powerman-pdu ^^^^^^^^^^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-nut-snmp]] nut-snmp ^^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-nut-xml]] nut-xml ^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-nut-clients]] nut-clients ^^^^^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-python-pynut]] python-pynut ^^^^^^^^^^^^ - Desc: - Files: - Size: - Deps: [[pkg-python-nut-gui]] python-nut-gui ^^^^^^^^^^^^^^ (or nut-control-center or Ultimate NUT Tool...) - Desc: - Files: - Size: - Deps: [[pkg-nut-doc]] nut-doc ^^^^^^^ - Desc: - Files: - Size: - Deps: ------------------------------------------------------------------------ *sandbox* nut-server ^^^^^^^^^^ Desc: Files: dummy/serial/USB drivers + upsd + upslog Size: Deps: nut-client, libusb, libc/ld B) nut-snmp Desc: Files: SNMP driver [/ manager ] Deps: nut-server, net-snmp, libc/ld C) nut-client Desc: don't force to have the server part/deps if not needed Files: upsmon, upsc, upscmd, upsrw + driver.list [+nut-dev (lib, .h, .pc, man] Deps: libc/ld E) nut-cgi Deps: Files: snmp-ups and powernet + manpages F) nut-doc: Deps: Files: dummycons + manpage G) nut-dev: Deps: Files: upsmon, upsc, upscmd, upsrw Note: "nut" can be a meta package This kind of tree obviously need modification on the conf/make files of NUT to allow build/install in a separate way. ... TO BE CONTINUED ... Configuration option ^^^^^^^^^^^^^^^^^^^^ name= "ups" or "nut" ./configure \ --prefix=/ \ --sysconfdir=/etc/$name \ --mandir=/usr/share/man \ --libdir=/usr/lib \ --includedir=/usr/include \ --datadir=/usr/share/$name \ --with-statepath=/var/run/nut \ --with-altpidpath=/var/run/nut \ --with-drvpath=/lib/nut \ --with-cgipath=/usr/lib/cgi-bin/$name \ html-path --with-pidpath=/var/run/$name \ --with-user=$name \ --with-cgi \ --without-ssl ... ------------------------------------------------------------------------ nut-2.7.4/docs/configure.txt0000644000175000017500000002644512640473702012745 00000000000000ifdef::website[] Configure options ================= endif::website[] There are a few options that can be given to configure to tweak compiles. See also "./configure --help" for a current and complete listing. Driver selection ---------------- --with-serial Build and install the serial drivers (default: yes) --with-usb Build and install the USB drivers (default: auto-detect) Note that you need to install the libusb development package or files. --with-snmp Build and install the SNMP drivers (default: auto-detect) Note that you need to install libsnmp development package or files. --with-neon Build and install the XML drivers (default: auto-detect) Note that you need to install neon development package or files. --with-powerman Build and install Powerman PDU client driver (default: auto-detect) This allows to interact with the Powerman daemon, and the numerous Power Distribution Units (PDU) supported by the project. Note that you need to install powerman development package or files. --with-ipmi --with-freeipmi Build and install IPMI PSU driver (default: auto-detect) This allows to monitor numerous Power Supply Units (PSU) found on servers. Note that you need to install freeipmi (0.8.5 or higher) development package or files. --with-linux_i2c Build and install i2c drivers (default: auto-detect) Note that you need to install libi2c development package or files. --with-drivers=,,... Specify exactly which driver or drivers to build and install (this works for serial, usb, and snmp drivers, and overrides the preceding three options). As of the time of this writing (2010), there are 46 UPS drivers available. Most users will only need one, a few will need two or three, and very few people will need all of them. To save time during the compile and disk space later on, you can use this option to just build and install a subset of the drivers. To select mge-shut and usbhid-ups, you'd do this: --with-drivers=apcsmart,usbhid-ups If you need to build more drivers later on, you will need to rerun configure with a different list. To make it build all of the drivers from scratch again, run 'make clean' before starting. Optional features ----------------- --with-cgi (default: no) Build and install the optional CGI programs, HTML files, and sample CGI configuration files. This is not enabled by default, as they are only useful on web servers. See data/html/README for additional information on how to set up CGI programs. --with-doc= (default: no) Build and install NUT documentation file(s). The possible values are "html-single" for single page HTML, "html-chunked" for multi pages HTML, "pdf" for a PDF file or "auto" to build all the possible previous documentation formats. Verbose output can be enabled using: ASCIIDOC_VERBOSE=-v make This feature requires AsciiDoc 8.6.3 (http://www.methods.co.nz/asciidoc). --with-dev (default: no) Build and install the upsclient and nutclient library and header files. --with-all (no default) Build and install all of the above (the serial, USB, SNMP, XML/HTTP and PowerMan drivers, the CGI programs and HTML files, and the upsclient library). --with-ssl (default: auto-detect) --with-nss (default: auto-detect) --with-openssl (default: auto-detect) Enable SSL support, using either Mozilla NSS or OpenSSL. If both are present, and nothing was specified, OpenSSL support will be preferred. Read docs/security.txt for instructions on SSL support. --with-wrap (default: auto-detect) Enable libwrap (tcp-wrappers) support. Refer to upsd man page for more information. --with-ipv6 (default: auto-detect) Enable IPv6 support. --with-avahi (default: auto-detect) Build and install Avahi support, to publish NUT server availability using mDNS protocol. This requires Avahi development files for the Core and Client parts. --with-libltdl (default: auto-detect) Enable libltdl (Libtool dlopen abstraction) support. This is required to build nut-scanner. Other configuration options --------------------------- --with-port=PORT Change the TCP port used by the network code. Default is 3493. Ancient versions of upsd used port 3305. NUT 2.0 and up use a substantially different network protocol and are not able to communicate with anything older than the 1.4 series. If you have to monitor a mixed environment, use the last 1.4 version, as it contains compatibility code for both the old "REQ" and the new "GET" versions of the protocol. --with-user= --with-group= Programs started as root will setuid() to for somewhat safer operation. You can override this with -u in several programs, including upsdrvctl (and all drivers by extension), upsd, and upsmon. The "user" directive in ups.conf overrides this at run time for the drivers. NOTE: upsmon does not totally drop root because it may need to initiate a shutdown. There is always at least a stub process remaining with root powers. The network code runs in another (separate) process as the new user. The is used for the permissions of some files, particularly the hotplugging rules for USB. The idea is that the device files for any UPS devices should be readable and writable by members of that group. The default value for both the username and groupname is "nobody". This was done since it's slightly better than staying around as root. Running things as nobody is not a good idea, since it's a hack for NFS access. You should create at least one separate user for this software. If you use one of the --with-user and --with-group options, then you have to use the other one too. See the INSTALL.nut document and the FAQ for more on this topic. --with-logfacility=FACILITY Change the facility used when writing to the log file. Read the man page for openlog to get some idea of what's available on your system. Default is LOG_DAEMON. Installation directories ------------------------ --prefix=PATH This is a fairly standard option with GNU autoconf, and it sets the base path for most of the other install directories. The default is /usr/local/ups, which puts everything but the state sockets in one easy place. If you like having things to be at more of a "system" level, setting the prefix to /usr/local or even /usr might be better. --exec_prefix=PATH This sets the base path for architecture dependent files. By default, it is the same as . --sysconfdir=PATH Changes the location where NUT's configuration files are stored. By default this path is /etc. Setting this to /etc or /etc/ups might be useful. The NUT_CONFPATH environment variable overrides this at run time. --bindir=PATH --sbindir=PATH Where executable files will be installed. Files that are normally executed by root (upsd, upsmon, upssched) go to sbindir, all others to bindir. The defaults are /bin and /sbin. --datadir=PATH Change the data directory, i.e., where architecture independent read-only data is installed. By default this is /share, i.e., /usr/local/ups/share. At the moment, this directory only holds two files - the optional cmdvartab and driver.list. --mandir=PATH Sets the base directories for the man pages. The default is /man, i.e., /usr/local/ups/man. --includedir=PATH Sets the path for include files to be installed when `--with-dev` is selected. For example, upsclient.h is installed here. The default is /include. --libdir=PATH Sets the installation path for libraries. This is just the upsclient library for now. The default is /lib. --with-drvpath=PATH The UPS drivers will be installed to this path. By default they install to "/bin", i.e., /usr/local/ups/bin. The "driverpath" global directive in the ups.conf file overrides this at run time. --with-cgipath=PATH The CGI programs will be installed to this path. By default, they install to "/cgi-bin", which is usually /usr/local/ups/cgi-bin. If you set the prefix to something like /usr, you should set the cgipath to something else, because /usr/cgi-bin is pretty ugly and non-standard. The CGI programs are not built or installed by default. Use "./configure --with-cgi" to request that they are built and installed. --with-htmlpath=PATH HTML files will be installed to this path. By default, this is "/html". Note that HTML files are only installed if --with-cgi is selected. --with-pkgconfig-dir=PATH Where to install pkg-config *.pc files. This option only has an effect if `--with-dev` is selected, and causes a pkg-config file to be installed in the named location. The default is /pkgconfig. Use --without-pkgconfig-dir to disable this feature altogether. --with-hotplug-dir=PATH Where to install Linux 2.4 hotplugging rules. The default is /etc/hotplug, if that directory exists, and not to install it otherwise. Note that this installation directory is not a subdirectory of by default. When installing NUT as a non-root user, you may have to override this option. Use --without-hotplug-dir to disable this feature altogether. --with-udev-dir=PATH Where to install Linux 2.6 hotplugging rules, for kernels that have the "udev" mechanism. The default is /etc/udev, if that directory exists, and not to install it otherwise. Note that this installation directory is not a subdirectory of by default. When installing NUT as a non-root user, you may have to override this option. Use --without-udev-dir to disable this feature altogether. Directories used by NUT at run-time ----------------------------------- --with-pidpath=PATH Changes the directory where pid files are stored. By default this is /var/run. Certain programs like upsmon will leave files here. --with-altpidpath=PATH Programs that normally don't have root powers, like the drivers and upsd, write their pid files here. By default this is whatever the statepath is, as those programs should be able to write there. --with-statepath=PATH Change the default location of the state sockets created by the drivers. The NUT_STATEPATH environment variable overrides this at run time. Default is /var/state/ups. Things the compiler might need to find -------------------------------------- --with-gd-includes="-I/foo/bar" If you installed gd in some place where your C preprocessor can't find the header files, use this switch to add additional -I flags. --with-gd-libs="-L/foo/bar -labcd -lxyz" If your copy of gd isn't linking properly, use this to give the proper -L and -l flags to make it work. See LIBS= in gd's Makefile. NOTE: the --with-gd switches are not necessary if you have gd 2.0.8 or higher installed properly. The gdlib-config script will be detected and used by default in that situation. --with-ssl-includes, --with-usb-includes, --with-snmp-includes, --with-neon-includes, --with-libltdl-includes, --with-powerman-includes="-I/foo/bar" If your system doesn't have pkg-config and support for any of the above libraries isn't found (but you know it is installed), you must specify the compiler flags that are needed. --with-ssl-libs, --with-usb-libs, --with-snmp-libs, --with-neon-libs, --with-libltdl-libs --with-powerman-libs="-L/foo/bar -labcd -lxyz" If system doesn't have pkg-config or it fails to provides hints for some of the settings that are needed to set it up properly and the build in defaults are not right, you can specify the right variables here. nut-2.7.4/docs/net-protocol.txt0000644000175000017500000003666512667572112013422 00000000000000Network protocol information ============================ Since May 2002, this protocol has an official port number from IANA, which is *3493*. The old number (3305) was a relic of the original code's ancestry, and conflicted with other services. Version 0.50.0 and up use 3493 by default. This protocol runs over TCP. UDP support was dropped in July 2003. It had been deprecated for some time and was only capable of the simplest query commands as authentication is impossible over a UDP socket. A library, named libupsclient, that implement this protocol is provided in both static and shared version to help the client application development. Old command removal notice -------------------------- Before version 1.5.0, a number of old commands were supported. These have been removed from the specification. For more information, consult an older version of the software. Command reference ----------------- Multi-word elements are contained within "quotes" for easier parsing. Embedded quotes are escaped with backslashes. Embedded backslashes are also escaped by representing them as \\. This protocol is intended to be interpreted with parseconf (NUT parser) or something similar. Revision history ---------------- Here's a table to present the various changes that happened to the NUT network protocol, over the time: [options="header,autowidth",frame="topbot",grid="rows",cols="^.^,^.^,<",align="center"] |=============================================================================== |Protocol version |NUT version |Description |1.0 |< 1.5.0 |Original protocol (legacy version) |1.1 |>= 1.5.0 |Original protocol (without old commands) .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION in *configure.in*. GET --- Retrieve a single response from the server. Possible sub-commands: NUMLOGINS ~~~~~~~~~ Form: GET NUMLOGINS GET NUMLOGINS su700 Response: NUMLOGINS NUMLOGINS su700 1 '' is the number of clients which have done LOGIN for this UPS. This is used by the master upsmon to determine how many clients are still connected when starting the shutdown process. This replaces the old "REQ NUMLOGINS" command. UPSDESC ~~~~~~~ Form: GET UPSDESC GET UPSDESC su700 Response: UPSDESC "" UPSDESC su700 "Development box" '' is the value of "desc=" from ups.conf for this UPS. If it is not set, upsd will return "Unavailable". This can be used to provide human-readable descriptions instead of a cryptic "upsname@hostname" string. VAR ~~~ Form: GET VAR GET VAR su700 ups.status Response: VAR "" VAR su700 ups.status "OL" This replaces the old "REQ" command. TYPE ~~~~ Form: GET TYPE GET TYPE su700 input.transfer.low Response: TYPE ... TYPE su700 input.transfer.low ENUM '' can be several values, and multiple words may be returned: - 'RW': this variable may be set to another value with SET - 'ENUM': an enumerated type, which supports a few specific values - 'STRING:n': this is a string of maximum length n - 'RANGE': this is an numeric, either integer or float, comprised in the range (see LIST RANGE) - 'NUMBER': this is a simple numeric value, either integer or float ENUM, STRING and RANGE are usually associated with RW, but not always. The default , when omitted, is numeric, so either integer or float. Each driver is then responsible for handling values as either integer or float. Note that float values are expressed using decimal (base 10) english-based representation, so using a dot, in non-scientific notation. So hexadecimal, exponents, and comma for thousands separator are forbidden. For example: "1200.20" is valid, while "1,200.20" and "1200,20" are invalid. This replaces the old "VARTYPE" command. DESC ~~~~ Form: GET DESC GET DESC su700 ups.status Response: DESC "" DESC su700 ups.status "UPS status" '' is a string that gives a brief explanation of the named variable. upsd may return "Unavailable" if the file which provides this description is not installed. Different versions of this file may be used in some situations to provide for localization and internationalization. This replaces the old "VARDESC" command. CMDDESC ~~~~~~~ Form: GET CMDDESC GET CMDDESC su700 load.on Response: CMDDESC "" CMDDESC su700 load.on "Turn on the load immediately" This is like DESC above, but it applies to the instant commands. This replaces the old "INSTCMDDESC" command. LIST ---- The LIST functions all share a common container format. They will return "BEGIN LIST" and then repeat the initial query. The list then follows, with as many lines are necessary to convey it. "END LIST" with the initial query attached then follows. The formatting may seem a bit redundant, but it makes a different form of client possible. You can send a LIST query and then go off and wait for it to get back to you. When it arrives, you don't need complicated state machines to remember which list is which. UPS ~~~ Form: LIST UPS Response: BEGIN LIST UPS UPS "" ... END LIST UPS BEGIN LIST UPS UPS su700 "Development box" END LIST UPS '' is a name from ups.conf, and is the value of desc= from ups.conf, if available. It will be set to "Unavailable" otherwise. This can be used to determine what values of are valid before calling other functions on the server. This is also a good way to handle situations where a single upsd supports multiple drivers. Clients which perform a UPS discovery process may find this useful. VAR ~~~ Form: LIST VAR LIST VAR su700 Response: BEGIN LIST VAR VAR "" ... END LIST VAR BEGIN LIST VAR su700 VAR su700 ups.mfr "APC" VAR su700 ups.mfr.date "10/17/96" ... END LIST VAR su700 This replaces the old "LISTVARS" command. RW ~~ Form: LIST RW LIST RW su700 Response: BEGIN LIST RW RW "" ... END LIST RW BEGIN LIST RW su700 RW su700 output.voltage.nominal "115" RW su700 ups.delay.shutdown "020" ... END LIST RW su700 This replaces the old "LISTRW" command. CMD ~~~ Form: LIST CMD LIST CMD su700 Response: BEGIN LIST CMD CMD ... END LIST CMD BEGIN LIST CMD su700 CMD su700 load.on CMD su700 test.panel.start ... END LIST CMD su700 This replaces the old "LISTINSTCMD" command. ENUM ~~~~ Form: LIST ENUM LIST ENUM su700 input.transfer.low Response: BEGIN LIST ENUM ENUM "" ... END LIST ENUM BEGIN LIST ENUM su700 input.transfer.low ENUM su700 input.transfer.low "103" ENUM su700 input.transfer.low "100" ... END LIST ENUM su700 input.transfer.low This replaces the old "ENUM" command. NOTE: this does not support the old "SELECTED" notation. You must request the current value separately. RANGE ~~~~~ Form: LIST RANGE LIST RANGE su700 input.transfer.low Response: BEGIN LIST RANGE RANGE "" "" ... END LIST RANGE BEGIN LIST RANGE su700 input.transfer.low RANGE su700 input.transfer.low "90" "100" RANGE su700 input.transfer.low "102" "105" ... END LIST RANGE su700 input.transfer.low CLIENT ~~~~~~ Form: LIST CLIENT LIST CLIENT ups1 Response: BEGIN LIST CLIENT CLIENT ... END LIST CLIENT BEGIN LIST CLIENT ups1 CLIENT ups1 ::1 CLIENT ups1 192.168.1.2 END LIST CLIENT ups1 SET --- Form: SET VAR "" SET VAR su700 ups.id "My UPS" INSTCMD ------- Form: INSTCMD INSTCMD su700 test.panel.start LOGOUT ------ Form: LOGOUT Response: OK Goodbye (recent versions) Goodbye... (older versions) Used to disconnect gracefully from the server. LOGIN ----- Form: LOGIN Response: OK (upon success) or <> NOTE: This requires "upsmon slave" or "upsmon master" in upsd.users Use this to log the fact that a system is drawing power from this UPS. The upsmon master will wait until the count of attached systems reaches 1 - itself. This allows the slaves to shut down first. NOTE: You probably shouldn't send this command unless you are upsmon, or a upsmon replacement. MASTER ------ Form: MASTER Response: OK (upon success) or <> NOTE: This requires "upsmon master" in upsd.users This function doesn't do much by itself. It is used by upsmon to make sure that master-level functions like FSD are available if necessary. FSD --- Form: FSD Response: OK FSD-SET (success) or <> NOTE: This requires "upsmon master" in upsd.users, or "FSD" action granted in upsd.users upsmon in master mode is the primary user of this function. It sets this "forced shutdown" flag on any UPS when it plans to power it off. This is done so that slave systems will know about it and shut down before the power disappears. Setting this flag makes "FSD" appear in a STATUS request for this UPS. Finding "FSD" in a status request should be treated just like a "OB LB". It should be noted that FSD is currently a latch - once set, there is no way to clear it short of restarting upsd or dropping then re-adding it in the ups.conf. This may cause issues when upsd is running on a system that is not shut down due to the UPS event. PASSWORD -------- Form: PASSWORD Response: OK (upon success) or <> Sets the password associated with a connection. Used for later authentication for commands that require it. USERNAME -------- Form: USERNAME Response: OK (upon success) or <> Sets the username associated with a connection. This is also used for authentication, specifically in conjunction with the upsd.users file. STARTTLS -------- Form: STARTTLS Response: OK STARTTLS or <> This tells upsd to switch to TLS mode internally, so all future communications will be encrypted. You must also change to TLS mode in the client after receiving the OK, or the connection will be useless. Other commands -------------- - HELP: lists the commands supported by this server - VER: shows the version of the server currently in use - NETVER: shows the version of the network protocol currently in use These three are not intended to be used directly by programs. Humans can make use of this program by using telnet or netcat. If you use telnet, make sure you don't have it set to negotiate extra options. upsd doesn't speak telnet and will probably misunderstand your first request due to the extra junk in the buffer. [[np-errors]] Error responses --------------- An error response has the following format: ERR [...] is always one element; it never contains spaces. This may be used to allow additional information () in the future. can have the following values: - 'ACCESS-DENIED' + The client's host and/or authentication details (username, password) are not sufficient to execute the requested command. - 'UNKNOWN-UPS' + The UPS specified in the request is not known to upsd. This usually means that it didn't match anything in ups.conf. - 'VAR-NOT-SUPPORTED' + The specified UPS doesn't support the variable in the request. + This is also sent for unrecognized variables which are in a space which is handled by upsd, such as server.*. - 'CMD-NOT-SUPPORTED' + The specified UPS doesn't support the instant command in the request. - 'INVALID-ARGUMENT' + The client sent an argument to a command which is not recognized or is otherwise invalid in this context. This is typically caused by sending a valid command like GET with an invalid subcommand. - 'INSTCMD-FAILED' + upsd failed to deliver the instant command request to the driver. No further information is available to the client. This typically indicates a dead or broken driver. - 'SET-FAILED' + upsd failed to deliver the set request to the driver. This is just like INSTCMD-FAILED above. - 'READONLY' + The requested variable in a SET command is not writable. - 'TOO-LONG' + The requested value in a SET command is too long. - 'FEATURE-NOT-SUPPORTED' + This instance of upsd does not support the requested feature. This is only used for TLS/SSL mode (STARTTLS) at the moment. - 'FEATURE-NOT-CONFIGURED' + This instance of upsd hasn't been configured properly to allow the requested feature to operate. This is also limited to STARTTLS for now. - 'ALREADY-SSL-MODE' + TLS/SSL mode is already enabled on this connection, so upsd can't start it again. - 'DRIVER-NOT-CONNECTED' + upsd can't perform the requested command, since the driver for that UPS is not connected. This usually means that the driver is not running, or if it is, the ups.conf is misconfigured. - 'DATA-STALE' + upsd is connected to the driver for the UPS, but that driver isn't providing regular updates or has specifically marked the data as stale. upsd refuses to provide variables on stale units to avoid false readings. + This generally means that the driver is running, but it has lost communications with the hardware. Check the physical connection to the equipment. - 'ALREADY-LOGGED-IN' + The client already sent LOGIN for a UPS and can't do it again. There is presently a limit of one LOGIN record per connection. - 'INVALID-PASSWORD' + The client sent an invalid PASSWORD - perhaps an empty one. - 'ALREADY-SET-PASSWORD' + The client already set a PASSWORD and can't set another. This also should never happen with normal NUT clients. - 'INVALID-USERNAME' + The client sent an invalid USERNAME. - 'ALREADY-SET-USERNAME' + The client has already set a USERNAME, and can't set another. This should never happen with normal NUT clients. - 'USERNAME-REQUIRED' + The requested command requires a username for authentication, but the client hasn't set one. - 'PASSWORD-REQUIRED' + The requested command requires a passname for authentication, but the client hasn't set one. - 'UNKNOWN-COMMAND' + upsd doesn't recognize the requested command. + This can be useful for backwards compatibility with older versions of upsd. Some NUT clients will try GET and fall back on REQ after receiving this response. - 'INVALID-VALUE' + The value specified in the request is not valid. This usually applies to a SET of an ENUM type which is using a value which is not in the list of allowed values. Future ideas ------------ Dense lists ~~~~~~~~~~~ The LIST commands may be given the ability to handle options some day. For example, "LIST VARS +DESC" would return the current value like now, but it would also append the description of that variable. Command status ~~~~~~~~~~~~~~ After sending an INSTCMD or SET, a client will eventually be able to poll to see whether it was completed successfully by the driver. Get collection ~~~~~~~~~~~~~~ Allow to request only a subtree, which can be a collection, or a sub collection. nut-2.7.4/docs/nut-names.txt0000644000175000017500000010164112667566346012703 00000000000000ifndef::external_title[] NUT command and variable naming scheme ====================================== endif::external_title[] This is a dump of the standard variables and command names used in NUT. Don't use a name with any of the dstate functions unless it exists here. If you need a new variable or command name, contact the Development Team first. Put another way: if you make up a name that's not in this list and it gets into the tree, and then we come up with a better name later, clients that use the undocumented variable will break when it is changed. NOTE: "opaque" means programs should not attempt to parse the value for that variable as it may vary greatly from one UPS to the next. These strings are best handled directly by the user. Variables --------- device: General unit information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NOTE: some of these data will be redundant with ups.* information during a transition period. The ups.* data will then be removed. [options="header"] |==================================================================================== | Name | Description | Example value | device.model | Device model | BladeUPS | device.mfr | Device manufacturer | Eaton | device.serial | Device serial number (opaque string) | WS9643050926 | device.type | Device type (ups, pdu, scd, psu, ats) | ups | device.description | Device description (opaque string) | Some ups | device.contact | Device administrator name (opaque string) | John Doe | device.location | Device physical location (opaque string) | 1st floor | device.part | Device part number (opaque string) | 123456789 | device.macaddr | Physical network address of the device | 68:b5:99:f5:89:27 | device.uptime | Device uptime in seconds | 1782 |===================================================================================== ups: General unit information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [options="header"] |=============================================================================== | Name | Description | Example value | ups.status | UPS status | linkdoc:developer-guide[OL,_status_data] | ups.alarm | UPS alarms | OVERHEAT | ups.time | Internal UPS clock time (opaque string) | 12:34 | ups.date | Internal UPS clock date (opaque string) | 01-02-03 | ups.model | UPS model | SMART-UPS 700 | ups.mfr | UPS manufacturer | APC | ups.mfr.date | UPS manufacturing date (opaque string) | 10/17/96 | ups.serial | UPS serial number (opaque string) | WS9643050926 | ups.vendorid | Vendor ID for USB devices | 0463 | ups.productid | Product ID for USB devices | 0001 | ups.firmware | UPS firmware (opaque string) | 50.9.D | ups.firmware.aux | Auxiliary device firmware | 4Kx | ups.temperature | UPS temperature (degrees C) | 042.7 | ups.load | Load on UPS (percent) | 023.4 | ups.load.high | Load when UPS switches to overload condition ("OVER") (percent) | 100 | ups.id | UPS system identifier (opaque string) | Sierra | ups.delay.start | Interval to wait before restarting the load (seconds) | 0 | ups.delay.reboot | Interval to wait before rebooting the UPS (seconds) | 60 | ups.delay.shutdown | Interval to wait after shutdown with delay command (seconds) | 20 | ups.timer.start | Time before the load will be started (seconds) | 30 | ups.timer.reboot | Time before the load will be rebooted (seconds) | 10 | ups.timer.shutdown | Time before the load will be shutdown (seconds) | 20 | ups.test.interval | Interval between self tests (seconds) | 1209600 (two weeks) | ups.test.result | Results of last self test (opaque string) | Bad battery pack | ups.test.date | Date of last self test (opaque string) | 07/17/12 | ups.display.language | Language to use on front panel (*** opaque) | E | ups.contacts | UPS external contact sensors (*** opaque) | F0 | ups.efficiency | Efficiency of the UPS (ratio of the output current on the input current) (percent) | 95 | ups.power | Current value of apparent power (Volt-Amps) | 500 | ups.power.nominal | Nominal value of apparent power (Volt-Amps) | 500 | ups.realpower | Current value of real power (Watts) | 300 | ups.realpower.nominal | Nominal value of real power (Watts) | 300 | ups.beeper.status | UPS beeper status (enabled, disabled or muted) | enabled | ups.type | UPS type (*** opaque) | offline | ups.watchdog.status | UPS watchdog status (enabled or disabled) | disabled | ups.start.auto | UPS starts when mains is (re)applied | yes | ups.start.battery | Allow to start UPS from battery | yes | ups.start.reboot | UPS coldstarts from battery (enabled or disabled) | yes | ups.shutdown | Enable or disable UPS shutdown ability (poweroff) | enabled |=============================================================================== NOTE: When present, the value of *ups.start.auto* has an impact on shutdown.* commands. For the sake of coherence, shutdown commands will set *ups.start.auto* to the right value before issuing the command. That is, shutdown.stayoff will first set *ups.start.auto* to *no*, while shutdown.return will set it to *yes*. input: Incoming line/power information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [options="header"] |================================================================================= | Name | Description | Example value | input.voltage | Input voltage (V) | 121.5 | input.voltage.maximum | Maximum incoming voltage seen (V) | 130 | input.voltage.minimum | Minimum incoming voltage seen (V) | 100 | input.voltage.status | Status relative to the thresholds | critical-low | input.voltage.low.warning | Low warning threshold (V) | 205 | input.voltage.low.critical | Low critical threshold (V) | 200 | input.voltage.high.warning | High warning threshold (V) | 230 | input.voltage.high.critical | High critical threshold (V) | 240 | input.voltage.nominal | Nominal input voltage (V) | 120 | input.voltage.extended | Extended input voltage range | no | input.transfer.delay | Delay before transfer to mains (seconds) | 60 | input.transfer.reason | Reason for last transfer to battery (*** opaque) | T | input.transfer.low | Low voltage transfer point (V) | 91 | input.transfer.high | High voltage transfer point (V) | 132 | input.transfer.low.min | smallest settable low voltage transfer point (V) | 85 | input.transfer.low.max | greatest settable low voltage transfer point (V) | 95 | input.transfer.high.min | smallest settable high voltage transfer point (V) | 131 | input.transfer.high.max | greatest settable high voltage transfer point (V) | 136 | input.sensitivity | Input power sensitivity | H (high) | input.quality | Input power quality (*** opaque) | FF | input.current | Input current (A) | 4.25 | input.current.nominal | Nominal input current (A) | 5.0 | input.current.status | Status relative to the thresholds | critical-high | input.current.low.warning | Low warning threshold (A) | 4 | input.current.low.critical | Low critical threshold (A) | 2 | input.current.high.warning | High warning threshold (A) | 10 | input.current.high.critical | High critical threshold (A) | 12 | input.frequency | Input line frequency (Hz) | 60.00 | input.frequency.nominal | Nominal input line frequency (Hz) | 60 | input.frequency.status | Frequency status | out-of-range | input.frequency.low | Input line frequency low (Hz) | 47 | input.frequency.high | Input line frequency high (Hz) | 63 | input.frequency.extended | Extended input frequency range | no | input.transfer.boost.low | Low voltage boosting transfer point (V) | 190 | input.transfer.boost.high | High voltage boosting transfer point (V) | 210 | input.transfer.trim.low | Low voltage trimming transfer point (V) | 230 | input.transfer.trim.high | High voltage trimming transfer point (V) | 240 | input.load | Load on (ePDU) input (percent of full) | 25 | input.realpower | Current sum value of all (ePDU) phases real power (W) | 300 | input.power | Current sum value of all (ePDU) phases apparent power (VA) | 500 | input.source | The current input power source | 1 | input.source.preferred | The preferred power source | 1 |================================================================================= output: Outgoing power/inverter information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [options="header"] |=============================================================================== | Name | Description | Example value | output.voltage | Output voltage (V) | 120.9 | output.voltage.nominal | Nominal output voltage (V) | 120 | output.frequency | Output frequency (Hz) | 59.9 | output.frequency.nominal | Nominal output frequency (Hz) | 60 | output.current | Output current (A) | 4.25 | output.current.nominal | Nominal output current (A) | 5.0 |=============================================================================== Three-phase additions ~~~~~~~~~~~~~~~~~~~~~ The additions for three-phase measurements would produce a very long table due to all the combinations that are possible, so these additions are broken down to their base components. Phase Count Determination ^^^^^^^^^^^^^^^^^^^^^^^^^ input.phases (3 for three-phase, absent or 1 for 1phase) output.phases (as for input.phases) DOMAINs ^^^^^^^ Any input or output is considered a valid DOMAIN. input (should really be called input.mains, but keep this for compat) input.bypass input.servicebypass output (should really be called output.load, but keep this for compat) output.bypass output.inverter output.servicebypass Specification (SPEC) ^^^^^^^^^^^^^^^^^^^^ Voltage, current, frequency, etc are considered to be a specification of the measurement. With this notation, the old 1phase naming scheme becomes DOMAIN.SPEC Example: `input.current` CONTEXT ^^^^^^^ When in three-phase mode, we need some way to specify the target for most measurements in more detail. We call this the CONTEXT. With this notation, the naming scheme becomes DOMAIN.CONTEXT.SPEC when in three-phase mode. Example: `input.L1.current` Valid CONTEXTs ^^^^^^^^^^^^^^ L1-L2 \ L2-L3 \ L3-L1 for voltage measurements L1-N / L2-N / L3-N / L1 \ L2 for current and power measurements L3 / N - for current measurement Valid SPECs ^^^^^^^^^^^ Valid with/without context (i.e. per phase or aggregated/averaged) [options="header"] |=============================================================================== | Name | Description | alarm | Alarms for phases, published in ups.alarm | current | Current (A) | current.maximum | Maximum seen current (A) | current.minimum | Minimum seen current (A) | current.status | Status relative to the thresholds | current.low.warning | Low warning threshold (A) | current.low.critical | Low critical threshold (A) | current.high.warning | High warning threshold (A) | current.high.critical | High critical threshold (A) | current.peak | Peak current | voltage | Voltage (V) | voltage.nominal | Nominal voltage (V) | voltage.maximum | Maximum seen voltage (V) | voltage.minimum | Minimum seen voltage (V) | voltage.status | Status relative to the thresholds | voltage.low.warning | Low warning threshold (V) | voltage.low.critical | Low critical threshold (V) | voltage.high.warning | High warning threshold (V) | voltage.high.critical | High critical threshold (V) | power | Apparent power (VA) | power.maximum | Maximum seen apparent power (VA) | power.minimum | Minimum seen apparent power (VA) | power.percent | Percentage of apparent power related to maximum load | power.maximum.percent | Maximum seen percentage of apparent power | power.minimum.percent | Minimum seen percentage of apparent power | realpower | Real power (W) | powerfactor | Power Factor (dimensionless value between 0.00 and 1.00) | crestfactor | Crest Factor (dimensionless value greater or equal to 1) | load | Load on (ePDU) input |=============================================================================== Valid without context (i.e. aggregation of all phases): [options="header"] |=============================================================================== | Name | Description | frequency | Frequency (Hz) | frequency.nominal | Nominal frequency (Hz) | realpower | Current value of real power (Watts) | power | Current value of apparent power (Volt-Amps) |=============================================================================== EXAMPLES ~~~~~~~~ Partial Three phase - Three phase example: input.phases: 3 input.frequency: 50.0 input.L1.current: 133.0 input.bypass.L1-L2.voltage: 398.3 output.phases: 3 output.L1.power: 35700 output.powerfactor: 0.82 Partial Three phase - One phase example: input.phases: 3 input.L2.current: 48.2 input.N.current: 3.4 input.L3-L1.voltage: 405.4 input.frequency: 50.1 output.phases: 1 output.current: 244.2 output.voltage: 120 output.frequency.nominal: 60.0 battery: Any battery details ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [options="header"] |=============================================================================== | Name | Description | Example value | battery.charge | Battery charge (percent) | 100.0 | battery.charge.low | Remaining battery level when UPS switches to LB (percent) | 20 | battery.charge.restart | Minimum battery level for UPS restart after power-off | 20 | battery.charge.warning | Battery level when UPS switches to "Warning" state (percent) | 50 | battery.charger.status | Status of the battery charger (see the note below) | charging | battery.voltage | Battery voltage (V) | 24.84 | battery.voltage.nominal | Nominal battery voltage (V) | 024 | battery.voltage.low | Minimum battery voltage, that triggers FSD status | 21,52 | battery.voltage.high | Maximum battery voltage (i.e. battery.charge = 100) | 26,9 | battery.capacity | Battery capacity (Ah) | 7.2 | battery.current | Battery current (A) | 1.19 | battery.current.total | Total battery current (A) | 1.19 | battery.temperature | Battery temperature (degrees C) | 050.7 | battery.runtime | Battery runtime (seconds) | 1080 | battery.runtime.low | Remaining battery runtime when UPS switches to LB (seconds) | 180 | battery.runtime.restart | Minimum battery runtime for UPS restart after power-off (seconds) | 120 | battery.alarm.threshold | Battery alarm threshold | 0 (immediate) | battery.date | Battery change date (opaque string) | 11/14/00 | battery.mfr.date | Battery manufacturing date (opaque string) | 2005/04/02 | battery.packs | Number of battery packs | 001 | battery.packs.bad | Number of bad battery packs | 000 | battery.type | Battery chemistry (opaque string) | PbAc | battery.protection | Prevent deep discharge of battery | yes | battery.energysave | Switch off when running on battery and no/low load | no | battery.energysave.load | Switch off UPS if on battery and load level lower (percent) | 5 | battery.energysave.delay | Delay before switch off UPS if on battery and load level low (min) | 3 | battery.energysave.realpower | Switch off UPS if on battery and load level lower (Watts) | 10 |=============================================================================== NOTE: battery.charger.status replaces the historic flags CHRG and DISCHRG that were exposed through ups.status. battery.charger.status can have one of the following value: - charging: battery is charging, - discharging: battery is discharging, - floating: battery has completed its charge cycle, and waiting to go to resting mode, - resting: the battery is fully charged, and not charging nor discharging. ambient: Conditions from external probe equipment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NOTE: multiple sensors can be exposed using the indexed notation. 'ambient.*', without index or using '0', relates to the embedded sensor. For example: 'ambient.temperature' represent the embedded sensor temperature. Other sensors (external, communication card, ...) can use indexes from '1' to 'n'. For example: 'ambient.1.temperature' for the first external sensor temperature. [options="header"] |================================================================================== | Name | Description | Example value | ambient.n.present | Ambient sensor presence | yes | ambient.n.temperature | Ambient temperature (degrees C) | 25.40 | ambient.n.temperature.alarm | Temperature alarm (enabled/disabled) | enabled | ambient.n.temperature.status | Ambient temperature status relative to the thresholds | warning-low | ambient.n.temperature.high | Temperature threshold high (degrees C) | 60 | ambient.n.temperature.high.warning | Temperature threshold high warning (degrees C) | 40 | ambient.n.temperature.high.critical | Temperature threshold high critical (degrees C) | 60 | ambient.n.temperature.low | Temperature threshold low (degrees C) | 5 | ambient.n.temperature.low.warning | Temperature threshold low warning (degrees C) | 10 | ambient.n.temperature.low.critical | Temperature threshold low critical (degrees C) | 5 | ambient.n.temperature.maximum | Maximum temperature seen (degrees C) | 37.6 | ambient.n.temperature.minimum | Minimum temperature seen (degrees C) | 18.1 | ambient.n.humidity | Ambient relative humidity (percent) | 038.8 | ambient.n.humidity.alarm | Relative humidity alarm (enabled/disabled) | enabled | ambient.n.humidity.status | Ambient humidity status relative to the thresholds | warning-low | ambient.n.humidity.high | Relative humidity threshold high (percent) | 80 | ambient.n.humidity.high.warning | Relative humidity threshold high warning (percent) | 70 | ambient.n.humidity.high.critical | Relative humidity threshold high critical (percent) | 80 | ambient.n.humidity.low | Relative humidity threshold low (percent) | 10 | ambient.n.humidity.low.warning | Relative humidity threshold low warning (percent) | 20 | ambient.n.humidity.low.critical | Relative humidity threshold low critical (percent) | 10 | ambient.n.humidity.maximum | Maximum relative humidity seen (percent) | 60 | ambient.n.humidity.minimum | Minimum relative humidity seen (percent) | 13 | ambient.n.contacts.x.status | State of the dry contact sensor x | open |================================================================================== outlet: Smart outlet management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NOTE: *n* stands for the outlet index. For more information, refer to the NUT outlets management and PDU notes chapter of the user manual. A special case is "outlet.0" which is equivalent to "outlet" (without index), and represent the whole set of outlets of the device. The most important data is "outlet.count", used to iterate over the whole set of outlets. [options="header"] |=============================================================================== | Name | Description | Example value | outlet.count | Total number of outlets | 12 | outlet.n.id | Outlet system identifier (opaque string) | 1 | outlet.n.desc | Outlet name / description (opaque string) | Main outlet | outlet.n.groupid | Identifier of the group to which the outlet belongs to | 1 | outlet.n.switch | Outlet switch control (on/off) | on | outlet.n.status | Outlet switch status (on/off) | on | outlet.n.alarm | Alarms for outlets and PDU, published in ups.alarm | outlet 1 low voltage warning | outlet.n.switchable | Outlet switch ability (yes/no) | yes | outlet.n.autoswitch.charge.low | Remaining battery level to power off this outlet (percent) | 80 | outlet.n.battery.charge.low | Remaining battery level to power off this outlet (percent) | 80 | outlet.n.delay.shutdown | Interval to wait before shutting down this outlet (seconds) | 180 | outlet.n.delay.start | Interval to wait before restarting this outlet (seconds) | 120 | outlet.n.timer.shutdown | Time before the outlet load will be shutdown (seconds) | 20 | outlet.n.timer.start | Time before the outlet load will be started (seconds) | 30 | outlet.n.current | Current (A) | 0.19 | outlet.n.current.maximum | Maximum seen current (A) | 0.56 | outlet.n.current.status | Current status relative to the thresholds | good | outlet.n.current.low.warning | Low warning threshold (A) | 0.10 | outlet.n.current.low.critical | Low critical threshold (A) | 0.05 | outlet.n.current.high.warning | High warning threshold (A) | 0.30 | outlet.n.current.high.critical | High critical threshold (A) | 0.40 | outlet.n.realpower | Current value of real power (W) | 28 | outlet.n.voltage | Voltage (V) | 247.0 | outlet.n.voltage.status | Voltage status relative to the thresholds | good | outlet.n.voltage.low.warning | Low warning threshold (V) | 205 | outlet.n.voltage.low.critical | Low critical threshold (V) | 200 | outlet.n.voltage.high.warning | High warning threshold (V) | 230 | outlet.n.voltage.high.critical | High critical threshold (V) | 240 | outlet.n.powerfactor | Power Factor (dimensionless, value between 0 and 1) | 0.85 | outlet.n.crestfactor | Crest Factor (dimensionless, equal to or greater than 1) | 1.41 | outlet.n.power | Apparent power (VA) | 46 |=============================================================================== outlet.group: groups of smart outlets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is a refinement of the outlet collection, providing grouped management for a set of outlets. The same principles and data than the outlet collection apply to outlet.group, especially for the indexing 'n' and "outlet.group.count". Most of the data published for outlets also apply to outlet.group, including: id, name (similar as outlet "desc"), status, current and voltage (including status, alarm and thresholds). Some specific data to outlet groups exists: [options="header"] |================================================================================= | Name | Description | Example value | outlet.group.n.type | Type of outlet group (OPAQUE) | outlet-section | outlet.group.n.count | Number of outlets in the group | 12 |================================================================================= Example: outlet.group.1.current: 0.00 outlet.group.1.current.high.critical: 16.00 outlet.group.1.current.high.warning: 12.80 outlet.group.1.current.low.warning: 0.00 outlet.group.1.current.nominal: 16.00 outlet.group.1.current.status: good outlet.group.1.id: 1 outlet.group.1.name: Branch Circuit A outlet.group.1.status: on outlet.group.1.voltage: 244.23 outlet.group.1.voltage.high.critical: 265.00 outlet.group.1.voltage.high.warning: 255.00 outlet.group.1.voltage.low.critical: 180.00 outlet.group.1.voltage.low.warning: 190.00 outlet.group.1.voltage.status: good ... outlet.group.count: 3.00 driver: Internal driver information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [options="header"] |=============================================================================== | Name | Description | Example value | driver.name | Driver name | usbhid-ups | driver.version | Driver version (NUT release) | X.Y.Z | driver.version.internal | Internal driver version | 1.23.45 | driver.version.data | Version of the internal data mapping, for generic drivers | Eaton HID 1.31 | driver.parameter.xxx | Parameter xxx (ups.conf or cmdline -x) setting | (varies) | driver.flag.xxx | Flag xxx (ups.conf or cmdline -x) status | enabled (or absent) |=============================================================================== server: Internal server information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [options="header"] |=============================================================================== | Name | Description | Example value | server.info | Server information | Network UPS Tools upsd vX.Y.Z - http://www.networkupstools.org/ | server.version | Server version | X.Y.Z |=============================================================================== Instant commands ---------------- [options="header"] |======================================================================== | Name | Description | load.off | Turn off the load immediately | load.on | Turn on the load immediately | load.off.delay | Turn off the load possibly after a delay | load.on.delay | Turn on the load possibly after a delay | shutdown.return | Turn off the load possibly after a delay and return when power is back | shutdown.stayoff | Turn off the load possibly after a delay and remain off even if power returns | shutdown.stop | Stop a shutdown in progress | shutdown.reboot | Shut down the load briefly while rebooting the UPS | shutdown.reboot.graceful | After a delay, shut down the load briefly while rebooting the UPS | test.panel.start | Start testing the UPS panel | test.panel.stop | Stop a UPS panel test | test.failure.start | Start a simulated power failure | test.failure.stop | Stop simulating a power failure | test.battery.start | Start a battery test | test.battery.start.quick | Start a "quick" battery test | test.battery.start.deep | Start a "deep" battery test | test.battery.stop | Stop the battery test | test.system.start | Start a system test | calibrate.start | Start runtime calibration | calibrate.stop | Stop runtime calibration | bypass.start | Put the UPS in bypass mode | bypass.stop | Take the UPS out of bypass mode | reset.input.minmax | Reset minimum and maximum input voltage status | reset.watchdog | Reset watchdog timer (forced reboot of load) | beeper.enable | Enable UPS beeper/buzzer | beeper.disable | Disable UPS beeper/buzzer | beeper.mute | Temporarily mute UPS beeper/buzzer | beeper.toggle | Toggle UPS beeper/buzzer | outlet.n.shutdown.return | Turn off the outlet possibly after a delay and return when power is back | outlet.n.load.off | Turn off the outlet immediately | outlet.n.load.on | Turn on the outlet immediately | outlet.n.load.cycle | Power cycle the outlet immediately | outlet.n.shutdown.return | Turn off the outlet and return when power is back |======================================================================== nut-2.7.4/docs/documentation.txt0000644000175000017500000001114112667753006013627 00000000000000ifdef::website[] Documentation ============= endif::website[] User Documentation ------------------ ifdef::website[] - FAQ - Frequently Asked Questions (link:docs/FAQ.html[online]) (link:docs/FAQ.pdf[PDF]) - NUT User Manual (link:docs/user-manual.chunked/index.html[online]) (link:docs/user-manual.pdf[PDF]) - Cables information (link:cables.html[online]) (link:docs/cables.pdf[PDF]) - link:docs/man/index.html#User_man[User manual pages] - link:ddl/index.html#_supported_devices[Devices Dumps Library (DDL)]: Provides information on how devices are supported endif::website[] ifndef::website[] - link:../FAQ.html[FAQ - Frequently Asked Questions] - linkdoc:user-manual[NUT user manual] - <> - link:../man/index.html#User_man[User manual pages] - link:http://www.networkupstools.org/ddl/index.html#_supported_devices[Devices Dumps Library (DDL)]: Provides information on how devices are supported endif::website[] Developer Documentation ----------------------- ifdef::website[] - NUT Developer Guide (link:docs/developer-guide.chunked/index.html[online]) (link:docs/developer-guide.pdf[PDF]) - NUT Packager Guide (link:docs/packager-guide.chunked/index.html[online]) (link:docs/packager-guide.pdf[PDF]) - link:ups-protocols.html[UPS protocols library] - link:docs/man/index.html#Developer_man[Developer manual pages] - link:nut-qa.html[NUT Quality Assurance] - link:ddl/index.html[Devices Dumps Library (DDL)]: Provides simulation data to the linkman:dummy-ups[8] driver endif::website[] ifndef::website[] - linkdoc:developer-guide[NUT Developer Guide] - linkdoc:packager-guide[NUT Packager Guide] - link:ups-protocols.html[UPS protocols library] - link:../man/index.html#Developer_man[Developer manual pages] - link:nut-qa.html[NUT Quality Assurance] - link:http://www.networkupstools.org/ddl/index.html[Devices Dumps Library (DDL)]: Provides simulation data to the linkman:dummy-ups[8] driver endif::website[] Offsite Links ------------- [[general_powerdev_info]] These are general information about UPS, PDU, ATS, PSU and SCD: - link:http://tldp.org/HOWTO/UPS-HOWTO/[UPS HOWTO] (The Linux Documentation Project) - link:http://en.wikipedia.org/wiki/Uninterruptible_power_supply[UPS on Wikipedia] - link:http://en.wikipedia.org/wiki/Power_distribution_unit[PDU on Wikipedia] - link:https://en.wikipedia.org/wiki/Transfer_switch[Automatic Transfer Switch] - link:https://en.wikipedia.org/wiki/Power_supply_unit_%28computer%29[Power Supply Units] - link:http://en.wikipedia.org/wiki/Solar_controller[Solar controller on Wikipedia] - link:http://www.pcguide.com/ref/power/ext/ups/over.htm[UPS on The PC Guide] These are writeups by users of the software. - link:http://rogerprice.org/NUT.html[NUT Setup with openSUSE] '(Roger Price)' - link:http://www.dimat.unina2.it/LCS/MonitoraggioUpsNutUbuntu10-eng.htm[Deploying NUT on an Ubuntu 10.04 cluster] '(Stefano Angelone)' - link:http://blog.shadypixel.com/monitoring-a-ups-with-nut-on-debian-or-ubuntu-linux[Monitoring a UPS with nut on Debian or Ubuntu Linux] '(Avery Fay)' - link:http://linux.developpez.com/cours/upsusb/[Installation et gestion d'un UPS USB en réseau sous linux] '(Olivier Van Hoof, french)' - link:http://trac.networkupstools.org/projects/nut/wiki/NutOnMacOSX[Network UPS Tools (NUT) on Mac OS X (10.4.10)] '(Andy Poush)' - link:http://www.llondel.org/ups.shtml[Interfacing a Contact-Closure UPS to Mac OS X and Linux] '(David Hough)' - link:http://fedoranews.org/contributors/kazutoshi_morioka/nut/[How to use UPS with nut on RedHat / Fedora Core] '(Kazutoshi Morioka)' - link:http://people.freebsd.org/~thierry/nut_FreeBSD_HowTo.txt[FreeBSD installation procedure] '(Thierry Thomas, from FreeBSD)' - link:http://www.usebox.net/jjm/ups-obsd/[Gestionando un SAI desde OpenBSD con NUT] '(Juan J. Martinez, spanish)' - link:http://forums.gentoo.org/viewtopic-p-2663684.html[HOWTO: MGE Ellipse 300 on gentoo] '(nielchiano)' - link:http://deschis.blogspot.com/2006/07/cum-se-configureaz-un-ups-apollo-seria.html[Cum se configurează un UPS Apollo seria 1000F pe Linux] '(deschis, Romanian)' - link:http://buffalo.nas-central.org/wiki/Install_a_UPS_%28nut%29[Install a UPS (nut) on a Buffalo NAS] '(various authors)' - link:http://blog.pointbre.com/2903/nutnetwork-ups-tool-korean-guidebook.html[NUT Korean GuideBook] '(PointBre)' News articles and Press releases -------------------------------- - link:http://www.crn.com/news/channel-programs/199000818/linux-ups-without-tears.htm[Linux UPS Without Tears] '(A. Lizard)' - link:http://www.enterprisenetworkingplanet.com/netsysm/article.php/3295841/Graceful-UPS-Shutdowns-on-Linux.htm[Graceful UPS shutdowns on Linux] '(Carla Schroder)' nut-2.7.4/docs/features.txt0000644000175000017500000002136412667753704012611 00000000000000Features ======== NUT provides many features, and is always improving. Thus this list may lag behind the current code. Features frequently appear during the development cycles, so be sure to look at the link:http://www.networkupstools.org/download.html[release notes and change logs] to see the latest additions. //////////////////////////////////////////////////////////////////////////////// *FIXME* statement that NUT is *the* de facto standard on Opensource system. all Linux distributors have standardized the Power Devices support using NUT. More and more appliances manufacturers are bundling NUT... => add an Appendix NUT Device Integration in the User Manual //////////////////////////////////////////////////////////////////////////////// Multiple manufacturer and device support ---------------------------------------- - Monitors many UPS, PDU, ATS, PSU and SCD models from more than 140 manufacturers with a unified interface (link:stable-hcl.html[Hardware Compatibility List]). - Various communication types and many protocols are supported with the same common interface: * serial, * USB, * network (SNMP, Eaton / MGE XML/HTTP). Multiple architecture support ----------------------------- - Cross-platform - different flavors of Unix can be managed together with a common set of tools, even crossing architectures. - This software has been reported to run on Linux distributions, the BSDs, Apple's OS X, Solaris, IRIX, HP/UX, Tru64 Unix, and AIX. - Windows users may be able to build it directly with Cygwin. There is also a port of the client-side monitoring to Windows called WinNUT. - Your system will probably run it too. You just need a good C compiler and possibly some more packages to gain access to the serial ports. Other features, such as USB / SNMP / whatever, will also need extra software installed. Layered and modular design with multiple processes -------------------------------------------------- - Three layers: drivers, server, clients. - Drivers run on the same host as the server, and clients communicate with the server over the network. - This means clients can monitor any UPS anywhere as long as there is a network path between them. WARNING: Be sure to plug your network's physical hardware (switches, hubs, routers, bridges, ...) into the UPS! Redundancy support - Hot swap/high availability power supplies -------------------------------------------------------------- - upsmon can handle high-end servers which receive power from multiple UPSes simultaneously. - upsmon won't initiate a shutdown until the total power situation across all source UPSes becomes critical (on battery and low battery). - You can lose a UPS completely as long as you still have at least the minimum number of sources available. The minimum value is configurable. Security and access control --------------------------- - Manager functions are granted with per-user granularity. The admin can have full powers, while the admin's helper can only do specific non-destructive tasks such as a battery test. - The drivers, server, and monitoring client (upsmon) can all run as separate user IDs if this is desired for privilege separation. - Only one tiny part of one program has root powers. upsmon starts as root and forks an unprivileged process which does the actual monitoring over the network. They remain connected over a pipe. When a shutdown is necessary, a single character is sent to the privileged process. It then calls the predefined shutdown command. In any other case, the privileged process exits. This was inspired by the auth mechanism in Solar Designer's excellent popa3d. - The drivers and network server may be run in a chroot jail for further security benefits. This is supported directly since version 1.4 and beyond with the 'chroot=' configuration directive. - IP-based access control relies on the local firewall and link:http://en.wikipedia.org/wiki/TCP_Wrapper[TCP Wrapper]. - SSL is available as a build option ("--with-ssl"). It encrypts sessions with upsd and can also be used to authenticate servers. Web-based monitoring -------------------- - Comes stock with CGI-based web interface tools for UPS monitoring and management, including graphical status displays. - Custom status web pages may be generated with the CGI programs, since they use templates to create the pages. This allows you to have status pages which fit the look and feel of the rest of your site. Free software ------------- - That's free beer and free speech. Licensed under the GNU General Public License version 2 or later. - Know your systems - all source code is available for inspection, so there are no mysteries or secrets in your critical monitoring tools. UPS management and control -------------------------- - Writable variables may be edited on higher end equipment for local customization - Status monitoring can generate notifications (email/pager/SMS/...) on alert conditions - Alert notices may be dampened to only trigger after a condition persists. This avoids the usual pager meltdown when something happens and no delay is used. - Maintenance actions such as battery runtime calibration are available where supported by the UPS hardware. - Power statistics can be logged in custom formats for later retrieval and analysis - All drivers are started and stopped with one common program. Starting one is as easy as starting ten: 'upsdrvctl start'. - Shutdowns and other procedures may be tested without stressing actual UPS hardware by simulating status values with the dummy-ups pseudo-driver. Anything which can happen in a driver can be replicated with dummy-ups. Monitoring diagrams ------------------- These are the most common situations for monitoring UPS hardware. Other ways are possible, but they are mostly variants on these four. NOTE: these examples show serial communications for simplicity, but USB or SNMP or any other monitoring is also possible. "Simple" configuration ~~~~~~~~~~~~~~~~~~~~~~ image:images/simple.png[] One UPS, one computer. This is also known as "Standalone" configuration. This is the configuration that most users will use. You need at least a driver, upsd, and upsmon running. "Advanced" configuration ~~~~~~~~~~~~~~~~~~~~~~~~ image:images/advanced.png[] One UPS, multiple computers. Only one of them can actually talk to the UPS directly. That's where the network comes in. The Master system runs the driver, upsd, and upsmon in master mode. The Slave systems only run upsmon in slave mode. This is useful when you have a very large UPS that's capable of running multiple systems simultaneously. There is no longer the need to buy a bunch of individual UPSes or "sharing" hardware, since this software will handle the sharing for you. //////////////////////////////////////////////////////////////////////////////// *FIXME* remainder === One UPS, many clients === - Multiple systems may monitor a single UPS using only their network connections - no special "UPS sharing" hardware is required. - "Slave and master" monitoring design synchronizes shutdowns so that slaves can bring down their operating systems cleanly before the master switches off the power. === Many UPSes, many clients === - Each upsd process can serve status data for multiple UPSes to many clients. - Each upsmon process can monitor multiple UPSes for status data. //////////////////////////////////////////////////////////////////////////////// "Big Box" configuration ~~~~~~~~~~~~~~~~~~~~~~~ image:images/bigbox.png[] Some systems have multiple power supplies and cords. You typically find this on high-end servers that allow hot-swap and other fun features. In this case, you run multiple drivers (one per UPS), a single upsd, and a single upsmon (as master for both UPS 1 and UPS 2) This software understands that some of these servers can also run with some of the supplies gone. For this reason, every UPS is assigned a "power value" - the quantity of power supplies that it feeds on a system. The total available "power value" is compared to the minimum that is required for that hardware. For example, if you have 3 power supplies and 3 UPSes, but only 2 supplies must be running at any given moment, the minimum would be 2. This means that you can safely lose any one UPS and the software will handle it properly by remaining online. "Bizarre" configuration ~~~~~~~~~~~~~~~~~~~~~~~ image:images/bizarre.png[] You can even have a UPS that has the serial port connected to a system that it's not feeding. Sometimes a PC will be close to a UPS that needs to be monitored, so it's drafted to supply a serial port for the purpose. This PC may in fact be getting power from some other UPS. This is not a problem. The first system ("mixed") is a Master for UPS 1, but is only monitoring UPS 2. The other systems are Slaves of UPS 2. Image credits ------------- Thanks to Eaton for providing shiny modern graphics. nut-2.7.4/docs/FAQ.txt0000644000175000017500000010631612667573433011402 00000000000000ifndef::external_title[] NUT Frequently Asked Questions ============================== endif::external_title[] == I just upgraded, and ... You have read link:UPGRADING[UPGRADING] in the base directory of the distribution, right? If not, go read it now, then come back to this file if your question wasn't answered in there. == My UPS driver now says it's 'broken', and won't start. What now? Or a variation like... == My favorite UPS driver disappeared after an upgrade. What now? Drivers are occasionally removed from the tree if they are no longer receiving maintenance, or sometimes renamed to better reflect their hardware support scope or replaced by a more generic driver. There have been several architectural changes to the driver code in recent times, and drivers which were not converted by someone are eventually dropped. This is called progress. We do this in order to avoid a situation where someone believes that a driver is being maintained when it is actually rotting slowly in the tree. It also keeps the tree free of old compatibility hacks for code that nobody actually uses anyway. To get a driver back into current releases, you need to convert it yourself or get someone to do it for you. This is not difficult. The hardest part of any driver is decoding the protocol, and that's already been done in the old version. == My UPS driver program won't work. I'm starting it as root, and root owns the device, so what's the problem? *Answer 1* The drivers drop root privileges long before the serial port is opened. You'll need to change the permissions on that port so that their new user id can access it. Normally this is "nobody", but it may be changed at compile-time by using configure --with-user. Read the error message. If you have a permissions mismatch, then you'll see something like this: Network UPS Tools - APC Smart protocol driver 0.60 (1.1.7) This program is currently running as youruid (UID 1234) /dev/ttyS2 is owned by user root (UID 0), mode 0600 Change the port name, or fix the permissions or ownership of /dev/ttyS2 and try again. Unable to open /dev/ttyS2: Permission denied Now is a good time to point out that using "nobody" is a bad idea, since it's a hack for NFS access. You should create a new role account (perhaps called "ups" or "nut"), and use that instead. Also, scroll down to the "security domains" question to see an even better way of restricting privileged operations. Neither the drivers nor upsd ever need root powers, and that answer tells you how to make it work. *Answer 2* You can also specify a user with "user=" in the global part of ups.conf. Just define it before any of your [sections]: user = nut [myups] driver = mge-shut port = /dev/ttyS0 == upsc, upsstats, and the other clients say 'access denied'. The device communication port (serial, USB or network) permissions are fine, so what gives? In this case, "access denied" means the access to upsd, not the device communication port. You're being denied since the system has no permission to speak to upsd according to the access controls. There can be various reasons. To fix it, check: - the LISTEN directive in upsd.conf. It should allow your local or remote access method, - your firewall rules. Port 3493/tcp must be opened to incoming connections, - your tcp-wrappers configuration (hosts.allow and hosts.deny). Refer to the upsd(8) and upsd.conf(5) manpages for more information. == I get a 'not listening on...' error from upsd. Verify your LISTEN directive. It should be one of the valid IP addresses for the computer running upsd (or 0.0.0.0, which is INADDR_ANY), not an address for a client. The LISTEN directive lets you pick which interface upsd listens on. If you are trying to limit the clients which can connect to upsd, you either need to use tcp-wrappers or kernel firewall rules. This isn't a NUT-specific limitation - it applies equally to your web server or mailer daemon. == Which UPS should I buy? One with a no-questions-asked money-back guarantee. Seriously. The NUT developers cannot take responsibility for recommending an UPS (see the LICENSE file for more details on the explicit lack of warranty), only to find out that the manufacturer has changed the internals of the UPS without changing the model name. That said, from time to time, certain vendors have helped out by providing hardware for testing, results of their testing efforts, or protocol specifications. We try to publish this information on the NUT website, so you can take this into consideration when selecting an UPS brand. == I have an APC Smart-UPS connected with a grey APC serial cable and it won't work. The Back-UPS type in the genericups driver works but then I don't get to use all the nifty features in there. Why doesn't the right driver work? The problem lies in your choice of cable. APC's grey cables generally only do "dumb" signalling - very basic yes/no info about the battery and line status. While that is sufficient to detect a low battery condition while on battery, you miss out on all the goodies that you paid for. Note that the 940-0095B happens to be a grey cable, but it is actually a dual mode cable and can be used in smart mode. If you have this cable, you need to edit your ups.conf to look like this: [myups] driver = apcsmart port = /dev/whatever cable = 940-0095B All other grey cables from APC are assumed to be "dumb". If your grey cable isn't the 940-0095B, the solution is to dump that cable and find one that supports APC's "smart" signalling. Typically these come with the UPS and are black. If your smart cable has wandered off, one can be built rather easily with some connectors and cable - there's no fancy wiring or resistors. See this URL for a handy diagram: http://www.networkupstools.org/cables/940-0024C.jpg There is also a text version of that diagram in the docs/cables directory of the NUT source distribution. Either one should allow you to build a good clone of APC's 940-0024C cable. There are simpler solutions involving 3 wires that work just fine too, but Powerchute won't find the loopback DTR-DCD and RTS-CTS and will be annoyed. If you don't ever plan to use Powerchute, 3 wires (RxD, TxD, GND) are sufficient. It should also be noted that the genericups driver has no way to detect the UPS, so it will fire up quite happily if it can open the serial port. Merely having it start up is not necessarily an indication of success. You should start it and then check the status with upsc or similar to be sure that it's reading the hardware properly. == Why doesn't upsd implement the functionality of upsmon? I have to run THREE programs to monitor my UPS! *Answer 1* We try to follow the "tool for the job" philosophy. It may mean more programs running, but the flexibility you get is usually worth it. Yes, the machine with the UPS attached will generally have 3 processes (driver, upsd, upsmon) running, but this design allows a much bigger setup. Imagine a data room with a bunch of machines all drawing power from the same UPS. The rest of them just run upsmon. Besides, if upsmon were rolled into upsd, upsd would get even bigger than it is now. You'd have one less process, but the RAM consumption would be pretty close to now. See the "Data Room" section in docs/config-notes.txt for more configuration ideas and explanations. *Answer 2* If this really bothers you, roll up your sleeves and use the sockdebug code to write a "upsmon" type program that sits on top of the state sockets. It won't work over the network, but it means you don't need upsd. It also means only one host can monitor the UPS. This is also a good option to consider if you can't use networked monitoring code for security or safety reasons. See the TODO file for more on this and other related topics. == Why isn't upssched part of upsmon? Most users will never have any reason to use upssched. It's complicated, and getting it right for your situation can be tricky. Having it live in a separate program saves resources and lets most people avoid it completely. It is also coherent with the answer to the previous question. == Why doesn't upsmon send a SIGPWR signal to init so it can deal with power events? *Answer 1* New versions of the init man page taken from the sysvinit package are saying that usage of SIGPWR is discouraged, since /dev/initctl control channel is the preferred way of communication. *Answer 2* The name of the game is portability. Not everyone's init handles that kind of signalling gracefully. What's more, some admins might want to do things differently even if they have that kind of init running. So, to be compatible, upsmon just invokes a shell command. If you want to use init's SIGPWR stuff, just put the right "kill" line in a shell script and make upsmon call it. Everyone wins. == Why won't bestups talk to my Best Fortress UPS? There are at least two different protocols being used for hardware with very similar names. The bestups driver tends to support the units built around the newer "PhoenixTec" protocol, and the bestfortress driver supports the older Best hardware. There is a similar problem with the tripplite_usb driver: it only supports the older, proprietary protocol. Newer standards-compliant Tripp Lite UPS models are supported by usbhid-ups. We name drivers based on the information available at the time the driver was first written, which often is incomplete. == What's this about 'data stale'? It means your UPS driver hasn't updated things in a little while. upsd refuses to serve up data that isn't fresh, so you get the errors about staleness. If this happens to you, make sure your driver is still running. Also look at the syslog. Sometimes the driver loses the connection to the UPS, and that will also make the data go stale. This might also happen on certain virtualization platforms. If you cannot reproduce the problem on a physical machine, please report the bug to the virtualization software vendor. If this happens a lot, you might consider cranking up DEADTIME in the upsmon.conf to suppress some of the warnings for shorter intervals. Use caution when adjusting this number, since it directly affects how long you run on battery without knowing what's going on with the UPS. Note: some drivers occasionally need more time to update than the default value of MAXAGE (in upsd.conf) allows. As a result, they are temporarily marked stale even though everything is fine. This can happen with MGE Ellipse equipment - see the mge-shut or usbhid-ups man pages. In such cases, you can raise the value of MAXAGE to avoid these warnings; try a value like 25 or 30. == Why do the client programs say 'Driver not connected' when I try to run them? This means that upsd can't connect to the driver for some reason. Your ups.conf entry might be wrong, or the driver might not be running. Maybe your state path is not configured properly. Check your syslog. upsd will complain regularly if it can't connect to a driver, and it should say why it can't connect. Note: if you jumped in with both feet and didn't follow the INSTALL.nut document, you probably started upsd by itself. You have to run 'upsdrvctl start' to start the drivers after configuring ups.conf. == Why don't the pathnames in your documentation match the package I installed? Each distribution has conventions for where specific file types should be stored. The NUT project cannot possibly track all of these conventions, so the documentation assumes the default installation directory prefix of `/usr/local/ups` when describing file locations. The distributions tend not to change the base name of the files, so you can search for drivers and configuration files in the package database of installed files. For instance, on Debian or Ubuntu derivatives, you can use `dpkg --search usbhid-ups` to see where the drivers are stored. == Everything works perfectly during the shutdown, and the UPS comes back on, but my system stays off. What's happening? Assuming you don't have the problem in the next question, then you probably have an ATX motherboard, have APM or ACPI enabled in your kernel (assuming Linux here), and are reaching the 'halt' at the bottom of your shutdown scripts. Your machine obeys and shuts down, and stays down, since it remembers the 'last state' when the UPS restarts. One solution is to change your shutdown scripts so you never reach that point. You *want* the system to die without reaching the part where the kernel tells it to shut down. A possible script might look like this: # other shutdown stuff here (mount -o remount,ro ...) if (test -f /etc/killpower) then /usr/local/ups/sbin/upsdrvctl shutdown sleep 600 # this should never return # uh oh, we never got shut down! (power race?) reboot fi halt -p The other solution is to change your BIOS setting to "always power on" instead of "last state", assuming that's possible. == My system has an ATX power supply. It will power off just fine, but it doesn't turn back on. What can I do to fix this? This depends on how clueful your motherboard manufacturer is, and isn't a matter of the OS. You have to do one of the following things depending on what's supported: - Set a jumper on the motherboard that means "return after outage" - Set something in the BIOS that says "power up after power failure" - Try using something (like a capacitor) across the power button to "push" it for you - this might not work if it needs a delay - Hack the cable between the power supply and the motherboard to fool it into powering up whenever line power is present - Teach a monkey to watch the machine and press the power button when the outage is over. This might work, but it creates high produce bills. If you can't use one of the first two options, give the board to an enemy. Let them worry about it. == My Mac won't power back up by itself into Linux after the UPS shuts down. What can I do about this? This is about the same situation as the ATX question above, only worse. Earlier Macs apparently supported a hack where you could cat some magic characters at /dev/adb to enable "server mode". This would instruct the system to reboot while unattended. From Usenet post <6boftzxz51.fsf@ecc-office.sp.cs.cmu.edu>: # Send packet over the ADB bus to the PowerMac CUDA chip # telling it to reboot automatically when power is restored # after a power failure. cat /etc/local/autoboot.adb > /dev/adb autoboot.adb contains these three bytes (in hex): 01 13 01 Later PowerPC Macs with a PMU and the appropriate kernel driver can achieve the same effect with the following command: echo server_mode=1 > /proc/pmu/options The following pages have some slightly more kludgy answers which involve the use of `setpci`, and are highly model-specific: - https://www.mythic-beasts.com/support/servers/colo/macminicolo_howto - http://superuser.com/questions/212434/reboot-after-power-failure-for-mac-running-ubuntu - http://ubuntuforums.org/showthread.php?t=1209576 Note: this question has been in the FAQ for several years now, and there's still no clean answer. Let me guess: everyone who runs a server on Mac hardware has a team of trained monkeys, and feeds them by growing bananas in the tropical environment formed by waste heat from the equipment. The rest of us are still waiting for the answer. Booting into the Mac OS to frob the "file server" panel is not an acceptable solution. == My Mac won't power back up by itself into Mac OS X after the UPS shuts down. What can I do about this? This is relatively simple to fix. If you have console or VNC access, log in as an administrator, go to System Preferences, click on Energy Saver, click on the options tab, check "Restart automatically after a power failure". Alternatively, you can connect via SSH and run "sudo pmset autorestart 1" to achieve the same effect. == I want to keep the drivers and upsd in their own security domains. How can this be accomplished? Using a few role accounts and a common group, you can limit access to resources such as the serial port(s) leading to the UPS hardware. This is just an example. Change the values to suit your systems. - Create a user called 'nutdev' and another called 'nutsrv'. Put them both in a group called 'nut'. - Change the owner of any serial ports that will be used to nutdev, and set the mode to 0600. Then change the ownership of your state directory (usually /var/state/ups) to nutdev.nut. For my development system this yields the following /dev entries: 0 crw------- 1 nutdev tty 4, 64 Sep 3 17:11 /dev/ttyS0 0 crw------- 1 nutdev tty 4, 65 Sep 3 17:11 /dev/ttyS1 - Switch to root, then start the drivers: # /usr/local/ups/sbin/upsdrvctl -u nutdev start - The listing for /var/state/ups then looks like this: 4 drwxrwx--- 2 nutdev nut 4096 Aug 20 18:37 . 4 drwxr-xr-x 4 root root 4096 May 14 21:20 .. 4 srw-rw---- 1 nutdev nut 0 Sep 3 17:10 apcsmart-ups1 4 srw-rw---- 1 nutdev nut 0 Sep 3 17:10 blazer_ser-ups2 You may have to remove old socket or state files first if you are changing to this security scheme from an older version. The drivers will create new files with the right owners and modes. Note that /var/state/ups is group writable since upsd will place the upsd.pid file here. You may have to change the groups of upsd.conf and upsd.users to make them readable. These files should not be owned by nutsrv, since someone could compromise the daemon and change the config files. Instead, put nutsrv in a group ("nut" in this example), then make the files owned by root.nut, with mode 0640. Once the config files are ready, start upsd: # /usr/local/ups/sbin/upsd -u nutsrv Check your syslog to be sure everything's happy, then be sure to update your startup scripts so it uses this procedure on your next boot. If you like this, you'll probably also find the chroot process to be useful and interesting. See security.txt for more details. == What's the point of that 'security domains' concept above? The point is limiting your losses. If someone should happen to break into upsd in that environment, they should only gain access to that one user account. Direct access to the serial device is not possible, since that is owned by another user. There is also the possibility of running the drivers and upsd in a chroot jail. See the chroot.txt provided in the source distribution for an example implementation. Why give would-be vandals any sort of help? Put it this way - I *wrote* good chunks of this stuff, and I still run the programs this way locally. You should definitely consider using this technique. == How can I make upsmon shut down my system after some fixed interval? You probably don't want to do this, since it doesn't maximize your runtime on battery. Assuming you have a good reason for it (see the next entry), then look at scheduling.txt or the upssched(8) man page for some ideas. ///////////////////////////////////////////////////////////////// TODO: figure out how to link to the upssched man page above. ///////////////////////////////////////////////////////////////// == Why doesn't upsmon shut down my system? I pulled the plug and nothing happened. Wait. upsmon doesn't consider a UPS to be critical until it's both 'on battery' and 'low battery' at the same time. This is by design. Nearly every UPS supports the notion of detecting the low battery all by itself. When the voltage drops below a certain point, it _will_ let you know about it. If your system has a really complicated shutdown procedure, you might need to shut down before the UPS raises the low battery flag. For most users, however, the default behavior is adequate. Ask yourself this: why buy a nice big UPS with the matching battery and corresponding runtime and then shutdown early? If anything, I'd rather have a few more minutes running on battery during which the power might return. Once the power's back, it's business as usual with no visible interruption in service. If you purposely shut down early, you guarantee an interruption in service by bringing down the box. See upssched.txt for information on how you can shutdown early if this is what you really want to do. == The CGI programs report 'access to that host is not authorized' - what's going on? Those programs need to see a host in your hosts.conf before they will attempt communications. This keeps people from feeding it random "host=" settings, which would annoy others with outgoing connection attempts from your system. If your hosts.conf turns out to be configured correctly with MONITOR entries and all that, check the permissions. Your web server may be running the CGI programs as a user that can't read the file. If you run your web server in a chroot jail, make sure the programs can still read hosts.conf. You may have to copy it into the jail for this to work. If you do that, make sure it's not writable by any of the user accounts which run inside the jail. == upsd is running, so why can't I connect to it? Assuming you haven't changed the TCP port number on the command line or at compile-time, then you may have some sort of firewall blocking the connection. upsd listens on TCP port 3493 by default. If you do not specify a LISTEN directive in upsd.conf, upsd only listens on the loopback interface. See the upsd.conf man page for details. == How do you make upsmon reload the config file? Or a variation like... == How do you make upsd reload the config file? Either find the pid of the background process and send it a SIGHUP, or just start it again with '-c reload'. If you send the signals yourself instead of using -c, be sure you hit the right process. There are usually two upsmons, and you should only send signals to one of them. To be safe, read the pid file. == I just bought a new WhizBang UPS that has a USB connector. How do I monitor it? There are several driver to support USB models. - usbhid-ups supports various manufacturers complying to the HID Power Device Class (PDC) standard, - tripplite_usb supports various older Tripp-Lite units (with USB ProductID 0001) - bcmxcp_usb supports various Powerware units, - nutdrv_qx and blazer_usb support various manufacturers that use the Megatec / Q1 protocol. Refer to the 'driver-name' (8) manpage for more information. You can also consult the Hardware Compatibility List (HCL) and filter on USB: http://www.networkupstools.org/stable-hcl.html?connection=USB == My USB UPS is supported but doesn't work! On Linux, udev rules are provided to set the correct permissions on device file. This allows the NUT driver to communicate with the UPS, through this device file. However, the driver may still fail to start and support the device, with a message like: failed to claim USB device: could not claim interface 0: Operation not permitted *Operation not permitted* is a message pointing to a privilege issue. The most frequent issue is that udev has not actually applied the rule: - if NUT has been freshly installed, - and if the device USB cord was already plugged when installing NUT. In this case, just unplug and plug back the USB cord, then restart NUT. Instead of unplugging, you might also be able to run `udevadm trigger --subsystem-match=usb --action=change` to fix permissions. There was a mistake in the naming of the NUT udev rules file which resulted in the rules being overridden by another udev configuration file. While this has been fixed in the Git master branch, your distribution may still be affected. Details are available in the following Github issue: https://github.com/networkupstools/nut/issues/140 == Why do you not use the Linux kernel HID driver when communicating with USB UPSes? When the `usbhid-ups` was first written, it replaced an older driver `hidups` which used the Linux kernel USB HID API. At the time, the kernel HID API could not distinguish between identical Usage IDs that were nested in different parent IDs, so many common measurements were not available from `hidups`. For this reason, the libusb approach was chosen, which has the added side effect of being more portable than the Linux HID API. The Linux hiddev device nodes have very similar permissions problems as the `/dev/bus/usb` nodes that the libusb approach uses. Due to difficulties in running libusb on OS X and Windows, those platforms might benefit more from a native HID approach. == I get a message from the kernel that the driver "did not claim interface 0 before use" On Linux, if two copies of a driver are competing for the UPS, these messages will appear in dmesg: usbfs: process 29641 (usbhid-ups) did not claim interface 0 before use This can be a symptom of a source install conflicting with a package install. There is a rudimentary locking mechanism in NUT, but there is a chance that the packages might not use the same directory as the NUT default, and the conflict will be reported by the kernel. == Why doesn't my package work? Or a variation like... == I can't run this because there's no package for it. Why isn't this in a package yet? Sorry, can't help you there. All official releases are source code and are posted on http://www.networkupstools.org/ along with PGP signatures for verification. This means all packages have been built by a third party. If you have an issue that's related to packaging, you will need to seek help with whoever built it for you. == Why are there two copies of upsmon running? It's not really two complete copies if your OS forks efficiently. By default, upsmon runs most of the grunt work as an unprivileged user and keeps a stub process around with root powers that can only shut down the system when necessary. This should make it much harder to gain root in the event a hole is ever discovered in upsmon. If this really bothers you and you like running lots of code as root, start upsmon with -p and it will go back to being one big process. This is not recommended, so don't blame us if something bad happens in this mode. == I get the following error while building: `make[4]: don't know how to make HP-UX/nut-drvctl.sh. Stop` NUT still has some hidden dependencies on GNU Make which show up while running `make distcheck`. If you are running `make distcheck` or its variants, you will need to install GNU Make (`devel/gmake` in the ports tree), which is incidentally what the official FreeBSD port of NUT does for all builds. == I have 'some problem' with 'some old version' ... Get the latest stable release, and see if it still happens. If it goes away, it means someone else reported it and got it fixed a long time ago. You may want to search the mailing lists to see if someone else has experienced the same problem. If so, there is a good chance that someone else has worked through the process necessary to shoehorn the latest NUT version into your distribution (potentially with unofficial packages). Some OS distributions contain old versions of NUT. If your hardware is newer than the NUT release, there is a good chance that support has not been added yet. Please do not tell us you have the "latest version for Distro XYZ" - even if the developers are familiar with that distribution, it helps others if you quote the exact package version. NOTE: check the release date on the version you have. If it's more than about 6-12 months old, there's probably a newer stable tree version out there. == I built NUT from Git, and it complains about lots of missing files. What happened? If you are not actively developing a driver, can you use a snapshot instead? The NUT instance of Buildbot generates tar files of the latest NUT source after each successful build, and these snapshots include a prebuilt version of the `./configure` script. Otherwise, you will need recent versions of autoconf, automake, libtool, asciidoc, a2x and its dependencies for DocBook/dblatex. Rather than publish a list of the exact versions needed (which will quickly become out of date), we recommend you consult your distribution's dependency list for building a NUT package, and use that as a starting point. == Do I have to use a serial connection to monitor the UPS? What about direct network connections (SNMP or otherwise)? NUT currently supports USB communication through several drivers, and also SNMP and XML/HTTP (Eaton and MGE) communications. Since NUT is very extensible, support for a new communication bus can be added easily. Any time there is a gap in features, it's usually because the group of people who own that hardware and the group of people who write code don't overlap. The fix is to make them overlap - turn an owner into a developer or vice-versa. == What happened to the patch I sent? We try to prioritize emails with patches, but you should understand that a simple fix for your bug might be complicated to integrate with the rest of NUT. Changing the way a fundamental component works, such as USB support, means a lot of testing to ensure that your fix does not break other drivers. Sometimes patches are put on hold due to a feature freeze. If it doesn't show up once the new version opens up, send it again. == I'm not much of a programmer. How can I help? There's always work to be done outside of the realm of code bashing. Documentation can always be improved. A user's perspective is sometimes needed to appreciate this. Bug reports on a project's documentation are just as valuable as those for the actual source. Fielding questions on the mailing lists is also helpful. This lets other people to focus on coding issues while allowing the original poster to get some information at the same time. It's quite a relief to open that mailbox and find that someone else has already handled it successfully. == I replaced the battery in my APC Smart-UPS and now it thinks the battery is low all the time. How do you fix this? Or a variation like... == My APC UPS keeps reporting 'OL LB', even after it's been charging for many hours. What can I do about this? This happened to me, and some other people too. The combination of our experiences should prove useful to you. First, you need to realize that the UPS apparently stores data about the battery, load, and runtime. After replacing the battery, it needs to be clued in to the new situation. If the traditional runtime calibration doesn't work, you have to try something a little more drastic. You need to *completely* drain the UPS while it has a good ground. This means you can't just pull the plug. You also have to disconnect it from the computer so this software won't shut it down. The easiest way to do this is to first unplug your computer(s) from it, and plug in a token load like a lamp. Also, move the UPS to a power strip that doesn't switch the ground line or an outlet that you can switch off at your panel. Once the UPS is up at 100% charge (this is important), disconnect the power. It _must_ remain connected to the ground, or the results may not be accurate. Ignore the sounds it makes, and go away until it's done. Don't do anything to the front panel while this is happening. After all of this, put things back the way they should be and let it charge up. You should find that it again gives reasonable values and behavior, as it was when it was new. Thanks to Matthew Dharm for helping us nail down this procedure. == upsstats returns temperatures in Celsius. I like Fahrenheit. Where's the config file to switch it back? Temperature scales are handled by the template files, so edit your upsstats.html and change it from TEMPC to TEMPF. == Why is the mailing list ignoring me? You probably asked a question that's answered in this FAQ, or somewhere else in the documentation, and nobody wants to quote it for you. There is a small chance that the mailing list spam filter ate your message. Check the list archives to see if your message appears there. Convincing the other subscribers that you've actually read down this far might be useful. You might mention "queequeg" for better results. This URL may also be helpful: http://www.catb.org/~esr/faqs/smart-questions.html == Why are you so insistent about sending emails to public mailing lists instead of to individuals? By and large, NUT is a volunteer effort. By emailing one person, you are asking them to take care of your question. If you email the list instead, you give others the opportunity to answer. In addition, the mailing lists are publicly archived, and therefore easily searchable. Chances are, you aren't the only person who will ever have that question. == If you want mailing list replies to go to the list, why don't you add a Reply-To: header? We are not going to rehash all of the arguments for and against this in a simple FAQ entry. If you intend for your reply to go to more than just the last person who posted, it is not too much trouble to hit "reply all". == I found some information about another kind of UPS protocol you don't support yet, but I don't know what to do with it. Can you help? If you're not a programmer, you can still help others by making that protocol available. You might host the document somewhere and send the URL to one of the mailing lists. == How can you answer questions to situations that nobody's encountered yet? Isn't this a frequently asked questions file? *Answer 1* It's a kind of Magic. *Answer 2* It's both that and a frequently *anticipated* questions file, too. The idea is to write it up in here so that nobody asks the mailing list when it finally does get released. == My UPS powers up immediately after a power failure instead of waiting for the batteries to recharge! You can rig up a little hack to handle this issue in software. Essentially, you need to test for the POWERDOWNFLAG in your *startup* scripts while the filesystems are still read-only. If it's there, you know your last shutdown was caused by a power failure and the UPS battery is probably still quite weak. In this situation, your best bet is to sleep it off. Pausing in your startup script to let the batteries recharge with the filesystems in a safe state is recommended. This way, if the power goes out again, you won't face a situation where there's not enough battery capacity left for upsmon to do its thing. Exactly how long to wait is a function of your UPS hardware, and will require careful testing. If this is too evil for you, buy another kind of UPS that will either wait for a minimum amount of charge, a minimum amount of time, or both. == I'm facing a power race Or a variation like... == The power came back during the shutdown, but before the UPS power off. Now the UPS does not reboot, and my computer stays off. How can I fix that? There is a situation where the power may return during the shutdown process. This is known as a race. Here's how we handle it. "Smart" UPSes typically handle this by using a command that forces the UPS to power the load off and back on. This way, you are assured that the systems will restart even if the power returns at the worst possible moment. Contact closure units (ala genericups), on the other hand, have the potential for a race when feeding multiple systems. This is due to the design of most contact closure UPSes. Typically, the "kill power" line only functions when running on battery. As a result, if the line power returns during the shutdown process, there is no way to power down the load. The workaround is to force your systems to reboot after some interval. This way, they won't be stuck in the halted state with the UPS running on line power. Implement this by modifying your shutdown script like this: if (test -f /etc/killpower) then /usr/local/ups/sbin/upsdrvctl shutdown sleep 120 # uh oh, we never got shut down! (power race?) reboot fi nut-2.7.4/docs/acknowledgements.txt0000644000175000017500000001626412667571022014317 00000000000000ifndef::external_title[] Acknowledgements ================ endif::external_title[] This project is the result of years of work by many individuals and companies. Many people have written or tweaked the software; the drivers, clients, server and documentation have all received valuable attention from numerous sources. Many of them are listed within the source code, AUTHORS file, release notes, and mailing list archives, but some prefer to be anonymous. This software would not be possible without their help. The NUT Team ------------ Active members ~~~~~~~~~~~~~~ - Arnaud Quette: project leader (since 2005), Debian packager and jack of all trades - Charles Lepple: senior lieutenant - Emilien Kia: senior developer - Daniele Pezzini: senior developer - Václav Krpec: junior developer - Kjell Claesson: senior developer - Alexander Gordeev: junior developer - Michal Soltys: junior developer - David Goncalves: Python developer - Jean Perriault: web consultant - Eric S. Raymond: Documentation consultant - Oden Eriksson: Mandriva packager - Stanislav Brabec: Novell / Suse packager - Michal Hlavinka: Redhat packager - Antoine Colombier: trainee For an up to date list of NUT developers, refer to link:https://github.com/orgs/networkupstools/members[GitHub]. Retired members ~~~~~~~~~~~~~~~ - Russell Kroll: Founder, and project leader from 1996 to 2005 - Arjen de Korte: senior lieutenant - Peter Selinger: senior lieutenant - Carlos Rodrigues: author of the "megatec" drivers, removing the numerous drivers for Megatec / Q1 protocol. These drivers have now been replaced by blazer_ser and blazer_usb - Niels Baggesen: ported and heavily extended upscode2 to NUT 2.0 driver model - Niklas Edmundsson: has worked on 3-phase support, and upscode2 updates - Martin Loyer: has worked a bit on mge-utalk - Jonathan Dion: MGE internship (summer 2006), who has worked on configuration - Doug Reynolds: has worked on CyberPower support (powerpanel driver) - Jon Gough: has worked on porting the megatec driver to USB (megatec_usb) - Dominique Lallement: Consultant (chairman of the USB/HID PDC Forum) - Julius Malkiewicz: junior developer - Tomas Smetana: former Redhat packager (2007-2008) - Frederic Bohe: senior developer, Eaton contractor (2009-2013) Supporting manufacturers ------------------------ UPS manufacturers ~~~~~~~~~~~~~~~~~ [[Eaton]] * link:http://powerquality.eaton.com[Eaton], has been the main NUT supporter in the past, between 2007 and 2011, continuing MGE UPS SYSTEMS efforts. As such, Eaton has been: - providing extensive technical documents (Eaton protocols library), - providing units to developers of NUT and related projects, - hosting the networkupstools.org webserver (from 2007 to August 2012), - providing artwork, - promoting NUT in general, - supporting its customers using NUT. + [WARNING] ================================================================================ *The situation has evolved, and since 2011 Eaton does not support NUT anymore.* *This may still evolve in the future.* *But for now, please do not consider anymore that buying Eaton products will provide you with official support from Eaton, or a better level of device support in NUT.* ================================================================================ * link:http://www.gamatronic.com[Gamatronic], through Nadav Moskovitch, has revived the 'sec' driver (as gamatronic), and expanded a bit genericups for its UPSs with alarm interface. * link:http://www.microdowell.com[Microdowell], through Elio Corbolante, has created the 'microdowell' driver to support the Enterprise Nxx/Bxx serial devices. They also proposes NUT as an alternative to its software for link:http://www.microdowell.com/fra/download.html[Linux / Unix]. * link:http://pcmups.com.tw[Powercom], through Alexey Morozov, has provided link:ups-protocols.html[extensive information] on its USB/HID devices, along with development units. * link:http://www.riello-ups.com[Riello UPS], through Massimo Zampieri, has provided link:ups-protocols.html[all protocols information]. Elio Parisi has also created riello_ser and riello_usb to support these protocols. * link:http://www.tripplite.com[Tripp Lite], through Eric Cobb, has provided test results from connecting their HID-compliant UPS hardware to NUT. Some of this information has been incorporated into the NUT hardware compatibility list, and the rest of the information is available via the link:http://article.gmane.org/gmane.comp.monitoring.nut.user/8173[list archives]. Appliances manufacturers ~~~~~~~~~~~~~~~~~~~~~~~~ - link:http://www.opengear.com[OpenGear] has worked with NUT's leader to successfully develop and integrate PDU support. Opengear, through Scott Burns, and Robert Waldie, has submitted several patches. Other contributors ------------------ - Pavel Korensky's original apcd provided the inspiration for pursuing APC's smart protocol in 1996 - Eric Lawson provided scans of the OneAC protocol - John Marley used OCR software to transform the SEC protocol scans into a HTML document - Chris McKinnon scanned and converted the Fortress protocol documentation - Tank provided documentation on the Belkin/Delta protocol - Potrans provided a Fenton PowerPal 600 (P series) for development of the safenet driver. Older entries (before 2005) --------------------------- - MGE UPS SYSTEMS was the previous NUT sponsor, from 2002 until its partial acquisition by Eaton. They provided protocols information, many units for development of NUT-related projects. Several drivers such as mge-utalk, mge-shut, snmp-ups, hidups, and usbhid-ups are the result of this collaboration, in addition to the WMNut, MGE HID Parser the libhid projects, ... through Arnaud Quette (who was also an MGE employee). All the MGE supporters have gone with Eaton (through MGE Office Protection Systems), which was temporarily the new NUT sponsor. - Fenton Technologies contributed a PowerPal 660 to the project. Their open stance and quick responses to technical inquiries were appreciated for making the development of the fentonups driver possible. Fenton has since been acquired by link:http://www.metapo.com[Metapo]. - Bo Kersey of link:http://www.vircio.com[VirCIO] provided a Best Power Fortress 750 to facilitate the bestups driver. - Invensys Energy Systems provided the SOLA/Best "Phoenixtec" protocol document. SOLA has since been acquired by Eaton. - PowerKinetics technical support provided documentation on their MiniCOL protocol, which is archived in the NUT protocol library. PowerKinetics was acquired by the link:http://www.jst.cc[JST Group] in June 2003. - link:http://www.cyberpowersystems.com[Cyber Power Systems] contributed a 700AVR model for testing and development of the cyberpower driver. - link:http://www.liebert.com[Liebert Corporation] supplied serial test boxes and a UPStation GXT2 with the Web/SNMP card for development of the liebert driver and expansion of the existing snmp-ups driver. Liebert has since been acquired by link:http://www.emerson.com[Emerson]. NOTE: If a company or individual isn't listed here, then we probably don't have enough information about the situation. Developers are requested to report vendor contributions to the NUT team so this list may reflect their help. If we have left you out, send us some mail. nut-2.7.4/docs/support.txt0000644000175000017500000000677212640443572012503 00000000000000ifdef::website[] Support instructions ==================== endif::website[] There are various ways to obtain support for NUT. Documentation ------------- - First, be sure to read the link:docs/FAQ.html[FAQ]. The most common problems are already addressed there. ifdef::website[] - Else, you can read the link:docs/user-manual.chunked/index.html[NUT User Manual]. endif::website[] ifndef::website[] - Else, you can read the linkdoc:user-manual[NUT user manual]. endif::website[] It also covers many areas about installing, configuring and using NUT. The specific steps on system integration are also discussed. - Finally, link:docs/man/index.html#User_man[User manual pages] will also complete the User Manual provided information. At least, read the manual page related to your driver(s). Mailing lists ------------- If you have still not found a solution, you should search the lists before posting a question. Someone may have already solved the problem: ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++
    ++++++++++++++++++++++++++++++++++++++ endif::backend-xhtml11[] ifndef::backend-xhtml11[] link:http://www.google.com/search?as_q=&as_oq=nut-upsuser+nut-upsdev&domains=lists.alioth.debian.org&sitesearch=lists.alioth.debian.org&btnG=Search+NUT+lists[search on the NUT lists using Google] endif::backend-xhtml11[] Finally, you can *subscribe* to a NUT mailing list to: Request help ~~~~~~~~~~~~ Use the link:http://lists.alioth.debian.org/mailman/listinfo/nut-upsuser[NUT Users] mailing list. In this case, be sure to include the following information: - OS name and version, - exact NUT version, - NUT installation method: from source tarball, package or Subversion, - exact device name and related information (manufacturing date, web pointers, ...), - complete problem description, with any relevant traces, like system log excerpts, and driver debug output. You can obtain the latter using the following command, as root and after having stopped NUT: /path/to/driver -DD -a If you don't include the above information in your help request, we will not be able to help you! Post a patch, ask a development question, ... ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the link:http://lists.alioth.debian.org/mailman/listinfo/nut-upsdev[NUT Developers] mailing list. Refer to the ifdef::website[] link:docs/developer-guide.chunked/index.html[NUT Developer Guide] for more information, and the chapter on how to link:docs/developer-guide.chunked/ar01s03.html#_submitting_patches[submit patches]. endif::website[] ifndef::website[] linkdoc:developer-guide[NUT Developer Guide] for more information, and the chapter on how to link:../developer-guide.chunked/ar01s03.html#_submitting_patches[submit patches]. endif::website[] Discuss packaging and related topics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the link:http://lists.alioth.debian.org/mailman/listinfo/nut-packaging[NUT Packagers] mailing list. Refer to the ifdef::website[] link:docs/packager-guide.chunked/index.html[NUT Packager Guide] endif::website[] ifndef::website[] linkdoc:packager-guide[NUT Packager Guide] endif::website[] for more information. nut-2.7.4/docs/nut-qa.txt0000644000175000017500000001312512640473702012160 00000000000000NUT Quality Assurance ===================== Recognizing the critical nature of NUT, the NUT Quality Assurance (NQA) effort has been established to improve NUT where necessary, and to maintain software quality as high as it should be. NQA is present in many aspects and areas of NUT. Documentation ------------- The documentation toolchain uses link:http://www.methods.co.nz/asciidoc/[AsciiDoc] to output both HTML pages and manual pages (troff). This single point of control fills many gaps, suppresses many redundancies, and optimizes documentation management in general. - The NUT website and HTML documentation are tested for W3C XHTML 1.1 and CSS compliance. This can be counter verified by clicking the W3C XHTML 1.1 and CSS icons, at the bottom of each page. //////////////////////////////////////////////////////////////////////////////// - the manual pages conformance is tested with link:http://catb.org/~esr/doclifter/index.html[doclifter] (outdated) //////////////////////////////////////////////////////////////////////////////// - Documentation source files are spell checked, using link:http://aspell.net[Aspell], both interactively (using 'make spellcheck-interactive') and automatically in Buildbot (using 'make spellcheck'). NOTE: A NUT dictionary is also available (docs/nut.dict), providing a glossary of terms related to power devices and management. Source code ----------- Use of standards ~~~~~~~~~~~~~~~~ NUT promotes and uses many standards, such as: - the variable names standard used in NUT, - the coding rules and best practices for developers, - the use of a software architecture limiting developments to the very minimum, - the use of standard Free and OpenSource Software components, like: * the USB library, * the Net SNMP project, * the Neon library, * the OpenSSL library (to be replaced by NSS, which is more license compliant with NUT and can be FIPS 140 certified), * the TCP Wrappers library. QA tools and metrics ~~~~~~~~~~~~~~~~~~~~ NUT's quality is constently monitored using many tools, like: - a Revision Control System (link:https://github.com/networkupstools/nut[Git]) to track development and ease regression fixes. //////////////////////////////////////////////////////////////////////////////// Any modification on the NUT source trees are reported on the link:http://lists.alioth.debian.org/mailman/listinfo/nut-commits[NUT Commits] mailing list. //////////////////////////////////////////////////////////////////////////////// - link:http://buildbot.networkupstools.org/public/nut/[Buildbot] to automate the compile/test cycle. Any build failure is caught early, and fixed quickly. //////////////////////////////////////////////////////////////////////////////// reported through the link:http://lists.alioth.debian.org/mailman/listinfo/nut-commits[NUT Commits] mailing list, and fixed quickly. //////////////////////////////////////////////////////////////////////////////// - a project portal with trackers for bugs, feature request, patchs and tasks NUT QA also relies on external tools and trackers, like: //////////////////////////////////////////////////////////////////////////////// FIXME (POST): - integrate static code analysis - consider splint, Frama-C, BLAST and Clang, and choose one. - integrate link:http://scan.coverity.com[Coverity Scan] program. Note: request made by Arnaud to scan-admin@coverity.com on Sep 24 2009 - point other distro BTS (use Launchpad as an aggregator?!) //////////////////////////////////////////////////////////////////////////////// - Clang - the Debian QA tools, available through the link:https://tracker.debian.org/pkg/nut[NUT Package Tracking System]: * Lintian general QA checks, * link:http://piuparts.debian.org/sid/source/n/nut.html[piuparts] automates the installation, upgrade and removal testing processes. - a runtime testing suite, which automates the inter-layer communication testing (driver - upsd - upsmon / clients), that is part of Ubuntu. link:http://bazaar.launchpad.net/~ubuntu-bugcontrol/qa-regression-testing/master/view/head:/scripts/test-nut.py[The NUT testing script] is available in the link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite]. It installs NUT, configures it with the dummy-ups driver, changes a few data and checks that these are well propagated with upsc. - link:https://bugzilla.redhat.com/buglist.cgi?component=nut[Redhat / Fedora Bug tracker] - link:https://www.openhub.net/p/nut[Black Duck Open Hub] (formerly Ohloh.net) provides metrics on NUT source code base and activity. Runtime quality ~~~~~~~~~~~~~~~ - NUT provides many link:user-manual.html#NUT_Security[security features] to ensure a maximum runtime security level. - Packages use several link:http://wiki.debian.org/Hardening[Hardening methods] to protect NUT binaries. //////////////////////////////////////////////////////////////////////////////// FIXME (POST): - write a code conformance checker (nut-lint) - write a § on driver maintenance status - consider using [http://forge.novell.com/modules/xfmod/project/?opensuse OpenSUSE Build Service tools] - provide software metrics and evolution over the time * [http://www.flossmetrics.org/ FlossMetrics] * [http://en.wikipedia.org/wiki/Software_metric Wikipedia] * [http://cccc.sourceforge.net/ CCCC - C and C++ Code Counter] * [http://open.ncsu.edu/se/tutorials/metrics/ Metrics with Eclipse] - Code documentation, for the core architecture (client and drivers are already documented) * [http://doxygen.org/ Doxygen] * [http://naturaldocs.org/ NaturalDocs] //////////////////////////////////////////////////////////////////////////////// nut-2.7.4/docs/new-clients.txt0000644000175000017500000001165112640473702013205 00000000000000Creating new client =================== NUT provides bindings for several common languages that are presented below. All these are released under the same license as NUT (the GNU General Public License). If none of these suits you for technical or legal reasons, you can implement one easily using the <>. The latter approach has been used to create the Python 'PyNUT' module, the Nagios 'check_ups' plugin (and probably others), which can serve as a reference. C / C++ ------- Client access library ~~~~~~~~~~~~~~~~~~~~~ `libupsclient` and `libnutclient` libraries can be linked into other programs to give access to upsd and UPS status information. Both static and shared versions are provided. These library files and associated header files are not installed by default. You must `./configure --with-dev` to enable building and installing these files. The libraries can then be built and installed with `make` and `make install` as usual. This must be done before building other (non-NUT) programs which depend on them. Low-level library: libupsclient ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `libupsclient` provides a low-level interface to directly dialog with upsd. It is a wrapper around the NUT network protocol. For more information, refer to the linkman:upsclient[3], manual page and the various link:../man/index.html#devclient[upscli_*(3)] functions documentation referenced in the same file. Clients like upsc are provided as examples of how to retrieve data using the upsclient functions. link:http://www.networkupstools.org/projects.html[Other programs] not included in this package may also use this library, such as wmnut. High level library: libnutclient ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `libnutclient` provides a high-level interface representing devices, variables and commands with an object-oriented API in C++ and C. For more information, refer to the linkman:libnutclient[3] manual page. #include #include using namespace nut; using namespace std; int main(int argc, char** argv) { try { // Connection Client* client = new TcpClient("localhost", 3493); Device mydev = client->getDevice("myups"); cout << mydev.getDescription() << endl; Variable var = mydev.getVariable("device.model"); cout << var.getValue()[0] << endl; } catch(NutException& ex) { cerr << "Unexpected problem : " << ex.str() << endl; } return 0; } Configuration helpers ~~~~~~~~~~~~~~~~~~~~~ NUT provides helper scripts to ease the configuration step of your program, by detecting the right compilation and link flags. For more information, refer to a <>. Python ------ The PyNUT module, contributed by David Goncalves, can be used for connecting a Python script to `upsd`. Note that this code (and the accompanying NUT-Monitor application) is licensed under the GPL v3. The `PyNUTClient` class abstracts the connection to the server. In order to list the status variables for `ups1` on the local `upsd`, the following commands could be used: $ cd scripts/python/module $ python ... >>> import PyNUT >>> from pprint import pprint >>> client = PyNUT.PyNUTClient() >>> vars = client.GetUPSVars('ups1') >>> pprint(vars) {'battery.charge': '90', 'battery.charge.low': '30', 'battery.runtime': '3690', 'battery.voltage': '230.0', ... Further examples are given in the `test_nutclient.py` file. To see the entire API, you can run `pydoc` from the `module` directory. If you wish to make the module available to everyone on the system, you will probably want to install it in the `site-packages` directory for your Python interpreter. (This is usually one of the last items in `sys.path`.) Perl ---- The old Perl bindings from CPAN have recently been updated and merged into the NUT source code. These operate in a similar fashion to the Python bindings, with the addition of access to single variables, and additional interpretation of the results. The Perl class instance encapsulates a single UPS, where the Python class instance represents a connection to the server (which may service multiple UPS units). use UPS::Nut; $ups = new UPS::Nut( NAME => "myups", HOST => "somemachine.somewhere.com", PORT => "3493", USERNAME => "upsuser", PASSWORD => "upspasswd", TIMEOUT => 30, DEBUG => 1, DEBUGOUT => "/some/file/somewhere", ); if ($ups->Status() =~ /OB/) { print "Oh, no! Power failure!\n"; } tie %other_ups, 'UPS::Nut', NAME => "myups", HOST => "somemachine.somewhere.com", ... # same options as new(); ; print $other_ups{MFR}, " ", $other_ups{MODEL}, "\n"; Java ---- The NUT Java support has been externalized. It is available at https://github.com/networkupstools/jnut nut-2.7.4/docs/sock-protocol.txt0000644000175000017500000001365212640443572013560 00000000000000Driver/server socket protocol ============================= Here's a brief explanation of the text-based protocol which is used between the drivers and server. The drivers may send things on the socket at any time. They will send out changes to their local storage immediately, without any sort of prompting from the server. As a result, the server must always check on any driver sockets for activity. Formatting ---------- All parsing on either side of the socket is done by parseconf, so the same rules about escaping characters and "quoting multi-word elements" apply here. Values which may contain odd characters are typically sent through pconf_encode to apply \ characters where necessary. The "" construct is used throughout to force a multi-word value to stay together on its way to the other end. Commands used by the drivers ---------------------------- SETINFO ~~~~~~~ SETINFO "" SETINFO ups.status "OB LB" There is no "ADDINFO" - if a given variable does not exist, it is created upon receiving the first SETINFO command. DELINFO ~~~~~~~ DELINFO DELINFO ups.temperature ADDENUM ~~~~~~~ ADDENUM "" ADDENUM input.transfer.low "95" DELENUM ~~~~~~~ DELENUM "" DELENUM input.transfer.low "98" ADDRANGE ~~~~~~~~ ADDRANGE ADDRANGE input.transfer.low 95 100 DELRANGE ~~~~~~~~ DELRANGE DELRANGE input.transfer.low 95 100 SETAUX ~~~~~~ SETAUX SETAUX ups.id 8 This overrides any previous value. The auxiliary value is presently used as a length byte for read-write variables that are strings. SETFLAGS ~~~~~~~~ SETFLAGS ... SETFLAGS ups.id RW STRING Note that this command takes a variable number of arguments, as multiple flags are supported. Also note that they are not crammed together in "", since "RW STRING" would mean something completely different. This also replaces any previous flags for a given variable. ADDCMD ~~~~~~ ADDCMD ADDCMD load.off DELCMD ~~~~~~ DELCMD DELCMD load.on DUMPDONE ~~~~~~~~ DUMPDONE This is only used to tell the server that every possible item has been transmitted in response to its DUMPALL request. Once this has been received by the server, it can be sure that it knows everything that the driver does. PONG ~~~~ PONG This is sent in response to a PING from the server. It is only used as a sanity check to make sure that the driver has not gotten stuck somewhere. DATAOK ~~~~~~ DATAOK This means that the driver is able to communicate with the UPS, and the data should be treated as usable. It is always sent at the end of the dump if the data is not stale. It may also be sent at other times. DATASTALE ~~~~~~~~~ DATASTALE This is sent by the driver to inform any listeners that the data is no longer usable. This usually means that the driver is unable to get any sort of meaningful response from the UPS. You must not rely on any status information once this has been sent. This will be sent in the beginning of a dump if the data is stale, and may be repeated. It is cleared by DATAOK. Commands sent by the server --------------------------- PING ~~~~ PING This is sent to check on the health of a driver. The server should only send this when it hasn't heard anything valid from a driver recently. Some drivers have very little to say in terms of updates, and this may be the only communications they have with the server on a normal basis. If a driver does not respond with the PONG within a few seconds at the most, it should be treated as dead/unavailable. Data stored in the server must not be passed on to the clients when this happens. INSTCMD ~~~~~~~ INSTCMD INSTCMD panel.test.start SET ~~~ SET "" SET ups.id "Data room" DUMPALL ~~~~~~~ DUMPALL The server uses this to request a complete copy of everything the driver knows. This is returned in the form of the same commands (SETINFO, etc.) that would be used if they were being updated normally. As a result, the same parsing happens either way. The server can tell when it has a full copy of the data by waiting for DUMPDONE. That special response from the driver is sent once the entire set has been transmitted. Design notes ------------ Requests ~~~~~~~~ There is no way to request just one variable. This was done on purpose to limit the complexity of the drivers. Their job is to send out updates and handle a few simple requests. DUMPALL is provided to give the server a known foundation. To track a limited set of variables, a server just needs to do DUMPALL, then only have handlers that remember values for the variables that matter. Anything else should be ignored. Access/Security ~~~~~~~~~~~~~~~ There are no access controls in the drivers. Anything that can connect to their sockets can make requests, including SET and INSTCMD if supported by the driver and hardware. These sockets must be kept secure. If your operating system does not honor permissions or modes on sockets, then you must store them in a directory with suitable permissions to limit access. Command limitations ~~~~~~~~~~~~~~~~~~~ As parseconf is used to handle decoding and chunking of the data, there are some limits on what may be used. These default to 32 arguments of 512 characters each, which should be more than enough for everything which is currently needed by the software. These limits are strictly for sanity purposes, and may be raised if necessary. parseconf itself can handle vast numbers of arguments and characters, with some speed penalty as things get really big. Re-establishing communications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the server loses its connection to the driver and later reconnects, it must flush any local storage and start again with DUMPALL. The driver may have changed the internal state considerably during that time, and anything other approach could leave old elements behind. nut-2.7.4/docs/cables.txt0000644000175000017500000000677312640473702012217 00000000000000ifdef::website[] Cables ====== endif::website[] APC --- 940-0024C clone ~~~~~~~~~~~~~~~ *From D. Stimits* image::images/cables/940-0024C.jpg[APCC 940-0024C clone diagram] NOTE: The original 940-0024C diagram was contributed by Steve Draper. 940-0024E clone ~~~~~~~~~~~~~~~ *Reported by Jonathan Laventhol* This cable is said to use the same wiring as 940-0024C clone. 940-0024C clone for Macs ~~~~~~~~~~~~~~~~~~~~~~~~ *From Miguel Howard* image::images/cables/mac-940-0024C.png[APCC 940-0024C clone cable for Macs] Belkin ------ OmniGuard F6C***-RKM ~~~~~~~~~~~~~~~~~~~~ *From "Daniel"* A straight-through RS-232 cable (with pins 2-7 connected through) should work with the following models: - F6C110-RKM-2U - F6C150-RKM-2U - F6C230-RKM-2U - F6C320-RKM-3U image::images/cables/belkin-f6cx-rkm-xu-cable.jpg[Belkin OmniGuard F6C***-RKM cable] Eaton ----- Documents in this section are provided courtesy of Eaton. MGE Office Protection Systems ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The three first cables also applies to MGE UPS SYSTEMS and Eaton. DB9-DB9 cable (ref 66049) ^^^^^^^^^^^^^^^^^^^^^^^^^ This is the standard serial cable, used on most units. image::images/cables/mge-66049.png[DB9-DB9 cable] DB9-RJ45 cable ^^^^^^^^^^^^^^ This cable is used on the more recent models, including Ellipse MAX, Protection Station, ... image::images/cables/mge-db9-rj45.jpg[DB9-RJ45 cable] NMC DB9-RJ45 cable ^^^^^^^^^^^^^^^^^^ The following applies to the MGE 66102 NMC (Network Management Card), and possibly other models. The NMC connection is an 8P8C RJ45-style jack. |==== |Signal | PC | NMC | | 1,4,6 | |TxD | 2 | 3 |RxD | 3 | 6 |GND | 5 | 4 | | 7,8 | | | shield | shield |==== USB-RJ45 cable ^^^^^^^^^^^^^^ This cable is used also on the more recent models, including Ellipse MAX, Protection Station, ... image::images/cables/mge-usb-rj45.jpg[USB-RJ45 cable] DB9-RJ12 cable ^^^^^^^^^^^^^^ This cable is used on some older Ellipse models. image::images/cables/mge-db9-rj12.jpg[DB9-RJ12 cable] Powerware LanSafe ~~~~~~~~~~~~~~~~~ image::images/cables/Lansafecable.jpg[Powerware LanSafe cable] SOLA-330 ~~~~~~~~ Just uses a normal serial cable, with pin 1-1 through to 9-9. image::images/cables/SOLA-330.png[SOLA-330 cable] HP - Compaq ----------- Older Compaq UPS Family ~~~~~~~~~~~~~~~~~~~~~~~ This cable can be used with the following models: T700, T1000, T1500, T1500j, T700h, T1000h, T1500h, R1500, R1500j, R1500h, T2000, T2000j, T2400h, T2400h-NA, R3000 / R3000j, R3000h, R3000h-International, R3000h-NA, R6000h-NA, R6000i, R6000j. UPS PC 9 pin connector 1 --------- 3 2 --------- 2 4 -\ 4 --------- 5 | 6 -/ 6 --------- 7 Contributed by Kjell Claesson and Arnaud Quette. Phoenixtec (Best Power) ----------------------- Many Best Power units (including the Patriot Pro II) have a female DB-9 socket with a non-standard pinout. |==== |Signal | PC | UPS | | 1,4,6 | NC |TxD | 2 | 2 |RxD | 3 | 1 |GND | 5 | 4 | | 7,8 | NC |==== Sources: * http://pinoutsguide.com/UPS/best_power_pinout.shtml * http://lit.powerware.com/ll_download.asp?file=m_patriotproii_jan99.pdf * Stan Gammons Tripp-Lite ---------- *From Tripp-Lite, via Bryan Kolodziej* This cable (black 73-0844 cable) is used on various models, using the "Lan 2.2 interface" and the genericups driver (upstype=5). image::images/cables/73-0724.png[73-0724 cable] nut-2.7.4/docs/images/0000755000175000017500000000000012670024741011533 500000000000000nut-2.7.4/docs/images/cables/0000755000175000017500000000000012670024741012764 500000000000000nut-2.7.4/docs/images/cables/940-0024C.jpg0000644000175000017500000003660212640443572014423 00000000000000ÿØÿàJFIFddÿþ940-0024 APC Cable PinoutÿÛC  !"$"$ÿÛCÿÂëS"ÿÄÿÄÿÚ ý”1yl2÷dÔ!5MBaûøVÞRZž}WõeMWŠû2X/Çsƒ¾)\…ùL\ª-À ç³@¨’~u¬´Ï‘m¿5ýÇyÀNÎìÈÚ!®¥ýñÓiËCSSÖw°Þu†4OŸ~yˈKDïÂY\i%/óúScÔWØOæ´RI² ò9ßùÿô½^óß ¿‹žâ÷ѤŸþùWi®~eq¬êfí2ûÌh"SÜK Dº‰Jñ°ûZí“çÙÅKu·‹+¤¼ç/¨1*_€ ívÏ|lÆ3Žäa›‘†í¤©-,@"pï÷ÏÆéÃå›[ÎÙx²¥k è qÇĹyè¯h/ñ:_€}€9}èž¼ý#÷áÜ–u)hm«i^³ë§JÖÙÝ'@ ðDz’ú=u‰,+ì n½%ÓÏrK ºÄè~3Äk?=¶5Œ˜Ö2~S04þs\Ë[l©§D–\JSKDrXúßYtóûß’³zLN€æTÕB&ªk:¢Äüoñßì/äòµ ÏŸ~]ÔœŸõæü >‰Ö¥Ÿ«Ë"qíèûã·XUÉéKò™\áE{a@ ü¾ !ÌxØ :˜ ¸î–~§àþ6Ð{Î q­PhsÁeΟýeùGì@D¨·–V÷—–+,ë,éx’âKœ1p ø©ÍnóäY†1³ÆÌa/¥„€Ÿ>É*¦HŽKúúVo²r¯éη¢ÐÆ“(PüDõæ´²WØÀ¯é"–è'‹z‰Uô}—ŽÒËÏZ ð âÅQܰFøJ@îHSÛ'¤!5pFúH@øX!E-ÀIq"–±+ÚÝÚ½­¬*íœbK‰,3ÖåP/ÊòÃy$ÎNµ°#W}¹(åYÏuAýA_W(É]ÌìQEÓŒç¹òÌu÷cÒ4#ô=øy&€"Kàñ+€«Ly½ ÃÙi…5Èb{l?ÆÎŸh{³:a“á³â`ô÷c· ývÄ~y¤8›p? P5ƒÝŒµVøf¾i€ÿÄ- 230@#$%1P`ÿÚúåÅÃ^y0¿Ðñ $k™!KÒ/x‘à6¸ ¯íz˜õ×µ={S×µ={„Ç×–•ÆHâø…Qý‘ójk›S\ÚšæÔ×6¦¹µ5Í©¨œÁ Àÿ'Þ£œKÔðŒF…à*óyºÜH@ÙÄ#k=ßU½zJíÇé´¶Xìü~›V(ìº: …=ž¢„¬ÙwsñÊM8ŽÉ¦xìí°ÅÏ®+6]ümMJhÕ' úýêxbª± Þn£÷$Ye±¬÷kžáµ¬rNc|gÛ3ÅÂ;þ*g÷ãjì£d¯UÕlí ìõµµg³¨’º£hS-…>ËSX/k«íŸ±5­ZÙN»iëËeU²”ΦýûM èSÂV64o7Qû´3„„Ö{´d¦{ñd0Æa®ŠKÏ\£òQÄLk<,XÖ׫…E«Ìf²Î9ıcwk 1fRÙY6€¶6ɧÁ¡í%¶]ÖÚX¸¡ö9枬ÚW&­}Rp¯¯èSÃjxÁƃ̉ÎdziÁ+Ò¹5Ƙà u<ßë=ß[! ìɈÉe¤}4¸ ËQkñÈszep3ûM éSøÞm2·¸Æ3Œãræ™?vü÷}–u}Àkðü¿¯—_.¾>]||ºÍóŠÔÄ‚]*x^Œ¸™à†[„à75pÐL•{ ¶9+Î “ZËÜÁÛ.@°+ÀÝžïµÒ“,u>JˆÈlõ)ám<œë"¼ÛšÂà&3뤛gîѰæ5žïµ„–9:Š1”b¥<,È{dFón$"Aóꯓ´¸%Îp>ÑEqc‰×r®5žïÜSÂÎXÄkÁ0 ÞmùÆ3©B×™çÛ‡»e1»=߸§†sˆá Fp7›¦>^?vü÷~âž‚ÈzÙùqQÍÒh3–ƒk„ÜÍÉc6užï°Dl-ýǬjg9©Ò§‡q¼ÝGîÑ×\û³Ýõþ &ù&¿-I"üŽ“_#¤×Èé5ò:Mch©s¯SkäÚù6‰}O(!iN¦>AM¥`Þ§†Å¶‚Ü%‰@Þn–]UyØ´¤CˆfRÊL9¬÷}_ÈU~“ߌzôãÎüã8Õz¤uÔתïSÂÀÛ”Ö\Þn•b©ˆÆ1Å£…¶I¬÷h¶.Ç;ß?*ˆ˜b&‹CË-’ €±8\m+f™Px–„&©¬Tág’V¶„°ª-OY¾êÔö¦þ;­ôBž0DSÁFo7Cf ?v˜ieó¬÷idI‹m2ªÌáe—Z6a›5чjuÇ›gP³âˆÖßÕûêi!VN³IÀEjÔ .þ„ª2¥Õ`Õ+ïÀr½R:êkUzðÚ‹ÕšÏ{’7› Ó[Ñû´éгšÏw×8ÆpÚ*éUÚ}?Çu¾‘éSøÞn„L6±£÷oÏwÙaZ†˜§ÙÕ壖%³yÏÅèµñz-|^‹_¢Ó9@/³t¤ÀËô©ád$.ªri,o6÷ØåU†=!§ Êm~VA޳ÝöØF8eHôÛ-̨Sº”ðÏP"1›Í¼†&Üø£3äp̈.2ë=߸§…ƒx*ÀY¼ÛÓ!ɽñD…Ó *¶±Ÿ\g»÷ðÜÄ™> €͹Ù*‹ÚÝd¼XÀ—öÁl\ŠÁ cç»÷ðî7›qðÎ[Þ~íùî߇Wç d¨ŽwV–8™ l UÖl H¤à—äÓåcž(êrŒ"»A<°Ð2á›g£œ`Æ,„m€ƒ]ÕÎB4´Å’KË¡O œÍ×12,™ ˜Á&bÇó÷jÁÙ…¥ †ÏvóÉ¢ë$9®1 W4¶c!^úþ¥ö²Q5WC QÓDD“ha¸\:9ûgXÄ!ñ•ÛÂåüL œ@ìÏBØÂÑÇüÞ§„ð‘ ¸bÍÔ~í26 ‘B"{µÎ$Íš˜[–UÀ3<ÿÀY¨iu1d’ò›1`(ïaå€PL<3,ðÅSïñ\GסdN£BR×£ôc^Œk—žè›)¸ÄN¢'Z¾d!sÿ*ÆV°à×d’-?Ä×B[g % O DÂ\ö‘Ĺ2*., ñÕa”\Ö É»cý’öF¿„¯,ç_VEÏÚq ¨5™  `ho!`9â_ÊXDÄEQ5â…p"¸™®Œ0Ó–êœÊÿŒ¥ _þÿÄ0!1@AQ‘"#2a¡Áð `q±ÑáñÿÚ?Î$“x¼^HØ’H®EAàÚ / ]þÎ!¨Ú˜Zÿ­’»+¦œ«åwrüo’JDc—¢uã•-]fSý‰fQJúz8î¿¶…5ž™IkH¢Ã¬IÝ\i^bÑÕ¶¹YMP[Æ7a»C*‰k˜Yãs1Âké•OBY5¯.ÿL´ñ„àá÷ÊÈK®¡À¬ÃJ ùAIdñç°”ªZ]VÓ$™jPÄ ÈXȉô!§‘ÿÿÄ'!"1@Q A¡ð2`ÑÿÚ?æ:ª&h…Ê7 ªT’oXaI†1ц!LŽ 0]ÄÒ šb ‘NÊbVa’#Ò1ŠÛtÃüZÕÜ?@á±qY“ÇÍb26¸7¥8pÚ~}¨”‹"2ìÛVH2=¾ÅhŽ.^k(óÅUf0¢»o¼qF”Ÿ?œlK¾8¸b[¦€ñ’z(/ÂHší0¬Kiñú?ÿÄG !1 "2AQaq#‘¡0345BRr’ÁÑ@b“ᔢ£±CS‚ðP`dsÂÿÚ?ù3”ÝW5¼u ÂGKår¿§ÈÏ, žT™×¹¶‚¤ŠlB;|¾¬ðŸqו›}oûkçÿË_?þZ@dÌw-k]«çš¾y«çš­œüŽ&£»ÄaÙí 2€)ãÄËPdw ðÌ‘€vMeÃ,3<™¤Ù]‰á—޽µ&%ýèüJnÕ|ú‹7è½ð¡‡xáŵ•¹¶¿…0²)vÌB (Ð <€Ù¦ãɽ–,»ÔÔMÁàjv·‰ã›*aí¥¯ÙJ̹XGea£Â¸‹|Ä4¶¾[V!1N%0°[[6Ř̹L¹}W á~»0Ø|<Ç €–u÷S>)³”¢Én˜­ÞéX–ÝB;Ï]C„‚.§´õžO`½ž!·sgb;7n:‡i¹f–´ èk¸ +9¯I¿Ì3up93]²C7.^—}é]±9¢-¤yžtÎø¬ñîÀ·bbà,a‰Þ – %ÇŽÃãòËš7\¬*oCbšóa>lýøú Û”prƒ7¿a‡J‡©…°ñ$H>Ê‹lõ¿T‡Ç>]vnñÇ*ö:Þ„q"¢ ¢ÀTÞ:á°~ÆöûMÊóÛ‰Ù!dÈÑ{Å\¸ˆÙ,# ÛQ~>AñùXñ|NhÆQ$•ˆì¯®½3þ+ûW×^™ÿý«ë¯LÿŠþÕõצÅjú÷Òßׯ¯}-ýzú÷Òßׯ¯}+ýz·üsÒ_Ô¨ðôuõò¼ë0ÆK‡QÿMT“°Z@ 9F¶êóµ,û¹T!:2ë›L«ÔWZyŠI»àA]k"É#îÅìWköQÄå“ þ]hâ¬ùòëX‰]`.S”ÜÞ¢ÄÝ·y¾í,±ß+pÒŒ‘6…ì˜ÎgîÍÓañùlT’E)ÃáHÌVvL¡‰°éqørÚ(Ø+qàuà{ ÄÆ!‚”'2u ­ú‡òõrüé&¹á(4 ”p4¥’c¯=ísNß^–à¢å¸«Ž12Y#Ë•»n*/DzIcÆÀ3h¹°åŠî~ÃãòÛÉP“×Î 7ˆëóå˜åEtn*ÂàÐH‘Q¢Ã—çE¢XØŽ;ÇÊ=ö4ÌñÊÙy­™[¼Ê‹ÄíhÜ]XXŠÜH§"]N´«4¡ p½K ¤iaº'íTJÒ"¶nÖ[‹öS;á'h—V‘rØ|oðØ|ó péž°îWãcRo2#çÈîïçQxžF¢õÎE>"¥Žl:Ð ŒSõ¨™ãV`ÜH­îQœ _º·I‚}à>Ë™r¡íã+l>?ÆùÑw`ª¢ä“ ¥t`ÊÂàƒ¡¨¼O*HÙ}ˆPTÛ®¢ü|ƒãüoFÒBÏ…N|„2 z¯˜+øè>‰å#G:¤BÙ“%ïP²ÌY…×-fÞ Ö^…ºéw±ÏìqåùÅ»^Öî÷öì>?+›&0#LʺEº±biÎìùïTwŽ\‰¥[žÁ#ÊóÛ‰åÅøö.þ¥Èn¹Ôl>?&ñKUt6`Té_OOÊh4ž™’D œDÚWٿƾžŸ”×ÓÓòšúz~S_OOÊkéñû}a }a }a “ !Ä{Å0ÿ™”’Ö“/nx(í¯¬!¡>A$gƒG2«,PÕ³œ#ËrK_U:ZÃßA ½Ç]EâyY&™U»*-óåÖü)!f³ÉÑãF`ÖÒ:ÛÖ}¯4‘|™{»v“_JDºdÞ=G‘§'A·Z‹ NFµG†ˆY#\£‘ç^ÃhF¡¢Ì|µ¨à¡„_QxžSã"ç—Ò红ü{%L:Ä’¬ÕÉm ø[O~Ãã³—‡mË” ë'4‡&{(ɯ܉ñ93î£/–ö½…$x¸"‹xl†9sÜÚöà:¬S>Îò¹œÇ@t˜iz0CnÊ›ÇÏ&Nn¼4î¨æNŒŠyÔ˜i…ã‘rš— /J3Ç´vÔ~‹ÜE‘6{s¶zÔ ¶R¶qM!µØßM˜Xž£tÊ2;'Ü$M¾L‡8Û³¤HQ2Œ‚¤ôœƒ2/Ôò|ë4™µàKä(:†ýå*}Æ¢ñ<’ó_/ (EÊ‹ÀT_b‰ñD[¢À¾Ãã²ldøL%ÚLé*ÈK¯0-º?¯^À18xfËÃx­EpÐE HÖ'\òÄÈ3pÔT2â’’YV9 ݬG`ê&±3IèïGJÒÉu‘ä9e ÷;¯Ç® ‰¢Ãb4¶þmX7Þߨ¨àNŒjx ƒÒ1/´‡I;×ûlÅIŠÄȘµù”FÙs˺˜º*Hp˜X„p~ݒ⟗¯e‚ÜFÈ£ÆÌa€ž{Žª–<,¦XCsõŠ‹ NFµG†ˆY#\£“çXyœâ7*®¯¸Í›\¶èëÕÕI¿ÍšæÙºYoÍ¿}­QxžJá<ì÷¸l‹ñìÅÉëY%Ðáá°öÜÑÍ÷߆Ããò…]XXŠ“®ï¥í_’“Òr<È¿SÊóÛ‰ä¶!"Ë©Lßxl‹ñòʧ®aÖ\õVY0:ñæ‡kÓnp1xÇ÷«úˆþ£~õôõ_÷¯ ÿªÿ½}ýWýëè?ê¿ïYޱ&Ê¢V¹>úÌþŠxOÝyþ i 2F‚Ê9^t»¼\Ø{Ó ¯æ‹Í3Í™Û!`ËÕÀ:‹Äòl¹Èུ9¡{†Ès”€ 4 Hx-„ ¸TC‡kêl5ϰøü°’6Ŭì™}Šhã¨E‡ÙP@Í™£TžÛTGveÝIœÇ{gЋ|jKŸF’ñŧ³[ 4ÓÏŸ/Ί܋Ž"–4U¢ñ<ˆðëЩfn͸W/l’ißYŠ)a×j‰ÉÒ2M»íoßañþ7΃HN¦À*–'À / Ên¤| Eây™£Èškm¸Vf±In;ö/¬ba‡7 îêã…ã|ê›Á–`YãL΂ÇP>Ô›ÌÆò®ë•ÜXj÷ö‰Û!sKnh¥ÞtíÎñÛ³:Ùî ›[­üÍ­ó3s©íˆƒ Ÿi…÷º·4w>— Än×ÙŸ±§ >?Æùí‹Äíƒtm¾ðò"ü|ƒãÈõK¸—¨ØàmcO ï³ »Z ÛÚÖ¤Ìå‹‹¨D.Hí°¡4 "þÔÓÌXFš± ZÞê(…Ã|¯!÷Q­®]¬*lH‘Œp±Y=›]H㥯A…õíØYˆ É=TV2ù‡SÆTüháû`¹²Û«N¿1אַÍÞrÄð{.ù¼ p¡0qõÓ¸,2tƒ!VGZ1!pà_,‘²yŠ Ïíeè‹ça÷S‰e#wÓ"6!|HŸ:Cð ö‘£\Ϋc¨zÚ§¾òC åÓ+74t®ØY#+{)ëÒžFÂÈŒ¼ñjy "2ðCŪY§6ÞQ/ÑäEøöDØ|8ɘ4ÊNðëÍ}üj,Bè%@ãÌQñäOŒåXÜe°ro^ÏzºêOWi`Ypñ®gÁ¹ÖîxèºVÝJØWHT¢)–ýC]n=ÕtŒÆ¤“b¹|íXÅTwg…•U±$ŽêÙæÞ6Bˆ7 …õѵêóõ/³OÿGßþÕé ÌoO¬#¦é†pÌåmÛÇãEg‹*f^lÍÇd‘ʹ£e!‡h¡:ÍŠ‘&Eõ…Ë©#ªÀõq=µ O‡,2g•K°fb‡Ž@:«ÒXP’Æ|Õ•Ö5]O³&?û¨#·ÅMañeD…ãf ™‰%:”) ¥HoYßåë}žÞêÅz@C9tˆ«º9Ø‚ßgÚ¬íûâœZEA÷F`?ðVI‡V¾$ÈeBíÍÈê/̰ãÛûÔØ\FgÏ$Œ2Â]d Äþ¶×“çYRg„ýäµþ ÖU¹Öäž$Ô^'—ãÙ–9Ö(Èç{;·‘¾žêXÐYTX >;&ŒoKAÓ´L{û5:õTÞb¸šËœß 8å£] ÁËq½:DÍ,­)áÇÃ`TiEÛ(/  ¿eÈãËq,¤núdFÄ/‰¤Ko"°*G<ÌÒÁ|ɰøžFîGlÖ¹ …²ŽÓnt“Ds#¨e= ÐÃÜï ÝC¿ÌQc};èK%I#U*t6:FP‹ï®‚ûë ¾úBÊüµÊEÁ¾µÒŽºQ×J:Õ†Ìfò Co¤FcŒ°<Å[iã×^‡„Alì0ï2­¢eû=æ½²O‰3ÎF‰5RÃ[»*Ô¬D†+.W–<ŽN·°Ó‡¼ìd‘Ý!LKÌ#l+¡>вó›QÐV ã°“O{z¹HËåê·DÞúøVTN6L8IeÊ ŠëÇ¿…&â‰d˜Ë,{›óMø¨"ÿgÝSïU[ GÌ®ãÞþB ;‰—(²4±2ž.F£dØ\FgÏ$Œ2Â]d Äþ¶Ö±MŠÂ<‘®;f…ˆ¸/{vG ô>2x%ia¶øî‰{&N=+V5G£Žìcb•Páø¯³Ì@·sTÉèØ}\å9àÅÎð RÉÿ˜Hóx±bÇf$Ë̳¸dxã/ö@¶œ8|j?¨ „ +¼Â™Gá²è¼8šôSb𸹕0Œ'és:C¯Ptó¬D±Ã,X;.î=Ù½õ½—ˆ4ñ¬L¤ñâ NèdÔjìWR;Å!\‘ù€`$7‹ ü‹6|¥‚ócfÔøSn‹]zJÊT#E£7ŠùƒcñˆÕŒ•{͉ÿ`ôP#W/¾ˆŒ±4–³ƒr…M‹Å1œÌ€0r9ºé“SÖj\3a¦T 3nÛ àd.Ö¶ºèwL4±ÎÀ,íºkÛvz÷eãXœØY›Ò%Ûq6ì5æY¸(á:ÄïpfHñ0Çу0vïzñ¯C>'eÜYU°å™F쎯ҵ,QaçH€Ëó~Ö«ÆÚ[Χh°û¤!@"&ˆ:dE¦àŠúM¹þ"ÐYdjر;0x8HßW›÷ük{&6ÓìþÍŒÿu!\›ûf/þ3ýÖ¬9^7µâ¤eA@аh7ZkãYbq_mýÑNA½ïo44AÄ ‹™"&Û´‚Í5}Žh~ª8,¼—‘žLÞèN,ÊâP Á_I·?ÄQº l+Íë>oßœ>¹dY…Ëb -¸Ê,J ^9ØÖ2%‚/z)÷,as‰ƒÔŒ_h¥Â!Š‘Ã€˜o(SPò%–M†…hQàíx4ÆÝfÉújƪ()€Íúœ˜ç¨êN•°ˆ£KÑŸ¸, {ÄT¯vÑ6ðïÓ„<õެˆg'jëPQCÕyöLV»KðE´­Ø;zg¯Ç.ït(N´ïµÑŽÒmÏñ#ë·«Ô'Ïàáó~øìhÐÌè:ÑÂJà Ôy¬7®û«ª8ŸË幫»h¶c½I17Ûæc {=JÑ5û½x‡s@ºã`œ$“¯›÷üƒ±$j5+ÁwWÕÅeièÜ.ƒ&qW‹‘ ÀXwo߯ •¹Œg{ÐIX#° ݃·¦züs`î÷Çé6àÝÚ©%2#gu§”šøŽO›÷ü¬1²8ÔÃoc¾¿ÞWûÊHoGõGëe9,«Õy°w{¦‚-ä¬z47Rp-‚bÓ5q:b¸•ŠFóâ¢R þ5·š†i•µ|¡¶¢+ãFrPHÅ:trmÞ‘Öœ§)ŒÓp¤º ˜ýPP½ û,kÃæýÿ5·ÎHe]#nçe  ^K¢ÑÙˆnÁ1&yðw{§U`“ÂëIÛ»GÔ)Ê•ÐÅŒWÒmÇ4ñ²ê‚‰^tâuâ¾#‚¯ßÑÇ`ë1}p|ß¿æ€  ƒØó‚Ü‚ÔkU@ðsàî÷WrÄ“#0ôS®Ô8hÙtc#_I·Ç85*ʸHÄ‹‘6½_iCU[…Y"÷ÖûS)]Í׊Ws†ñÆŽ7ïþÜÞêLnÌÇg¢¯A²@.DãAu_I· Å>,˜AŠM9ùuºÖhÀ$1K—jõôTÕõ6ÈéuÉ>oßý¸;½Õ«üpn´}¹>êWÒmÍHµ_Éó~ÿíÁÝî‘w l(×_¸y"¼Á4¯¤Ûšïp_{éj*úfk½ûWa­^©¡òC&]k_ÑÃæýÿ(ˆ] "ŒŽz·…‚)꽩¯Wþü Ø;½ñúM¹þ#ƒ­ Š÷'>oßñ²FÍ(ΕößÕ&ì€ÍÄÞë}·õ_mýWÛUößÕ?˜?Uþ³ýWúÏõ_ë?Õ@íåIÉDœAìLººWúÏõHÓ‹<˜;½ÔÖj€—¡/À7+é6æ¤NK(nþ+ÛŠ»m^ašŽrO4wQwCÃæýÿ•ÌÓ§ƒ‘v9d$`–8à ­è'²=7|ԛï&ïu:¨Ç‘©N%†ðA꾓neBÕàÑi¦2 à±”!ˆ„¢¬¼>oßû\  O£ß‘ƒV b¤…N@©&;qWÃÝÅr,u ^Ò×­ˆÔ–é˜ /zš¶kØIRiïû¢gËiå:ÅDÞºþó´pÖ ÀCÛ T-‚ Y±ÂušáqÏã^Ùe`é~™¢fbpO-f^síö<<¸;½Ð4¹–l ÑÜ™8<×ÒmÊìJ‘³jß: ŠøŽ+Blç‡ÍûààÑXPpY‹ Ñ< K‡À&“ª@ÞÔ¥„µBð;ÒXšMV0²¾hJ2†Œ»B5kEó`€¹Jæ‰õ½{p½ØØ/»åð¼;p7m¿S…’Srí8±›/ÀêÑ/í¿N¬ðeo˜)ü85öG¦ï‚ZƒÓxuåÁÝî­HH)4¶oÝ}¦¤ùÔûY¯¤Û•o-0°/^Â-Y’D¦œe·›÷ü†¥…a•äejcñÅ™yÏ·Øðó`î÷Çé6å) " 3·ˆäù¿ÊÂVRHË8íFI2GŸ ,Ý‚Í q).MÐýrµ×]CŽ*КÝ_Dßu·ÍÈ{¡Íƒ»Ýk"LŸ?J3@dT|±„¼h§ÒmÉ|0Ô±D q¼,MåMAkäË8¬+Aƒ±7cFÜ>oßó"a*»~ä«·®º£Ṗ`Ÿ&Ø×˜Š3'« ²ƒƒ»Ýe.‡RˆFÐ1_I·$¢ ǰ8“§(ô¢øÀ„•‹Ç6üºpù¿öàî÷GIƒ…€*À¶4js*¹ëé6än#Z„uxÇF?CƒÓ©o@(‘5¯›÷ÿnïunÓP&YEgAK ºe‘"R`µƒô›qFÁ¤ÏšˆPÃ#¢ü[R2a¹^bË”ÄíW*í%]"GÁtQ1$¶3éŠù¿öàî÷Çé6㘵{XŽOˆäù¿|ˆ•ùÁ„ÝÜaѦ¨ŠVSiÌYÚ‰íåëbåñG@‰*,õÉaà,u©Iq ï N´sèí+Øâ…Cb6¨ÐoFpI‘¹Á©­ÀLÌn“©j’+\° °‰Ð™²¡¸ÌŒÕ28–¸wÅá]JnÌ^Q¼Ú7£/ð±ikÕæ\ׄ)råE>hÛ0-ЂÙ3(ÅF2͹æHÑg—wº“6} —A²Ú¯þ0`CŽÅ&&‘'¥SxI& µMá$˜&ÔÕBà¶÷íÉñ @†C  ÖZ‹HY°î¾oß!Œ#@ Â’Ò¸p9è툇$«±¢PÒÿ¨9ƒbðuéPË‚Q–]BUo{Þ¦I\rÓ(…`Ý2\ÛPŸ3øßGþé¹$â3/ ÚlµŠš° R6Óƒ* Å„µñMR¢j©M$ºšWµý‘#H,´7.Èá*%…7JBÓ­Îù¥“,Y‚ù@EY„Ü'C·Û½*×UXFÉ)¬Í™E.J›uv@Áµ$Ú»´’w€ ZÌqµ) 'GNLÞêÿ2`}–Ÿ“ò~U•ké6çøŽ¡Ãäl<•CöÀA_7ï´ÔðÁ!bMê>zRö. à[ä'«ê–PD‘ %¢oQ梖Wo*P–m±Sx&$¨Å ·ØäŒe›sÌ ‘¢Í`X“¤ ‰Çñsbl‹Èža1ë ³åž HÒ̧²,ØÍèäK"üjÌj0P/É"[¼O|ûKŽtÈ”ðÿ¯ðÈÉYànAwt­ËÖj± 2°dÌèÔ œl0FP…qšŒÆµÚ !…Š%›lVYݺÀ›–¶¡È·<¢ÍÖ«HÁp v9ü6 3‰še”L‰ìqQú‘ÕÉ…RÔЭ=‘‚C„“¼`PBÖcªOS¥Qd5ØÖ·$ãÙ0kJµ'*t³"ºIÂùô©?8;Eˆæhµ³Žo< ?Lr'°[{~TqP—3A\‹ôjMec 7 ëE‚Rf¡cfÇØY,¢Í‚â©4XŽf‹[8æóÿYG"ÉËt§IDl"'⤋› vWÙKÛƒù?ø­cæÑ÷é8Í[ež-‡¹†[Ôä~Ð(È­¿J abˆ‘F0tZÕ`uÈ ¬l LXÍ"•H˜Ìˆ1r:mP-H@8©ŽƒPY$8›[€^ʵTå|Wa9ÃÙÿáÿÚ óÏ<óŒ0à 0óÏ<óÏÓÏ<óO4Ry™_<óÏ<óÏ<°Ç²Ã~ÙT\óË-oZsÏ<óÏ<óÏ ±Ï<ðË ¹õ<óÏ<óË<óË<óÎ4ó‡yÓÏ<óÏ<óÏ<óÏ<³O<ï<óÏ<óÏ<óÏóÎ<óÎ1ÓÏ<ãŽó(â<óÏ<›ß<óÃ,3Í<ñÏóË<óϼóÏ<óÏ*E…Á0ƒ!ånϽ_ ˆ˜‹}¾?ÇŠîe ¥öcëJ—B›(ÙˆÜn4™~f'ˆ¨g 6_™ÏèD¶Œ6ÑçãÂSCpßÒÝe=óŸ“Âeä¹kÊ,»cÐÿÿÄ&!1AQa@¡‘ð `qÑáÿÚ?ó Ù.,¾1;èÓ2F8½çÖ¢"o›ÁçT°»‰Ú¨å­·¨Ðb'ãoÅ sK½%ŸZ œõ¤!㎵°° ¬»·ÏiQÊÍÊqÅ~Òlþ½®\“õ$@5—k ;Û¬éy£øï=øF„Œævãþë:2äéÆ=úÖaJU~Ìw3Þ4ÌȾ’(«vžV˜„ækŒ߯+Ñ‹ç ëEÈ;¬ä†ŸjtÑ1xÎß,&óêöÒ@Ç¿Ö3ë¿t¢u0K Å7ë~ƒ³L‰Šc'û«Š0ɾöî“ÁÿÿÄ*!1Q Aðaq0@‘ñP¡ÁÑá`ÿÚ?üd'5é˜!|ƒ8tò}Nü KØßðù¡ÀO € aXƒŠ*0»Â0wZwB摆Šo¯wýú÷ߥíà¥Üï¬7˜Ž¿Çšÿküy«6v.ñOÆ¢$8†1è3‡€F/’:´@1 ˆ·(XwÎ:téÓ²è$ö:NíÞ%Nû7Ñr·)íD2À™uèó¬º þÎÏ[ËO:‡‰0µ²µ¶]²Ú€ëð!î8tž%=ÀDxtËÂý¬sYúoà zÞ_޲Ž:³rHÆù¶Ç@MæB«v6šT@Lž ˆƒEbàñ¡‡LÙN âUåÖkìÄü,¿mºc4ŽfX^c2DHDó¨Ö/̯’€n¨tv€[-” cñ¢ÌdhÂÉf¢\·ÐV„BW#ì_¹Ùëyi”!6ˆ Öå»§^Šõ-PÀ«\~=Ç“ÂD§¸-Ž€‚ YÏO[Ë£eå9LÈÄìÙÆªâÆBà•ìOé·B°sð®4O³ºË`‰…nt;Zošø"XÑw‚CM$)Êš Á”ò_ ?9<è)…ZY]¨3|©Æ…v½æç3ÌÙÒ€¬ÜŽ.Ô3n4"…-T70¾ZùÒ Éæb¼¯œâèc®?> ¾V‘w4ЩÜòà ”É“ÖúyÙ!— Ø8 !ÊѲxsµ 7 ÒÏ[[à ÜæLü³.[û“ba¹Ûëyhe@JHÎén dü({Ž„ýYa1NWO[Ë¢§CÞ¦HTÍÀˆÀõ‰“5 +*)´a9'7Xj•–cã¦q‚ìæŠ¬Ðv;kjM£ÀF˜8¶h)Nõ)+(QÔÕÁpŽmDŦßµ< »æ7‘²èÈ’E0‰ç@B@#6·ÜÚ%"kš¨ZójšïŠ+ËÎMõh"ø¢Œ‘T6ïж褦 ´Û.:'6úŒ)…’Æe4¯Ñi¢Šo9fÚ†PâËv·ÐV„BW#ì_¹Ûëyh­hÅ !I!P‚S`K#•>tóuT°ì.Vèc.Ы! ¾Ìt>6,Îgc"rPÍ„Á cSš¾[1‹ƒç_'ñA6\3½ÓP7øSƒ2E2tõ¼¿'‰J¤£8|Žã7SÍÛ°}Â7NaGhˆØóã£b`-’ìž:z¼€«º†ëårôvó‰ûüÿ›ç¤Yñ-àwßPÉa‡ú×Ë2忹6&[Þ·—b" Žã«R5¢…ËÐèðä‘7:ã#–áSœaÆ5î8{=o/Êu’+–!¨v÷¶1Œ\Ĭ !÷:”QqÑ¥CúwÒB‚&èë‹õ¥zÂ…ý÷zÞZŽW%È!J¸˜Î k3Ÿ ^&ˆ@¦%®LÃäuz&kÎú¡$°æ&˜ÂÐʉ£„D¢Þï¥ÈØsóîÿZ &cÓ+¶Ãè)àÑ·È|iLÔTrei”a&ÖÈAMzÞ_š6Ç^% fpRwÊU1NxÍgÊÛV/_"ƒØ-r÷zÞZn`Àrà‰A2!“6ŒìpÀñØ…UBx9qp]õ!@ìt6€ÐZ˜TˆÕ‹¯[Ëù¾·–°“äÇ!qøÝÉ¥ è‰+KF€Cµ†€1ç[í|Ï¥4õ_õY% 4 òÈdÊø¹Óü3Å&¿K£ŠÑC8`¹¦½o/æúÞZi\ IT`*èМÂÔL ˆ˜G½ Ô\å9œkÜpözÞ_Íõ¼´Žp'̦ m£aM öRn8ÜÙ‡rzÉ@Ùy¨>%Ôeâˆ^eÃã_×÷ßûäñ¢$ÄÝel+ zÞ_—$·c¦,–£~‘ðS&Èhß"r:Õ¥#˜ €Û»ÖòüH{Ž•î‰Ì{6°dàéëy~2Íåi@ýΘ÷ŽÞ³P™€ §ƒ·RW3Ê \«Uò¯g­å¦­±7ç WÕ¦ÓJËÙòÁÞ„mÊ|Dcæa—îŽÀ%Lxé)1tƒŒoO[Ë£çfA\šî…u^Mæ+>@"ÇU;ÀÁÊ:˜¤ÐhA°äу(®dɺ#F!Š’‘M“ιyÑA¸ø G²š¤B3ñO½%o¹f²æ1P8Ñ9YW.\®ú÷= ÐСWÁzzÞ]4€Uº6AÁ¥ÖÊ™PYagÀd8PA`ãMqTƒªD2Àí£@콡˜„RL¥å9)Q$¨°‚Š«°La ƒåh»û€Ûô'”c3wMé~ÇŽ† Ûò]¸;sÐŽ®î€DÞm#8QL1R˜gMÝ] ÝÜ$`ÁËÑŸ$ceD-ŒÔ9ÙH}XYauF r…Êü|¤®g”¹V«å^ß[ËN ãš@*vL-þê/÷Þw3Ü‚’ Jl”ãùß§¸áéS§D¬VA"¥µO[ËòÎ.ò"šr6û«wya+ñpV'‹‡íü>{½o/À…+XŒJ§aٞㇳÖòü£HX£xÓJSRÈyOìÀŒ™¢$"yÒq—‚ÿ¡;jªª˜H×íÆ%Âå@]gÃÄLò`¹åÌÑÒêÙU_*ªµUîõ¼µûð_I±ÈÎE¸™¤ÄêvˆXr€ñÚ€Áâ ÆoZ%1À*È W>z4à˜Z>S:ÞÍ(T‘‰”t½…ù I õ¼¿3Âø³x‚i-3¤†µ_Ù/pumCrÄ+¬õeâo:),Ç­å¦)¤¸\”F&æ5±ì°0èÔ\>b6[¸øGï ÀsB´0¯Á¦×‚ù¢”š60í»HnØ)”=·—ó}o-qCB,Àˆ&ÂéÕhcAM2@0>{P3ÑÌRY¸œ=eÒ-2û} ¡e›‘yh¿© d|šõ¼¿›ëykVYÏ~Á9Ãm\,SØA€ôAÕ€„Þ,Á–Ï3ÎÚµŽp̪ČO¸Sø¸lxI6SAãD2œ‹.S%•Þ|ÍzÞ_Íõ¼» \o6Î_'=žã‡³Öò쉃ñ(X…“ô3y80È`§æÊTŠH*<䘦s `* ƒ|ÌcH ©aÝÌ ±Vñ­…“Tb¸º޼×d,nšlƒún*Á‡èÈ”€NDdè%Ô`ª¥À«ª°VŒËñ'fs©´–TlWv‰":!ž‹(: ¸èð¥„1ˆB–]I‹Lr±Á~ì—„ú.QEÚ‡àbé˜õ‹R ¡ç\«§3ЪË<…hÎÉå0a©—³ÖòÓ8r `¥èh^<ª¿£ÊSMv“©8ýè·+aŒ¸ûÑnVÂ#q÷¤…Ê`ÆD˜+šõ÷=ê\PÝÂN:Ê´6ÂK ޽o.ÈÖùþ¯jã–/­•‘l×ô”bè|‘ì4ƒ ±  ç+t(Œ J@ºEEuÛ³¬†êÒ@ª«½Â‚e[=¿|à~´Æ‘i—ë ‹î@õ©mAŒ ¯€»‡EHœDdTφg®FÂŒ¨à¨di6 V @nKúÙW CMÜs"ŽGv¯‰¶¶îè:~©@ŒmtT8T¸e4EQ  _©\`QQ½²éKd½®8’”7H4ß 8 2­W[ìtè¼É!ýZD…ø3×g­å¬&þÄÎîIÝ7ç–_èïCÜpôsqšeòËO÷:#ƒvÇè^·—IÝØå–ÆUHh®˜Mvª H…ׇAø‘Hè‚dÕ,UÌ”-ðö.ÉD’´_Ðeýizå\ôN3‘(õ€Ã;'”8Á„"¤Y-E³ä!X1'Læ-î-‹XW²Ã‰ªÕ2Ça‡8t)G‰¢1JxM<öä&¶Y ñ9!°à|h;–T2’0NɱÉËuþ]¯òíQËIÞ L‰$Ê)e[Ð4¿´ˆ>(O(¦LUÎÐ…¼ ¡®ÅÏ‚ wpU±>5_AæIÍ .„’´_Ðeýh!´7]QÌ 4[xÈ "gŸ`,NÍ3ȈDÆö’Š2¢ÔTv÷'„€`Æ®•î$A X,º.–û:/2HV„™u`µŠJ€ttPÂJÈ…&hþDK!®€Ët—»táaY°g&¨Ã¼¡B2¥åº#GÖ›Á@¶·K(yS«©L"B`Êl8b“Epd©|ô†H-4H D QBêkHÔ›hF;Q‡yB„eJË”ÙË"“(( ¡© $$–1&Eã[Qæ$ÔqlÊQÊšŒÙA цÍÐÑðÒÓ¦‚à†S ηÈô‹9׿“€Á…6 æBBÛ{8˜‰)=Ä`aõ*nºüh »À<|3_h‚¨DM:”üèŠâªdÛs…¹uÚB/+ÿ…ÿÙnut-2.7.4/docs/images/cables/mge-db9-rj45.jpg0000644000175000017500000004227512640443572015432 00000000000000ÿØÿàJFIFHHÿþCreated with GIMPÿÛC  !"$"$ÿÛCÿÀ=º"ÿÄ ÿÄ`  !1VÔ"AQW”•Ò2Ta‘“–Ñ #367quv´µÓ$58BRUt¡±²³4Sbdrs¤Âðñcƒ„¢ÄCe‚’áÿÄÿÄÿÚ ?ý–ÎK›˜Î½§V„±œg‘ n\›D®[ÔÚÜ[>ÕmŠ„F¥4‡.i¤¥ÄˆL³ƒò€½#ÎKê•£óšGapÚ_T­œÒ; à<á´¾©Z?9¤vç ¥õJÑùÍ#°€®?fÖªÕUÕãV©piÓ)³SH‡9R›Y :J%©¦Ìã|]ù›9ëå±ëV} ï™³ž¾[µgÚXO¾fÎzùlzÕŸh;æl篖ǭYö€V“ž¾[µgÚù›9ëå±ëV} €$ûæl篖ǭYöƒ¾fÎzùlzÕŸh` >ù›9ëå±ëV} ï™³ž¾[µgÚXO¾fÎzùlzÕŸh;æl篖ǭYö€V“ž¾[µgÚù›9ëå±ëV} €$ûæl篖ǭYöƒ¾fÎzùlzÕŸh` >ù›9ëå±ëV} ï™³ž¾[µgÚXO¾fÎzùlzÕŸhzEÚ-*S1#^öãÒu-2Óu6T§¥R”‘+&ffDD]&d (½Ÿ¹ØÚ’¦B\æé‘Hß¼Ü×´²‡$¾—ÿr’wO’HJÖ¥šIë,éÁõqõq qµ6´’¢2QA‘€çôº¥ÌþÍŸ~—mN¦íb|R[N4N°ÉOyÄï””¡´ø)3ÆI$eŒ‰Z%R­Ìû=ªT§ÔSv›Kiꉲ”úÖâPì£&]4¸DG¼Q:•–”xfÞTeÙ)°aS`³ˆ‘A!¦l†Ò]”—!”/ž¹/(gwÉ÷›ÊµçV5kΜx´ã9㑘žÛôË’âj¡R^ÐîJrJ­>;q¢F§M6̧ZA'yk?“RŒóŸÀ:–Ù‡ïÿǵ_ÓßáÜ…ÁçNðôjWbä.:w‡£R»°3í0_QíFïQJ3#JÁðèÿ"=˜Ú•×¶kk¼ÞÒîÈÈrIe¸ÔÃCdl É)Õ Õ‚è,™Ÿ”Ìu%*I¥DJI– Œ¸ Ù^̰[8³È‹ÿ²Fö;¸<éÞJìAÜ…ÁçNðôjWbõ›1óqgú’7°ë6cæâÏõ$o`¹ ƒÎáèÔ®ÄÈ\tïF¥v ïY³7©#{Þ³f>n,ÿRFöOÚÖ­uwØ„í.ìiMÕJ–˜Ô̺g 1êVa™gIá‚ÂK†rgAÜ…ÁçNðôjWbëvß [܇oPé”xÎ8n­˜Â³"#Q¥DjÂRYéÁl€G÷!pyÓ¼=•؃¹ ƒÎáèÔ®Ä,ÆËæÔ'ZZꕪ2˜©T"—Úq Íy¤‰´¥ЄçJHŒøàS‰-’ýÉÉü{Xýe$V€ MéÉvuj#3æSzžûh— §~9©µ8ÚðÔ´ç$IðŒÈˆ¸Ž{kW«TzTÍ嬘»)†b9Ó˜þâ;Œº†%ºÃffûx3ÝëlÏVVYë›YU ›ºF¢É:³ˆvd£—M1lÇŽÆ´¼‡ÔŒ-fá¡‚_¹Y™#ìjQá<š‰Ýo¦˜ÕÌŠçtM«[¨˜šyÀÔÞÿQ«÷:‹s½$dÉÃN0df]Xr½¤³swÉ¥¹B)Ò õÒÒD…KCqnj×1y&Î2÷‘ÌÒ¢ZТ&ÓJSd:;/8uÙQÍçM¤Eeihâ-(Iš#Q<~ Ìô‘ Š4‘Ÿ¤f€“±~ê/ßÊ¿VA‚NÅû¨¿(ýY`‡©N½¥ßµ* ±@¦Æ 4¶9eéJu.)Ä+ÃL¦‹RTÑå$ž¶Ï&jÁdsvÓºßgüדÛÀXæí§u¾Ïù¯'·‡7m;­öÍy=¼€þnÚw[ìÿšò{xsvÓºßgüדÛÀXæí§u¾Ïù¯'·‡7m;­öÍy=¼€þnÚw[ìÿšò{xsvÓºßgüדÛÀXæí§u¾Ïù¯'·‡7m;­öÍy=¼€þnÚw[ìÿšò{xsvÓºßgüדÛÀX™7h‹†ß…:»lT›ªOäî1‚üwÒZq×Ös$‘xÉ ËRÐ\2/€žÖ>åbþ=£þ²Œ+žÖ>åbþ=£þ²Œ°¶Ì?x'þ=ªþžø©Œþ­%); ¦šLÈûºªtúÄÀ³~AúÚjR¨w¾¥þé‡Òèº?-íeÅ–Ü.â%¨‹ºY¾?õ•€þ±üÏõÆÌËb4c#2>éâ_Ѥˆÿ­œ¥)«ÿRŒü*wIÿIû ò‡oÎ8[r¾ËZ±Ý îÿÇXý·õÀ Óõ?8i3#çXÝþ`?A€üOõ´Ô¥WomJ3ýͤÿÒtqªék/ª:ò"Zˆ¹[}ÿ‚ØéøÎÿTÉ™}D«23#æÚOüØã‡ýn%­[d®’”£.çœé?õ†~øüÍúµ´ýSWq%j"Õ ÿÔØÐ-‡™žÅ¬c>žç)ÿ£6ï²_¹9?k¬¤ŠÑ%²_¹9?k¬¤ŠÐ'býÔ_¿” ~¬‚+‹÷Q~þP5ú² M¯LUÿoû+UšÚbU™DÚl}Iúz#Fpµ4K8ëBÖá ÒkÔ¥$”fI""Éí(Ρ^%iϪ?S6 ³énÈð¤4†VÚiÅ‘²#}ƒB+<¬”g¤X4.è,Šc6Û6¤êõ& IšSôÇã¥mÅOÙylÉHN”’k%’u+I™ÙÛÔúµBårê¯BnÅT:diqØÌ­I[ÊuiÊMn)¶r„š’’e8Qš”ÆûÅ.â¶.Ÿ(0é2Ö£3Äy¦„$’EÒ£”ˆeŸuŠñ«»hÍÜ6½N†ëîFLø®0OµöƤ™%Äy“ˆüFDcÆÅ¬»pZ4Ú´–c¬é›+ÔQä Í³Ÿ¡Ô­ð¤Àiv©5˜lÒ“V«K£ÛÎÈYT¦Æ¨úH›Q¶‡NÃjQqp”ƒÔ”#WÙ0z­€ÜÍÝ:Ę2gH¤F¨%šz¦ËL§ÙÆeÅ Þ%,Ý"SŠÂ”¥+‰–OI! u‡CËeJI¥. ˆÔƒ2÷E¨Œ²]3áãÑ‹¾×·j Ô-kª­tF(ï^!T\¨™,’[³ðÔ¢Žù¹„“Dm'JžZ’IdÍiîÛj•tÓ ŸVmóBMJiÆ_[N4µ6¶ÍIRL¸éqeÇ%…HÀl ÈDÈ,Km*Jm.$•ÒD¢Égãj·Z$"½"EÍ>>Ñ-Jjn!/¡/(¡ ¡‘èu—[&ÇM 4ï=ã{¿±öDit¨”ØÊuLÅe ¶n¬Ö³JHˆ²£âgäe­]qéWm*Þ‘ j]QF†D†uj$©F{£Y8h"G%'Ež2Ú$úc7dx·Ã"Þ¶¹ò,†ê®S›‘/xd´9!µ¡I4 ›4#ZI{Ç ÑàSV-¾u­BŸ.³Q8Ðä"KP– êÉ*Õ»Þ—É‘,³Ð~ šO|ceÒê3lØïÔž’ú¹D”F~J4:üT¾âcº²ÁxJd›VpYÎpYÁi6»tK¢³بөõ ƒ«Çq£SM™)Çt$ŒÜ/±¶²RÉ$hI©e‚N¤ô!9&Ô'ï–n³¯URë1ù;p´ÇTd#&kÓ©£q&³Ó¨Ò²3Ð’èI ÌçS߸êq­[‚U~ØDF‰.TWPi¹F· hnRÔµ8f’A©ÔHðqxW…h­»^¥^U>]A¸Õ!ÈñM²qHIeF[Å%< &|s‚ƭĪUdÒiÎAt¦L£xËLÜ2Ö…§Üj#3Ið3ÆA©¢:šöÓ*b,Æ¡ÀnœÆK™ ¹2ü¥»(X?šËËœ›ÎuJEv‘iÒ'®›"¤Ô‰r&¡´©Æb°m%ÂkQwŠ[ì¤DdI5ž2D>›!¤Ì¤lú˜Š™¾u9iTéÛý&â_}Fâ£IOF­‚" D2/ MMÊ•2ã ¢;µZa:×'³B%F{AºÖ²#ЭM4´«Yl’x%ˆ5ó¬¹tȎδkÕÆªè-â©UäÍ‹)EÇtâZÉ´¨øš$)>#Ái¹øÚ7üà9WÖÑýý½ÿ£Cþó£‹ýWÂ>òþ–ßø-ŽÑõ´oèÐÿ¼èâÿUß𼿥·þ `?\}S?À‘‹i?âÇ;ëp}ù+¿“Î~‘wªgø/ñm'üXã‡}n¿%wòyÏÒ#€…úµ„ÝßþÔOÐØÐM‡ýåloÉÊèÍçßÕ«ü&îÿö¢~†Àþ‚l?ï+c~NSÿFlßd¿rrÖ?YI¢KdßrrÖ?YI NÅû¨¿(ýYV ;î¢ýü kõd€$m:MírÛØ4ÇyÆë0ˆ‘¥ Kú’óiþR‰öœyGþ²‘\#vˆë4JÍ·x<ãL± YÓg<áðLY††øx‹÷J!™¨ú•€²§îª×ë%Ó›öƒº«_¬”NoÚ4åÛr&÷"i”Î瘜äe²o/•›(|Ù\‚µ½Å4·PK3-.‘’ &i$éQ– IÊ‹YrV­É[H·JØ–M$ôÉên¸²Œ¦F„2¦‰ÝÙ)n<…šrieÂü”:~ÍhÓâK‡^ˆ¢‚J(ß®©æ!e&ƒÜ´· ø&¤ ÁB Ò“4€¿NÍB%*ÕFOŸj ÇEĹ#doH% m³dÏÊú|£oÝU¯ÖJ?§7í9õ7Õj]çR­Ö^“>m:;Òw¯*;dödtÖh%'u-¥$œ`Èø)AÞÀIícîV/ãÚ?ë(°IícîV/ãÚ?ë(À+rÚm—d^õK‹£gôJ¥ÖÍ…šLßKê\fdoŒÍÂŒ¤`Ëĉôޤ4ô f“B¨ÕjäLLŠ´‚“4ÞžûéqÂI ”IqjJ<¥> ¤ºXº®Ì6lQdÍ¡ÙV”$ ÙoMQÇe¦•œR‰*èøpDFgž<{wd,®ÐiÕÚŽËhÔùÕ­K‘øÆn0ã‰%©µd’z’fdy"<—At ëªÜ¥\ôö V)qڒܤy¯FQ:ÙêBµ4´¨ô« "3Á))>”‘–ÆvâDf+JuM²Ú[IºêY‘ RÖf¥”ÔfgÒf`9&ÐìÛb‰NŸ³ÚB›P˜Ón¶âM­Z Z´nS‚Q%kJœÉ¥jÒ•TRöC³*Q8Të"{æí‚-XÎ3ñŸÆ(*ÖÕ&­TP¨&cëŒãn¶ÉÎx£ëmZÛY°KÝ©IVFi3ÊR})N78m©bìŠî¼kÐËfVáÅŽÄi±giÖ©È}o Ü4é"JML(Òdj%¡hQjÀ¢Úedô”ÆÍé•ç&2 '&Zü¾†Öæ–Òµ™6…©fIIžbÖ‹i[TJ̪ʼnÙl7÷#´M’›mKRKIx%ÅjÉ‘Ÿ çJq±«@f§pä9)¶Ödf¨ÒœŽá`ÈË mIQq.8>%ÀòFdžX;6Ù\š jM—@`å$Ò÷'Œ´’V…Vƒ%¥+J’¢RM*JT“##"22½§ìãdÖýEÅ+f”Z”§$ÆŒ„reœq÷›az¥¸Fd”©FDdDg‚>§B¤À¡ÒÚ¦S6c4jQœSŠR”£ZÖ¥¨ÍKZ”¥)JQ™¨ÌÌÌÌÌ»I§×)«§TØ7£©m¹„¸¤)+miZ•$ÉIRV”¨”FFFDd ذvmtYì8ýŸA~:Ô¶\i¸îi[.©µ$ÒâR´©*A‘¥I#JˆËŽ257Î,kJ£KvßÙ­ªáÔf±Ó¨Ú}ZÜ#pÐ’I’‰¶’ãÆY#ÒÒ‡S Ò)ô:b)´ÆTÌd8ã˜SŠqJ[‹SŽ-JQš”¥-JQ™™™™™¼ºl)uSä°NÈ‚¥®2”£ÃJZM Q'8Õ¤ÔXÉ”DdJVB.vÅvQ:RåL°hO¾¼jqqˆÌðX/ê"v$ &±vȶKgÔHqb¦r[j3ÂbJLb"ÊI.³¨÷f­Ñ–‡0£,ö±¡£Yöý"¶åfG‘1| ‰K–ëˆl¤:O>M¡J4·­Ä¥JÒE“"òÙQiTÚ-5ªm&¡´j6Ùe”$Ô£RŒ‹áRŒÏá3€NÅû¨¿(ýYV¦éW•*ã¸fQÑ@‘­=¹ˆåo¼ÛšbÇ`Òd”(‹Îðµ)½Ú7¼mOL‘û ÞíÞ6§¦Hý ±õy¶Þel¼Úmi4­ ,¥D| Œ¤„¶÷hÞñµ=2Gìƒ{´oxÚž™#ö@6ýÎ[ßÌT¿Doèç-ïæ*_¢7ô F÷hÞñµ=2Gìƒ{´oxÚž™#ö@6ýÎ[ßÌT¿Doèç-ïæ*_¢7ô F÷hÞñµ=2Gìƒ{´oxÚž™#ö@6ýÎ[ßÌT¿Doèç-ïæ*_¢7ô F÷hÞñµ=2Gìƒ{´oxÚž™#ö@6ýÎ[ßÌT¿Doèç-ïæ*_¢7ô F÷hÞñµ=2Gìƒ{´oxÚž™#ö@6ýÎ[ßÌT¿Doèç-ïæ*_¢7ô F÷hÞñµ=2Gìƒ{´oxÚž™#ö@6ýÎ[ßÌT¿Doèñ"ƆÂcÄŽÔvS-´‚JK'“Á‘3½Ú7¼mOL‘û ÞíÞ6§¦Hý °›Ý£{ÆÔôɲ îѽãjzdÙ«{Xû•‹øöúÊ0ùÞíÞ6§¦Hý×שwåz4Xš¶£EEJ·\fCë^–%4ù’HÛ"32oOÇ`'Jç[÷ºE2‰> Š|†£O”ÛŒ¡¸î8„9§ Y)Z[u·‚÷+-:•”•]­»¸%ÕéµÚ­,ç<Ô‰±£¥…5%Ä! ëVñ¥¨ŒÛm´•'‚ <™†’§µ: (5©j$ÓI¨1 dµ´Ñ;½q‰âRÖIKdòAš'öž1¤Ïgi_4Û‘ÚcpâKhê1fIiNf’Lia~¢Q)K%!I3J“Ç#?'—>R#àY‡žDæ}õ#üeÿ Ÿì?ö—îŒ3€LÝ5Šº.ZM±BT(ójeMT¹Œ-æšj:˜B’M¡h5-JÞ<"""QñÁëk;CBª·FŸLŸQœ–Nª”Ò^%HDUÉS)F½Dµ!µ¯òŸ’Žž»C¥ÖÛa5Æâ£¯xíº¶eFF“48ƒ%$ÌŒÈðe’3#àc\v=¬rÓ(©(C¨Nhui$žàãë"%`œÜžïxE«IgX é›X¢Æ‹â.ZžiçœD'Y|ÚCKJT^ ò¥å\I¼‘ VNÔ ÒéON¨Êf$VÞ^·]Q%)˦E“Éô™‘tøú  µåeÛdLš`:—šRÕÊ-â}Ízu“ŽêÖâU¡JÔdz’ðK¨?iVιýõ|ÿ^3é0öI’’JI䌲F5W bM(Ø(öõZ¯½Õ¨à“?bÆ=ÖñÄtç†3ÐyÇ í€§uõ/7÷gÅ´uõ/7÷gÅ´ °)Ý}KÍýÙñCíÝ}KÍýÙñCí¬AWv”tD0ª…‰x$ä-hi GŽòÖimn+ CÆg„6³èãŒLÈ6-í.Tf¤Æ±nwØyãN¶pÔ•¤Ë$¢2‘ƒ#.9!÷iHŸ¶Ò­âUE 7 Èü9l…ë#/*y1þðÇÛe r5œŠK6ÉRf˦°Ê ¨ÌHq¸Åò dþçÆÝ}KÍýÙñCíÝ}KÍýÙñCí¬Jw_Róv|Pû@w_Róv|Pû@«×Ô¼ßÝŸ>Ð×Ô¼ßÝŸ>Ð*À§uõ/7÷gÅ´uõ/7÷gÅ´ °)Ý}KÍýÙñCíÝ}KÍýÙñCí¬Jw_Róv|Pû@w_Róv|Pû@«×Ô¼ßÝŸ>Ð×Ô¼ßÝŸ>Ð*ÀÁ´ÔÐXKÕ;ïm*K‹$µ;«ÐÚ kV”>g¥)IäñÒiIeJIÉ•AÄ%h°n¥!E”¨Š‘——ü |ÇJ¦ívk¯0“n‘Ba¸ô.[È‘OàÏÂ>6Bij‡Lm•´ÍDª;³3Q³C‘›Q™ôšÒUùÀ}»¯©y¿»>(} ;¯©y¿»>(} U€ Nëê^oîÏŠhëê^oîÏŠh`b%ÕP~S,.ƹã¥Å¥ë¥CdgJÃæx.“Áü)À È‹'ÐB%w9"Û§»o;SÕŠËð¡ÈW‡µÏ)n/BÌÔIKkÂIIÔ­)=Ö£Ñl4­S Ö(2)õH¨“É(Û^x)2¤(JL”•%*Jˆ²“"4©X%˜ië7‚íeÓ)õô•Nl§P‡]¦°M¥¤¸ê[CŠin)iFVY=F^ ±“ÂFö«HD2utÉÍÈ\äÂDGe¡fÛ®õ¼³à°áádF|1“3ÆýË&ÙqQÔõ4Þ\sI’ÝêÔá¥Íâw¦j3wJü4ëÕ¥\KdÛJJÍT÷ò–…¦J¥:r4ÉÛÆ­ãd’uÒ$¡DDN¸DDKVCk@©5Y¡À«°Ä†›¹jCzl–’Q%iñ(³ƒ/(ÍQ#µ+QXI¥¦PM F£$‘`¸Ÿü&=@x@ûB±þuÏ'òÕä3ÿ¯t¸ñ…ʳŸ¶¹ÓŸåŸ”‹þ¼gÒaì$¬—Õ.î¾^q%®5]ˆM/Æm&WH¿2ßwã6C.Fº¯–Ý_Û«MIeF•"3ø m»ñûìÝmJ‡[ª6i3™]œKÇò˜xâÿdr4g-±ÜñÌÏvT*KÄ^-Jz “?‰´ü@+ÀŒ‰ÓßO<­M9]l£žrZN†FEø'?>GņëÇqß1,F¯ £¤‹I]>ª?ÎãŽá3 ”È\»v£)Â-â® »feã&ªOþÖÒ_˜zQ$!½§\ÔÆóÆN¨/É­ÕIgû±R¤…Ìcü®OA§üúüŠQ^|¤“ÊK%®Ô§¤>{Émš£ÐK|‚kt­M‘’ÔF¥imÌ!Y=FXú´'jUȵ}ÅpÂfm¯&¢‰¥&^å©%¸ÝF·ÚI®½Ys²è]NRˆþ#ŒÉˆÆÔe/”1ûºˆÉgYôw´ͬ["M‘M–ý›nºä”)óZ錙™-jQÈd1*6E”°Ða&Ï·Š3ÖýMÕ´TÆt©h‘’£-8ÉÔDé”Låq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ¿åq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ¿åq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ¿åq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ¿åq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ¿åq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ¿åq}òÏÊr¸¾ùgåh;ßX]H¶½TDzï¬.¤[^ªcÙ³!3jÛ}”¢UN£1%¬ºšû¥ýñáM[Û~QÈgvõ¿Km'¬¸š$O3ÿ¾1¦Ù™dT¶UiÔ¤Y¶ë¯K¢Ä}Å®–É©J[)Q™™§‰™˜÷‘bØím" ~ãmÝÔšD•îù±:šy‚Î4ôáî•Å÷Ë?(AÊâû埔! ï}au"ÚõSÈw¾°º‘mz©dÿ•Å÷Ë?(AÊâû埔! ï}au"ÚõSÈw¾°º‘mz©dý2£)D”ÈdÌÏD²â=„ôkÉ‹%©1¬ëy‡ÙY8ÓÓJ¢<’ˆÉ9##âFB„dÈHÚÔ%ÓUi¸—6¢\á#“­K-Ìv‰“VäÒd—Ö­)%šlÌü#ÎÊ›*R©Õ†‘æÃ’M‰¶/(ÜpÏI!j24ŸÕ¤óâ#ÊJ„`Ðõr'5’³Ê¤{¢Vq¾^=ÒRxÇÁ!¨°£$hº“²kÁ ØuXVÚ*ô”ƬL}fÿ%{ç¡jQ¸œ*2‰HA¥ÇxX¨˜gnmF*jñÔó 3O]vg+Š— Énî”jL¶ˆÖkRÜQnÒ•èJGSï #0:ä´Ìߦ>®Fé²N©½ê[7tè%8–L²fEÒdG¿5K¹WK×Dj‚X«E‰‘]q…8†R†RÚ5‘/[{Ò%p4šÈË:x†öܹè·–T‰J’m²ÛÎý‰IÝk56½DZ]#B‰MŸ†Œ¢NK;~7 Æ1¾w£çä3ú|¤GÀ§l+=V›³”ÕUɈ©(¥Î'[âìõ}ºA£ÐN#ìEà§AiÆO;™öjtåE”¹£~µf<§£¯‚ÌËÃA¡Xø3ƒ/)q0ð¹nJmº†Þª&b#™œÜGi„‘‘ÜZRd„–K&gÀ²}f[/|[µšüº_"­A‰#»ùåS×!œI¤Û5^lÈdjÓÄZLýÎÂä£Ôj§à]ušëV²§µ{ìã·ì;Œ`ñ§Oº<ç†p8ìû„φÕoüi]ˆ|wqyÖ¼½“Ø€Yî:âó­yz-'±’³î?¾­âúZWbï±×wû#³¤¡Bpÿ ˜AŸöŒj¿ß¾ØømªÇé4ϤKlf×®ÊØý—%­¤ÝQv߀´Çf=4ÐÑt!&¸ŠQ‘t¥ðâf|GÚ§i×Kk–óG´›©N*ƒURdzn¶È¤Sò‚"‰§ Éå&~pdZ‰AÖ€G÷!pyÓ¼=•؃¹ ƒÎáèÔ®ÄÀr;ÃÑ©]ˆ;¸<éÞJì@,G÷!pyÓ¼=•؇ÁÙ÷ ŸßZñ/ý-+±±ÜuÅçZòôZObã®/:×—¢Ò{ ÅgÜ$|v«xŸá‹JìCç¹ ƒÎáèÔ®ÄÀr;ÃÑ©]ˆ;¸<éÞJì@,G÷!pyÓ¼=•؃¹ ƒÎáèÔ®Äé°¼…ˆ~[ržðͽYÅÚ-–Š«v®£ü%&šEý¦$¶/kW$lvÊÎÒn¨m»oÀZ#³šm´GHI®"”d]©Fx.&gÄ*¶µq;b¶ãžÒn¥8»~¬´È8ôÝãd™â4M:U¨Œò“<¡82-D ë #ŽÎ¸³÷Ö¼½“؇Çq×kËÑi=ˆÞã®/:×—¢Ò{ù+>á.ªÞ'øbÒ» íZëÙ}Ý¥]r›mÄ­L;˜HtˆòiQ¢"Uƒè=&GÇ‘ñ`4ÒnZl[Ž- Jf3&c†ÌgÂeçI¥:m¥Ìi5nеtÿEÒFCÒ’ûqèÒ¤´5&ZÔ–PKR°ó†dIA«*éáî³ÀÉ'”–¦¥jKŸ´ eÎäês-ÓT­Ñ1O4Ëuµ2´ÈÞ)­nš4„”Jr{;nq×:v¹¦ô©Nïô—ÖÚI.¬“¡·0–ËÈI¸VXQ†ªÑmyqjä˃ 2^ˆ©S¡»”ºÉºN¥KZI)Òl:FfdDiÆri#¦¦Ëj}>4æPòÒ]B^iM8IQd‰HQ’|x¤ÈŒº ˆÄT­œ5>×U±P¬:ºKõYõ)m°ÖéÇ•"[²ZA/Qš ¥ºJÉqRšAø)Ô…YÒššÅ2+5m̘Û)Kògt—VDZ–HÊ´‘žOĈèǤØVœc|ïF:u«=O—’û#¸«, Ó+Ž2Ô¦–Ói†Û h‰½Ù)4¬µ–ø•¬’fd„éAáÄ:*œˆL4o31Ýô¥2Ìw4™©X5a9J8{£ðK‡`ÀlDm«U£\4Pªr‘MB4)QÔQÕ$ë„‚5'O(7¬‰&…iI¤”¢ÒJÍÇ*ãŒlsR%jßrº’¢îñ:t´æ¬øYèÆ §<nK6‰ÔÛ{ç9ÓhM·¾q9ÙV€’çM¢u6ÞùÄçdtÚ'S­ïœNv@; ûÈX“tïÑ›õS.ü6â|goÕþ"œ$ö7>ügd6c1-JñÑ@‚–œr¼ãjZ :0fžLzLˤ²xòŸHøªTïÎüê•jP“$­ú¶í¢¯8iR9E;R\›‘éÁ`ó¨ø–0aÖ@Is¦Ñ:›o|âs²:m©¶÷Î'; Ð\é´N¦Ûß8œìΛDêm½ó‰ÎÈ´—:m©¶÷Î'; s¦Ñ:›o|âs²­%ΛDêm½ó‰ÎÈé´N¦Ûß8œì€+@Is¦Ñ:›o|âs²:m©¶÷Î'; Ð\é´N¦Ûß8œìΛDêm½ó‰ÎÈ´—:m©¶÷Î'; s¦Ñ:o|âs²ñØOÞBÃü›§~ŒØV~ýö·äÝgôš`—صJüFÇ,¤Cµ(OÆM¿™uÊó©h(èÒ£IF=&eƒÆOSé ­Fü=±Ûn*Ô¡’·êÄÛE^pÒ¤Šv¥¹7#$:‰cl—:m©¶÷Î'; s¦Ñ:›o|âs²­%ΛDêm½ó‰ÎÈé´N¦Ûß8œì€+@KÄ©_«–Ê%Zt&c©Ä“®7^qjB3ÄÉ'µO,ùHT }Û8.nôiårs§F3¿^}É™g9Ïç9"<‘N?S¨/j S)uÇe°ÙêÐmžO £e[²BÉ$áÈ[š¤Ô¤“ZÌÉ:š5m`T”pjÍ%©¨~ßVñøî¥ ÊÜ4èS‰"Y{œ‘pÁ™`Ì7À8ÕµzÝ]ÈTäÍ•(ê‘cÓeÉQf1Iš©‚i<ŸìdÓË#i·8- Ök#$+£ìö£*¯gÓêse"D©)S’h&5ž¦0dJ#hòÑê"VPzˆ•’¾ã"$Y$¢‘—µ¶¦•¼A+Rî’yé#Ádº a\ú] ¶Qyírha˜ñœó¦E“ÐÓIRÕ‚,™‘p.'€åïj¶ì–]¬4ÓÑi©ª>ˈZDU)I' Z°JBˆËIàŒ‹%ß“-%õ¾M ZR…8I-JJLÌˆÏÆDjV á?(ò§šM…iÓó½éÖ¬ôÿß.#Uî I­s;sL³}q›ÞÅu¶žy£[mº¤’ZI 3JfD•yxZ·*Õ«;×=ÖsgŽŸûy8`“”ºc•Fê®S¢. Ò ¶å)”›ÈIç)%ã$\O†|c,cÕ'D¦SdÔg¾ˆñ"´§^u}BK&gùˆ)s¢Té‘jPKñ%²‡ØtˆÈ–ÚÒJJ‹Î(XäP[†eä6KvÖƒ(R_Ú‹©Éj…EAãà}õvp èô“ª•XépN ]ù:wÅàé÷xϹáÓÑÀyÐwK§ºH&Í.Q'A–wîjΓ2ÎsœñÎuH¼£\t©7 š uËvlE$!f>h %ݦ]54™&>œ¿!-­ûÆ_þ²œÇ?]ÝB{ÖŒ}!Ï×wPžõ£H«œýwu ïZ1ô‡?]ÝB{ÖŒ}"¬JsõÝÔ'½hÇÒýwu ïZ1ôа)Ï×wPžõ£HsõÝÔ'½hÇÒ*À§?]ÝB{ÖŒ}!Ï×wPžõ£H«œýwu ïZ1ô‡?]ÝB{ÖŒ}"¬JsõÝÔ'½hÇÒýwu ïZ1ôаËvcq]N[r«)×’šåYŠ¢ÂH´ÔdœÿsÐxÉpTÊÕÔ[a¸^+)õ8vý)*gœYð ”T0¬çž%‚âZxô¯ÙÓlÃb»Ik¡Wf)Â#Ï…!|³û$¤-g››{ÝòK‹°äE¦+‡ñQ úå¨ÓŸ®î¡=ëF>ç뻨OzѤU€ N~»º„÷­úCŸ®î¡=ëF>‘V&bVî—e2ÛöKÌ4µ¥+têL+BLø«y<¦&Ô’æÑc\Q¦Óió~\˜Ê_*©$ã$ÃÅ‚N„¨Ð²3Rø²‚$‘ñ,˜”(õZ zŠ„§!U—23Œ´¶‰,œu 6Í à£ÔfzG«¤ˆòB aQ·œÍá¸jå/ã^¼ã|¼{¾8Æ1âÆ4ø8S±.zÛ‘ ­V„†c4<Ë.¾¸ó‰±d§g¡…hŽâ )×ñêY’K>VæÏ. jK²)õ|õ"º¹±S)N#D^ojM-^–´%¤åGîñ“231Ô@–É¡*Ý·ÓNvRe>¹R¦Hu-îЧ¤HrCºf£J5º¢I”dœ™ŸÝǧéÜ+Fœo÷8ÆwŠÏG çóùxädz·JÕ«;Åû¬çÏ?öòpÀ`%l©˜›G¼)M£HLÊò›­*)ž0ÙÛZê7…U“ŒTnÑ‘ôn#Lj²üÎFp¿0ÿ+컚%ãU|ãP×N~Iä2¥éqOFRð\DR^3qöÒDf¡¨ÙÍÿ³ªE™4ûþÍQxœ›PeøËKrä8§ä%*×Å$ë‹ÇÁ€@}=˜ùdzýwÛú{1ógúî7¶À}=˜ùdzýwÛú{1ógúî7¶À}=˜ùdzýwÛú{1ógúî7¶À}=˜ùdzýwÛú{1ógúî7¶À}=˜ùdzýwÛú{1ógúî7¶À}=˜ùdzýwÛú{1ógúî7¶À}=˜ùdzýwÛú{1ógúî7¶ïh´Üòõ„§IR&ʉWÐ_ÅiÈ­ÅOƨ.†Íù<¥Üõ¸ÙÅNà“¬Ä¸¤ˆ øÎ8‘«m7gÔÍ Ç­Sîº U*[¤µMœÜ¹N¼ÂضÓffz‰É‰ÿIjiá)$wÛ?¥Ì£Ù´È52Î{õEL}­rÝ3rBÓŸ[Šüà7 5öþë;¹Ýiå’s»Ñ[÷5{ŽÎsãÎuxY‡G×ɼÞg”¿zóêñîøãÇ‹Óàà` z~´éÆùßsŒgx¬ôxóùü¼r2Œ-[•jÕëžë9ƳÇO‹ú¼œ0Ø„ÙW…JåªA Õ¨4Ø´óiSiNËqÅ-³VQ!¢Iq"Æ ÏâM”6™rF’2}ÍFzxg9Îxç:¸äN9hU‹jLÝ­Õ!9I[o2äSß¡½ÒR†á+‚^ñÌp7Óžj]:j¢U”íNkå1×ÐÌi$’n7Ù"Ðzw†JÉ„¥‚Jp@<Û¾ì· ;9»ªŒ¸Ì’ ×1M’ÉFƒV(’£#>'Žóú³$J«¢§¤š ”r´¸kQ¡\xêQKÊe‚ɉze›sɆÁÖSH…"±"‡¸s}©âgS®)M6i$œt’H’® Yç ‡ÒãÙå^¥Yv¨ÌölRé-ÆŠo­,É“ KÒ4?„“J5¶D¢É—„f“Á%AÓ@=;O'V8ß;îqŒïžõùxädy&U«Vw®{¬ÿ,ü¦G“‚싚ڠߗ3ËŠKyåEq¶æMm•);’,‘(È̲GÄ]‰zvœ¾nu:Ók3ä˜5$†èÀzÅ¿liRZ‹ó·}å“m4ÝQ•-j3Á%$JÉ™™àˆ…ñLXÉQ)1Ù##É ¸`±*ºZÛsÎI2M=Ê’ZˆGQ57”é:•”ò’qÚÐn©'œx¯¤.¢ªme´Ä†ÜT½+‘»F·]Yºî½hKiJJò)jQäÕ…g:ßbÚ©[<ž'7s‘SÍ[•êÉÀå{Íþ­Ù/VÉôëÒdî­'¾MÅN¦Ñ+²\}r£™S#¶j[ÈA)Ť´©F|RŸ²EŒi"Nƒ—ƉT›@ŒÕ.uãI‚TV9áÙÐjO,ç&DU6†ÙY¥å%I)HxÙ4–•‘©\ËëSxÔ¨eÓ)uŠ PY¡¹3Ðâ·s¥ÆP³%)Õ¶•©¦ð|"AåÍÕ[¾è{2¬ÝîM¶”ü c•DÅE=å ’Û+qLo7å¬ÌÉ$NéOAžïÂÂ}»¢¹iWdJUÇ*œ[m"kTI™®8£$¤'–ˆª%$»«Z±¤ò²IÛk'JÒJ"Q–¤šOó‘ñ/Àcì±[£Q·ïWNå ݳʤ¡­â¿’FY?€‡´š„ÌJ~Dè̳ &©N8êR–“¨Ífgà–ž<|\@dŒxw ÓŒoœèÇNµg ‹éòäøŒv«”GknК¬SÜ«2nÁL”„'ySyÔE…$òeã/(ʇʵjÎñΜÿ,ü¦G“‚ìÁsa·¼×-„îÝC+Ë„Z\^?"•­.“ÔXé!¯î¦ØÑP_tt}Õiž®ZÞ"žL°ïòF^8 ¸ zlèU(-N§L2#Å©§ãºN6²èÉ)&d˜|ªl4¢JÕ-‚LCÄ“7 ø$¿ù> ’¸øŒ ¸„©*I)&JI–HÈø%>“uEº*J ª)ÇžÓ$´MiÓZÙ(¸ƒ##!Z5ë®Ñ\E uŠzjÎ#Z œ”…'y&ó¨ËgœxŒ¢"/þTÉË“lr}âwÄÔwõš3ÇIšñœtdS5¾Êm…¼Ú]tŒÛA¨‰KÇN njĭV¨ÔF~µVLiÕîÛr\„2•«ÒF£,žøÆ;b‚w?9¾qÞo·º•§{ºÝotgN÷uö=æ5hðs§€È eÍÞ<®NtiÆwëϹ",ç9ñç93<™ý¬RµH-Ëœ“TF!$ä‚"Éšœ¨ˆ¸ðÏö£Ê"õëÏ)ÝšÌñ½^=Ñ™ãÇcH‹A¬‹f[1©© ÒÐTÙÑ•èŠujd˜4šM” ÏKmáFZI/€d̶èÓ*íÕeDS²[ZI)åîµ£Ü,ÚÕ ÖžQ§%‚Á–{rˆuábžubFðàò”r‚N3«wXÇàlÕÅ¡´ÜZP‚éR@>ÀÊ¥1¶g¼åF"§”å©ä’bá´¸{ÃÏ„)+ð±à¨ Èe‘‘–HòFiÖ…R듘w©Lólø>ÒpÌÞ&‰(RJµ ô«V*àXQdEVm+–󴮺|$³F[®%µÂ—ÇZ’ò)Í6M¡Õ)³R÷t‰DjhŽ ¶€Je¹h<ã™™’á¿]©%É* (¨eÄsZÔÒ‰M¶fg§ÁA‘šòFU)jk‰dáKa†Ñ)Å>•°NˆÞ+)##I$þ+áÉñ=óŒÚ›lÒ¥j3ZÕÒgÀÔf]&~#üL7½^‘Y¨C‡·aÔë´º¿-SéJLW"›­­>èÔiŠZ $df椓“ÓFÙ›­SdÔª««Ô*mÔg½49Û8m½Tå‰[*RJ^[afO)E”šKfGÖÀŠMÕ/f V~{Õ’’§ Pœe8ÑHRÚKºTÓjQµ $) QëÒx2µëRû¨Ñ73w’MêsÑ×UÖ[n[â¶Û»Å/^å·”FFkQï‰zV¢|sª5»tGÚJªSfTNžOoÜÊ#ŒlrD·¹Z¼¤ÉÒ5‘!¼d‰Zò¥ tPʯûVð—S¯·e=6’ªËNd‡e´Q]yTåGeæÌ’ry."9e&”hB•ÅFD}Tr‰Ô ª¥WX¨ÑêODv¨ãñéíÖÎ;ì°¨™$¥m¬’Ÿ³¶âÍ$²"ɬ²¬ÑϧܑaÚ/ºÏ?TiˆÓQ[n!ó‡M©Â%™ gœxˆú ãÙ¥y…S£FZ£ªD¸rg›.4¨Q˜Uz{Q[ÔIwìdòšA¥$“$£ƒdFGÓm榮º‰qùúRy:XN½ï)pÍ{Äé,c%§FsÄԣɞècS˜r4u6êÉj7pŒ”£à§¢/Ìø‘tà±À’X"tºÕƒT· R‘½~±6¤Š«sÖ¶_’ãÆÚL¾ÊÛæÛ‡Y`’G­+àIúÍzϦHäªT䦕XæÈ‹¬¹¼‹-ÓŒp·‹JÌ—¥M¾¢#5%½d‚É$ŒuàÄ®›jïjð™J¦vU>\J£”­ÝiöÑfÅ- 8림å/ò¥‘©XRÖ”¨ËyuÛ—^Ê"›uø¥!ù¤êÒd¥Me¶‰)=IÙ##Áe«'§¨€9sZ7ÔÙ•·)’ë0ùTSH$Tðˆ¹¦îRÃÆnçQHÔæ¤%|t«y’ÁZ^” h3L¢]JfB]$±/[®qQ™8§]A¸ƒ3ɧx“?ðÁ×€#uY[B«Ðjñ*쳟N“¨‘+N¡–$»O‚ÒÊÔJ6RóS2FjQïujQ•m½@¹ãmUF¥: ìE>ë-¹É—M’PÊ›7< ñî[.)5kÊÖ“è ?ÿÙnut-2.7.4/docs/images/cables/belkin-f6cx-rkm-xu-cable.jpg0000644000175000017500000004165712640443572020024 00000000000000ÿØÿàJFIFHHÿá ÎExifMM*, šÿØÿàJFIFÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀOÄ"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ïíôŠªË~«¡(r@œ„ ‘ùG…*yÜ:úóŠÌêÃYºÔL\ ‰õ–H™<Ýâ&2ªï*U°T“´qÅzs­âyc´þÐ}1ÿ³YÂÇ:JÎdT ÎF2p9Á  ¼®âÀhóCsp 4Ò@'†!Ç?(\‚U[$óŠÔÔ|3«[Q¼²ðÝ”Šö†Šâtd™üØÙ$lN’ÁóʨS‚Muòø¿E#y®e]]{y7nåàÇ^=ÆUü[£Å?‘,ÓE)Mê’[J¥‡W“ó<â€á]5´ ÙX¼Mħz3+ʼn?wŒdçÓµlW97ô{Y¥K¦¸· ´%´€±!‰v瀧?þª³'‰¬³g¿·\A Â@Ì‘7ÌLÞ>|ü9Î(jŠÁ“ÆZ/Ës$eѺÞA‚›·òõ޼{Œ±|m ·SzTÏ›âa€X¨'#€XN(¡¢°í¼Saqm{tVxí­Z5Þð8gÞ© ·w;‡jAâí!¥ò‘î^^m%,³.ÜmûÙG㮞œÐíÎÇãm"F˜î¸òãù¼Ánäö#™8((9éSÉâÍ.4gÍÓ*–Ã-¬„:®w2¼¨ÁÉÞ£ tU=3PMRÈÝF ',k†ÎBHÉœûíÏãW(¢Š†{¨­ÙVBû˜#1ã¯@}EMETþÑ·ôŸÿäÿâiñßA,‹2oº&\ñžàPŠ(¢€ (¢€ *µC¹—~Jœpj3¨[‚ïÿ~Ûü(Õ\«4M¸+mn àþÄQ@ÖAðÆ”Òoh€}è3”‹‡%W8\²ƒÇÓ¡­z( _hsÉ#½£fRåñ3€Å‹“ý4Τ¸ð®u}%äÖîg‘·–8Ã` ÀÁùFéŽ+fŠÅ éNÛÌwgüô2ÏÍ“ÙÉÞß§ Å£¢Xg=€…–Ýå󊬌ýûò97<Т€0¿áÑ<á7Ù¤ó:–eÍ–?7-ó·'žiðøOG·pÑÛÈvç>{þôä°ßÏÏ‚Iç=MmQ@á{¥\éöí<)pc.þs» ›p'#@àñR/†´´FT†Efes ™Ãî Í»vsœÈù9çqŠ×¢€0ÿáÑz gU(#*³¸ ›UJ‘žA ƒ×hÎidðž(`ÐÌánd\Îånî9QÇä1·EAig…¿‘mŽ-îûG@Y‹ÔšžŠ­qs$WC>cº³òû@ Tì€,Ö}ÕÌÚ­³OŒzÿA†XØéöÏwy>­|’ÜÒ@׌ d"ƒÀç§µZ•5 oc»:<‹$l»P ÁŒr0Oóê4l与Õ#–Â`ø%°ÈFIÉþ*Ʊ¾ÒµI´]BÂöK›yV_³³!bí´“ó1 ¸<~”ž°Óíd_²]]'—fœŽœ.ò=Ðàÿ¼>šW’´z†Í$¿Èÿ¿*“…8QŒõÆ?zÈ·½Õ>K«}6#G³x²tfËG‘Ë dn_,œóÀã}‰>ö¦ëõ¹¨Ùôæ:»~xþµbÖöæM>Öile3Ii6®ÆÇ# ÀõúÔŸk›þ×?÷Ôü]PÛ¥ãþBòÿàyÿâ©Á´”Yˆ÷¾n?ñê¾.¥=lnÔÇÿÅS¼ù?çÖoÍ?øªÍûVŠ «Æý¿øª’Â8W^Ô™eV™–"È!(Upv峆Μ{v¡æ¾ãÞ_ÍƨÙ:6³¨…³x™Dy™Ãb_½Ó<`c·­iÑEQEQEQEQE„àO§­sËã=4à¼WQ‚ ËGž=£)ú0­B¹íÄvw"ÚBÃ2l …ï€k!Äâ_&W´d18Y“³m;K:çoA޵1|q£µºJLãzT''¦Gý~„èb‘f‰%C•u ±¬G³ñ ¶…’þÝ.WO,’NrÄã }ÞíÚ®ÙA{>žÑj̾q~¶ìSŽàç¨?…X±mÜÓiOþDj³XÒèЬ˨¢J³Jõ•‡ÎÝAj²G##PBý?7ÿ@V×Å"bZòW* îÆâÌq€sÇ~0w­u³…n™Zà 2ô-ŽqøÕ úGýÿ[ÿŠ¥iþ_Óñ½oþ*€ÿÏþ׳òï¢LrË‚ß/P˜ù°pzð3Ç5ÍÊodÓßËñ%„/"/™ƒ rÛ ävàx­Ë¨l©e(K‰I‚B²Å"²ìçq'qûý¸Éè1ÎD4{¸â¹Ú¼ë·qܱ&Ï-`9ãr OQ@vž·M¦Äc»Ë38|´ë´’@ N5iðg|ð7¦ØHÿÙR´ŽÂ=Ì$r›R¢.@##;sž^~¦•SMcÄ~1J?¥_Ûqÿ=cÿ¿gühÛqÿ=cÿ¿güj™µÓØ`Û¹èôÖÓô¶5¦G¼nhîÛùëýû?ãU,–éu}@Ë:<¡Ž0ùhøç#°=:û?HŒ†û ñˆ\ÿJn0ëš«F·!ÜÆÎ\©$9îAõkÑEQEQEQEQEQEQÔQEQEQEe_M~š½´vöqËnÑ?™#AÁ g …Î}{àã%Ϋ%žå²·4mæ,¶%2sÆ7ŒnìÕŒÆv/tÛ‹RÞê;¶Š8ÑÕ£ úà"¢Ð´i´ƒ0’õîQÑ mÆìûs»°íÎMhX—:}±’!žRîŒ. ŒvÅX¢Š(¢Š+6Â{§Õuå´X VC2œ`’zƒ§lV•gÙÙÝA©ßO5É’ ŠùQ–'f3ž:£§¥hQEÿÙÿþCreated by Daniel HarbottleÿÛC  !"$"$ÿÛCÿÀËô"ÿÄÿÄY  !1"AQWaÓ#2BUVq‘“”•Ô$3RSu’´57s¢±ÂÄÑÒ&'6FT–¡³Á48bfƒ„¤ÃðÿÄÿÄÿÚ ?ޤݱ•[ð¼2Ró,< —ÔRÔT)a²úÝUÙ#¾û@ÑÐxw¨5곸‡Uî( ¨6Óï7!Ø«ïë7Ä$¬wÒ‚“²R5­­¹ð¶é÷7‹kÌð™o3–ãÇœFÐç•éî\ ÑIöùvó­Ðoƒ—Pò~£#$ê;W LX2”ë“Ô\“9À®ÉIå±®=Ô£ÛÕÐVûÛ]D˜mÝ?Èî¿ŵJ{†õË‹J:߳ʾ èßàÛmé›-u.Ç2á‘8ëêqhŽÿdoHB…%>@ «Î¾ïêd9W›äð!0·åI³Ëe–6¥­L¬%#é$ZC¤]9Á.7Oó{;–«¡\¥²© Åå©·G·Ú666’G¶‚·ðhÄó¨ýêCVX—[CWu<¬V<Õ–ÝJKjÒ€?kÈ^Òý5ªlúElé»<êMï†M¢ÏÊO-°ò¶R½;¶ @íçÛwôÊÉÖÙ= ÉzgtU×É-c±^üoVKIP!¤º!äRÛIZuö¤,‡#ë†CÒºet>}Êöô/AøÙùHqžÈ ô‚¥lxÞÝøŸmëo±åt†>{ÐL7ÔÙ— z“!é̱³uŽKm¯h ˆ«E#¶Ò׸5›_Ã:ÞßG z ŠÁm\|†L0UÅðZxlUá¤ì©]ø’yÏq>±á?O¥X]¢éq1.÷6»’§9ú2U½¤á] W˜QÕ§àºz‰‹.#¢íâøãéqÉ—UM>·Cj!n§|¹)A)îv/„ÞÓþŸcM×#·_2ü§/[îÀ¹Èh)6Õ'‡—ƒiO1­X'`;_ãtžOR¾x-;8-BˆÃNrÞD†ç6”è4ÈvHí¾àåÚ«y>Iðc˺y’têFZýÝôÞ`†›ŒupÄ6¯[‰ÙÑÐѨ¼» ëN!ðuÅ:UƒZ¦LŸ)™ßfDèé[Ü„vÖ¥€7â®>a*#±Už!jŤü4-#£ Éc·¸ÖC*ÞÄZŠ<ôI>wíP$RIúò¾oø17ÔŒAË>+¢ñ±œy\ÍÂî'¡×–èeK8ÚŠ”ŸrAh ¤(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(:Gq.²‡PâBÇ$)E'¸×äÕw¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥b9t¶7rE±w‰œ´óDe<ê“ï ÞÈî=”t®«ZR´¤¬ñH'[>z˜×j)J)J)J)J+«Î¶ËeÇœChOš”tå®Ô R” R” R¼cË‹!çÙbK.¹a¡ SJ (%@yt}„P{R” R” R” R” R•áo›á ¹–ùlKŒæËo0àZ££¥Ǹ"ƒÞ”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥¸øIåwã3®¦=ÍL¶¥mÂÁkH± lÖÐéæ/Ãê~x™i‘."×%Ç ~Å(<‹Dç¤(öíÛ΂Vç›u=Û‰³ØúgÓ#@"|˕б :6¦Z[WÇD G1¼H}n†×LoùeîĸW+%ÝÛ#¶¦%¥ó"jV”! 9¡°¢¡Ü¤hwªç¸ÕÒSòIÖ•ç%†\ǘ·\!4 ‹ºÉy±ËžÏ"Øþ*­b=+ÎÑÓl±†1x–Û9“-–Ô‡QèÊK~Ó+$¥#ziìwAµíùÇTbß Ûr^˜0ÃwD¼"J¶ÜÕ)¸®¥¾m·(†¾Æ•WÄû+^ôs¨÷Œ7£7üŸ+²¢Cj¾ÌL"Õ̽"|×%)? qC\ö}T“ÄkU±¡e}EɲÜz» ºb¶†Ô©ÙwpŠÒÚ;µ¨’¥·Ú™Èù*ŸÄκªÅƒ ºà²l1#ܘ›¹IÚ£),+e¸¼V¿ëJPN‡˜Ý¢ý˜u1üžëmÅpz-ÖÕþ1¾ÜU3TS¿°%-«i\‰ï±åUÅõíÅôšÇ•1Ž0ÝÞï{6$Æ“<7< ¥‚·$qìÞ›$vÙÞ½„Õ^ó„Ýœê6[79ée˨²¤ÜK¸Ü¥Moâö"¨Û CŽÐG~Jà­÷:÷úbxf}Žô)xûØŽô¸¹4‡®vAft%,¬…^¨Ñ))æ”öIì4 Žõ.*ðŽ¢b‘-¶Ë}±Ë¢ov™j• m#eM’¤¥AÀ‘Ë]ö=ƒ¶êg¬}Fco©RðK*p‡D¯GEÙFæÔeýœŽ UÏ€ÑÐîj é<©¹Yɉ^z}„Þ1éçàM¸"cÀ %kJ±²{ï··Q10+œ<&&(ßÁד™2‘ wÙ DU±a#BaP<ÔT"޹×ÑA¶òn¥ßæf-â]6Æáß§"ØÕÒd¹ó+ ºtÒ QRÔ6­vo}Aü.7k®}ÕY·Û1³\•xЉ0üo6¤EJ6•èrJ¸ò]Â…z˳å]<ê˜Êlø«Ù%’éb‹l›ÎeÈoÆäSm8°<"•#ÇÚOmçt ט1•õ Ëì*³;|¹Ç•Ÿ.ÐŽ”„òOb¤€¯g u±Þƒ;ªI¾cþ5…㸻WË–AS‘˳<6¶¸¬ñ:@IR‰=€¾ê%=^È-ËlÙ†'ߓر÷/ÑcDž_bá!`ðQ@RHRBHÑó&£:ív¿ØzÿÓ •‚̻˫‹teèM%ךmN-z€‚y ”ë¶ûùÜqÜ»:¿æ¹µ×¸YãŒNN?b´¾ó~—(¸ ‹u\P‚¥p@þç}½¡“­YL.ž+?É0hÐìÓâ°» \¼ISŸy@4Ò“ÃMóžým'ÞjM]KαŒ‚Å©8e¦Õi¾Ün:ÝuTƒBÒ¢ÚIll(Ž;·sX·œ&º|pË-½´ÄÊñ¸–©ñ£HP2â!²³Üw!Iß–õ³­Ö.HsŽ«Ý±,¬í‰Z-×÷›Ì˃쒵°y&3! QX+ ó!=“åì! ‚fÙ>5Ÿõ“"Ë­ÐÑdµ:ÛòËë­©›ðZi% ´h“±¥yw©™ýVê†5d›æ½>´ÂÄqŸHD;’ÝŸmeÅt¸!´§D¢+-À²{þIÕŒ2N;!6¬ÌG—o¾Pc2ë1ÑÅ$°|V’;ù¼ýs/á/©7‡Ó]?•ÇŒ§Û¶ºÚdÕµ m.)!hXÑä4Iìt7w´u‚S¸¯Tn6X“ov;”÷4˨C­Û¥%-†ƒÝз•o°;#Ìw vŸeÑ:‰§ýI³Y`Ý.0Ý—kh’âãL Ÿ²7ÅÀ•¥:Wrv7å^_?ýÜñ/òR?¼»Qxå§/ÎúÓiê&GŠÈÅmxÄ)í°¥>Û’¥¾úV²JR€’@ÚŽÈN¬¿›ßè†7c¾Ár Ê+oã¸AR }Å è‘äAü´”¥”¥”¥¢rkãv6"¨Ûç\_—#Àb<4¡N-\²}u$h% $§™ÿÓúÖG÷ 4–À³û~¾Ÿ,.€9gö"ý}[*‰’f™n ¯Ç1÷‡Yµ5r~C÷Q(K޼ÚR†²NÙ=þ‘AŸòÂáø–b/×Óå…Ãð,þÄ_¯ªô©JÉ-ö–0Ì}3/—ìYòü!6ËÊajuÖÒæöê”qIå¢{W,*ñs½ZýæÁ&Å=‡×øÎ¬8’R~ݵsm@‚¡ô€ExÌ'w+Ár–Ò%JD]ÿ§©ûÆ=âËNŒÜ–y+‚ÒaÑérÿIÿ"¿ôƒégø±Å©aÿä"‚ÉJRJRJRJRJRƒóvga§§º¶ÒóÉa †Vâ–â¾Õ!(’uî¬Eä¿â—“£®Ö‰=ûoú?Éøê3©õkúþ/úÕb¼Nf×i™s•©˜Œ-÷*)BJŽ·®úä@W¢^\õ‚t‹D’{où?!ækNk‡8|K“»ØMØä‘ø¾Ò²XÊ­ÏY1ûºZ”¿)„ÅIBy'Æl¸žcz‡}ßßU[Wm²£C¹;Šäðì’æˆH»¼Ôc‹ +àòœJKºNÊ<ÈÞ‡z Í ëå[×Är}Àÿ3é¯?—pòÏØRਘ½QfMÁ k¿*Ä»ª­ ¼ÉkÒCŞ퇪*7ÆôÒGÚû?²”Ô¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥( n¸…Šç™Y²ù‘V»Å™·š‚ðu@6—“ÅÀR•±ïªz” R” R” R” R” R” R” R” UO3ÿ¦8?õ¬îjÙTî¥3D¼rñØ•|zÙpq×¢"Sl(¡qžkaNžÊq=·Aq­i”t¾Ý•õfVE‘C2mƃ79æã%ù ^ÃjNÓÅÄy“í©%å9úw®—H_}v½Åïß[î/âú{W‹ywP”­+¤“=ê¾CןУøÿ%=ߺb¹=¿#À±ûtÈÑí=gN-%ߥ²®%;Nka@ïcFÑ‚·• Kò3¡|a&RÞn,NíÃdè!½ál¬²¢`*9V~YC‡¥’R¥Íî/$÷ÖÏ}}=‰ÿÒ³Q|ÍŠvpF’}Æòßþ‰ ²Ü¿ÁÒȯý úYþ,q_êXù¬9W|Ù讲0vAZ A7†ûlkù•3…[dY°Û%žR›Tˆ6øñ-’RV†Ò“¢uÛb‚^”¥”¥”¥”¥”¥O©õkúþ/úÕ=À7[ÆÖðŒÈ®Çñ5¾<ÐS½{uº…ê4iïB´Io‘=P®ñäºËÂÚI #š’¶Ÿ–ë25ösé ù'|o¹ìâ£$öÿëPk¼b>q.ŒÍÂeZÇÜaW+„™‘ÜaÄÇamÈmÅ,•¨¤ŽIH Þôu\ô¯¤Vèv+d¼‹Ñ¹Fœü³o‘zyèhsÒ[KÃ…Ÿ"•W±Ñó­ŽÝâzùÉ{Âx«^²ã ý#ìÞUÕ‹Ü÷J‡É[Ú8ëíÕo~ï³Pj9¸¶Q+-/Ûpy˜ÅýW_ìŠÑsm«d¦|p²ãу¼ÞZš$-¢JÉî”7µDük?ðfíúHß]Ogþ Ý¿Ië¨g}¢2O F^þS‡_möÉ“¢µá}½RYR‹n¶¤­!Å2ꃭn‚"UתMǬ ;ŠZ¯2 Ï“puøoJaÏGz;m©”¥ä)aþD(¨ŽÃÙ³?Ó\ŽëyEæÓ‘1 «í†¡N0ùxri·›uD”…6êIIØÙó¨YXbÛ¶;…«¨ ·xƒkSeϵi”e:˪àߌŸ)S %¨=›6Lw·Í3®®^.×)j›qœ¶ƒ^3¥)@ @'‚„! N΂Grvh,”¥(¥(¥(¥(¥(¥(SÉ2ùœóưN’ÍÝÕ¡ë‘#FÓ/8”r?ná,ŸTy²|¶TQj™r»ã2cx^¶ê©ry¨ƒÀÅÐâ5Üòu>îÛ ž¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)ASºüwú‚ëýâÝVÊ£çMµæ–+ì%Ø–¦mÓâ*=ÆéèJXuÈ‹æƒá¯Oƒ¢45Í5ÿR§G[HÎÚßÁ,ûs°?ìßM«¹Ë¸uüÝŠØüx³c´íá*qW;S~ƒªDdèø\ü]¤+Ü7pƳ›œî«#Eâ<œsj1.idM|0…*V¸ò@%â½’~×Í ¬‡úªóEDü†à•³–þÍ^lõq*u uÜ*JY%#ÚuèÝè3zÏ–Yp›®/‘äºÅ¾+ÒÔâ›eN¨ÿ^€ ÷ú{{Ȫ¿Kþ8–j‹ÝÁØ7KEªÍ3×`¼úÝä’TWà¥hoÈžDžæ¬Ö¬Ž_žYRÅÃy}õ"àÉyjR<2߆ŸTl«gÏËÛV|GÅ1Öv5fjrì¦×1¸ÛC*R‚T–÷ÁJ;âûozHë7NV®)½ËQ÷ LÃÿê©ì;6ƲåÉF?9éF0I{œ7™ å½wq ÈùUŠº¸’¶Ô€µ ¨É>céǺe­õ‚NJœ…mÄuÜ‘ Emm« ÒÒ¾J!C‚ÊTxú Ãõ?2™gêmÒÜ»&±Ø­ØüK‚…¢ÊÔÔ¥N=) [ª,:¤§M·îIúMZºsÓ&ðy 0s\®á›®¹{±–ÓθIS‹(a+ZÉ$ì«ÏÏueo·#.Ÿ“É“­Ì[žmd¼&–êÓ¤ëÌ——½’4aßa¯ê]ûÃqéy=ŠMÆeì<͹l–ÛrL¢ê½‡¥¢ëµåë§w™¹ôœ¾yp!ÆÄvLï¦#@zèkÄJ¸x‡ºÈî­'¾’(6)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J Óüoã¿Ô_ïê¶UNéþ7ñßê ¯÷‹u[(+Y>gnÇî‘mÓ-×§–¿:¢Ûœy/‚×À)#\¸¶³¯p«gÐû :‘âBV8¨7¢‘ÑP¹U²]Âó‹ÈŒ„–í×UJJ€Óf$††½ç“¨íøê¡fÄ2Ýc•˜½ -R]y”ÂnBŠ˜>R'4¸ï†¤꧉Ù<ö ÕךË1s+RfLì‚Ë~'£¨ŽJ Q :#²N·½hú&Q»òÅqAÛ¶²Igõ*÷½9ŸãŒ('h6BO«½¤2#ßÉÓÜkØ ÑѱÐiž¿_:©oéúe¾Ëh·Lm x-wÙJBËÈ ·èˆæI:ãÈl;ùƒm÷¬w\gÆê½‚ ¹¤°ZÕàLw^eæ5¤öï¿Pæ÷ØÛÊCJï½sA¤±6Õaë%æ\åYï°&^º‰­_^âÛèY@WBãqR¥¡¡ªÍëfc¸e¸ÁM\D«¾DÌI囬–’ë ‰!E<àJ’AÚ@'ÒwxÇðl7¾¢Ñ$þp ud4‡ ü6ÊŠ[IPàü椯)ǤܭÌ]\€¹±d&Tžu!iwŠÛJÒ’vN–°?öÐPW;êe«¥v¸¶ÑæÙ~$‡¤­Å1)_ŠÓœÖV·Ô[>ÞŠJ”Að”ϘóŒGS­EzRÆ´ÓEG¿³š’?ñªýÚÁ…;quw8Öá9ùlÜ”§^âéy%§AÞÇ:íçï;”rÿbm¶ÜrõmBM©RÀ$û÷~J uÞn Rq+º”FÊ|hnßË}'óV<‹ýí·Ò†°{ÓÍvà“ ÷>Âþýßž¬(RV€´()*‚+š ŸÊl—ææûú쯧Êl—ææûú쯫e(*)²_››ïë°>¾Ÿ)²_››ïë°>¾­” ©ü¦É~no¿®Àúú|¦É~no¿®Àúú¶R‚¤œŸ'ï˦÷Áß¶§A=¿O]$åbP nš^^û‡.0P5øÃÆ® ¢±•çŠmEþ•ÏBô8„^a¨ýöJÆ´;ŽÝÏnÞuÌŒ¯:JWèý,¸8 ½ .ñ IíÜégGÏ·Çî¼Ò‚¥‹_óÔF½àÙ")FR®ÌHCÉ<ß¿¾ªf—~Æä5’ǾÞAq‚€#´B¶Gñ›L…IH<ÝJÐ¥h„’ŽÄqîªÛJȺia¾d2.ҥݛjqiW;s2µàZ6^Aö HÒJB‚@W!Ú‚ëJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRT¸Ôë‹N¿nÀ2™qÛôoÛÒ•-§TÒô(¡C¸Õ]*«Ò¢ƒ‰:P•¤½Ð½l+ã ¾ÐuùY|ùµË?OmýîŸ+/Ÿ6¹géí¿½Õ²”Ó%rí{ºÁº' ê²d&ŽÛ°fZ’TÛªiKJ¹IP=ÙlþJÃðr½½Wý~ÍõÕµéAª<ŸïoUÿ_³}ur¦2`t-ýVW`v'Ù½Þ_ÊÖÖ¥¯²·wƒ±z“Šuç%ˆîÆgÓ¦ÚT†Ðê›+ "B{’Ò=þUfUìïþn2¡¯{öîÿþ]Z©A/·3ÏþFßG5öX~·aåücé×}yVkwÌýÁÛ<öiµ¬CW„·—Äx-I$ë°åùªB”¤k½Ç¯3.—ûUòÒï6ëÑ¥FR"K¼û”¤óÐæ¾GCb¦WeÁî½h꙯˜Ûû_.Œ4¿ ·ù”©cÕ†È×­…hÈ0k¶Nêm7¼r}ù ˜îˆ²ÙvR[BÉ(WV•°{O¶ ú¤Qîm\³«.+"æò@iÉÖæ¤JZí‚–RŸ’}º Õc–+§FðÛÂÉo¸%Y|–ÙSc%é µ*ïņֵ‚JK*Žøñ:ÕNæØ´vú¼«f7ÓìFôÌ<<®=ªzÄt¨ÌqZm!¥ )J=÷ÄlìšÛ.]ðë»6œ›hžÍÉÁ"ÐPâ[:q+kÌ#ˆP#¸#cʱï9^d¹H›s¹Û"Ïiö­O:¤8¶ËíÇ$GhQXOÓº/ƒŒh1zOoLZ)vD—žŽÓE¤Byo­NFKeJ( ,©w÷58îUyCÎ!¾eO! R‰zÞ½9©@èëc`v¬œ*é‡Ý[¸?‰Iµ=ÊZœ¸A)_¤SÉ!d$wPÙ{*ÁASùY|ùµË?OmýîŸ+/Ÿ6¹géí¿½Õ²”?•—Ï›\³ôößÞë±Ê¯aÂàã*#zåãÛµøÿö½Õª”´ä—rëˆ8H„%eèY|Gñìùw}5ÅòæâVU‡_)ނ݇µùùióÿ޼êr”,ß®Ž0§…ßšP#M©Ø\ŽÀ=µ ŽÛ#ÏØu¾Äú·y¸­Ç¬Nò€…•)Øš_`v4þõß]õåùjf”»ÏRŽ/wI#z.EØú;=CwŸÌ'ä½ßD¿.‡Ñü·ÿÚ©zPaÛæI’â’õ¦l ŸSD+è£ùëHõ+=µÎêu²ß&í6ßÈ!ÇQÝN’±ÉkY ¶„©!#~²Ô£÷)Þú¨{ì¼h̉ õ6؉-¼‰Y’ú°à$!iI;ÞöMÅp€R€’¢¢ŠŽ¶~žÕÍ(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(8R’”•)A)d“ sX×h1®–©vɇ#Kal<ƒ÷HZJT;ý×ÍåVf³C¸ütÃl%ì„0[{v3»r²vvG}A\)IHÚ”']϶´-­«çË«æa½½Ábà¦Rý¾*›f× rZ”­rW#ÇÕØìEbû–dY س·{Û^Û½Ù^‰´Ûa3X1Ù}RõÃÄþUn¤©*O‡Ä‚h>¢¥|õ?É£ÁÈy%ÎþÔ[{7|Ž·´M±Àó)~8 Òä+ÂYSƒÀIåÜ“‚2Þ©*Åp…u¾¹j—dð"Ün2Ó-¼ì§Kã‹¥µ¶ÑD °µ$¤)ßXAô*³Ò»œëÏOl×+’ßr[Ì}‘Ç”­ÂRê’…$ Aª³P*±ÓÐÞ.êPTA»ÜÕë'Gf{äþMŸËVz¯ôñ)«*J[IrD—WÁ²„•®CŠQâ®ãj$è÷ïÞ‚ÁJRU>®Í¸Ûº{s™k’ôGQád²‚§`º€û©Y-¨vÖý•l¥ÌÝO¸ZlÖׇ̖Üom¦V2·Z™"\ÖJPôp‡ŽÜRÚpø‹JU±ÌsÞõÚG-½Þ&v/’\.0gZ Ã6SÂ$Çré ”…‚”-®AG·sOaç¾#Xì±XyˆÖ‹{ ? òŒ„¥ÍyrwÖýµï.ßg‰épcHñ_ŠÒUÉì$ìwï¯}ºÀdÀÕ»Õ¹½>ÄÕ¡‰Rg®[q¦—V”¥*Z”R¥6©èpIÐ*Ú¶ucÀƒ ßÑàC“á°ØBwïЬŠpµqB•Ä«Czf¹¥ˆèÕçÑ2kŲ«ÍòÝ貕z¶]!0ÛÖ»GŠ[m*C¥Åp)Z×Ë’”7­‰[ÎC ê†gqÉ'À´È¹Áˆl7;¨)‡á!¾*Ž\jCÅK(ØR‚Á×k¦1Ô„ªÕòó„BÈã3y™ejðŸ -*G¾j!±È÷G%r ÙŽÀÙ ²Ò” R” R” R” R” R” R” R” R” R” R” R” R” £^:‘Û2ðÞž¶Ù¦· uÉ¥F,¶µ¡¥ì$ºPôoH'Ï@ꥭ¹¾7.$¹ÜãÁô?RQ)Ô¡M6™Ç+¿d)ÆVöëØ{T]¿§V72{íÿ µÚî³'][›×c…)„6à ¶“±æ•2T÷ƒçTœ¤9Ä^Z·ÝmIEæ áHSáͲ‘q“1 HÖ$HàvG;¼¨6{Ž&îP¼aœŠÖ»âVR»z$¤¾G3´o}’6}ÕvϬöî£ÁÂ×c×9‰k‹­†ø¡.%õ$V)޽©)!;NÈïª!lÈÑÔ«M£â©)´Y/w‰ë›.¸à_áöS¶Ö ŸíÁD© †ÊxœÜÓ¥Ù5×9‘Y¯ÑàÈvZ$7sSÏ&cQÒÚR«pÐôe­%d… ‰âH  ¿äù²\­öè–…ÞeÅ/8†¡©„–‚9)E×>í#±&¢lOÆgÁ›6rŸ±G€Ç‹1wN ¥‚$?H$(‚ äuŽÄƒ´è’t3²ì:O٦ݢę ÞÔ”®;èåÉnx|T=¸+óÕk*é“®)HÅѸð™…*/é,%Ä$©­øàò@åÙC°Q4ø¦%>Ö»¤,ŠÛ"pÌå¾Ûé(Lp¥$¸O± ¡cñ¤eM»ÍÈËôw‡ƒá­IäHìHØßâØ­'¦“ཋc²$J”¿Œ'ɼJCN:Ã’©^Ž·\'’”òGrTR\>ÝáAIfêrR}?;°:wÛÁǎݽòUôÔGNm™ë˜lü¯·7ȺJ\²(~gÇî}õ³j·Ó"ÉÂ-æ3ž#eð×ßÖOнýü½ýè1þ)Ï -_°×Óâœ÷ðÒÕûý}[)ASø§=ü4µ~Â?_OŠsßÃKWì#õõl¥Oâœ÷ðÒÕûý}v6œï€2µòÙÙøŒë^ÎÞ?ã«U(*翆–¯ØGëëºm9φ ¬ÆØVHâE“@{öþÙðá^b•ésbá1Ô¬!Ö¢ú:@)Ðy+Ûíß¶¥ë«Š(mKRÊA©@IQ&§ÍéÌW¬yœÉ˜öI5›Œ+i†»}¥ét¶—ÂÓÍ)à’ ‡eçSاRY¾æ‡{Êl·Á3”«‹ %´µÏ€$¶òÈ*VÀwâ¯q¬›žvXÉî¸ý·Èor­L2ôµÂ1„‡BŠñŸB”HB»h5CV)î;‹GÉìy…¦sPå¼›Î*”üe¿%n® Èiµí-²AIR¸ë¾V?‰æy>EˆÊÈ.™=™öqë«nÝ"4Ô7×¹ìz:^HJ…­R;I>Z"®¯uz°YïÑq ¢eºìûy¤EO .¼X -.>•%aÁÅG\A×­®õ›?¨sÛ¼·c·àwù·dÁôùüxˆTVJÖ„rWŒP¥,¶®)B•Û\Šh1ziºX±»¥®ÿ joÝUÂå!jX»9Å L´(ù6”í8ñölÛoöY×7Ûr.Qy³¥ â[„˜Å+;ó>++;üD¢½ñ{Ìl‡·ß!³%†'GCèjKE·[ â´û<ˆ÷Š‘I$¤ ë¿ÓAUù%xùÆË?GoýÖŸ$¯8ÙgèíÿºÕ®”O’Wœl³ôvÿÝiòJñó–~Žßû­ZéARV!wPÑê6[æd@쵌¬⤔ž¥fÚ#]Ÿˆ?ÙêíJ c˜=ÍÅ%JêNf Wâ.CHÞµÜýÇÑåíÖë }4”¥¦õgÝqdÌ­J ~žšJIØêoP<ˆïqdùC\R~sº…ûIŸ©­ƒJ æ%йÊ}õå9%çÅm( Üå¥Ô7¯5$% Ñ>ß?£U¥3)2-]AÌbË»Ir÷:ñe‹1š[7v|8èõ¹¶¢´¥M9äG„RW¾û¯£ª‰™uFÍŠ] écÊT€ûQÓ-‹C«ŽãŽh%(sÉGg]½»÷P^éJP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP+ZÙ:}šÙá1nÕ{‹P$6ѳCR‚I'\Š~þz­•J +X–rÙz¯uQä“êÚ §ÕÙä?’=ÈÐØA:;ÐÉ‹ŠåiJý+©××IY(-Ûà#IíØíƒ³çß·â«(*%ò/œœõKîÔù/‘|ääª[ÿv«e(*%ò/œœõKîÔù/‘|ääª[ÿv«e(*%ò/œœõKîÔù/‘|ääª[ÿv«e(*%ò/œœõKîÕ`³D“ÞÜiwIWG’O)2ÚV½†Ò”öòì‘åY•ÂÔ…(‚@Ð?š‚Ók5ÎÇ*½ß"–®7[ÓŲ¥¥GÐÚÓQ€ $¡<øöÒœ^ÆÉª†MÒûŽMÔ Öê«¶A`3-ð™´Î¶ÝÝŒ’êèQ[m,s QGÛ"uæj_¦P9…ö%½Vø ¦}±Ë›5ÀHv2êð¤ $xn½ëgE+Iî‚NvC—e2»†7ƒã¶ëœ‹K-;q•q¸*3[€© #ƒkRœ)G`$'g¸ ƒÈgô— ´Œa›UÂ×{´».6[e¨ÓS®!\´RR‚°7ȃ¢ ·^W³J¸eM?'§2²(ˆ„‘ édžÜ+”7‚–V…:§šPl‚‚ž =ùlyVMó¨}·³^U‚Ãa7)±mÊ‹:ð¶$G’ü€À Jc­%°¢Ì+e'a>ÃÒ÷ÔŽ.]n׌C¾ªÜ'¸‰×¥¡‡¹:¶Ûj:Ë!N«ÔÚÈ +`µt¾C§öh™T•ȼ· +qÀâÁÙ)J–;-IO•}Ñûk7!Çm×Õ2©ÎÝY#Ðî’bozß/Äòòöï^Ï:ÈÇ'J¹Ø`\fÛ$Z¥Ia= õ9dl¡E=‰¶ë>‚§ücßö¬›þóÜ~¾¹?ÇÁÒ²^ßüÍqúúµÒ‚bq(ÉÞy“½ÔÂoÜx¼}¾ïôVBqËjw¥\{÷¹H?ëÔ½("Ž[R4qó'½ÊAó;þ}>N[yòåqÞµþ‘¯ÍÎ¥éAòzÛüë‡íñ× Ç-kUéêãp|ÿ¯Rô ‰ù=mþuÃöŒøë…ã–·¤/Ó”… )&àù{¯Rô ‡±c+—dÚ­éŒó©àâÂÔ¢¡½ë¹5y*õÕëCrâI6k¹w–¦Ï€ìçVZo¾´¥¶Ú]:ÞÇŠ“îÕÞ¨9RãYòYvÅY&ȃo—Â{n¶J) ¥-•spmÆöR;rí½ú”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥œGv3n»qÜRASK)%ÜJIòô¥(¥(¥(¥(¥(¥(ç)¥=ÖRámN ¤,y¤‘­Šô¬{œŸB·I™á-ß•»ÁjWN‡ÒuAªzUÓËö=‘XŸÓng³;ir\7‹^ÊËD>èâž-©\TTy8®úîfoœÏÎ/9j¶^¢ßÛ`Ë‹.iŒ¸òšG„—‚¸()¢ÚPräuÓŒú÷t¾ãŒ^'YgGÊí/Ý¡5oiIU´7àŸÅ•Ÿö¹ñlòAõ{ú±¹ÇPn–î£ß¬ÎuÄ-öˆÑix>3“ ©YPÒ:z Ÿ_ñPYSwÁñÛmÒïáyƒ|·Ü§J(,¶´³1­-¤­$qH>z>f¼z‹‹d«Ô«1–Ø%B ‹mé~†úJ¶¶ÜK.%aC{ÑIFÁï¡ÓÎ/÷웃t€ÄÜñE]ä° ¤>iEZ!%+'‰ò­“A^鵊v3‚Ú,7;Æ2á0r@ ]É<‰Ñ}´Þ®™#÷™–ë+¶u<ór<‚‚}aWr¥±\ôÿGG%4tµjºwÔ°Ì×êj&‰Œ–bîmkK‹Cä;¸í]Ü?ÿÊÖZäT\[ÓWI²¨·mL6³êl;b%­ÞÎyÝ—wÅã<°6hˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆ¼ÑúÚû_f«}¢ÛMÛÒAG_IQ µ¸ OY= Ž$’!kK¹`¸õrâ/ý–6kEÆâûõ==tt쉂WSÏŽ`Ò5xs»qÚ»šIEyÖ:žÿ¦Ù5Uhw »WU¶šfÈÆáG.z:ŠZyc~ÖyòY®áßöþ±ôÑÿUÃý”Mª¦qÃg‰ßq௮ž}ç|ÓܱOOîË¡-“TœSÇYMK ¥=Xhp¹9-»xqÃÂ3Ü.™ò\?ÙAOÓÃï±üà<>úÏœÉá¿ýbéŸ%Cý•¦¦Ó|­¾Mb¦²h*›¼™¨c¦¥uDxë݆>P‚ÿ¦‹ßó‚tÑ{ã>pS á·vþ±´×’áþÊë¬áÿ héeªªÑºVžš_$²Û kÑÌ’Kpñ”}<^øÏœ¦‹ß󂇰iNj 7VX´öŠ»RµÛ ÔT´Ó0;Âܰž®K`8iðr4.šêȲ‚§¥‹ß󓥋ßó‚„Ô_„:zš:›õ‹EZ`‘û#’¶žš½Þ ^%l)øyÚˆY<:3LËcÙn„µÍ<Á7˜ùPUôÑgÝü+Žš,g¤f>ø)‡pׇŽöÚMŸõl_ÙQÜVáþŒîvšÅhÒ6*ZûõlVئ†ß_ n%Õ4íäæÀɈ?ã õ¶»>Êë‚8ቱDÐÆ1¡­hêAjµ«Ó:vH£ÔŽÏhtÃ16º¶8 ÇV@yüˆ7*Xýv4Þܾ…ŠÊަ Êhê©gŠx%htrFðæ¼¢äBÖ?]÷·/¡b €ˆHÚ¾¼‡ð ¯éëÕr‘µ}x5à_Ó× ®DD…|µÐÞìõ¶{œ¢†ºžJj˜I IÚZöäFA#‘k5B·„šÌÈ~ë}2çØ—C~кyr·Ó+”A ìK¡¿h]<¹[铨—C~кyr·Ó+”A ìK¡¿h]<¹[铨—C~кyr·Ó+”A¦Ò:^É¥-Ò[ì4f’šZ‡ÔÈÓ3åså~7=Î{œâNY[”DDA Æ?ÔZ_ñ¦ÛôÁ\7©Cñõ—üi¶ý0W êAÊ" """ """ ¥Ãoð޳üd—ú=:³=J7†£7iÏö’_èôè5ú.ÕGªîU:ÞûM }C.T¶˜§`{-ðA3àÌ`òHc/sý·~œ5wq.ÅGAn­×Vzx¨u ¢™ÕBªš¸£ÝM61ÒFð {ìí$9¸!#†÷¢¯í·Ùª¯Zr¾¢JÆÇHövE¾y_0Ø÷4>'¼ºA‚\×9ämø¼I}×1 VJë-† béW_±“TAöPC\â7ŒµÏ~Ý­'h. ´.i¤mE4s4²0=¹ðn‹‰öMwªtáw÷%T¬½Ð·<˜'ËgŒ}É£tŸùêÝ ­4 Rˆâ–Õ©ô¾­‡”TÕ†×p:Z²Ö4ÿ£PÚsò7z …ÄèÅ6¦Òú‚çEQpÓö×Ôš¸a¥}K ¨{X ª1°À&n@%½0v0 ˇ øP@ðû±îÂÿ¨ì¶Ú›}’ºvošÔ½TÃ'IP#xÆ×FΑÀoØ1ÐO]n¶¸ÁÄã§gŽš†Ô*!¥eEMQ2Éxk*z¸—m s²H#-Ðvrëþ9pÑÔu÷Æ\j®W9i›Wok04Æ¢ r`·{pZ×mkƒ748·9ÈIV\,ºoXj·êÛ5m[¯DhgŠÍ5kjiôt¯Ûµ…Ÿø{ž$Gö@Ü `gÚt|VÍM[|¥¾]ÇfÔ:¢¢‘΄Ã#ËvŒž¤À`oå´®J” ×ê*Ê›u–¶¾’³f§…Ò²”GÒ3ÇêPº:éS­uµ¢ñ,LŠ’Ï`‚¥ìa%†º¹yh?ä¡oðT…è7FÓºßTÚ¹tÆ'tÏ.Àk0wø03ÍEpÝØ¼=£¸¤‡¶Ç³bŽS—²˜µ¬¥k¿t)ã„;«¾osé½x¾RCÕTt¯}Ï‘Gš–¶jQc¦½I¦îwjŠ©²éldrEXÑí^ænk¢ý“q´§`ò¦Z¨ônº´›M¢µßç–š²Ý C`m@‰ó2¢6Lq=¯“·4‘–ä÷k®ÆƒûۗбvØèïwýS£¾ÐU ½¯mªÝ$Œ|æG·kê&,%­vÒZÆ4œ<¸’àÕ¬~»ïn_BÄ"" """ ""‘µ}x5à_Óתå#júðjÀ6¿§¯A\ˆ‡©'²Þ‹,|‘I4M.4:Zç$gi öÓ–¸r<Á!QwSaî#»^Îþðv·¶—пõ/EÒô›1¿ÚsÛÞ g’ÑpÃ>Ä–îxþã“ùÞ£Ïì"ÿöÛÿmA—gý|»Ýèí6íiÓVVÎÊzxûWXÝò=Á­nL@ ’I[q]é^Ø£¾k §k-òT6™³v<³fG5Î Û\z˜îxÇ%ü¶àÿ×kG~¡úv/Û¿¦#õ‰¢ü?Oô3 ôÇNk­GÒº§¶9˜ùkêbËZ2ãºHÚÞCå[%qg‡ü7ª£¦Ö—þÕËZÇ>½‡<ÛÚÒ9‰Ž™x_…ÿ@w쌵ÿ™U}^…úe_®]þgSÿ~©á§4\Í}í£¨ Hì9áØ¾êÆç;OVV£[ñç…+RTiÍMª»éLeƒµõRmhs{æFZyÔWçŸÒÓýW­ù:Oç•y/èßý‘Úƒ÷ªo bèOµ¶˜×Ú·úJçÛ+oLè:n‚H»öã#lk¼#ž0´Ç‹Z0‰ÔsÇœÓ,^å,D´âÚr× ƒÌš~—çìoájŸæbõì[GÏì*~–D­òÇA{µÏÙ hê©eØæô‘HÐæ;Œ´ƒ‚ñ¬ÕÀ¯¬Žƒü[·FY †ãê-/øÓmú`®Ô¡øÇú‹Kþ4Û~˜+†õ åRá§øKZ~2KýY¥ÃOð–´üd—ú=: ,Àh+”@Z]meJ]l}1§um+âŽ`9Äò;ÉÊ×aß‘n‘ÿ/ÓjMk»ÕÓŠjÙ¡Ù[N?ð*XK&ýöþE@¢4\®´ëýQ¥¥ilRÉî€ã“¢Ÿ-™£ålñ½Ç䙊ÝJj=c-¾ôl–m;tÔ—8 mEL/‚6ÓDâC ’M# ‹]µ —¤5V¢îvmIjÕµÚ‡KÃk®mÒ¢®¢¯©}>Ù"Ü4r5’uµÛ\ÂÞ{ZA!Á¾Òwê-GhmƉ³Ä†jzˆú9©æaÚø¤oÍ#‰‘‚ Û)Ýaª±[*s«Š®ëq¬’ºá41–Dfxlm$Ʊ¬`ÉÉ ÉæJŸâdÑÕŽÌvÆSRÐ:á;«j]ê×ítP;éæsã`Ç}’q.&¹E¥ïÓi˜^æ>ÿ`u0 8t¢>“¦t ƒ™d`no|¯#p{Cƒƒ‚¼ÎŸHk$î[å³G§N¢‚æùd5pP»-èDvºV°ì1È8°Z}"Žé©ã§ˆb8˜ÀNpÀ0µ-ê“OÙê.µý)‚£dL/’G¹ÁŒc:Üç9­Â\›Okë¯1Ú/ZZó¦êêçÑŠ÷SÈÊÑ—5¯‚I2Çq’7qÆýQZoÔº6†*ætÖʺiçéçT:³êd­ln…®p{›½®-iš®·ZµUÛS[®ÚžE¾šÓÒIMKAS%C¦ìtfG½ìfÖ†9à04’]’î@ ³Sz»T¾ËWKm·ØîWë­S$tTF&–ÆÜH÷Êö1ÀsvI<çŠ'sð¯.¼wy⨴C¦+šËd¨¦–i¡b™~¢] kÃd/ÇzA rÀ .4Ž¢‡PÒÔÚ8È9û®kÖŽ#}޵°»§$?õJÁG‹W±úôÓÞnIëkžÕqážžórO[Uè‚8Úx“ž÷ZéÌ|ºnSÿV´¯‡ÚïRIfñÙ?Fb¨¦†ÀñLd‚Zð꣌ã˜Zì3ÕPD«âGÃ=7æÜ¾¸¾{UĽÙîÓLãÄtÌÞ¸¬Q“Þøi­®×©®rëëD «ìnÎ¥ƒOøIúîü4Ö´ú®ÉÄŠ*šÈ"’&ÇU§œèÈ{vœ†ÕüksÇ/Ð󨸻]l¬ÔÝâˆQi×´8<‚wn«wˆu/R¼ëû^µ¶Ù*-06’ã[Øt²:¯3¸CÒ¾XáÛƒ95ĸß` Ù¬ ðþ‡ýKÂ).ÓÜAµU››clÝ›§í¡›±·m[Æ*sŠ¿¡BéÄmoY«o!i½+hÓ´Z×MIKj¡‚Š˦¦/s"¬iv+@É à•³íoþé_6gõՇà ÷qÒM-4²QÃ[LèkÛ4­ŽBí¬š<ðïw·;›»-çx:@]ô޶¾TÚçWØd£·Ý)OË’˜_¸0=Õo ÏiWàaRv…,º\«­ú³QÛƨÕM4”ý±¬%»áq o…X" ãn?:·çÒzºwqøÁÕ¿>“ÕÕr ‘î6ãñƒ«~}'«§q·Œ[óé=]W" ãn?:·çÒzºwqøÁÕ¿>“ÕÕr ‘î6ãñƒ«~}'«§q·Œ[óé=]W" ãn?:·çÒzºwqøÁÕ¿>“ÕÕr ‘î6ãñƒ«~}'«§q·Œ[óé=]W" ãn?:·çÒzºwqøÁÕ¿>“ÕÕr ‘î6ãñƒ«~}'«§q·Œ[óé=]W" ãn?:·çÒzºwqøÁÕ¿>“ÕÕr ‘î6ãñƒ«~}'«§q·Œ[óé=]W" ãn?:·çÒzºwqøÁÕ¿>“ÕÕr ‘î6ãñƒ«~}'«¥ˆl’ß~®Ô×ûµE½“6š:É èÙÒ´5ÇÄÒN.j¹ ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆ8vyak4­’—NX)¬´rÏ,ÁÁ˜‚ó¹ÅÇ$:Éð-¢ ™×ZF-[ ¾‹µÆ‚:¡TÖÓ2²W†Þ‘“E#\Næòäà×u´c{CO-=0MW=d‘°5Õ $#ìœÖ·'ä|d¢ K¶Žíþú»íÒJ«a¯mµÆ3ž  \[Ò1¹hq`v ÀçT}ªåIiÎi½9©&¿Yi'¥žZ6јECÝ csòÖ’pIw@sÍÚ‰õöÚš6ÕÔѺhœÆÔS86X‰êsIduŒ‚i÷u¸œž ‚6åÄ›Õ6¿»ÚcšØmôš Dmµ%òÅQ 3Ý!ªèXZj€[Ì4V¯_^À[V¼dT1\ë©mÒ½Ž§’X#uL°±äF×opBCC²H iKÃ;4²ª.Ý3館¥5®1Nêx¢Š2ñÖ{Ø#ÈηžrR“†Öj}*4À¸^ŸjŽ8#§õ¤ö?A#$ˆ±ØÈ!Ñ·™ÏW…ÓC]kî¶É箯¦¬{f-c µOBÚ &{œã“íÁà+yn¨}U 2RÏHùXè'ÛÒFOØ»i##ä$|« Éju®#7…v÷îßY7Hæòê†Ρ€RÑÃL%šazIž^÷cÂâzÏʃ¹\rµï‘£­ŽÚ€ë ±cÓÙ<ú¥Ü7øPd""" """ """ ""-¾º]¬ÚJºåc¶:ç_gGNÖ9çi{Z÷ìo|ýŒ.~Æ÷ÎÛ´s!DSñõj£†;~¦–®òh®ô•; ¢qº ™@ðè[íÞAa$Pz¢/*g¬,¢ž±ÔÕµÕA”ôÁ°SÊØ…5KË4Íc‹{%£‚KƒZ×—Œ\LŠiêº×W|lòÆë\Væ°I51£†¡Ó;¦‘tÀu‚w4c9(=›CÝæ¿hû5î¢6E-} 5/cZZç°8€1×Ôy­Ê" """ """ """ ""8_1HÙ\Þ âßÊó.Jê¢÷sÏÕàÇÙȈ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆƒVÍG§Ÿ}u…—Û[®ínçP ¸Í@Îz<îÆ9õ-žáã^WaŠ®‚ -=Q£g¸Ý ÔUÉY<´ícª$“³Ý[ÌnÀh;·;i ‘CC­\ùá«—YÁK[IK5H=7ERÙ%écsÛ;$Ç&›£c°Ý¬,i?Dîù®Š~SÔg>ê1Ÿ¼oRñˆ(µÔ÷ëõsj 1-¸SÆÃ=Fyì¡;„ÑÄçà8ÌÇ¥¥£xÂõʸë䊱¶ÉéiêŒÍÚùát¬k3–‡4çºüH6`åËn œs /¤ „Q²i-PéæñCQ1¥Äµ¢‚ÜCGˆs.;’Õ_zýÂÝêÈ,ÑFw%ª¾4õû…»Õ“¹-Uñ§¨ÿÜ-Þ¬‚Í2£;’Õ_zýÂÝêË®}+ª#ÒIÅmC œçP[pë$ö2 l…ÎW†hóÄ;Fž¯¯âeê^¦‚yè-vöËU¯=ß)÷=Ãk˜G5~4ž«?ýèê'Ûý]¢(Îäõ_Æ…ÿÉöÿWNäõ_Æ…ÿÉöÿWAfŠ3¹=Wñ¡ò}¿ÕÓ¹=Wñ¡ò}¿ÕÐoõ-’‡PZ$¶Ü¢'I¬|2I‘¼IØáÌ9¯k\ȵvíl¦ºAw©©¯¸ÜàªM«©”o.Ë [†€ÐÐÉäÀsq=k ÚOV“ËŠWñþ®·úº %«çÅ+ñù ¶ßèuRpËOÐBæÚ§º[et…Æ¢ž«aÐC›—0YMƒ · ƒ’³k4¢jŽÊ§¨¸ÐÕ‰7Š˜*I— -ËòÈÙœä’ÐìçšéîWVüg^üAè¹][ñ{òu AI`µÒÙl´Vн´´Tì§„=åÎ cC[’y“€9•œ£{•Õ¿׿'PzîWVüg^üAèY&Torº·ã:÷äê@¾;“Õÿ7¿&Pz¹LŒn•Õ㯉÷“÷m”>…sܶ®øÌ»ù6‡Ð ²Èñ¦GF÷-«¾3.þM¡ô)ܶ®øÌ»ù6‡Ð ²Èñ  ¨;Å›RZm5—Jî(]¢¤£õ¼Ûh°Ö1¥Î>ãà©]%§»Û­·î"\)ç»YûiLÆZ¨‰Í{DП©sØ&ƒŸYË¿ {2eE-¬qõλy.‡Ñ/¡¥õ€ø—t?/jè½ $QÝÌjÿŒ«Ÿ“(ýw1«þ2®~L£ôh,QGw1«þ2®~L£ôiÜÆ¯øÊ¹ù2Ñ °Èñ®V›MÚ®öîÈí®¥ª½6ô}5,0ôXÎqѵ¹ÎG^}¨Ç…nPpâ°¤­£·[f­¸ÕCGK žùf¨1ŒnãÍÎ8}Õ“VÙO#ißs‘¤asZìr$ &B‡ÖT7)-zzº¢·¦Úï"®ãKG:fm™ìÎ%Æ7É»rOÔûܸ4 °³]íWª×ÙîtW*G’=$íš7ÖšH+7#Êò-WÛ ´’Û¦ïŠ ËÆúŠª^ȧž¥­¥ I,P:9¶—í`ïÙÍs»ÞF^ÃOÄÖ½•p»TË{šÖè^+Üøá¦[†×à½ÔÒƒRÑȵ’‰Iq ‡èLŒã+•ç|¢»Ð²ì+ko3Ð9ðö+.qNÉ6”µÕË+%™†‡mÈ%z"" """ """ """ """ ãh €¹Dm ¢—Ýê}Ü7øVBǦÏOQ׎”uýãPd""" ""“âüg oñDòÉj©G‡X|ø‰¿Æð«We´Ù-`e×-Cm„}ÆT2wÿ" éâ³#´ië Þ•…–;ݽÍkF ’VÒËùS¼þEtÞ²¥xÅGÙÜ)ÕTÍÚj_É#cs˜#€*’ßPÚº*˜;Ù¢lƒî8ýh;ÑDñ¡†·C›NÛÕÂŽÙ(f¨³ý‘8•§½hËüc‚úÈ&wŽ*¨¤§-?'I$.û¬ ¿^âMA¢é:Ä—·<·Æ#¤¨~! þ$âñhZŠÇ{Z:º:·}ìUQH‰¥xêDH€ˆˆˆ€ˆˆ8+¦ŒŽñàÇÙÜWUzg>èþ¿¾(;p±¤Ž¥Ê ãksÔ¹D@DDD@DDD@DDD@DDD@DD^_EÅ*ŠCOke šcQz¨´²šžê_ZΉò·¦|&}Ks»îõ§<ñƒßY¯u=¾—QUWi›s¢±Íö-ÂI÷HÈœŽ€b6‰A{¹Ö¸€PzJÆ¥ÇdTõ{¨êëöëZ- ©*5 =TÒGj|0ÈÖÅUl¸¶® ò2@ 5ÍsO"ÑÖ'ž7ÙªIpJ:Æ>Á¨2‘Dñ]·IÝm6á»u|5ž¾´ÓA!1eáæLÍð€ãžJÎo…;{æƒÞœÈ|!Ú" ""†â éõߨñÛÝE[¾ävê¦åJÕr£/ìñJ3ö½ªéSùwÒF?ŠB‚šý©±×Ó8nÓH‡-!j¸e9ªáΚª'qšÓJò|y…¥P¸4ƒÌEGpBC' ´Ó $Á@Êcÿ•˜ñü”Hˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆ"u3Œ¼]Ñt˜ÈeÒ³æ x³ÿ×þ5‘Ƙ_QÂa~è,•ngß6ü`,:‚fãåÉÅ–©åâ骠ôĪõ ¯°\( w šYa#ǹ„Z ‹}Lu”4õœÇ@õ$ZÝ/q¨»X).5TŒ¤švntL¨dìȲ3“š@È<ŽÈ l™ÐÖÞÆ¢‰•L}í÷˜å7™,’9„íö‡¥{|xðçšÈ¬Ò±ÊËÓ©®·*‹­\5nžšP×Ã$M‰­ÛˤDܵÀ‡ày*DA7¥´»¬×;Ú¦ã%ÂãqlLžcp·l[¶Ö3ß»$äžC8 m¶Ýw§«¢ºÛé«é_+K ª‰²ÆâÂ;×9|ü+f±©qÙ<‡ºŽ£û†õ Ój½9U{°Ó_«­PºšZZˆ Ž'²XäOiÚàÇVãycuk£‚Ýl¥·Ò°²ž–CIÎÐ~à !FËgâ;¦{¢×&F\KZtãÉhð öW5óÚn%ü;°ù´ÿZVˆ‚/´ÜKøwaóiþ´¦â_û›Oõ¥hˆ"Í›‰~ w`üºiþ´¥+-EwmÌv¶±¦XêË%qá­c§¦ËK{+™%­ç‘Œuòõõ#'}Æ¿Éi÷ÿ*¡¿ØAŽÛ?€ç®ôá?‹zâàí¯ˆŽÐâ:=ca§†žés¦˧¤‘ÀÅ_Q9M%¤Ž@Ï>Ê¡¸*HÓ—¨þ©½¸qãþ$¨âwí7æÄž¸¨âwí7æÄž¸­QWj8ðëMù±'®.gâ†9k½6>î—ÿÖ+dAÚn)ü=ÓjËë«èZ8¢ëçKù­/®«dAÚž(ü9ÒþkMë©Úž(ü9ÒþkMëªÕEv§Š?t¿šÓzêv§Š?t¿šÓzêµD´qOìuÖ•wJÌë—ÑÅ\þ¾ô§š“zò¸D=ªâŸƒ\i?5gõäíW>i/5gõålˆ"{UÅO‡KÍYýy;UÅO‡KÍYýy["·Ú¸7ï%ºÃL¶²—OÐ5ò76ÂÉj+hgfä8‰.ÜA dÖö§ŠyÖúHàüŸ×—ޟ㮊Ñigð>´ÿÌ­pZÙÄÂ5¯Ó0RAoŽž¦Ó“Êö61°ñXÐâ6õà}ÅeÚ¾(ü4Ò>kTzúç‚­ÙÃ{|^õ=\_6¦VÿR²AÚ¾(ü4Ò>kTzúv¯Š? 4šÕ¾¬Ñgjø£ðÓHù­Qëëƒlâ—ƒZi5ª=}Z" —¤Õt棺kÅšã»o@möÉ)6c;·ož]ÙïqŒc¯<·§©¢Ãd‚Ï5Îx¥šiîuÏ­©’\–µhÀëYl#Fry¬9ô½¢«K^lÔQ[inÍ©Ž¤ÑÄÈÉ2‡1òr/Ç„ƒÔ3•D| ¦‹ ±tQýÑA©¥Ót”º¦+üJÉ[mí{¢¡`=®<³¸ÀsÆyxµ±èɨ¬Ö‹}—PÜ­æ×©Øâ+'Ûr%Ãkˆ-8m#Ÿ< z ÓèÛ.˜ÓðÙé$’Xã’Y\÷†´¹òHév´´n{°Ö€äÜ" """ """ """ """ ""^Q¦ø¯_}íc(ív)ªn†¡”ô^‹ê"|l‘ÀÌ·¼a1†— í/o#•êç«’ˆ‹‡t4ºjÑg·\먥´I,”µÑ²#8sã•„œ°´àLHåÖsÏ!“ÃýjÍdê©è-ÓAEHòÎv¹µ˜ÌÔá¸çÑw­sÆâæŽm*–ÖÍRK€(ëû­’Ñ–­-W<–C=54ôðÇ-.àc|‘·`œäg¥sZçg¾ ny…´¬¶ÛîôõtW[}5}+æitQ6XÜCXGzàG#ÏŸ…¿Ôði-/Wz–šjÉ"Æ XqÒT<4»h'ä×8¸òkZây¸µUvu®–·fÎÈ…’íÎvîh8þ5¢×:&ìm‚†ñO7Ôâ–:y`¨|O‡¤fǰ€yx‘ò-Í‚ÛOf²QZi † 8fI ÜZÖ€2ãÌž]h3QBMÂ}4ÏšX/»äqs±¨îdœœ>ùö"Ðþñ~ó’ãéÐ^¢‚ö"Ðþñ~ó’ãéÓØ‹CûÅûÎK§Az¤açÆ:ÏÜéè?Ž¢o̰=ˆ´?¼_¼ä¸úu1 ´yâ¥uAyèd¦˜Ú ðýΞp{î›qèäNZd=J„m>ªŒu3T\1þ”ßÎ⺴F9A~ó’ãéÔ x_¤+%ÕM¨Šô{QTÃËý{;ÐØÈÎÙ†O}íŽIð”ÖŠ Ø‹CûÅûÎK§Ob-ïï9.>ê(/b-ïï9.>=ˆ´?¼_¼ä¸út¨ ½ˆ´?¼_¼ä¸útö"Ðþñ~ó’ãéÐ^¢‚ö"Ðþñ~ó’ãéÓØ‹CûÅûÎK§AzŠ Ø‹CûÅûÎK§Ob-ïï9.>ê(/b-ïï9.>=ˆ´?¼_¼ä¸út¨ ½ˆ´?¼_¼ä¸útö"Ðþñ~ó’ãéÐ^¢‚ö"Ðþñ~ó’ãéÓØ‹CûÅûÎK§A™¦†x©¬\AåMnn~ãf?Ö¬Jñ;Âí75]+à½tTí¢ÙA^ßFòrá6]÷WTè‘ÕO~ó’ãéÐl8H6飯w›£Üõ*åãœ6ánŽ­ÓÕRÏOymÞåèõ{ÖÖÌÖòœ“ÖO3’ITÞÄZÞ/Þr\}: ÔP^ÄZÞ/Þr\}:{hx¿yÉqôè/QA{hx¿yÉqôéìE¡ýâýç%ÇÓ ½\;ä´OHYt±©hë˜*vt½•r©ªÎÜã4ÛíV3Ë9ÀÆýÙÛ„:gXOuÕ5V Ë[(ja¦ì ÆÕ¶Y"a~Ö‰ØèžàC€Ë²3Ï’ßM-km“¾Ù=EP‘ýsÊbcŽó\Ö¸Ž_!þµ¦ÓúJKn ŠËÝmÎh©_ILgc™Þ×î2;¼`>ã$½ŠÙo¶ÓÎËuº–‰’ÔÉ4‚&Æ$¸åîÚ\p9ž|‚ë.½¾Ôè*­a]¦©™IM4›à£®|ót0Ï$U˜›’Ñ{X2\29O¢/îÔÖw^¢¦lVê‰\mÒt›M8äÙÈÀÚÍÍ=éi$´k{„·Ý"êº×ÛU$Õ-ÜÐê˜äÓ>œ{›‹ËH%¹æVßJØ Ó´ÕTtuºŽZ§ÔSÓÉ·m 7E;7np8Ý€¸DDD@DDD@DDD@DDD@DDD@Eó)xŀȀOÝð(*]]­&–ÿt}²y,Î t4÷—:J‡˜£—l{ hÉkð77 `/Ö=.;"§ÏJ:?hÞµ? µtZ΂¢ñk¤‘–S'GAW)-u^9Hñµ¡ù`$åÅ®8ÆÒwÐ86zœ>ª:Æ>Á¿Âƒ)vºÖuz]òÎl3U[©b†Jš“0Œ¸É!`Šàô²Œd³-ëh—aXŽ®h¢e⎒ŠgÅ!Ô˜â×mÓw29uˆ0Wʺ?Ǩ|Ù¸úè¢=•tPù³qô ì«£üz‡Í› An¤)~¼·#þ¤þ‘R±Å}Ó‚ý@?Ù› RôüMÒ>ÊÕ•½-ï¢}’ž!?_»pžb{Þ‡pðæF<äƒØ¼ #…5zÔø¨©ÇäŠüà®tv=ÚùæíÃÐ)ñ#JQ»Tºy¯WÔur³e޵ýé ;a8<ºŽð„ÌŠ#ÙWFûõóÍÛ‡ Oe]ï×Ï7nº(e]ï׿7n|û,h¯ Eè³ÕþÊ(fñ_E;ªªð>킼<+ïÙSE~Û»y»Ð ¶Eì©¢¿mݼ‡]èSÙSE~Û»y»Ð ¶Eì©¢¿mݼ‡]èWŠº, š»®?×z衽–t?íë§ë½ úo´K†E}ÇòÙkÿÔ‚ÝO²¦‰ûapò=g¢OeMöÂáäzÏD‚ÙO²¦‰ûapò=g¢OeMöÂáäjÏDƒ»JŸûÉÖcÃýÀú.Uî^A§ø—£ â«ª–áZ"”=ª²rØÞGE‘ùU9âÆ…ûg]äŠÏDƒ3„ÿ­ªÞXþþ]§N«W‘pˉZ6“NTÇ=Ê¥¯uâç ÛRîõÕ³¹§”~"< ŸÙSD}´ªò]W£AlŠ'ÙSD}´ªò]W£OeMöÒ«Éu^²(‘ÅMöÒ«Éu^pî+èVœ:ïR>í²«Ñ ·E¡ÒÚ¶Å©ÍGhë$¨ìm½6úibÛ»8÷F·>Ôõg©o\pÜ„xU¸»«ÝÕ÷ÅLiÝ[YqÔݤ¸Yn’J9+`Ý8|‚&JØþªÌÎÜÑ—dxBØj+Çhtuæú"mGkijªú"ýý{öîÁÆvã8(7¨¼ÞÍÄ[µÒ¢åEAbµÝki-¸FË]äTG&_·¡sÌmÊàXðrZUutZÒ‚{ͲŽFY Ã(jåËV@ú£º2cZü°dä–;K‚êSW'U.¡† ¥Â‰×ÙXú‰iÞÖÉDQÄæÆìw»™7uÄ‚¦DÚKJ[ô±®‚Êéiíµ2¶hííÇAJüaýÆZ€âÜãvH¸ç2²Ùnº™c¹Û©kYCe‰µ0¶V±áÚ9ãëë[UKŽž§ÏJ:?hÞ´‹FKuÕ´Ú’;åU<ôià4ðÍ'q.’0öp!¥ÃžÇ<×4`arˆˆ€ˆˆ Eïj9û}??Ѩ“ûJ¹HÏÞñ†—ü¦Ÿ›ù5h ®<‚†àá2Pji¿ÇÕ71óg,ÿ‘\ø?Ú;”¹ÌDÚšøð|c¶u-Ì‚án›8â¾°ÇEm“øE@ÿ•YaÈãF¬gŽÉh“øe¯ò«B‚C„.é4Y›9é®·)~utçúÕz‹à‰ßà D¾úg”|»ç‘ßÖ­õrDA%¡ôtš^¢®FÞj+Å[ß,æzx[,²¹ÙÞùÐç2Ñ’@eTé‹U^•¼Ø)¨ã·S]™UI¥‰‘’ecßÈ`»¬ç¨eQ;Àº¨½ÅÝ^èþ£û¢‚Zï¡©kjê*i.uÖ§ÔÚ»W3¨¶1Æ.;pvܵøÞÐàr7’0@#?GéKv•}t6S%5²¥ì’+s@R¼45Æ!Œ´?–ç·8`¹Ù DD@DDD@DDD@DDD@DDD@DD4ÚöãQ55dVJeª¯u>êØê¥ —¢tÍì ,ØnÛÌ4œ5cYx§OquÆö‰)ë(µj/œš\ê6Õ4íö¥ñÉ–ã‘iê+º¿†­«¥u¨ßªâ²Šþφ‘”ño‰ý8œ±²‘‘H3·¯nvò]•\/³OÚ¹ U\u6ËõEæØZã5[ª¤§w,Œ…¼ºþ¦Ãœ„}t¨Ê뎞§¥´\î/·ÁUyšHäI ‘˜Ûµ®txÈs°\ÞXÉôÞïS}Ü5GÚx|)&·GU¯¬·[k¤¯¥¡tq1‚g:G4½Á»ždq d4œáXB7KTH2¿¼j kõª‚Öúª |—*ìŽ8#xnKž—8òk[Î<ðÖœy ©£Õv']"†UOLãÂh¤tRG =„ŽGÂ1»jôí9kM<˧$?õh,…ǽã%”{柸&¢ûKàÚx—ðÛNy³/®)-IA¯éx™¤_.­°>z˜.‘ÊÛkrÈ¥ ·²Žìô¹Œ`õ寨~·þíhæý³Y_S÷zZÙäÿ™vÍnâ×Zi‘÷ţþ±|ö«‰¹ý{i6&õÔh£ûYį†ZcÍ™½u;YÄ¿†ZcÍ™½u‚(þÖq/á–˜ófo]NÖq/á–˜ófo]A`Š?µœKøe¦<Ù›×WË­|MÏ-i¥€üX›×PY"¾'ÏZiSþËÏë«ëµ¼Løc¥|ØŸ×X"ío>é_6'õäío>é_6'õä£û[Äφ:W͉ýyqÚÞ&|1Ò¾lÏëÈ:)\aãÕÁ™åU¥éHùz*ªŒý0VÏsXÒçÑ’|Axíu¿ˆtüh³j}3Ùuºz½Œ—´ôa±TRÒÎËÉ'¥Èvá€0s‘½Ö-âE«GÞ®u:¯K>*K}Dï Ó³µÄ278àöaÁÀëÁû…Ë€Ípঋ{ý¼ÖJI÷_^âV«Ìø}bâ=·@éÛu6¦ÒÐÃKj¥‚8äÓó½Ìkbk@.ƒ$c¯>!Ô·¯âoÂÍ%æåG® °EÚþ&ü,Ò^nTzêv¿‰¿ 4—›•º‚Ái·ñ?îªÒ$üºv Ö/žÀâŸÂ}ä [Ah¸qÀÊÒé¸5D=?t—+Eq;zo ’›o^íÛæ“wØãÆ^yníqÍf×QjË•u4QÛ!m+žÈ®"ZŒ6G3sâØ6·¯'Ÿ.µ¸¿_)tö—¹ßk7¾ž‚9§{Xçm.ï@=dœò•‰dÒõ7ö^+ïU9©ée¤¤BÆâ’F=ÁÅ ovcŒg—&õd’»gÒÖJÝ3v°‹l´7C;j›Mc/t„‡IÞoÔwuò$·ž ^l6»íUïO[ãšÙdªº¶;Ëgq0³C(1±Ìs‡Sš×·“²G{»¦áÅ"ËU¾¢‚È'««‹SMWÑv=@¬¦¤|/pc°ZúŒ—rÈwÀ»ï ߨ™Qݦ­¬’[Me©²EM$ESò;¿0O!àhÉÏv¥áu²í¨ª¯Ô÷ëueM<Ê!ØèŸ$5LÉË\Õ?¹£a>´Ô æ•Ô•w;ÅÖÅu·Eo»[²Å OOá˜?£‘-iæc‘¤‚ X šU=¥ô×j+®7J«…EÊér1 Š™šÖžŽ Dqµ­5­ÜóãËÜrr¨B" """ """ """ """ ""ǦÇdTõ{¨ê<ý£zÖBÇ¥ýQSûèð~á¿Âƒ!7q§áí~9C© O>&ÍAWþ[™ùp®ToÚ“‚³õ âÛTÓþ(el;¿\?* ö¯ªm’¼V¸á´ôÊ~ãcqþ¥Ó¡)M‰±Qž¸-ÔñãïchZn7Ô:ê(£÷ZêCo‹÷Ê—ùwJ„,lq¶6 5€4 ûDDD@DDD@DDD@DDDA«c踗¢+¼}u|€KÿüKëÛÂ-W ©µOLÜxåaŒ—×H‚¿IW»ÚÁ¨!kï°ÍþT­N,ý-OI]Ww·@þ0ud[‡Í'ätmkÀZ0ðʤ@DDD@DD;Àºh½ÅÝ^èþ£û¢»Šê¢÷~øÿ>È îDDD@DDD@DDD@DDD@DDD@DDD@XôÞïSÖ~ª:þñ½K!cÓc§©ê÷QÔpÞ´ˆ€ˆˆˆ€§x‘l©¼è;õ²ˆf²¢‚fSò» gò€TK‚9 ó­At¥Öv~º…ÄÑß.4wLg˜†Ícsÿ™-?)^Š›;…÷ mDn–M{w´SÄÙÛCE $±Ò6y³5†HÜpç€F}¨G.KcÜ~´øÙ¿y.ßè\¦GA­óË‹—À?ÛýûîG[|k^<“AèP\¢†îG[|k^<“AèS¹mñ­xòM¡Arйuž\Xºþ[=¢_CJk‘ÕÅ;‘è½ ”PýÊkŸ;‘èÿ°ÊkŸ;‘èÿ°‚á2¶iccDŽy-kÞ@Éwƒ«±“œÊí3m­ÒW«,Û »ÇU柳­;¦c¤ä0]ƒœž¼  ÕÑêíE%ÍÖ*7o§½ËCÙô1 ³OQ dk%ú¯C¹®a’<ŽŒƒ¼`õãGï®¶i꺭7c¡7öÉ%)©Ô8ØÆG¼ïy§äïh<Î| ¯Oiih/Æût»Íu¸6ŒPÓ½Ð6&Aà÷µ­o…îk ‰';ÕƒžŠ-l‚-3 ³>ª== ±BÉ£itŒ %ÃcA²Ðz‹º:Ë·b²˜™æÍŽq4N1HèË£½„´ì [å¨Ò¶H4ý¾ziè$¬¨ªc CD],®°È4>E·@DDD@DDD@DDD@DDD@DDD@Xôߪ*z½Ôu¾ñ½k!cÒ㧩Ǿîƒ!/.vûUÏEW]+©h)#¿óÔÊ#¹¢ª.væ³Ï¸xºÓ9ü)ö—O£cïúh{{~r2?PÕªÑ `DÀ>F„Í?©´ö¡éûC{·]E>Þ˜ÑÔ²]›³Œí'Áþ¶Ïö…p#cNZÐ>à\»Ú”=Á‰. Õ2âÊy릢žKŒÑ2¡’SÌÚ†ûä,{œç4†·kcpi §]‹G u”5TG3­—àŽ–WNdoDý§›C‰'½ã—2¾ôN¾¥Ô÷ú»TB rJÚ–Jæ†Lb-™ç ɆœäÔZBÝßo4šKܯ•¥Î¦ ŠiäŽùÁ¤ x\yã$xÐxõÎßg”]"ÒÖZôÓìLŽí%²Þóe˜z'ˆFÞ61?LßÜçµkª­QÝ{Íg°iêš1¨¨e’j i»QPM=Pp4ä–±ÍÚÂýŽp%ñää`zý%ç[JÚˆj4m =OBÙiˆ¼—@ü¸G#Ä;™ ðöŸñ”Ä\Pº²Åb¸Ü4õ’Øo¤š'Ö_+CY¸¶Y?y#¹05Û°ã‘„žÛf³Ñ_í³ÓSÒ˜/AI£¥†KD-wS0rqËyWR¼ _§k*.VZjêºPTJÜÉNéY.Ã’9=„‡Œ‚:ÁòD@DDD@DDD@DDD@DDD@DDMîõ=dt£ûÆõ,…Mîõ^ê:îÖƒ%gLÐUék›(«jà·Þzz–ÒSºi¥¨Œ;cqžÑÈxr»»½¶}¥Õ^@«ôj³ œ Óiëõ=ðÔz;¥/C·"¶†ZmÙϵé7usÇW/ÜkÉç•ʽ¡[§nñWvÄÔ2š’Z:V vÆîŽI!2¸ª¼ËÂy—¶U:bÍ_¦.ÖÁŽ–ŠéÙ ©í .t„‡? rwQä%Bïé¢÷u{£úŽ~È ŸÓÚzíI|íµïQOuž:CI‚&°¼9ÏsˆtŽ-f]ÈÜ4 œá³EÕÑXmËEöX¶™ÔÎeLzz¨Ý·=,Yn\ AkwØ$4A¥Ñ~ /¦éì´ÒºVDù$.,ké$tŽ c@k ÈkG&€n‘šPñ>¦mCKo–Ëoìj«Ôöˆ=ØMXägJê~ŒaŸR.$<íiÏ0ú÷­jªÔ‘:ÓWV,Vø+ž)œ×>fËÓdœ·¡<óáù9âIÛ_AL`™Ôõ^eºödQ5³“$’½ñîñ‰\Üü}Ù4EM5¾óÖû=Ê®ënŠÝ%S¡ pŠ8äcFNé •îs²'¨ \5Ìôn Ú~­ÖÚ†Qô•†V°5õO c#ÝKInì€áŒžB²œâj“‘î£Áû†¨ñ¡jâÔöûÄWÆ>: X)ià© l½ #<Äíã{Á;œ8 ÀSÕÛè®ÕÑÜi#«§t­&)ØÇÖ‘Èü£?uMúö-W %§éMָчoÛÑb fÝÕÏܱŽ^Û> (Šþ)Ö3PÜ,ôVKX43U2I®7¡FÇ6‡%¤Äá“Óu1·¯Ÿ+ Uašï¶Z*îÀ¬¶UŠºI "VtoˆµÌÈÜÒÉ98psÉi4ÿ-”7—Þnr2ï[?dIPg¥gFéf‘Ž.kNvˆØÐ2ydœ’‹_Ÿy¯£‚Ña…Æz :óI]se-|±ÔFÙ3ilÁØq20nkš:¹Øê}aaÓuT´·ŠÉ¢š¥‘Š’Y¶FÂÆ¾WômwGLŒïÃFá’´ÚÇBTê7×ÒÏ{Õp,t´ÕmžJbÖ“K#ÔI Ú»k²æà•ÛÄM&®u6Û¯kú%„HÚV¾xKË>­À‡Å+vò!żùµØ1õ7ì–9Û:–Ë=K¨äªe-4ÊÝ­qh/‘±â&—w¡ÒŒçÄVöÏ«ì7[äÖj*¹_W”wô²Ç½öKÑJæ†K±ýë¶m<ŽÝ׆ÒT[d·Ð_¥¢ŠªÙÚÊ×fÈé!éö–óŽGŒàŒ¬…—¢ø{E¦/ϯ¥’•дÔô ml™¢izGÍÍÏÚr6äûq€¸DDD@DDD@DDD@DDD@DDD@DDD@DD¼@ÖÎÓ=¶*[{ê.¦nŠk•ðiZb̻٘õWoïß¼ämçœÍ_oŠØú‹“_AW=4Õ4ïÝ ¨qdM.fZì½¥¹ŽY8 «4ý}Þªš¦Šï؆(¤‚jZšqSGRÇ–¾"æåí,ï\ ç‚<¦cáci¨á·Qߦ†„ÓÑCPÎÅi|†–wÌÂÓœ1¥ÒZp ÆnxoÄ+.¸µRTP™é«e·Á]-$ðKk%nrǽ089½#2ÜBÔ³‰5ñË•ÃMÇž¥¼ÍhulUæYÄŒœÀט:1†¹øžâ7GšØè .™6þʼÉs«Dv{~iÛGNÒÒKðN÷»£ŒÈaƒIǤáôñÔÉú‚i-/½IzM¦c\gtÆvIÌ–²M®IhÉ#9 ††ÖmÔÕž[k­óº‚žå C%ßM>îÄ·Ú»¼v[Ô9`ŸÒùz¤Óú^å}­s- sÏ ¹sƒKŽð“Œ!it„§ÓU•¯ž–¦ªªž*W¾ éXèã/!Îc9^Kœ094´{:1h­ÑÕú`Q6–ÝYð>8#Ú\ ›€yäuà ÖǬ.¶ÖÉ&°Ó½¨€±†šZ‡Ü:W½ÛDdé²AÚÖ¼c'w"¾îJÑô44µ³Üª: ˜]P×Go¨Ç_±î”5„ÄòíûKO#‚°j45òºãIvºjé*n6àÁns(ÄpFAËß$AÿT{Æ]–àghnN{iøyLÛT´rÜê$ô³C<¢6Ï–¡Õ<~ça¾ƒ²§Š:6™¬3×WÆ÷6g¾#iªéadNh•ò³£Ý[½„¹àQÊÉÕÿNéý?v¼TÕMS±ÒE,T°>I+i»'£h1wÛ³·›† œ¾ðþóS®ªkí—n¡¸Ûka¬™Ð¶WNúq±$`íˆãX9Âí¾p®;œ7;{o“ÓZ«¤’nÇdÒ2GÛûýPžm ïƒp;ï9 ÜÕñ'HÑÏÐVܧ¥{aJé¨*È\aé„R?fÖMÑwý "B;yŒìj5…Š›L3RMSPËl’28ŸØs&säÆ#‹n÷—¸´3kNíÀŒ‚ »p†’é=ÅÕ7 m·?ªÕ¼Zâ|Ý7b¶ Ç#òXÎñÛÍÁÀáà+][a7Ý8û;f‚ ]ÍE#*"xcö¾7r-;ppZ᜴ƒ‚ƒPÞ$鮌NꚇC+Ù4pÐTËU+ȹ½ŽØº@X"“pÁ-ÚíÛp¾,ÜNÓU¶ë=MDòSIu§‚¡dÍ-™å™fc6DàZÒòÐâZËW æ´ÏEq·êIÅÚ’YEE?KŽN:~Dmh`Ý–ˆ˜2FAê£á¾Žª†X뢜CMIOS%]º¥˜S=Îk˜â1pqk°ÓÈÐÓͧ4äer¸`ÀçÖ¹@DDD@DDD@DDD@DD×A’Hàrdvãó@þ¤Dˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆˆ€ˆˆ|C‰…£¨¹ÎþOõ¢ ûDDD@DDD@DDD@DDDAÿÙnut-2.7.4/docs/images/cables/mac-940-0024C.png0000644000175000017500000003034412640443572015162 00000000000000‰PNG  IHDRØ—ÆÊÅiPLTEÆÆ!!!)))111999BBBJJJRRRZZZccckkksss{{{„„„ŒŒŒ”””œœœ¥¥¥­­­µµµ½½½ÆÆÆÎÎÎÖÖÖÞÞÞçççïïï÷÷÷ÿÿÿÿ}±Z{tEXtSoftwaregif2png 2.4.2£^G IDATxœí]‰š£ªæ Š»ÙÔ÷ëû?䥷$Ýž±¾s&íV"üP U€Úƒš!ôÓ8èséÇA³t€ã Y:ÀqÐ,à8h–ö‚£ÌKé2gT4ü\SyQ¿£Xe5°iê²Z-U%~»âuE 4)Ÿ¦Àývj8Á‡æeÓNh¶$®“£¥à(Ml–ýôJ†‰ŒóLÃ0H¸Ö”[^c¿à'1Èr]fÄp ¥2c«ê ÊÈ4 'æ­X'®a˜ñ¢U''”/ìþœrjî®IŸ3ÈYÅ–ß„ëäèsi'8rŒpÞÿAÿdÑo½›ü;/£ãýÙØðw¾xûÅ@.ü†pkÒsèŸàLa‹SØp+í õ »?'œêw:…T =¿ ×ÉÑçÒ«àÀ´¯j­&pD ,ãb BX¿, ´ŠÑßÊ„[ݦ+Ÿã¡-i—^Ç=/+#§) dœ‹*wh³j¤ñÞ×tˆˆ‡?ë’ ôžuÕK÷ÂDŒÒ )-DÑ•ïNÕ„ºŽéY¿m|zÂ‰ŠºH‚6žœ¡ ™SM¶Ò¢®ò=>wÏéø f¬šR>bOIJÒÒËà€#ZkfMÁAõÈ,â½_àO ±RŸ ¥L‹7c ú ¹²á;2‘Eߨ¸Û´W¥ TØ@é(pŒƒ)§c‘ÓLNŒ¿P‡Ì ¾“—¡¡¢ïS~rI šŠ[HïÈ}zÝ>o•ù¤®¡Ãx·¼iêW±ÁÀa°«MŠxKéÆÞ¸Ø7|x•‡Y¦'ÂvÓ*mA“Òv´;õñб×LNŒ¿PÆ© Ç”J?˜ðS LØ!&þ WÒø-Ó`½õ¶[•çƒfÓTZãéEzƒXÉ®ÔHð`ü€0œëëÆ,¯K)ýaC2­|*öM Úí›T à:@ExW¾Ñ¦«"z.“ ÚÖ´‘lªŽà[ǽ)¨ŒŸ˜|áŒSSƒNažRhŒ¡Lø©&¼Ä˜T‚%‚8è öZ Ö[o»U¶i¸âíUäÐaÔ¹½n&ªô8z™^À?&%úÝ`#d†¾€—ÇIÆ–I-ªó ª3‹Žä¼â¯x`%±ÁÔRŸ §¶×9òÇÍ¥gnÌëirB~µNçè81‚Ð3Hp¯æù)ærò\†¯âŒ©meÞ«â‚…$\®þYƒíFe)ï:¶Á$òf-w/8 ®ûµ)ý£î­<ìÕàØiŠìäÐJ¹¼Z*¨=*ྲྀ¡£BUY9Ä­éèy‘vSªêa*W¨^è·àä`f,Õ ­ZÀËã–Ѭ ÜJ¿šœ_=‡Ä©•lvlg³ü”3&PHÐf}¡žÒ?Ïà[£ím­ˆVÑpSo»5NoíW63Ÿ`T²ÞâìéY±cx-LYÇOjpì¨æºþ­›^ZnEÿ0ìM]Ü/6³” û)ÜÃzmÑžMÿ¦1Ò<¿ÑAå.û90¦*aêþ ãò« ùÏ' òêÒ‚€.dæ³üä3VÌs‚Úaµc€ZÕ\iTV0ÃzÛ-áÆ­}¤~V7zjlB¿F;ÁQl°gú—#F\^•ð­WÅ Æ”KŒ´gÝ)Ò|È› "›ÕÒMsnÈRŒ ƒË8è£Â;a:®Úc‰;(ÍêɉžÝÀûÑwz‰“p}×ÅÍ·ŒÁ¥£á'˜p®÷®SÁGô´Ì—ãc!VNˆp\c³›ÒÕ@ö[µÒà %Aæùžø QUg¨~œ\LÉ.|–ããË j…Ö6è©J7¥Uâ´¬Ö‡º.{ïDWn" ö„ê1õø„ÆZ¹â§n„¤TŸSvò‚F.0+qŒ%p0ç»A¬€Òj/(áW—yüh^š*Ž‹e>;IGÅuÍdÇ™„€¢«à`xK;ö Ä' ßÓÁ”€`øi~fàÀ¢>ʼï´Š»º¦#µh”¨&ØMÐp£cr¢«£Óó(g€ÝˆSi ßÇü;3/¨äs„µ¼˜|Äebå:G–$kmÊ­7³z`a؃W¥¼c5yç÷{?Ià(3GY´Ð¸´R¹‡¤ãzK…ÙŽÃwø9xíõºàý£ñÄãÔ€ã ;à¥e(;õ.ÏèPcpЊÄïñ “Ý‚ÊÈÎg|"`Ä ÆNÂ+¨r$õjÌO)0cc?Ø0á ²9˜ ¦ƒðz}€Û•Z6{p›ÓîHuÿYÕúuÀQ[½æu_x IÇu݈‰_›Ø’V\\z锿.÷xíÑV÷VÞoÂ’ƒa»Ÿ)CaÈ‚JTm&3ƒ£=ƒ‹!L7ÏàöÌä„ œ?=ßä.“1§z¯{M“«eH¾1?µÀL£´iÇcªIoÊbrM6Bª^œz‘DUY˜€Ê{m§¹g_e­4!h8T0x§Í@|ƒ3TK¼ö.P±¢*k|Jll¢§À †áÈÈ©ör×y×ý¨:a\&à¨]pœ‹¤©î„ Òb’“ylÎí À§ÀlG»š{Z`>YÄdnЖ%;¡Ò×TnÚS̃ Ïžz ßAòq/¦ }+:$±ÒTžy¹'·hIçøâµ—aÔi„¹P8©ñǬ{.°qîQ[û¤ª ÉÆàhK_ؘ¤ú‚ §›zgs%˜5W£ósxJÊO)0›`EêžeÆçø {Ue`¦=fÆk& 9“˜Æ>X+pɺ ³]$+¤ƒ¹Bñ²Bù*qyU{„œ{á•ùTfݪ„Ú÷ÚêBe[ø¾&˜åLüÚ!N®J< &;Q±ç=ö''ºç™ät¬Ê¢Pvö@´†wµ)Füä“;=öÒFbYG”‹{^w(3Ñ ܰoAQSWd rÎ<¥T‚á• ¨½¤€Ãg>™N[ÿIâòª©Åܵ*ÊÄEå,œ{ŸÄ{•+šÊd/K6Ôx0t0Þm"Êç’ßÉÁ!ðà=¤˜²  ‘êséÜÝý 1p0Ç 3ì‡þ{Ý·Ä„ï’!ñ ©~ŽüEtÌŠæî>ègˆÉ¡Â&Äe‘^†‚­ÚæAå ¿¤*>üžzH+®P´¦#‡fÎÔ‚R»Ö„ô 5\4‰%ú6é æ¾ù':qý©ÕÝ…™^1Ö âŠ€*+$îoŸ„ôá”GQt ÀÂ"}oéUžåšž>ÌÍCDmw4ÌI&·ÛnÃí£ðƒƒ>êlj4Nƒa­‚ãfá~^Z&>3ɦŸÝ¦;ÂC5Õˆ ÉΙ†ô销K{ŠY¯€#©™ÇaóFE]úlþˆU~?' Ax§’^3ØÓðƒƒ>|h²›ªÓ*:‡‚&&JGç%¬èÈvG÷~NRL Æ|ò\~ð•J"cÛ]?¤"Vc5L9©ÞQªºóÀ¨]H_ÜTÀej5ᱹ߾fêáIÊ|¦o(3cªµ‚œ8¥¦ìÄ^î]È!Lnó‡ÄfaÅì2¯¸th™„ì§Ü WŠU¾O!Œ ‹€PeŸrR>h#Ãï-¦bs€ÛCë+b%ãó9Ó`èIÈÄÊE Xñ’œéÀ¡†즫)Rº¢ä¦` s[ñ*'åƒ&ìæA.cF‹é‹h2ç²ù⦀&F"OO¾µ­¿Œ™Šç¢{è)Süžâ¨ÔH°J£$B³¥:3KJ€™ú>B9©€¼c£ÛEDj÷Œ4é‹ýÇÊIŠviž²n16’÷K3]ìieÆcœþ8Jx„§"Âoá@‡-ce!†©‘È6Àw©I ä,ªÊ ³Ÿ5ÿ\?NJp„Ì”5Édn¥+IÁÚ¤;J:TÝz,Ǩ„ìG™pû‡jByŒæÝ 1îÃ>•“•|Pò ý¾¨£iúb_Ir’b—vÙÇú #ºP¬ø€J_šÓG ¬Ïó£¢w#.WÆH亾U àÖ@¬Z»ME*€‘ï#ER*àð‘ Žû©mÇí“ûÍ¢%(ºxìÈêF ˆöôˆ“»õ·KáOƒ[&ëðwHogŒ I¾‘®œÊÉ\¹CL=4~¢.ͦC*IŠ<íà¤Äœµ|„ó'飄GÞç NÏT_ác}äPÄœCRê¡Âˆé ß/>ñŒ© pZg²CÇ‘(˽ÎÁÓoº[žÖ¥m„P1CÒç(üà)±…„\.jÿÐl?”öáÊTºþd¦½ƒ§áIŒfÓ!å$E–vy)2¯ã$ÒëL–ƒ¥¦?ŽY0ص¤¢›ŽÿM¶UçPDŽ\ÑCÅÂz!„‚˜ÏšŠtÐÀ8%HÎÌTÀqfìô:€c¶e?(öY±B!íÒÅGá»ÁѸÛtmnÿœ![´ˆ!'u¥“Ofš;JªCÁuŒÆ¯–“©ác€ÎiZp€ñvg«œ¤?ŽsP”Û´[­ÕHäàxÐfty]ˆÈpwè™O˜ŠxŠ~çüÈa‰:ƒƒ”lƉ#‘Æ xr]÷R·Káóß,=xv |¦Q?XbÙЦŒ]Â2Zxè€r2“jQj«Ô,ƒmÊhü¥r’bÖYKáŒXÉ}lx $dަ飄Ç\Xw£KdÙŽËàÈS,™hÁ­•A¦?a*l`Pþ¥è󢩳,»ßnÑ´ÖÆAu½G „<ã~¼ñh\Z^R¹, –Ù~¥¬^˜ÝJné²XlÀ¸QËŒæÒ!•$EypÖ^éšÉ,Sž1J'<æ"é-W€¶HS#‘ƒà{‘QöÖÊK¦"3e“LMpœñù,Œß›ŽûUÇu¯!äîð øXÃÌ ñ¹76hréöNÚ•|µ•1Ñò„¢žQ1“©$)fXXM=8XóPÉé a^}fpÓÇ šqg­4F"?UJʦ²µò”©XÇïÇJOÀq%×+1õ¦ìwSçmr¨®Ð» Yç¸ðv™t£ò­Nzµ|@âÞͯ0ÊgÒ!•$Å>m<ìD«ë›Ô¼¹˜n[OÒG O‚C5»d­hðaPü8²å‘ã SÆWJŒI¬ÔMSWT°”?ž¶R¾Lôõ¤€OJ!H+d‰ÄLºì’OªL>‰”%•ÑL:¤’¤ÁÝNÍÝó¢A3áwïš—çlÒf™¦?ŽGà˜[B¦©‘Ø£v3dŒ _÷=a*žÅâ‡eW±ê>Of‚}¾™ O‡\Ó›Å,Eަ–Ìn³cp±HQ'ÊIå´3áz«Êh.RIRxu@°ãØA²íÂòßn" VNÄ£„ÇéÈ=Vjzb$˰eFj*r×FÓ=ôŒ©è£«ø›tNG‚y°ÏGç$f—°ß»Š… <ž+SNÊ5_ô†Y «º«±™tH%I‘-šcvkÖ]Àú¨ 0¬@¤?âQ£ Ž‚±ZIŸ‰vÙ[gÒ›uâ»®Ÿ6ýCϘŠ'ÆW¤¤¥Œ<¤vUW6:·?LµÈ,4í§«Û›©ÌArRNJ…¢f¨ŒæÒ!•$E˜Ö¬ñg2S:| ëtš¤?âQ£ ?ëþKM¤ygý5³öâs¦ba¡€ƒ¾Þ”mÁCj\ÓÈ@~4u’~/5МPª»¾T7È!TgˆÔ“ÃA垺>4t§™tH%I±¹{®{éŒå’0Oơ㓋sœþ8JxìN‰ßê •”¶Qeô›òk¥)„ygûkvDO9L„ÎújªÇHxwÉšœÏ˜Ý\ŸúüGÅ ê’š ×„&Ò9'é>ªïU²ÊS“¿‹º`Ÿ @ýR2Ó\ÙZ?1»‡šÔÅn¸¶"É/¢]°OžGý—¢¥ž¤,0i dzšþpôbE:÷~pgj ‡Eø7ã¹éÃ_E½X‘νÕ \{4í_ŽU"ßI Vnžºp<+«Ë’ÝGuB­~7Šü»ÀñPm!yiryV¯Ù( ‡"K¤Ôàø]ÄtÚ~z±r¦ç6}h;1=ôÒ>8~ Ã’ú*8^ð› =´?>Àñ»ˆ›²gOo­\É ®¯^íéÇï¢2¹b"3lv9ž£®XŽnµ{bEÒC{:Àñ»(ìg(‡v“LÙ*q˜'‘»³Y=TzYÏåœZõxgáê©©«ôÞQZ½ÇÊö‘KÅŠH0LQKà` ²i·]ûªzhOSpüot¼³ô¿Žþã4:~gS=¢Ðu,ܲ7Œ^_ôÀx6RC#=àÓ cð ,;´æ˜n4Únzp{q±Íà‰üÂÄ!jd‚ìSeY¢M‡ÓDE+bE@A@¤ÿóo¦·€#£Ð¸OF‚yp´lkå_À†ƒ0ÙŽò¬«fF¹yçËOäw=8`®döi-e²aå¼Ù¤¦‘#‚Üzh»Ž”¦ÖÊX[ά^YºNGîœvÑÄÄÝ¡˜RSÖ©1&XÈr–Â:fý¡íŽ÷Ru24ëÁöÔLÓq¦”¹f´Yóð‘Gµ°ìS›z§<ð/3ÃÑ‚?tJ8^¡Ò%»5Ê)Ugó´U´øÏf«{ «àh X¤6Ò®¤6£‡ÎÑŽèáxïÉ„Hmo£Qû0ØR“ެ1(à ¶.îÖ¿Ѭ:G¿ý÷»hñk ßݸÑ}%u©ÎîªK0ðEjÍéfô³zhŽè¿‚* ý6p,££>Ù›ÔRi@}®lŒÌR§ä,è¡C‘ Ÿnö4_K#Åúû)2÷Dkn@ǦÕx¤…ñõ¹²%Á×,¿tUÍ¡dñˆºg?Z¬|28öaãmè`Æ!nöͯºÏ]¶º£ªsTެ‡¢É Û àxŽl,Ô5ê[b [ÇŽ–-w¦Oj¢”"dLtŽ˜˜ƒ Å9Àñ…”ް1[štŒž«CgÕfYNjj™Î‘OtŽP¾óÀq€ã)ʬÓPùk=±»6,hÜL¥»ºÕrRSËV8ßó‡ªs(­ÝH‚¦÷ ô°’v|}É\‘tèlNëJéäÊÑÕt¯XUJÆ­¼_!ý#H9Ü[Î÷Ò7ƒ7’4†°¿z ŒÞT»× £E[V9”Ñ¡EUíOßk"ÀÍPGæòä°Ž?ú§¾‰¾A¬t¬Æ¦ò§ s&ÅF•ƒ‘:thÌÈÒ~Ü熃ÌÓ°{ÐËàh£¿_¬h•4>!?”[“À÷‘2tè¾âBæà$÷¹ß³‘oˆØ²¦à€èÿ58ZÑ<{ÀÑìõG´êG¯œ…õWr‘Fp°íÊ]¤óVžÌ¾®­2úƒCÖ9$,´Ó¡Dz(–—+\ªíùk«èH-m¤NG¸ðgÀAí+ÎG‘`¼•'³¯kÉF¬pUãGg­Ì€£´eKe©¶®:–úpø £S}eÎso`(•¯ÈKÝŠŠ¼•gg_ç蟇äçõÏ…ôìÊ=r©¶—®ý÷ßr~ª±,SéƒØhJõ|)ÆX'öÞ¨öÒåï‡â!›®@¤7d¾ÕöÐJnw.–/1‘Aᑚ)ûð½ÒÈñw[+#n8žgí½4?tøÈ~°]'AkÊ};8½»ŸCå¦7+U*ß4p¬:f´Žviò,«†¤‘ƒè‚G¿Â”ýql|ç¬ìl´‘õ]»ò<Ì9e±ºYûühä‚ÏdȼŸF6e_;»Vaû’W±m»™/C»šà àørzß·!çè©QáR¥ëÒ9ú÷Àñ9ôŸ>gùȃ5¯5Ñ™²st€ãçè >}ÎæÓöŠ.å­e•÷WdDί6«£©7÷ÏçѸП%VîDiñ5è˜y[ˆ¼ÆG¤ÎaËéõMB(¨ÎØJGO-‚cêÍýi$èhTèGzóòkÆŽÊÒ &;Ù1AfžáíÍ!§n`‹mE=ZÇœOî“ÄÊgƒ£$ssõ_ƒŽ³ÖÕQŸa§ú,wç–<¸…àÉRžþ tŽÇ(ùRŸ‰™Î;9¾)VFnÚ–™T˜áå 2øC7d¾;Øç‡é³À1~wäÍO•~:ª¹‰{i²^¼½ÿ+ÇB9!rŒo#ùUúeƒqè¨R˜÷££ñfbqÀÑ8È8Çiäauæ/Q¿”âܺÞõâ _1t\fœ¤ àh WØŽ&Øg7à^µŽLÙ)AbŠŽ­œõ”$Žv 8 ¯£å„”0ÆÄ·ˆƪ~t€ã%ê1»®wl›Jq€MZxóX2 [8ÆËó(à(òrŠ©/‘ŠVzšè£j ȸ‰w€cæB5òd ‚H0ƒÜÅØØŽ™[MÓ<`gH¦ìI?®(N°G}Eöxè8Àñ©à˜ú9jK;K:‡‡}r3w†šõËû¤&Œ5k‚åçíá±ûìÇK¤Z+SX1]¼|wú8¢W#{ªZæ<¥‰’nM0öJïHjúaúä¹ͶÓö –MŒPkÀñ¶3äCÚªEñÞó;B÷G¤(³ïHjúaúeà˜–a¤ nã¼>"ÿ¼ 8xŽ=ý‘íàw$5ý0ý:pLè½nt=8;›H{¯,m³#[6vãK)77-?ý^pLünÜ˳ò5ÑF‚ñmÊÄÅÆtäØMŽyúÉE˜4;h»Zߪ~øÐ’Š Ùi]3a‚°· ì=ëþýSöóô“àXÐìÆWfµ¾?VõÃTÑá#¿t"ò‘28ê»N;ùë'Þ–è'uŽg¨eúóçÏÚzÃo닱1¸O%é ,åÑr:Àñ¥´M!ÝNÓ€Ù)éÀ;àŒ+³(Ô™²5UÈ-™8:p|)½šhê)í7e©&‚ ÷‚p|U1´ô~p¬ß´ß”m¾ÁÔ—Ñ|îŽ/¥‚¬yŒöÐ&l´WÍj +¦,•87‹ÞbKëŽiÎn:À±f&Þž£MBE?ñ¶nʶ5>LÙÒÙÔ´é±o¥‡ýÄ¢äs´ m  !Y3e9•7ÛÞŽ_™Ô´DßÔ¤ ®x’6bCB²lÊÔ(éÐ9¾–.skíì¦B¥-ukäçlN¶Ì2i`ø'²ì?‰f¢ã9ÚˆÙ”· ý#àØAíºý£h[uÄ[Ǫ8Ê<ÏïºxŽÝt€ã»©Ý ŽÓeýF*8R“í©‰çØMŸŽ]ôíYö›ÛêMÔ¸[gÕÏaðí!Ý¿,žc};86òo*[ii}²uø~¬Ey[Q\Mw‡<Àñ4u#¾z¬Þ³Y?Üñ³%&è®]òV¤¤¬##ñ*óÌBÞ#ׯî¦@F胨²Ö‚µû’­”m[hz¨]섬(ºH¶r¥0A³ÒCçx#³ñBïÚµ½Oå Z+Û¦µ´gVKöщª#Òjqí4†6‡¸÷ü0eßFSph¼`ПoãS_Cµ;7Fù(¬›ŠÌš²öÃâé‡)û.šZ+:l´íí›vãIf7ï¸!L¢†—˦lSÃ9§Åá>ÿvZ:Ö ‚ÃüÀÑÖ°¿ŠQ­ÔD ñÅÈpnY:ä.úp´7{qèX36 ÉœÛB*l–eÅüÄ[[Æ>Ó9Ô pp|=ÕîÂ:ÆëÁ6ƒ¡vç÷+øœô )P°±ËæÊŽo¡•­ÄßBÊ–è#êÖ“³T±B`ܰ.{¶Ô˜£;éì}õÖ²9YPlòˆ’ìŽSj¢±¦:Ç÷SEU½ºÊ²$NÞo½4³Æ´&ÊR£c#0ã‡^œ–Í} IJ𢠲‘2n¹\¹r×e$ôÄt!9ÅJ­‰âæâCçø¡76 R6rõ{ö¶¿AgÚ¦ë,ZËBç°d­d$Vö‚Î1uÏþ†\Ù]ôcàhs£÷¢ëfÚOÒ >±iû‹: Ó9ÎØž™[ âÇåÈM²Žß+»‹~mŒ»µ1®ú]/vRÙ¯TÇÞ·ÚšqŸ5š"É­¬‰Cû ¹²»èÁÑžD¥iWËßO+ž¥k/¸¿1ƒ*³¦¬–VÀ±å±_E? ŽÆÁL!¬Þ¢rôy¸uh™k¹üÎ!/{Á±%¼yòد¢ŸlI¶÷¨½Òq5Éi-`„éQ¬xÉ”š¨øœ[–Ìú9p|1eGyÊÑ)©i-+£@+îóújbÃËÛÆBs~ŽC¬|9%تޤr¥#'¦»K´â>212ÓÚTolVùñ<8v,Ð~€ãY:!ûM*(E€Í1ˆ³îs1`Ü PpÛédzÔxê¢l¯PI¼ØXvŸSUƒ>÷?LUhyoZœ”ŠƒºíŠûüÊN¾B7.5ÃÏ7 Ð'”¢ õ»{î¥&2ýÓ»+îóæJâº)ùÕ—qû:8:Ǻ\ïÝ)™>¡Y>¤õɼ¾nÍ–!Ù·Žå¼û¼¹`l¹a_R¯ïñÆ«[³¥ÝÑ,RŠ&&þ«¢%uœŸ¹D+îóBÌajÚÓë{¼õààni aõ@ùŒfùŒR´mæ[ê:;;©:“ó½vÙ}Nuï‘_eXÙUãõ=ÞúvŸ€£»ˆpL©IìÔñ¶íšûœÚ¸³sœ`K4j]Ý”L?xŒ`2ü|}F)Q½ôüœÇ£8[·:KvÊÊ“â=—GÙeíÃPÜdo‡¸z€c/5w‡<âLü½±ì™lB‹™Å[î|#ûÆBOí9ªÔCÂB;Jö}Ï×Ðg”¢£êfQxìÑ=šìDìd·¶rBÄ…5¿æâ97¦Åh®Þ3oc•a µr€c…Á}k0z»¦=ºî# ‹ 9°Yñs˜»‹³² 4ö[Ìù9dý³CÄ¡.R•Ä:?Öuˆêë´áF ùØžÍ[É02èñ“ i«Ö뜇tlºJùŒfùŒRŒ¨Œ<â\Å|»×Ez¶Iðt¾ Õ4‘ï#%µEª ¹0+‹£§À1– 3}Žø˜£-]S$'›8§d´\ª³( ×®é+óuY”Ôe¦ O™xys7ž›x‹…Yp|Šø˜£.]GžE,/¼€§)Éø¹„®Eì YUž%¥¯³1åÙYÙmàØòCôÙÅ££Dñ4%Û±YÎÒ‰ÂäkÒl¥šH7e¿`Vö7ÑgƒC¢ºþêÌkÅZyâ*­öÍÊj¦äÆó0¿Œ¾¦¦!)5Ñ@H*Å/†È]D¨fJî'ZôôþZþtêBÏoá3,ê™!û¶ ÏLÉýZ±ò/R0ôŒÅŒ7w ŽÔ3"ç|l@­ÐŽ_DyyȸBô¹,7T±R6í…Dc)Â6ª6‚}ž·¿‹ä×miÎ&R?l|)Œ¬I @•¸Ân¼Ãýv€ãwQ·éŒÎ‘›ÈéeSß<*]¬ëæùã¿‹ê…žgŒ¯ˆä¥.…‰t¡ÏMq¡ÒÅ Û”¿Ê,ËUÑ Ì­xíÖ7vçÚµŠ¨Ð1Üd‹òq€ã—Ñò–Ž‘º±Ðlo}¨ò±hÚ :Àñ»heK+2¯ ÊûÁdjò3ÙbÚàø]´¼¥(¤ˆj«!Õ;—­’òfSéÜW§8~-o©AÑ7mÿ´*4êuÓv#8*°ZϪT­£’­Ä˜óò5E®,n×%¿§lå_ùIÆ,ÇWÙ«a û™3[H”ªÔ=¦)AwßÌX<)ðôÉÍ4ÿÀÊ–/Àæ~›|ÝŸÑú†¾à¼ª†¶î.3ÉYt¥êŸ^¥£•5ÁBdW%(¥ööA´~Àbé–Æ´ÝŸ­œŒóä¹·õÉÀ˜¯TÍqƒqÝ)øÝüÛÈÌ0Ú6³<Ž&DT Ê+Ç­Û„6¥øÍŘ,žŒQ˜ff0F¦âBÙÆÈ¨ö†#Ò;âÌ lR[˜˜–‘œ-`Ã03”ôU(Ê!`Ÿë¶¹ÑÛ™äbÕkÒ[»S¥G¥cÎ/å!HC^›âÃs§¡à¢ªºõ—!€¼â,ºRõÏ ¯ÒÒÊš`>m¦ÖCð9;H˜¶§l$]¶ƒÃ¬h§f’Éä„U‡l ÷y_ÆwZÅô´;tí$r[°¥T˜Vâµ1ÆTUÓ0«¬¿…“Ù“YLä,Yœ=\¡¯î®L[ŸŽXN$=·rÒO©#—6NÙné<þž~܃~ëÝÄ!kIì‚D =9 ÷‡ÌÚtÉhÇf’"¦£üÛ=‰-'Èx}Ñ7ˆv¯,Ö?OcöÅž]Ëà°Mfº¡Nm†8›]IsY!©¨Ì¡Ar‰î §ÀçÊ[ .pL©yÉ:vwq[_pQUwÜ=Ù]ÎÛŽEÖƒ£/eÿ*•!h&µ0†;p ¾«ì†ý¦'ÄM[)­s+8ðL„„~ ³?Ø'–Òåü~]¾ê3t´2Æv*ãúÖ¶6¯l&ê3ƒïÒý¶Ý“® ðj¿ÂfATÕâ¦I{´i±MlXÜM.Tºßþ=¹ôF^‰¼biKˆ¾*±oÐ*Šî±ÛH!Í{»õ%åCÜ'Ów’L<LSqб8õà(ÊŽÜ;°KÅmý%ªJ€£”/w,îòoWÊÓ<8¨Ü J‡H0t)9ú¼îiC³ê¨J`Õ(nÚn5G™¾"ûF»Cf¡sbGè)Á§Çyè\ô-ô ªÊDäšÐñ¡¼ ”öДªøÈf÷+\³à |N‰Ûé»={*¹OT/ecEžSf0£0xFà@qLe@WRÊ>¹7 Nâ! òà`_<˜P+$§@NÌþã’„ž¿{¼'”»ÜFáÝTµ›Û0r4òåF°HE©¤gúWi["*Ø¿³Òשîc‚¶ƒãBÝO â<WºˆË™ÃN³éÑ T8CëÃm$Wxl2l>Äo&\³à ¦8­Âþä‚ÂaRU¸àn5¤¦‘^Õÿ“š¼à]IYÎq!ô[†ø±Ñ¸`ùÈÝ7F0˜caB°KWv>„÷Ô‚ȇw* 9DU p´òeñ¶°4¥{¸ÅÓ¿j+½=¿)À´5OÞ6÷9팅íÕðûð\÷óNÿà(œ®Î®er?G×u#B¡ó\×½T*¯Êa²Ñκ_ñÄÐéé_ìdw¦‰]×K[•=}%­Ä„ø6—³Îx6¤çÿ¤q©¤”£ÏËs=°`3ßu#,x›EŸ§šØs}ñZöØÃw=±˜`o(#ßµ¨˜ëÞ(ªªÿ:é2¸³ QªþcåWm¤¯HÒ(!&H]d– 3vÿwÇ£Ë ðFW'¹Ä/&›áw·'›)=ÿ‹‹+ÆGwgÿìa4*©æ[fÏÏÝ'Ø5MiW&û”‚wÒª—¥‡§;ÿ*}M˜¶Á×o¢û¯PÅĈ1ç¸^¹ü<}UzW³k©¢ƒ–)ןoü•ËOÓ?˜ûwÐV:ÀqÐ,à8h–p4Kÿ0x¼‚2^IEND®B`‚nut-2.7.4/docs/images/cables/Lansafecable.jpg0000644000175000017500000014675212640443572015771 00000000000000ÿØÿàJFIFddÿìDucky<ÿî&AdobedÀ #_“ˆÍèÿÛ„       ÿšÿÄï5 0!4%16@P"2#3p°A$D&FE !1AQÑ"2aq‘4 ¡±B’²#30ÁáRbr‚¢sƒ“£Ò³$ðâCcDt@PÂSpñÄÄ5Ó°!1AQa ðq0‘¡ñ±ÁÑá@Pp°ÿÚ ú¡!H´ÀLjɈ‹§‚ɸ@N|€Þ3jS$!$=K ªmMР"5G’ùHò[)–P´W%&("ÐG Í0dI|œÔƒ ÉèÁìÈÃfÁ0dlì9³¤ªZ˜Ó'¢Xz%=ÎÄœÃf Fd€ò[/ž$ó¤BØ™ÖrÇPT4„àèpÖ'% 9 _Ç|H I `É“ÉèòdÉl¶HÕ“¹º/žÊ„&ØéMq¦=›âÈ$u ©ÕÎ|²^6§^6F—‹‚báxÙV\&=“ƒÐ)K%lkKd¦L ñ@˜¤Q+K6ç\;B3‘%7fÈ“:ÀLë 8m ͹È7f•‹DåÂBÙ²/ƒT]7E3Xz<ž ‘´(œù¸!5ÆÜ 4ECTT#>˜nζv¤G0V:’à,u ™Ö¢™)Ñœ±©<ãѲ%&:Â@kQTº]M1´6¦Ð¡ H¢l͉X¬nÀÒšcdm  ÑÒ€s‡DdM!§-“Žt°{Ç~{Ä$dFè¸z‚ÁÒ玄ž:Zh‹§‚±xß™ €¤Ó— Ç¢ñ¶.sÇBÏ XªHz"#6¦@LŒ›Pxè@9ã¡Ð”éd”<×;àÏ6t€Á€eRB\.’Ïd†L‘˜Š':{<á®"2W&2  ã¢cnx=ìÈ玄•: @ z".=Ã`DS(—'€NdòzL,±D°x:’By=2dƒ^lGƒ&Œ¢D[,•Iΰ°Ï(u`iŽT¼vg FU5¢éèOf'  ˜,4 u&à×0d òd×Ɉ ¸0rÅ2ñÔ@9ã¡å¬xÐŽ¬Ù‚2‰®Ó `Á“@£Xj àïaàÈ5ÅòbnlЖ 鹞:PêÀ#(šbréƒÐ0d2dÀ-“–=Ô€ð0`ÉäôFJS6…“ZmŒ€sÇBÊP“Td²]F¸òz<žŒŒ0 žL‚CÈ= †ÈºYñЀr‡VsÇZ';ZtÅðŽ+ Å2™Ø›`DÕѾ2Ï(u`¬æK%C¦2{5Æô˜²Áp €@0y=ƒ@9ã¡æŽ”òzO@À4…³%Ré¦/cŽ6åcdlÁƒ È ƒÉèÀ2Ï8t`F” mM®0y%%<ƒØÓšâé½0äöN(´D^:CÈ=‰O³É“& €ƒ'ƒÙàösÇBÎ ’‰T¼`çË¥“ÉàÓ›Ò2cÁƒ&ÔÔ•fðÉìò{@X7@däÉËåÉd¨ ‰ÁàòH`¶kŒž™xsÇBÎ èç ih´JQ.Í1Ó‘’@6†œ¦[7@<œA - ©Ì‹%3Áµ*ž E¢é!=’žÏ&ИOGv\-•HÉÉ q³*›²¹èÛ@xè@9Ó¢3”%'-˜5ДöyÛ‚pÝ£Ñè¢oÁÄÍ™¯-žKæ°¼BdKfJä‡@^"2LDzƒ&ÉH„ê9¢sÉ`¢_5䇢bCpÉ`0tà`æ ÁLÁ’2R"SÑ \Þôl1DôU.`Q/‹²±ìôo‰™ÖsGJ“™6Æ0dò`ð 9ÖÕ`˜®d„´@{*–LÊÆ1Xö`ôm€“:Àxè@2ÉàÉ€ ”ŽˆÉ`­Ä?ÙK}¶[Žë°]—´Ž6‘ÆÒ8ÚGHãiF†Ò8ÚGHàá2cim#¤q´`m#¤q´Ž6‘ÆÒ8ÚGñÚ[Hãim#¤q´Ž6‘ÆÑ´Ž6‘ÆÒ8ÚGHãkm#¤q´Ž6‘ÆÒ8’‚E÷ZÅ}C©ÊŒNt—m#XZÎV>æöp¢û>÷Ül0wóÐ?1Ë ¹û•ãS¿r Ö=&™§önm¸ŠüÍ$/îW}Ç8/îø‹7îhÿ´cþÌó8zUôVâ_À”ÿE¿_¦gê.µ†|¡d;-†ßþƒ¨i,:º×¥,‡{ºdÌÍñ=!!—'ºãåfØx®Þ²Z&“;:" …öòŒ¡ý´a³FŽ–JÒÒ¬Í#–HM‚”[ç±¾xoÞ×BìM#‘ø•†DiM¾ŸiÈTæ®2^ÂAÞ! ¢¡ã€‹Øì í· Í„îe {ä‡ÖqÒæá´ÇL5f˜L–ÿY¨úÕù`äHîKrwÓœiz òÛß&†Ãÿ'14‡?´¦дԢ I—Ögê.µ¿Q"cL8ö߉„oîªÌ¶C0Ô"ùª¿¿)aóÏ«Ñ!Њ¹¦¨‹Geµ Aq²Tçì»ö#¿b'*[ª¯Ö•ˆ3‘ ©¢‘„¼Št¡Þ!4Lb,âY;¢.ñ™û–å¹*¾5”÷ìG~ÄwìG~ÄwìG~Àn,A)|oÃu#ƹ_!r{2aN´fffÓ¿2ƒß“6 RqqÇ%q•R™LwúÌO×úÖÖŒ2â™q.ÂbD*÷›” Ðòe)«2z13[eל!ÓT¶å$º–ÑPi+·›&ì~ßYœ¿·´7§·Òk.º‰:äÁ®.3{çFùѾtoÜzKJ‡'0»ñ“§ûÞÓò¨[{uöò‚åPZ(ÌÉ¢M„¦Rp’¹|ñ©d²ˆŽô*õ‰&6ÝÍÜÄlØÝušŸ­õƒð·<ú>=&Ö·YmÉÿ¸ƒÞ`Ÿû€„ŽyøÜ¢òüõ~ÝœFlÍÏ凔Ê¡´L ôË“ÈíéägIhšØBEÔ&Îeþíð)p(H;‰ Æ¥Ly^Äß/Ö—}°»§ÉR-ÐÜ™vçå]«TÉÒ“ޤXýÑ-ù-º‹µªCvÄä×­e$ÊÙÖÞEâŒ;c!0¢-Çë7Ëõ‡åÃñ™|޲‡`ÆuõŠã‡_²upÿvH‡@ãá÷Šbq#›\d;ƒ‹ƒ¥¶ÐÚ:ÍòýaùnÆ;ϽجÎßÁ ºÓ‰ý»8´´ÔpŠûN{“|¿Xgõ¯FHd†¤H"Ö€J#é/åb²ÎÞ¾#…`ÁIL¯ªéW#‚æFm‡Ô‚¹ \ŽLí3›a›a›a›@ÆìZ÷Ê:!ÏB¶²TI˜È÷3l3jvÑe›Q›a›a›a›aõQ›a›a,¦)vð˜(UÐÚ85<̈ÆÈkH5 †´ãcºÞœ–=|¿Xf˜:œió€òÛL;5Hn’2ƒÜ†ôC‘k¤³.3 nséÔÌSÌh͸‰èLÓ“Û´5vmÁ³t;7&DݦI6£M¶1j1j1iŒZŒZ7dv%'’T{¼€LY•]Í‚“c«E±6ÃE¾MÃM¾ ãÃ㣢INïZ"VǧÇÙ4·\5VX!ÖM ¨F¬0ºçž˜ìi<\rÓ¬ß/ÖêM³N9D Æ=‚ÛOòôsûp1²ƒÞäZ9›³]¡«!ªÌjµ¬†«1ªÓ:¬†«1ªÌj²¬Ã;£1%7­÷û5dï`)VµYVcU˜Õf5YVCU˜Õd5YcU ×d$œÞí¢Í+Ô¥C©ñý2D {Ú¨“™ÌÈm]?ë7Ëõ‡ú‡Óh‰]ÄÁ³l͗ξµ&˜þÝoйÇjÓ²/sÉAgÑð <{ó|¿XÉÅøQ™ ³×$ È‹[³‚f1B}Ç£·.kȉ ÞDò“ÜÅ+J^·D¨Šu}§Ðú¦ÀnShèFGé3ÁáÔÏ~¼U(’@ò3îMòýZÏ9è3Á:¥¥ òNFCiS,#±>D†Øo°ü³zišjË]òuž>¿ùªþKÿ¬x"´"ãž-a¬ò@ˆˆ†ŸD§HQà7÷ ¨·˜ÑóSIèV«tÇð¨°xôÅ#øµ ü~9øúÔôI¨úMòýYó¸ø‰2ÐËG"q.<”>™/¶ÓdVˆj$vŸ®ã˜ÊØ…J'd¡rÜv'þþ¸¿ÙVxúÿæ«ù5yR21mãäèdþ¢€%j‘üzú`²jÒÛ)††"FvE9ˆp«Ô>= âB–Vo¼òì .é½|ë$É[r¹£KKº4)/w‘=\‰ÜF"æcwnßlíÚ%®ê"C³ØióºŽFWL©-ÝÄP…'sÑ7ËõgΧVØ2‰ÊíMu¹nM¶B” £ú%ž2L6YŒÚåŠ Çÿ‚Ï­ù:¿_üÕ$~U%‚³3:ãÇuäÏ= à^‚" ¥¥)km¥£¨S§&™µ7a:—lÚòxŠs‘’ÛU.B‘¹S…ȦÝ«)Í?7&«BÞ­Sñ‰"ÁUpŒÕYˉƒ’©„U hz¾+ËUd%+‰ƒ§‡¯Ë,¶Ë}’I ¾_«>w¥™–Úyö䜶cM¸Kn"7Ú/7åc|óe„ÿÂd‹}\E´«ñðøêþHþ6„-¼|£Y¥œÿg‹‚8ØDN×ÂRø¸ã¢ib3,‹B ˜ì°Ì˜éª9u.½JLªÇä­U÷Á—jóekJ…52ˡį¤ÿñ|¿V|ïG ¶†å÷‘ -#›4mGLhÈ‘VuÙ2óo6Ê|¾E£úoÊVxúÿæ«ù/,-¼|醱È{ø5|jJþàk¶ÝÃfmZ¶ëûE%¹•mœy§Z‰5i7—V£'ꛑ,êZ”—ë;k*øî%mÆ+¬%«h®§šÚMøòû^Ìß/ÕŸ;ésûq޵P™7?eíñ³-“ÒÄÔ¼,ÃyN­ùJ½\}óUü‘ùamãÕÛî¤ÕÉû'WÉ5°’ º)¢¶ïNB2P‡j͘ÎÖ¦µÉ5ï*2«—¤Ô´â‡5òv¥L¾üdÔ¥$Uje¹tÍ´[e¬¤5³bË`íã$—jÊXæÚËwqW¦o—êÉýÔm£J+AGÛP‘Z’ëR’„®•fGŒV£V(–š¿’?,&¹dlKîvÒ¶Šq{œ"Ñä/›Ší’S»Êè§c&b\®m1U÷{¨SŒÊ‰-Ù ßÚB¨R\›_Û*šå°¸1VÂj ¥-ÀŠÚ² …Àеl"Ûâá†ë¡¶ï¦o—êÑÿØý—¤4È)ÑÌJz†Ú³KjM•rK•‚}§Û¯¯ˆìf!Æ`ÃÍ!æ»6 –ÉÇÖE‚ö€Ó†ªøj91 Åaž«¬„·W a˜QÙQÕBÃ0£2éÕÂQ"T%º¸é ­†’JI)÷¦ùŽ­~§ögŸõYTFƒŽCÕEÛÜô­þË­šÅJT_t®dŽr1íÍÅ5lƒ«4Îê1½†—yÆÈãJRîZmɆÛì[ÃzG0À;¸$†lã<j•º›¤wJå‡aÉ·}€ýº[fE²tï"a˘ä¶m¡:ÉÛGHæca70–|Ü\Ä–‰M‰¾_«©ý™˜\ExâkÁTÀ%q°Á×D1ÅÁ ´†›÷O8A©—c,—÷é{u^AÕW-ÅI§¹ F’«Ú-ú¦Ù•ƘTø q¹l-×*M¢~¡ÁX†œv‘*¯TUU©¶Ñ^ê¥K­6eL€àTªt)ôUFy높~¯CÒ z‹$ˆkb aá7Ëõoõ?íÆx&ç0¢Lš¶qÚË”Òd½+‰øg;øô̧i”=J·¤.¿ºË•)H§ z°`«ž‘31к؋Q,×Cd˜¯ˆÂøx‹…ž*©5Ñd)ÚèŽ(éà˜n¶#k᫲Ô6m J&ù~­þ§÷%É’V ˆ«GŠje²Ú—)Ç+œqÈ ý³È]d%¶ªè¸8¬Œ×CeÞ. 2Y(Q‰¥ÔÀ0ŠØD·*á8²¬…ÛUtE,ê«òÕtF¿a™åú·úŸ¬Õ-Ü–ù!Ùó¸ççKeÖ,,‰NX,âÀQªBüz‘/[ædÉK“Ú)²xèò¤ªl…Xc{%•1>À‚ì¨p®' ÿ×faW­‡{»ãÚÆ~B.Ÿ2E—ûe\(Øç'S}®î*å;e]½Ø+T)“¶p”åÊZŽ«]Ñ(ŽÔÊw±°»F¶µ—h˜ç±7Ëõoõ?£ƒ/„º´-sˆõØíˆïYvÈw,0§,pnYiîÙv£6á'l3l‘#Õa‹‰RXxiO­ÊèŽ ê j¬„¤.¶ɘw‰€_lqñ;|TšH\ª„àM\$°ìF^Â.Y!ÊèN!5PÉk§®ì¢Dʪá*BáBDXõðÒ÷±7Ëõoõ?¥G„Ó¾·T§åÜL‰€¤Í‰ƒq0n&“3“7‰¹ÜΉÀ¤Mù¸93…¼·™õ%kð&/ ÁÝ6¶NêV»ØHS²T–œÛN¦í¥‘T¤ÍiÈʱCpNéÍÄíóðÂm›ï¢î>ƒ¼cZ­Q·;¸D¨òß§OÇÓ7Ëõoõ?¤ÿ ‰/:îâ`ÜËÉK—Ô¼œ‰™)ðoË"hÜΉú7qaÅ€ÜX‘?U´•¶÷°pcœtUAA®ž ƒñЫ=§àE}ÎEd48ÌViÈ\kˆ‚A4ñIôUBAñ1ûüD>"xèûwi"`Á(Éöí<·VÿSúªåH}Ó˜¬‚É;ó3ã¾<Õgx±¾poœçùÁ¾poœçùÁe%h~¦Sï=ê<ãšBG7 7pƒ$Ø’â9p²"{lÊ+wU ›¢RtƧmã G´Žû¿±Úùn©Çæ_Iþ²d<§$HJ·’†òPÞJ¹#y(n¤Ì¡¹”*iŽô±¹š"I¿$Æâv,;æá¬Ãrá:J~œÁ¿F»®u3~]2ÒS+’¥ØV½kU­#U!Õ¶úî!š±a§•y´íƒM…[D$•Ëfâ-â-r¬PÁrŠ3UÒgs¥Gl°Ú”¦ýV¾[© ‹î?J²I¥’óÁS —½1½1½1½1½1½1½1½V7‹9d7Ë1Dg9@æ¯6’å·'Ø:¸FÑÔÁÐå\º¸j ÁŠÃ‹ª‚´®®…®ÄŠøÒ‰…©º¸M”ªf_S•¬½!06lUÃ@:¨f«ˆÛªcˆ…ƒªˆ£*¨˜E\4û¾[¯þ‡Õ[Þ5*DÂVæpÜÍ™Ãs8ng ÌṜ73†æpÜΙÃs8ngÈš-fHa¹­|·_ý¤ÿ i’_R§iW c1¿øä r9Ž@Ç c1È䬧™s¯Ç>Ô‰¤ÎT“äE!Õ óº §ö[§I{ÇRRà!ùcŠt[£‹p?W+*„¹f -—±Æ9ž-Ü?ÖØL«®;’Þ~bn÷¯þ‡ÒyÅ4·_5XUÉäHrD9"‘D‡"C‘!ÉäˆrD9‰Ø›ÊCµ²ž~G»\”›0&¦T«4‘IŸ%Æe*t­3J‚hBˆÛI‚a¢,[fÞr¹øn)½œÔI82Q¢Ûo޾ZbCB›‰ÒgÊ,Í5ôޏ·ìˆ¿1uøsþ”XËʦL7ܰZZ),›;öý¿`oØöý¿`oÙöý¿`æLí[MùªKsœÃs›9þá´êÛ©Û2^ËYÅ ¯8«­‚¥qÑq+] Ãq«\YÅ­H8’8ØÁúØäËJtÍÓy.ÅŒãÏdaµ®'¸¸ÂZVÒ£Õ¡Lmk_2ã“_pu•*<[Þz¤sÕ ¯*̹êÌ.Æ"çꯪH¹ú|óõ8çêAßÔÌ5:¹ê¼Ü5&?0ÕÌCóVJþ¤Á_Ô˜çªG=R9ÚÎSŽnœstêíPçªG=R9êWÕF9ê‘ÏTÌ#›¬ÓÏU~¤WÜV´Ù_ÔÌ8çªG=R%Y@rK Õ¶KEj‘ŠâZÓSØZ+Ä•fjìjáDzö¡LÖÜÕ³ž¨}PcŸ¨ËóëUcÏÔÌ㞨l+^“ÎTáùÐå}Ã×ûŸÆ`†„ ¶Æ„í·4$iH4 ËJGi¡Û@&Û#ÿ¿ÿÚÿ5£ÿÚÿ5£ÿÚ?VÿÚ?VÿÚ?‡0fmwgØ,rËh†’йÎ ÜQ­™0.p‡ Ã­¦íféÿžÞ¦ÝL»J> O,%àÝÕaÔ(M‡!Æô´°ÁòÖÓµdÝÛØöäš·UrÈÆõ}Š”áM4üìf˜Eç†5á÷Z÷[–»C)C“Îû¥\XÆÔ«²Ù÷ãÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÆ1ŒcÇà°Ê|1ÅÕÃÏ…k^BâÜÆ.Úu–ÔDZWR m©U$¡(uÇÍf^üX¸ë ¦ðºu\[ºmI€‹\8²Ö\yÖ©ÂR$¦«žm‡niäW?$^~á®ÝYeýW(!ü©‘"÷l¦é®r礆cTŒ^°x}{VÝshhyÉ„äaÞƒY{'ŽøÒ”Ç´¶ÂØyl¥S©ÉÆópÚÞ7XLï7táŸ,3†Ý p5ÚC…To;¡2Ųl«.9Í«‘® éžû8mfk9ØÓ<;ñrãXÐÖº†£ÎBJ0€Œ`#ÀF0€Œ`#ÀF0€Œ`#ÀF0€Œ`#ÀF0€Œ`#ÀF9øô^kÔïÀ&vDÀƒx¹Õ¹+Ör9L,åS´ÍÜ”jòNQlºfÕH«:Â:©ÍDUmy‰$K‹ˆ‡â*py-$«*TºÁmíKpÁ$ÔJr™6iŽ‹›Â»ÖÐìvN NKT«o…¹·. ÁµoQ„%T« z¹… msQ ªHÀâÐ.€ƒÁ‹cŵ66h£)œÌÜ<âI8|ÜpäŠmŸ´åsºIŒGDb:#ÑŽˆÄtF#¢1ˆèŒGDb:#ÑŽˆÄtF#¢1ˆèŒGDb:#ÑŽˆÄtF#¢1ˆèŒGDb:#ÑŽˆÄtF#¢1ˆèŒGDb:#ÑŽˆÄtF#¢1ˆèÿÀî?x®¦ð ™¡±³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1³ß1}áAm·j9¢.9Öѯ!· Ó’,¾Íº·Ï  a¯â›C¼z\çbÍ‹.p{^pk…i,l÷Ìlž“=ó=ó=ó=óoÞtl÷Ìl÷Ìl÷Ì–y1³ß1³ß1³ß1³ß1‡|ÆÏ|ÆÏ|ÆÏ|ÆÏ|ÆÏ|Ë”åÁ´r<ÆÏ|ÆÏ|ÆÏ|ÆÏ|ÆÏ|ÆÏ|ÆÉé:cg¾cg¾cg¾cg¾cg¾c¼cg¾cg¾cg¾cg¾cg¾c¶ÒE·2ásj(Hô÷›s]ié`ÑÚ fëÃT/d²ýA¡¤‰”Én{¿sìÍu Þ‡‡a? cÂýøÿK÷ãý/&Ü>óý˜¶Ø¨¥k(G‹UbB8K¦Ú¶Ø–‘Èì±>W!âƒ_ \Ò B {×5´‡{6ij—]`qäÿ†¹fßçÜ|Ë„ç>üK‚ýÏîÆ§;¤ãÑ¥Ÿz5[o¡Ñ¼°8zA¤ÕRÇú^mxÇ…ûñþ—ïÀâ8¯g[½ ¨¹ IClZ¯xåEjaÙ /e`ì’¶÷áÝðvžõ+·æDâ݇Iwd$\âxÛ¤Zm¶ê¥‚—R0Åb÷ ìa–5n GsG­hب¿-j˜óv.ÜW³†Ýu»a¥­Ze㳋–¸~$·sЮ È'ÿidôÝÚ÷…«…W»?Þm…Mé1rÉÜ_eÆS[ cÔpÜHúVÃ^;Ðâî[9®0¶5¸Âáóše<&4êî Àÿf)ß9ÙõÍ8m)æìÛz eÖ8÷99£[†¸Ñâ­zL¸zg[Å·†ºZ|aIÿª:ﻦ:ï»üQÔï}ß⎩{îé‰ð·ü‘¦.}Ý1.ï@Ó•k›'ÛpGqðwùmDÖÑrJ³Š=¶éÊ›Ét¤+xÃK6•ÃÃ{{î& cjðG ±ÄÞL Ú-·>XsÃÚc\êêºù…älSíö ñ¡îg¿ãí5d š‘ÿêpýLYmî$\·Ä\mc)X7-ñFóí¸Wiå‡Ui8F=‡X ÿÜåTHáá} _JÜ‹3‹Œ¡üWtÓ`ëÛVÄ!ùÀ¦h©Ìʇ´÷áÝðvžõúöüÎÅ»Ïjܵ°bàe†ñÚ.z7áëLqÆå!xoß É(áþ ì=üKÜÄ¿wg¨8¬Žhâ øc†Õží¹9"ÒðŸálq%ó.© žªÈCiV.ì„—ƒš»â.Zв–ukxYt9ô<9™ €ÉÏqŒŽêðßÌ1sðüë‘b›ìk7mAB¤¹àÕzÚdF0í²Üº‡LX}÷‡ÚmÊ‘­C&8烼¶æ>ÁD_žÐrrF/õŒ eÔ5ÅÎ >So`ÒG)Â=`›¨ûY£y¼Hw60»À‰Ws<"äí¸J|k ¼ró3øaÛ nºëĤ2' ÀCøŽ–o8{O!ár»4mY^gi‹¼5ƵÔX¾*cβ†]k­¼Tˆì¹1«=ÓVW$¦6¬t;LmÙ=Çi«=ÓVz¦6¬ô;Lk!vòýy•€ÍÿÇïEÿý¿ò›…³E»ah\1‹Ïõk…4êâS$]W•e5©ÝŒ°½”ñÉïYíÿ.¨u}aáÄ>ÝÂë‚TäÖºÒä)ó‡aÜÑî°î!Ö¨¾<°1ÆW÷‡*íVå: ·j²É£´÷áÝðvžõNߙشÇãt ðxL^ö‚ñkqwxúÓVÅ,ÝŸñ0ñòôŶ® 0q‡³‰q7® e[*ªœœñÇ}OˆG ømðE¯Â…±s»ñŵ3¥’é‡Qiï©õI¥0p‹Ns»aáÕ½Á²Ppžhq¨Rx¶Í&°ÙῘbê¸Bü¿æ¾,S‚7mC¼ä推?Phލ?PhŽ—,óãÕ:,#ˆ¹q†È¸æÒƒñB"ÕÇÌZ«W!ªPÛµšÚàõ΋:Ã/9V™`ª‹Ý†‘t‡5®h9qY %ªãë¸äHK“‡Ü77…à"råÍØi¶ÚÜ×´Ó„²Àkl¿ÙíÖÚƒRn玨?Phuö…„c›PuDª&A‚CÙºmÖ¹H ,.1Õê Õê Õê Õê Õê ÕúƒDuAúƒD,šw—ën)·›ÿùøå6m‚í²Ž4¸aHÉ[rÅÆ¹´êŸœ¸…p7SLóAs±5.l#ÝÿkùpcÝ_XråÄ Mp¿•j§Ö¶´YcØsWY0vî›mÃR³s$¼^Xã÷¸æÔê·™ù#Ùêô¨©Ú{¹ßBðïvžô?Jß™ØcÞÀç[›cmo¬9åíB223‹¼Vîóx‹¡7E®Ÿ4)Ä6«t¾ØÏŠwbãx»OqÞÚ¸Ö¸«\…5s–8®!¶uÔ@86{¾øsôÆòßz¤¦eÆ]ÕŠÝî˛Öm^z`ݵÁYá˜Æ—í¹wå·¸›–×-¦ gšÂÞö‹ÕW) ÌØ %9nI€ÏgEªÉO´Ú‰Ü”ðCiÙMTÁ;-6“{mÂã ÁFNè‹f÷ t:ÖFîÜ&)‰p·ÁÏM˜,5÷+‹”Ñ—˜ÇS½÷?Š:ï¹üQÔï}Ï⎧{îû5溡&,â©ÂîI?†ßÿ¤\þžëËÊ¢1£7Î1w‰¾(}Äkm‰ÒÖ®'9_ƒu»­nñŽ5j¬qÉ º'«:"ƒÃK6ﻆ_´¹YPè„áx¾!Í8oX„ßsƒ·Å°:‚*Žï,)÷UÅoøs,CwœãDÚŠß'°ñŸ9úc„ á®Ùµaã!9B³Y¹í7Èůh8“T†‹¼]~±”RŸp¼?˧‰á0e°ã1•sÜû9±b笸ò*L¨Ð³1íIéƒiUÉÍÚ{¹Ü—‚}žÓÞ<»£÷{vÜö›Öi–]ON1r× ÆñÜúÅÛ÷5ð4O…±ú‡Djp¶—–çÉá,÷.r׳Yà-]á8÷ Ujܦµ|IÝ«æà§DclrÃ’ý¤tеÚ`±ÜKTâC˜k~hOø›·Yy‚³P§¿8õ–È ÝS¦Úd›¢lkŠ”!ú`­ áøšaÖ™Á¶íD¾³ttGÿœÂy/ÿó›!ÿy° ¶ÞG]çÖ2áåÖ\ Á”€D6øâ¸‹„*¶ãÕ¥z>Ý¿æÓÞ?“æžÅ¦Y 7/:‘Z ‘99¡©hÔßã‰yaC‚Ë `ÖÊîRêeÍ–8›T+­[k­}'‘t5­ôvw€,Ëmhm‹LedçeO/ü®Ý®­.¸íuñSL7ÑÿNâ3P­©Wfi€/“:Ž­U}\‘|µ¢ÉsptÑ2á–,¶ûEKÁpm4€’vs5l 7œáq«6…¥§¦,^k¹yô$ÊcLá ¹p#Î@£Â‡´÷oçyƒ´÷äù§°Ö­&¦äŸrè¶@h™ÀLž}í[ŒÃ7"ŽHÞ>ØsóóøàZ`Y}m¨¤S–§a›ùcwͪ•I‘Ž8@»»„#2€€¦AâéÅÝÄÚ.Z,ÔºIxÎL›UcQ'ÏÍ=ô¾³—,S»ÕUJˆË,2Meðöžíüï0vžñüŸ4ü%†²LK•’ n2ˆëM{†²§<"ƒbߖتÛÃÛ¥äp1uŒõŽØ´Ê®"ÖÛ–ƒÍ»zÂC%2ü/»;̧¼%Ÿ4ö¸ög\‹Tá¡{¾£¼kÑ·a¹h¼o[Ä(Ý­TÈuc‹8·Nð(rfŒ,U“m"[•˵ös›n6lt¿Dag¥ØôF®ç»TLYOµÚ#fÇKôFÍŽ—è›/ÑX^w胿ÇÍ ŸŽvÀ.»iáÁ£*êœ9á–Å_ÓÜk-ÜrAW0T‹»æ¹Ï ¼æ¹µ4WTˆø 0µûµ²Z­¸ñô¦ çìEÚÉK•:#fÇKôFÍŽ—è‹- £š\ùˆÙ±Òý³c¥ú#fÇKôF:]¢6lyOÑ6:_¢6lt¿DlØé~ˆá[ĶÖèÞ §!Î#ÑØhÖme­Á«=™§4YßXnð7ÆlûñkíyÆÈ ° plÏÄfîÄÜ k ÈFÐ銪çYB¬»_vþw˜;Ox ”Zðv—½C´Á[Ö“  é‰¾Ï8k´ÆÝžz]¦6¬t;LmXèv˜Ú²¼ÎÓVz¦6¬ô;Lhu´ñhQá‚X^ [݆×=mdMN˜7­Ö·· ÓUH •d%‚AË¢ÓKXÖ:½§«\EYŒZ`/®ËÅ«o+IØMG‘Ñox ^j%®Æn0i}ªr+JøcÖÙÈ×cӲϒí1ë,'Õv˜“ìZ]¦=eŽCK´ÄîY?eÚcjÇC´ÆÕއi«ÓV:¦6¬t;Lp›ãnøØvNxq­ÌÃY«ß¦`gH³¾$ܧX»·ö¼ãÑ›Ö2àuË_9£å…w[l¶Õ…‚\¹vW’¾êÝû-ÕxðE¶Q¼µlZ¤IÎJ•À‡b®á ö tYn«K¸ UÆ.¹–[Q·nyßQÞÎæÉbÛ™cÙøW*°†”Õ™,›Bá5£Ã›b‡š¸Š©P$[“FÖÖy{Ovþw˜;Ox~®Ú–3xka§9Nhôv-zMͤi¢¤I,¦e[Ú”“ªéLÌLg—²îc:Ûo‚/—oC H.*;[â€2G¸m²7k$xƒ01êìžg;DlZò¢=]¯)Ú#bÏ”íêíyNÑ|§h‹>S¿†=]¯)Ú#ÕÙò¢=]Ÿ)Ú#ÕÚò¢=]Ÿ)Ú ïÚÀ;®[{›m—-|ìŒ zCwi»à7ËMsþÂ]tÖíÎójŒŠ³ìYj…’¹ÊêìùNÑ®×”íêíyNÑ®×”íêìùNÑ®×”íêìùNÑ®×”íêí/Övˆõv|§h‹>S´G ¾m°ÍøÙ$“œ$Wˆ•½£<'Ñ»ú4/Ç s›b‡l—4o%Hs™8uñ¼¦Ó,Íîx3:ÈÖ„'ž/šß½Ì·ªõ™F|Ø/²÷>Û-\uËmÞLâï&¢,—L–5Or Í*‡,cc¡úcÓØè~˜ÆÇCãôëöãü]¸ÿN¿o ¸On ¸eñfþü jØ¥'7ªôG ½¢ûR•]—g‹ÆÛ¨xa-p†‰-ý¯8ü/»;̧øv¾òʶqšÖ€*Näu«ã’¡¢sÝpÔç?“àËésÓÅlÌukþGË^ú}O–:µÿ#å‰p÷ÇØŽ­Èùc«_ò>Xê×üˆê×ü–:µÿ#厭Èùc«_ò>Xê×üˆê÷ù¨Ž­ȉð÷üˆõ±M˜MÅá–lè/OèõwG; ·ÛCƒšæ4‚trÅ[Þ4ä@ßîÇ[¿Òßáe‹K3ã9ü/»;̧¼?×Á5ŠSsqÀ) ˆJ$[JŸP58Û¾÷ ãTœõµ~m\“Æõ¸8–ÛñÙ¨Fâ`«î~££jïê?NH}·—– M VìI<¼‘¸,7Ç8T^0¨ù¢Úh*C™Šy> ècîo·©»ªz!³{ÄYL»ÒÏfF-[}»íÔ¸ìEBã@'<£‰¾lÞ]ß#¦ŽC8-{ž¶¨rüíóÁû¡DZuoªûW‡Œ~l—sj­ÍyRãÐ\ÂÖæFÆâT̃¹sE·¹ÉèîB—H ªPÑrùöwÕ2ð¡&Z o»_ÅÞ®;DD§ðžíüï0vžðü;_Û¶‹CƒÍeñ“74k>GhÜÓçk™sL{7 åe»‚íÛæ§ð$ЦqÖYúÞŽ²ÏÓþô:ýۛǹ¡’m"SåÏãì±Ï/¸®#é˜í41ƒŽÊ5ûÇ|Û`¿À°æ…mÆm[pGøO‚払íáýÛùÞ`í8Þկ± §7k<{'“RP °–É·Áœn‰9ÿW0å†pö˜¯OGe¹¾!Ë€+ R.Ù°ÍÓ¶*zå˜uM¡ìqcÚªrÀs8§ÛmW5X|sœFá÷MæºÙz¸A3ÁviÅëlk­»v â C1Ež£…6P2ãÂ3¹ iá·Ï´0Ûðöñ/Þ:¢ZîC€ì˵ÏÚ¬bŸ§Ä §Â{·ó¼ÁÚqIŽâß„ö¹ù!îhRHçÍ÷´[°97FJ>´{¹Î.&ò ºÎŸ£'>x6NéÖ÷”’¨jI,oQ¾AËq#wÃäá¾pÿ3D9¼9 m¿[Ʀ)óŒ]Ùkµ][ýc—+óa„¯sÏ1cêâ¿Þ ú×?˜a¼¶ž!Nu3Ž:ÛŸí ‘Á‰ŒË Sý7«ùºÂ]„G/`2ɧ²fJ•œ,)´¡weɬ_K;Ž\aÌwÍå·±—5åév—L^·ì¡ÞλÊ\r5~jEmúBðÔ¶¥*¹CHŒâµÕ+Ýì Ý€}”Íð/`Ôræì{·ó¼ÁÚq?ƒoÂîËÜ5ÜÒ@3©ÅäÆ)6s>íóà¤Õ¦—±Á1®ºÚ­kfâNa#èhAê°QÁ‹€êÛiHMTÉ ¯« ·TULcÚ®¸êà^âäú ¬àºêÙá± IîK74qÝ‹VEû ËK—™"øPQ–×?nçžbÇÔÅ~;¼õ®0Ã?Þs{»žQ¼[ÃX••G™®Ñõ‚èð×5ÙÚR7|S¿èk‘Ù$aêF`ÇhY÷]¢7–S0^h­ìW9÷Yß<òÁu¦#Œ‹¦OIì:Óö^ ¬¾Ï›p!ò†ˆºî,45ìÝ6Û'«‰+,°Ÿlìµ·wÎoÎvNh%Ö¨¸žwÉÝ)ÔÒ÷j9R=RI—:øOe÷ɼfRx ÈL·Q2&«áŠ˜Ô Žcÿ”%Æ¢­äna¦¿Æ5Lå$0¾ÃØÇ‚Uȹ)éª-‹6Üðç¶Ûßâ¶§$^cí¿Ð’¤Õh»›Z7m´^=¶qqpè†ØbÖà¡yªðD­Ý+±«´©䊦¿7.(9',«wm¡î¸D…ðE‹l¶åºäp|‹E5Ý€×Ú{-‘·›X¶|’‹üG*6|WK3çÛ­7~Ó0 -Jjf‘nã[kxªMFÕJç9"Øs}­ï ¹È ¦šËi ­o%IÐ Û`%à· ~ô]ºÇjÙÛøºcÒ2忟Svs/:J‹™WWgʇ‹d½Í(B—stC•¯¹ §ÅA®s h7שh¨"¦QÉØ÷oçyƒ´½þÕ¾Áuµ•nI÷#Õ}çiU÷¦*µ³«´ÆÁM§i‰´ùNÓ¯ï;L lm,náŠc‘b¿fcý0´Œ.$9ºÆé ³nÑ*kúH¤ä3uúºÍÈJ­|f†QfýÎͼídZª±å‹œEË"ãèlÒ¢-œ°RêÏ+¨n®9’=©ü2Þ¹mÃ,q–ªÃn{3‚¿t-¬ëre^Aõ¾îÜYo¬yya¯½iõ\õÐMr nIaóø{»éµ'KEAè¸Ny–^×[ôl9ššªS&Xs¬ð×.5în»j\fYmCí¶ÉªâI¾5 ÀCn6Øh¾ÑSy2òEËÆÑôW‹2â£PbsE˶æZ“…¨TVœ™eU¦18`ÀëÎRL”&\)É ´Ûw…ÒçT‡‰.ràA-¶Ó‹m9 ÝqLF¬1×-šN¦ö`Ž¡În‡UoÅ“M Ž(øô–\Ô t‰ñ”¡š&¤{5pâB¤Ì°¼»ÃÜ%I¬¦y'8Ž÷E÷nj ŽoO50êCš³Õ$ODÔÇxpî'‡qnK[DjX6ì›M¥õ(ž²'Úì{·ó¼ÁÚ^ÿjß?þ=a¤ðî7íàc>sÁI’'N0ÛÖ8gšXíDJ*8œiÝ®ª÷Nsê3qîÃl2Ù©¨Xy€ÕÇ0‚Û–¸5û;Z\ ±Éý’.‹–o°¸6Ȩ.¨ †bPûÞs XIªáÕ.ÅPÅ¿Bæµ×7¢¬Wò¦bí¼º\Âĵ¤Ïža÷7>‰—1wÌ3œóÁcxwµºÁîÌç¬Çè¤6ÝËOkª ´âÉÕV<°ÑìÏm·`j¥.håTƒÃ·†{n{§þÙiÁV]ȢХ¹”Ÿ f£¬á2U`š \As“ÁsCÃ-ÊàKŠ¥Gwž+¶Äz%D’gÏÍôsq¨ë]1±,$Ò b­Þi)I^ôUqºÄ"‚D§¦ç6vÅ-BD»‘6Ÿ)Ùx!®kN¬Úp#Õd§/O?,>ÛA¦ê›ŠI%Bc­ÙhAØ÷oçyƒ´½þÔyÿ ÍÝ^Ïæý0;É}ÜcRåË·×6àršn#4 ÏE»nk‰u:írG[~éu¶Û¤ÔQ_¶ŸÚPÆÚ¼ç²‹Žu7Š5‘{‘båÇT÷0Ió|/ÑËrÕD$P‚|Ð`hËÍMy2rv€Ðy.[e.Ï‚7r¹µ3”¬³Nd3Ѿo™SÝÆjF:°>–x‡L‰Á1†\õ™„ÏIÎBÃÞæk? Î3XÝ‹hÕñ¿–k*âg†=±UdIÀäæCpI©YŸwþÝ¿æÒïûQçö—ÞÉ=¶ÜZyR-qEÖ š7î kº¦Ö˜a;L—¤½ª*‘D^Xàî9i6‰¿l|â€}ã+©Í°ãíŽAã¼´³É3G—w¥·ÍTË[h¡H¶âêÔm-]ô“Ú™EÂ1 )Ñ Ýñ.¸ÃºÞÜT¥îZ›R–0ÃuÏh?ê[N¶²…Έö‚ÏHµSË8rÔ±t»x'‹¦bÝÆ½÷iwAÏ.HP|»;Ì¥ïö£Ïí‰Á"úÞÞ³VœóËÈA8RìÚíœuCå²:¡òÙLùlލ|¶GT>[#ª-‘Õ ä×dO„w”Èꇞ¶ÇS?¨Ø?Òä—¤措™õÄK…ûâ:šæ×lZ,ykäw#LHb¼Ý±sŸ"6rÍWmjPSU™5]HSå‹n!©S&¶s¹®V‚Úê8$ÈåšCr¶=å HÙå=0.6Ù›ƒit‘J,nË^éTòС£"ó¤4¶Û÷híáÊÔIó#¢Ý»º—./Õ)>T‡Þ±é(]QŠˆµÅ\iôº­ÎáÊÆ9Ö¦·2IªS–*k^é.¤¼¤CCšðòƺŽW&¯<áöÜÇ#HfÉ­ö¥(Ð[rå"x”[Fú7M×OŸ46ó-\*ðÃh„|ù i¶¦ËDÅgÆ*)8!Q#Û*žÛÝ¿æÒ÷ûVùýhcˆkîï­ŠM·Ë.8"sAþ—ï¶:£‘1©½> þS4ÇS)©šayëlO…#’¶Ä¸eûmŽ«÷ÛO÷ ìsù»ÆÇSýÁ¢:Ÿîꟸ4GSýÁ¢<Ž'x%YnþéçY‰»<²| xrí³lÊ„ÂqªÄäRHe.ÈAÀÏ&a¢é7žÓUnøóáÒ+¨,æµx`\ºÊœ‰–c—\`Ò¡&IñQ<Û“¥­2x‡T§<Ì(¶™dNd€¬\dIš•ŸLn5é ê@çXôKmø°ª€G'r6ÍËŽÛqÎIvß„÷W×»æö—¿Ú·Ïíø‘y¥”¹´Û-JTaË8kǸ4žÿ@ÓZú u~XqvFp×¼‘“»{5îtcªÞèn˜ê—¾ïñGT½Ðß⎩{îÿuKßwø£ª^û¿ÅR÷Ýþ(ê—¾ïñGT½÷Š,[eÓmÏÈ‚œFÑ>îo58"nßQtÞXÃwŒDo¥Ì=R‘†c¨‚.IE@Yƽ§´ÖZ‰²…Y•!ÜCm<4[Þ6 Š;ðÛíµ«qô±®(äÊNiÅ‹n—šã¼ÍH^üZ¿» }ˆÝ/%.(hð÷Ô›@ÊUS X·Km<¡¸ˆ0åœ\¯y´µÒ0LüéÓCƒÚÚ.÷þÝ?^ç›Ú=1ö@¾_ow~ýãÑ¥FÀUÕÀLrÁ ážð0p-ŸIŽ¥s¥šc©\éf˜êW:Y¦:ΖiŽ¥s¥šcª?Êf˜êŽò™¦ô®—Òf˜ŸáöÙ[Ÿ\GT>[V%¹ßiºb|1i±Õ5¾¸‹ ³uÖî]4 JPMÄÈä‰w~ã¯YkmÝ÷¹ŽuU­iÏ’ w'rÖ¨Çjl¥¼ÁKͱþ"”>4ÔΕ‡Y ¸ê€²X6Z³É°jJž&ÜÁÀ÷–-²ès€` ÆM“§<ˆ¦Zysãd&ÓÔ‡߃{ˆ´ðJ9‹¶ÖìµÒ˜ÙÞnu *Q£f¢…<1Û«Þ•f®ÖWeÖÖœ ¼:ºèFšÜJ6UŽxW[¸­Ö¢™#W4ãvðìŠð °éH­¬{Ý­èÀž¨Yæ‘‹u5ËqµÈ-"S=0Ó¬âñSZÖ©*Hø %·›)¬ô˜Á%˜‡E ¨’êƒ{Ú¦,Ó霈dZ2“ͦά÷O.®¥Ô’s$3yÖµáZ¤)(¸få‚ר!Öýx] |bêXÖ³ëxË€ç䆹ͥÄ)nnNßÝ?^ç›Ú9ß;„ŸrçlS»½r”kœ2F|æû=ãIJ€y§Z÷’4ÇV½ä1Õ¯y#LukÞHÓZ÷’4ÇV½ä1Õïy#L/³^èb\5ÓÜcªÞèoñAþ–ô°Ó^éî 0…¼Wi‚=–ñNFÏ¿µeºkL®5NU‘ÃàYjKm-`\¦|M²æSñÀk­ÈL9“≶sÖƒ2²0nZe.!?·DRmʲù®Ç¦,˜$‚ šŒS»ÔD¥Jç(ëpÈ‘,Å9¡Ž¤­°f|R¢ ,Ä&'.ð˜ik¹RôÅÍ{û·ušæ†Ñ’KÓŒ:–f–™œ¤÷„ K7B~/ö¥F?Û,lœ‚DàDo AÕ6xc!ɬc]µú=Ö±]X!<5Œ¦²îÎ U9DÁq9)^t€.š¸“7Mu³ÆÉ3i™'cg£à=Óõîy½§þÓÿS·¾óuï³UƒÓÅ‘2¸d5¶qÔÏ–ÈêgËdu3å²:™òÙLùlަ|¶GS>[#©Ÿ-‘ÔÏ–ÈêgËdu3å²:™òÙLþ£ ÂzÙ8´ÇPëÞ½­ªJ”÷äþéú÷<ÞÓÿiÿ©ÛÞJ¥.R©Wgh-ö{Å2†ËÃZÿ“òÇV¿äü±Õ¯ù?,ukþOËZÿ“òÇV¿äü±Õ¯ù?,ukþOËZÿ“òÇV¿äü±Õ¯ù?,ukþOËZÿ“òÇV¿äü±d¶åñs%S¦0PW‡«~ýk·mIª×ÒŠ›Å8ɺ•¡kΘGáÅ^>Òä¸ñ —t@þ­Í¨*ï 6KC¥µïn)u4›“`Pý1jñ⯹Ìk‘Á5ž’Ç',Rë×CF77’ ÕJKèÃZ_z²gé0T¸g‚ÃÄÜ–.¯•5±‹×ïÚ,¥v5À¬uþ'ÊnˆëüGKtG^â<¦èçÿct³!©‰àŽ¿}QR¦áÑ ÿØ^ «´Üz ]}~³tEO÷…æ‰sÚ‚õ¼Dç´4G]â<¡¢:÷å7D=㌿¨×;›š×q¥J厩99aœ9ânTæï ›BQŸgçJ=Ùm÷ÝuºäWNo¢iÿ´ÿÔíŠcCÍII$5Ѝտç‚=žùI(aHêÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜êÜGé˜Og¿<ì‹ ] Ê@Î&IÀ$^ªà¸ÁÌ&¾(ÆžSðÏP©~êyf/[{v ­iC3ŒãÝÀþO¨aŒ o³”uÍPf\™ÇJC‰m“UG|[¨waK3Ÿ­\!ϲiûBP„9c¼ÐÝF«yiy–T¼‰‰Î;°\ÎâÛ®\<-Ø (Y§d"ûøvÓvïîq,ª`c†Hö{œœórèkد k‘\’Wf‹|C-ݽ¶\òu[H 8$Œq¯p¥Î¹uδáéQÅ€T…¬ù"Ë)sXÐZªˆ3öoþ¼ÁÐô²®}[°¡¢dÎ.oN»íZx ¬›Ð±îÅ)+žÐ ¾Ì«öðíŽîîùä·VTúÄL’Ϲۖ­Òð­`“€èV—µÙÅRì=U@ß5ÕÛùÍÖðFÍÏÓ~ˆÙ¹úoÑ7?Mú#fçé¿DlÜý7蛟¦ý³sôߢ0¹úOÑ7?Mú#fçé¿DlÜý7è„KŸ¦ýn›» ë€9ØK5E@îÃŽÓCmk8Z\ô2š¨‡ÜùcIk ×<Ôì ß›·ÐK[fµCDŒ–ëÏ·múç_ÑbåÉ8W±Âõ¶­°“h©TŸ(fòÚ—#œÀ.I.H qr`Ýã²eÇ$bÿÔ~NìÝÒúu]M×\ðç p ¥t“°[nés† ]qäϺB¦v=0ï i•Ó'&1ÏÔ~˜{z†’=#ósÅ¡­pÚñ®*”R«ËZŽm V¶«‡¾±enÛÃËп”E«*Æ7%þcôdzï]¾!hÞº¤éŒn~£ôÅúw… ƒmUqQ$õ\ÀöŠ÷…j»øŽH-t¸Í–·Žæn~£ôÇ»ZÇ.[‰ÉËÚ —ÝC HtñÞrGXÑ`t;D.÷“eÚ!ÇzQ»ZÑñq …®'¡#ÖLýh‰Ü‘ °íë1úÑ.á5Ú#×ý×h_÷]¢ße¥Éà…Þ’9í}2&v»DzÓä;DºJfc´G®îÒäðAôè™Úíëþë´G¯ï;DuÐíëÇC´G¯K´G¯o’tG®o’tG¯Ñ¿¼íÖC´GXÑ`t;DzÿºíÖC´G¯ï;DzÿºíVö_UÚ#×'ÙvˆõÿuÚ"è}ÐÒë×$ì ¤pޱÞvˆõßuÚ#¬‡hް:¢~ׯ1Ì!ÌqZ“›4OS"5]" Ë 4qí`Ǥ‡†g5ª¿Æ%zC§»¿yî}~óÜúýç¹õûÏsë÷žç×ï=ϯÞ{Ÿ_¼,Õ:þü÷>¿yî}~ðé:QÇœd $–W·óšèÏ.ÓžIKÆžgã=æBbµ)Þ{Ÿ_¼O?÷Ïsë÷žç×ï=ϯÞ{Ÿ_¼¯§ÉÿL÷>¿yî}~óÜúýãR@Þ߬{Ÿ_¼÷>¿yî}~ñê__¼‡ãësÜúýç¹õûÏsë÷žç×ï=ϯÞ¢"ð€9è3Üúýç¹õûÏsë÷žç×ï=ϯÞ{Ÿ_¼DZzÍî}~óÜúýç¹õûÏsë÷žç×ïå—Göç¹õûÏsë÷žç×ï=ϯÞ{Ÿ_¼ÒzIzùìþ̾G>lü_‹¬öë-Ób!Ö?§Ê\¬ó…Î{º)Œ«Q:6g¹œm#‘[Ÿ» *8hûwli÷tq)Uøœ ƒ`µÚ†“.M2C¢ã!ðIÌ@5¶Þ?Hmõò—wñóñ;„ÒÔAÕ8¢n¬.3kWrô51éßÒâ‚uäuû·Òº …àûçg<'‹÷ÇqË5™!SÑwž_«//£à4¼e‘'G7ô–eº0{ñš<±UtIãµ83bêIb¸¤êTß³Þ›šitÞÞŸ‚Ø,Ô¥u‡{À•§@Ú²nšñrm{^²=à>A Ð0&‹ÛUÞ´dæŒ&NûÅÇúàŸ’LÒëo)ÁvÖ^…²ð<›„ô}Ý^ƒÉ´vÆ5õñ¥wÃS€7À9}{ÍÅo¬ˆˆ—ý±ò´!CK¥Þˆs ×ýóaÙ¯ýü&/¿|ü{ÈìV.ó”ÚÖ6+×¼no]<çLÕÖ&–v/ׂ›?Oug™Êû#€„ÃT:>Ž=âTb‰Çêuˆ£dy,„7%Ž,é½ùõމ}‘©›“ #£‘³Î#¹-æsã,kÅÏå›ëàbt'ÛL»ÄChòø^Üÿ?8»ü6­WjÖb„Hñ5Þ pà#è8äz>m÷º]~ ?œ¯€x†ÎN1…ɕбÓAñÄpÔ…GY€#zÂDš´öçmßöf¹ø ­yMR½ç⸙~ô/LÒüÏo86mø Ÿøi7ÔûäÓ÷Óðslgs{^>Í‹ð1§”¸áþ»«\øÿÏÿ6o ñ×ß"ÑZíáŸàÍÏ…­‡o®W–ðOËîÅm¥ñ·l#AæN¯‘Æ £Ï€0úHŒø¦Íï)Ζ$ þµ”…öLèÒ¼_´Î%®B·нa /(^Ædac„‹E?Á‡T:)AÁÎ.ñž! "a·ütHM—®± î>>ð›v—…=C‘Yp!IH–ù½üÓfÍ›5^ZløQ¯H¦§F”—x4;&þþ^ñŒ}KM˜—©sc{ÎZ¬1ªzxr[· u“ߊü¢Ÿ¦KÖ„-ÒÙ3ÌÆCL(ûbvë¡'Ê‚!˜Ê\$0ß›þï†_CÉ ¿¾(rb¢_Ó¼1ØFîW+»vM#Pi>Ÿ¶"×߀|ÒÇS8¹95¿Æ±gk¥Ç[k¦n™òÞí[èÃÍS Grñ¬­\‡ùq1ìô+‘²h²»â\™s•]Iiâtá¤DtÀ#Én5ž)}ôz"®³ZºOð_"oéý°Ûòzº_žŒø¶kœ<¿%Ÿ ݺVr™BœFtœå»Ù²zÄmôUaäã‹+o¯Ý¬ñO*£ bØÕçñ‚s{óý0Œ ì+=ðeÙD¢aÁÿŒÛ•8x¼öáŠÐ+ìOЛ7ñŸ?¦ÐÑ_l²ëYh?‰š¹B}45öÉEjÑGöçÐx{o—9­:f­ ¼à0& XV«&>¤;;ßO[Â*ó“‘ðî\JH’ý¡1UÊS°2$M“Ëå½wú à|,„š¼°>$bä…%<±S9«Z¦±¹³““SwþxÎ?R4?bùÈŠ„,ºágÝÀQ²ü`•{ßÊ—¼ÿà(ø8Ž6œ\„E’ïÝxÍä™:†à/Î7©úJw¾o_!K.üg­ã6J_ßIáw¼æ9Ÿû›ü¦ç3Î<Ð.†ü+W:¿ßo î×-´jì:ét†¸Ã¤é—¬ßLöw«ÆiÈFË£ë[þ2"›S¿)#º÷gtŸK¦n&&Õ«ö —Þ/r7~Ó-]Ò›þ_ Ñ£z÷úêÃlÇ•ûØ0Üâ.¯ñž+—÷"ì‰ù¥©åœÑZ«NÈgÁ{Š~Li?¥7Y£¢ãþ„Ùòm6‘'œŽÏ øÚ4juކxÄ"cI¦6¤1Ø®wLí¥Ùœà8 =*SaZ ›‘³Î~íØü>ùºw¬K`®Coƒ7Mõ$rsûf–û‰,ëÃ×è©°9sÝ|œ"•jÇ%t™¾?T=BÿßPü&ŽûÙ‡èÀδŠàÁH_%wJ{ohyóÒŽÅÜŽ3ŠTf“*P$ ‚Öª ';Œ³96ôðëFÀÀ‡¼üÌbúFÀA)®ç‹œ»ŽžÌnwµxs°_‰Í»!—/Uëˆ!ýðçv}ïÛžù0m·í}ù¶ ´_tæñ£ç§Ns/hv’SWU°b2ˆ¦€[EG‹–a9šQ¨+ÞozØ…ÍJZC?–k *ªXÓ¿ŒÉ ðø¯¬ø€ñ›‹ówü€ÁƒnŸŠÓÉ3N;\»jòÛü\—…ïgùÏæâ©Àt´h2»È“‰õå+'® 0‘*|íJ„)²+דÔÝÆjÝeÒ•:ÇípéDãð§ŒRéDäF:]c§¸Ýx´öý>ƒáù´6§‹ÐóŠ+“p=*›AZÅ~k&€>Är¶"›N>?Dñ€ ¼äƒî¼ÓÙüÙûñdìéÿvFúo9þCýYÛÓtö7· î;ÿV)x½~Ü;1¢ìc_îËû¼ÞiÐ2·5¶‹´6ÇR1Áy€ë™üÜEïàò–ÿˆKî4áïbUÑ»\¸ò8”Y:ÛRoWÆRÎÎ~Øèíý>ƒáù­ËÁܺʞ['N£îÄž¾e(€ ›÷|~­ã³ÏúùÁ …"îµÀ¤j‰ž9gÛs¬Ý;b×I÷³ŽÉWÆ>Î#—'ÙÇÙÇÙÇÙĦp_Þ=2„~õRq’O3ñ6>ç#XSahøƒ‚cÎRÈs‡ˆNüY3DyŽÌöqö}cìãìãìãìãìãìãìâl¨Äƒ·£ŸJËx¦Ø„ìâ‹þD¶ÊŠšoyüߊl͉‡h¦Ì#Œ»Žéœ ØÉ}M_ÿLp?Jû/¬\Ÿd‡¿ÒSè>Ò)ðú·Œú?Žw,šÌò+ŽZ8«o]žn6?Oœ˜ÄÆ&11‰„âcÛ“®ß&q1ƒVG„ýí8EÝE "Ïyd E.À‘sû±IåT èÓºã6A`Z½¹¿¶-v¬Ú©·f¼+ÜfLbcòÍy?|ŸTÿ8”€‘ûÜämžƒöÛ‡}K­ ï_Œé]´ý“.ëi=òqüúÃiÿ Fw ס& ñ (…Ðç2DþLþn*ñÜùBôÔ?—üoèø˜=+í€DFÐUÿ9xZK_våý2‚t%&‚püao)ayï:4Ö³GÁŒð‡'Ž…¼ÏŸ¿Á†ëˇàúwðc3°¿Œ-{þØìéãDûóqš‹¾¹ÄŸI«ç&-·|füÓ’~>øµíÊN¼øÅ–mì\Æ*œQ%O.³gÜ$vøÿ,÷2.ª‘wg'ó‚6uÎ ý%~U>ƒáý't+„†™Ž ùFµ÷ÎXþA9J¹ÎÐŒ­M£Þ>Bø?àsxî,|ßAŒÍ[ ès[ÒõjiQÜßcH‘)t|ëô©l±Â;¼G:FÙfûᬣ…'6Õ“cQª{õpxÓŸ/ß™FhE …ø½Âm[”%Ù‚”á%'He>¯Ž¾k¬ãž±>¶"½—RH%ücØ&Âd¹Ñ1b&”:ºÁÕQžM?ü }Ãò„ùcL‰ˆ•Øã EhƒøÎÒØ“Dõ2T¬«Á®Ë¯Mu²#`ľ\œ+UžÞõ–"PÀ­u÷~'Sú¨¿ˆýðq™ªœáé>*³åq=gž~n,ø]„ç¿Æõë ä>;J›ÿXAÈóÇ–vñðJ§ß ÚèC¹ƒ^'SùÿàP6[Ûû~߯Šáþ~V‡Hw?ãã¼]ðxŤV€ó&/á>ñç“לã…#49zö96åÙÒóÆ4ÔïÑ/ßfí‚ "`çïË›‚ꩾÂájý89o%‰Þ«àÔð`àßoBŽûëæàè½j|§Ç~2ž™~ãiÓ/_8«ß>!¹)>‚×8p±¦Y#ÿ¶9¦¥üd<"ÓðÎ>´ | Ž¿|o¾yi¾ü|€6Yz=d~@¿ã"c.‰S‚œN¡u Ý*ìje­{\ÐÕan«šKà1ÖlBTø cNþÙMÓÃðAücf¹øCQÜbcŸ´ìýïúø#ˆ[žCŸóJÓ£ÇÀ¥´Öü|ÊÁÓðqýœs?ÝÈ×zñ£kz>¾ß2ˆ/œ¨jNºst×ß!ëh•ìöÀâ´ˆïèÿ–ð¤×€Ý'Ù£Þsirº)ØäÑÓ_[ç"ÙöGJg³ocJéqUÓälõ¼Ñ¯‹ ¶¹>›÷ÖEÙLÑ„ð»N²ÜhÇ­þ2ˆ©Ø|z;¯«à¶¡:cŸÂÄ1êvØïÑ`µ- D2ÎcyBB½Y½>0µŠ·aÍIbï·xQRK]{…Ä­þ“žY`¬ 8 ‡-eѸð=7¤æP SE¾rv°ŽkI¹/g÷|ƒÎ88>u7ÂÙ¿?Ù\`Tþowóœo¼ð]$5¼:+œ…ïxΙ(H{›¿@¸QD=áýÞ×öuˆÍ©8Ž0›¸ÅšQ§<9Æ5,|&¾‰øügy/_wá%Fã—+åÉûûÏábÙRG˼ !!Á4ÞFmÃH`Û€¼0‹7o ÛcÎa žw,š7JÔ¢£ÀÛ£4éDª-í¶íÅåbÐÈ:ÆÛø$Z‡+|Ög~ ‡T&¼`Cb- êÙé1Yz†XSÞŸ¶l‚ µ ¼q›Ð-ÞÒì£à6TN¦t^f±ΞIŸRi½bI Ð1ë‡#Î0LAˆ”ÜG&ÀBg Ãzß¿…ºY&QûV§®8Ì>©Xxs°¾g&»×ÆÒ3áå—±sËÝ3åÊâ“ê'róü½›ˆ4QÆsp\æ‰Mkè Ž²Ã&1vì>GX6KKB P¤Írâ'»´•‹ÿL{‹çÀßÅ»–Œk®ñ Ž_œPäíàAŒbÖhsËÓ—nÝ‹èý²xž†«´0XZ TDO¹ÿȦöŠs«öË€ öW¼¶¤JPvgÙ®æ®]fó¾ŒüÍ8ëÀ{Éÿ†>ƒâ÷íø®Öò'.±­°ÃoØÔüO_j>LÓáŸÂÅS?j§K:^2DlÑÒóú($xqkˆælMפ1•>*ÂöøÔ„‡{5<ö¯µR€G âóÁó¡[ï·–R¾ÛÈsÂ:~&pƒÂ8ótо3µõ—7ZÛzeU–9ÜÃÈc{ ò9¹¢Ú€„ê§œ‘rÑß)ú@ö‘S*ËKQ¨ ›¸½j¨•ÔWB±ÇšºQÛ|&öëãn´$}ÜpûgÖä­»g¼-`½ÉlÝ”:,ÇW¡»€ÑÜ›U®ÆÞŒ¸¾^‰š£!£œ÷Ú^ÜùõúJqNZý¿ïòÆ»ü|¦h;Íž4ºãÈ¡‰Hüü&õ9(Ï©S¬1AÇ’{6e ež„ìǼ±À pÝ·ch5Ç=eP¢h"õGá&ýY»§Žó{°!@5ºHŽä€y»åÑœNè¾ñ=sr%l~†þºòâûʲ,$›œtLDç‹ËÍÍ/`.)ä*;A#¼!EÙhb½‡Äõm¡]T"L°¹Mœ¨èeg¬ïlÅX¤Xs¹ã=˨ªÀV;²÷Ž‹ £6lŽða²‘UvœñÖ*Åtu%-ÜäiZºeUkÉ”;;ËP+C5¬F¤˜ é[“×Éx1ÔP‚[ ÖùÙ­9û­¤u$kôæíü|VõÆKÒ*«MÃBŸqštJ`!§­ËÖðg°V/¡Å6QÓpÆ h¨i?&e 9ƒ¿2è`H½ +·>)HÊy,âï!‚¶WEEÞ©)š!Ù[ÙdÞŠøž¶yâ£-Àc›kÄË‚m¬cFv‡Ú†Ý„S|aa`þFý˜‰3s“|Mc,ÙE­¹nc€eL«\ ­4fUÒÖˆšÅ¨PA >§y¨N›eB¯gC“o°ØÎy°.ØyÅÚøÛ /ÙŠp -ÆïÚ[V(öhÀ «h©©ís2rXÐWÀÅÂ¬ØæqØÚáý4Ýõ×Ëw<ü{OÄ¢%<9Ãá£Á޾Ûûѧ ¶ùß—x£ãñŽmsùùÇ&Åæ6B’ß-¿•ùÛ×ççêº> ï+šŽÞmò÷›|€Pã«x¬9ƒ¶/$‘ ¾ÿŒ-ƒ&l ­Þ½åÆWiÔbW–¬ÄpæŸÀWí䑯õØè^ÖõŸw•ÑŽn¦²ûpTnÝÙÝC÷Åa.=£)h¼qœAQ¤è‘¢ä¤¢ÉUVÇŒ0B2EÒ¨†ž3ÆDûaÕ>5½­éÁ9bÂ"!kæM°ÂÝš{-ÊÕpßz”:+áIÀ{n‰¾1úKå¶M#ÎprÛä´ûã<ýÍ¡©6DüÍ%•€kžpBGmV…u8ĵ׸ ]›ñ•€³@ y3üãéD ¡5ÁàBb¯ÂéA É­7Æoסæ+:ž'éÂÞÿøèr…˜ië:>°‹áŽÍ.C¯7L¥!À#£uÚhnònt+¸V‚‰1‘Þ{.S{´çÖ\eÐËøÙ7íL É !i\Ò†)l×Uz™E†—‹b<)yÁ´äÚ º9 „ ]ì@4`Kh$Ô¯]–›Êƒ=稆+ÏÉ‹L¥m6£•LB™‡òð$ 7¼ ÄME©yÀ9¥*UÝ>XIÔYÄr¦ð—±yj½6èßY`©Wvxg5LØ”/4×¼L´ „pÓQ‹³¼2Iª™X§Wœ‚‚÷› íÅôÞ¿{ÁËB¦ì0N€CæP÷|º÷çåñž|-Nã–&žq[ßÇPøâo&ìN{¬V)ÞA¥¶ÑßãOmÑï{\2æ¬nôø)>óälÖßœ&ACíšæ<âÃH¦ p@/Aup[ó¦éÓW<#Ø6E¦Tï4ðiåËmfËŠ #ÚµÍÂ× •ä´­Z¼çƒ"ƒÉiuþ3…äµ6TˆQxÍ~ RÉé4Þl¢]”Û¶óËd@ÐOŒUx›$£ÏC£ÎnHò3ªÕ®þK³_¦Ç|üS—ô|…¦ñ«~BrŽ q©ltEfa¶xŠÏÌùЧBÆ7?ÃašÆ£`né¾YÅüƒCÂN±Ô´¯fÞ•ûψžŸŠRyÍ4%ÍîåG4éæ;Åj4à̱Aº@Ú&ñˆ…€Sâ”<ác]˜§–î2”—ÚݧŒ®“‡`BsÀ˜=*Ä æC¬æd7·ÝžgÊ¢|f–ß7ÁEXðä:ÒPÝ'.»ÃðÔ`ÉK9Æ/Š’‰·S¹„ÙHLûFVŸl¡¾‰µØo•)ev‡åµÃðá™Iaø¹7-êkP™Çžü`˜Fí}DOi‡Ýñ¶tÞ0(…ˆHØ s’{šÔ‰G¹)C,Á%RÌ`§ ê.”§™÷Ív84{ îáî²·Wc pU¼[ò AHF QM s“õ7±ÓWÎSšB‰.€½ƒåÝ|uú o3ù]cÒ`1W54xxÁ«ö-¥ÒžyÍÙÎ@èà<¦"y¬˜%ò?õƒ¿Q`=?Ó>_¦±Mv§§KƒË;h£¶›üƒ’û:s{KÇQ ÷¯9¡YÛ˼°Cn"lkÍÈxçœõ÷ëhøF»ü|R“.Ü!ÍÞDÍå •B­¯ DtE¶ ¦4"Ô­Pi¼qo²jéeb¹Ï9ªok_½x1£¬al¨Zæ¸MÐ uÅù P@#7m)¿=öGÛŒh0R{ÈÈúEE¶Â(Mti¼ÿ9õçûÁ¨ý,í¾qEØÜI/\õšÆ­U¯ÏyÿŽÈÔjvôáüâ%êÛ]¶Kd1Ü쀞¼ºpð›¯ã:@*’0º'›ƒKòœ«M—Ÿ<|ÀØäk"a€Ý34­Xcç³¼ì-0› ÎlŒ‡¯N3LwûgP!Êþ[Á$àÍûg-ûdíÛƒ.b¾ƒ»Z9dhÆÄxfOR ºP߬UJ"%7“{áÇ‚"$ì—<ë¿Y¾»cÙ6Þ\ …¾‚+WžÎr¨„‘Bk–¬Ñj¥¹xHhÅ6h$-½‚â+p¡ŠDÚoõ.sðkíý-ŸYq,ÔWkÛw nÉêù>1ƒ¡ßØÉ¹ó ×ãfzÄ`bŠ'çzÍvÆê åÏX™!-Lûå¼õùi¿‡õù_—õù_—õùO“(!è·n2PìÇ®Åâ…HïçØü$\„ œÌwÕàΨ Wì†9”­Ÿ|ÐKžÑò¤9kL/%Ðfü ù˜*y  žè=D"þ|c±ûj: ê´Â Pqhìôû~„ØÞ:ùPyßÏõ¿“Œ&ÇÏYó4IÈkc*tØ#ÆÜb½ Wd˜xðýÇŠYîG÷3T‘•¾n1¢5ˆ;ý2Ãáþ˜ë9rôwú¨NâïÆƒÎG÷ÿÎlô[ýãæ¶Ö½>glãJ jØo¼­»^~ÿ:‚øÅ¦h@PÒ©•+u¡Q»öÀxÓnÉnËÛf—XÆi±mGœºÆc¸7(GÀ&ñ48=Vß”Ã~QW`µzÀ‚s£kkuã=<äð6¯ï*äs–¹c¼ØºY#K¡í0S;: )ÀçXU*nÒk…EMªÁ ä{tdøA&dV}ÞrnÆÙö‹žJ¶DÅ'–ÌÓÜ/T§É.Dø ,Õ}Ü ;Š%"%8œñlÑ®[ý²± !ÞŽþŸœ€ ^é7^¾­øü„ËKøù’CËp4w¼Þß|d {´3ñóë×§N½h§vÁ½Îä˜urQÓ=ï,sý“úáÌ¡­iû8àxœØm}®H¨èŸYX¥D•)è_? ïX€æ1°~Jë5€Wj_*ëÝÁ†¾€l#OëU¡ °D¯Ž³yÅ“W^X® “V…k~¼1[PH/œ¯íŠ>T5: ¤q˜:Ö[‚”1Ï÷©XüÅyÃ9ª< õ· ☠5Ó¦°g†ñ„À³[O“ÈbM‘\åzïW5¯%@®‚ôgEi"¢Žøá‹"Î4ã‘1ådíE|Åí»yÅ›$>NÚ¦±ƒ/6¾‹ƒa ¦Í­yÀÄò£o ³ô>·ãòtù—f ÙjŸ7ØCí‡h©<ŹõwûÂÿ]ûçÕßï>®ÿyõwûÏ«¿Þ}]þóêï÷ŸW¼ú»ýçÕßï>®ÿyõgûÊšßžq>{L|5ƒKÿâ—wñŸ[ñù:|ʉñ’c _A@"ìÆÛrJ©ØôÏèøþ‹—ñN?£áùqýÑñýÑñýÑñýÑñ_ùqWz`opŠÂy|~™)}¨_Ú¾°B˜ß íÔ6`¨"&óäsˆŽlßµNŸÆE#z^¢rƳÝðgWž-º³4ã *û¾üÚ£¯¼<ýd§Ð_ÆhuJ¢HØç?lòæ, ~éXÆSÑ’BÙöÆ \€p§˜r‡ !G¸u¼€?ó·£à*x+CìUìié[ŒðJCÚº€L8bhQ½ÝûàxÚDa¢R©º¹Ýe¡œBW&äâó+•·ó>YóÃjã(’Ñ_]ã)X€CÂì…¼bxCÞŸ#Pi:ûkÉ»ótâC•Úå½CäÍfÅÈçbrêo!¬TØ5¢<âÿ­Fh¥ßП>|ùóç³ÕþG>|ñƒs6|åy¬Îp ,è ú³!@P›”Ømq¢ 4Qø?T7íØ3“¼ì›ÆôcPfog(ÎÅ7‘ÁÇç6ž×"ô‹gýà MSÈâÃ7r»®‡šfËãÉæ4àEÐg<±`‘G Ö³¼Ùß`Éjk `>wj@w_uÉç‰Áxª<‚—Ó gжi°`ýŒ.ßl½e„î±@F61·£óÈâ•ã>WçžìÑcG©ªS(>246œbhô™œ >°*©²Ðoöù6à!Ù4zø„ 4r£½5®Ø(;®ÿ‡ß6hö$üä:D+O8ß7´DƱ÷?—ù—6Ï,Ñ÷LšµšL äŽ[ìòÄçarãèwíÛ5Ý[ü>1¼!äoÛ¶p¨Ü£ÓÞ<àMìðÁ.š» Yø@ŠcÆ@8ðú0r(ï}/XR]\bƒ/DTHšGä~xø ‰u~Ù9<ÿ'³í›Í¹ANÎý1§¿&;¡ƒ ~àÄô=6ÃS×­¿@‘2NÍ1¯%²á)üž|à€‹¨@wR½Ã¬x^ïuà×  âm éƒN̳÷§ts¾¼â@V‚\²kƒ~°ÑeaÈ 80Ú’ƒ¯8Cš®š0_ ·‡øÆe:ÿœÚ胇“ÝÚ®uÈã9zyÀ‰X=¼8?Ýâ@bQ‰¦—š$½¼5. Æ•Òë×Éè眆Cä†C!Èd2d2 †C!Èd2 †C!Èd2 ‡Â †G3!Èd2 †z9׳w:7‹5Dá†Áþ|8ÁyŸÆhÎNît`*„¼èÅZ³æɦù×9X€‡3BDy&-Ê÷Ã6ò8°ÈåÿüÿÚ?!ÿýZ?ÿÚ?!ÿýZ?ÿÚ ‚ $’I$’I$’I$’I$’I$’I$’I$’I$€$’H  €$’I$$’I$’I$’I$’I$’I$I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$I’I$‚I$’I$I$I$’ ’I I$’I’ $A ’H$‚  ‚I$’I’@$ $’ AH@$€@$‚H$‚A A$H @ €$’I$A ‚A$’A ’I$@H$’H$  $’ ’I$‚I ’I ’H’ ’ $’I$’I$’I$’A‚I  $’H’I$I$’I$’I @’ $’I ’ $’I$’I$‚I$H I ’H$’HH$ €’$@’@$€I ’I$’A@‚’@€$‚I$’H$$€H  IA’$’ $’I’I€I$’ ’ $’  $’H$’I$’‚‚‚@€I$ $I ’ $’A$’ ’@$’A$‚@’I$I$’I ’$’I$€@’$’ ’I$’ $’A$A$H$H‚$ I$’H$‚I$’@$H‚$‚I ’   $‚ $I$$’$’ ‚H$$’$I H$  $’ ’ @’H‚‚A$H$I H $’I$III’I’A’H €$I ‚$’  ’I$‚I$’H$’ $’A ’H$@$’ $’A  ’I‚H$ ’H$’I$’I$€I$’I$€A €$’A$’I ’ $’I$’I$H$I A‚H ’A$ ’H$’A A I$’ $’I$’I$’ I$’ $‚$‚I’ $‚I$‚A HI$’H ’$’A$‚$’@‚I$@’H$‚H$ H ‚IH‚ ’ ’I   ’A €$@$’H’I$‚H I$ $$I$’I ’I$€‚ $€ $’ $$’H$I$’H$’@€@$’A @‚A€ $’I ’I$@’I € $‚$’$’I$‚I ‚ $’I‚I$’I$’I$’I$’I’ $’ I$ ’I$‚@$’$’HH$’€ ’I$’H  $’I@H$’I$’H$@@$’HH $ ’AA’H$’@$’‚@$ $I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$ÿÚ?ËhRRjF«¯… –AkÙ¥¤{Âq¥pt}ƒi aèŠô—ét‰Žq¶ÄxýØÅtL†®È½çÀ{xqú$RZ4 DB+Œ ½Rá‰ö£z p!k•Ø4@Dz hØÎ^¿˜ÿYôÇúϦ?Ö}1þ³éõŸL¬úcýgÓë>˜ÿYôÇúϦ?Ö}1þ³éõŸL¬úcýgÓë>˜ÿYôÇúϦ?Ö}1þ³éõŸL¬úcýgÓë>˜ÿYôÇúϦ?Ö}1þ³éõŸL¬úcýgÓë>˜ÿYôÇúϦ?Ö€@4rýØRœ§Î#Že«Ý¯Ç̉lÙW%rä2©P–ð]x2ÐAòÖº.Þ¬iRÆ?±#  gÓ<ˆHÁŽ1»©}K„§[}˜+#¢#V(¢íy˸ÃQ‡5ìÁò1Uþ`|ã³@„@ØåNVÞp,pêJó7ƒŸ’ÖþaLh*Âu •åÉÄ–´H^gŠ 4S ‘Ð]g÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ï÷Üÿ}Ì@BUÑ_ÿ†„€¢\Ž¿N|ùóçÏŸ>|ø|_>h7“¿¿ã>y©Cà&¨ä¢Ä‰N˜+Å7•1§‘ ò©OdâAE´¿dal´C Ëf¾Ï„ wäuùgÏŸ>˜šÓ¼GÇäŸ>{ì…H&Ü ü“çÏ×»e¼}æ…qW”ù³çÏŸ=“ojXÏÏ>|ùóç$,ú/ÌŸ>|ùówW€¼þióçÏž0{&)UÅù/rìî¿“À5&Aׯ`)ÂÚY#zµyøšPøx–Lߌ¬j»ÿ8‹è„•p®9 Á@œ«aÖ&îÅEB¯¨$Vˆ @ëùÁ)A$ÝòÑ9ªQF¨D²™£I*æh}±IA/OÁ-jÒ+]T íý!@µF‰ÕÕçž¾^7Áº÷óŒn}Qð 3œ1z¼Aî8Ð&nG'düæ¶ÃRµetÍR±áËH[âg =äÊ£¨|ë6“Ä[ùã,°&€5‡:œ½c)F£½^‹'¬ŠÀ®A†IP'Ù®¨èøH†ºtâgÎ'šUb:×é3öí$_ᚤN”îwêøÁCv7:"3¡{Å-¢¹mèi¹Î¸‰•šA½ë.÷¨ÑXÖ͵oÁYpdh®1C**QBoñ—×&š9>ã!4ÍçqÏß]ØZ*ß åtÀ+‹@çÖ%('Ê$àåÌR¼%§7 ` n‹¼kkê¨Pêt÷‘é4L2 ñˆÛê…õ4ßÄ©PüšCB%LËc `“OlÞ@W nª‹KæÒc¯¼”u4†!j‚± k·~0Q ÊC8¯A¼j³®øXnÐ Té‡l ƒc‚å?„óÖ$²lÅ£cœw²Yvð³ÎU+T™Cïpœ~˜™…«Xì-.±”U·ÊÝ›Mä« `Jðõ‚y¬z… ’ûßÛ ˜VŠ1§ %YYK`»çdº$*2•m;¼:ÉŒ‚¢ì.²i]mB¦¾z˜h9’ Ù'f•õŽÝVˆÈ"ýâ<ü˜l–Yzªþ1 Þ¼¨äí ÷pcÙ*‹ ;6ç&Œ}ü<ßßä'r‰Æ·ó@,637nЃ²†+¦ NF«Œ¿ó›¦D]„¿ÈÏ®{øëkõ;µ§x¸$ÞŠ»Ó¯XµTYu‘x¶ {?| k×uR8±r¢Vú>1xK€CÏm ÀNd× ºÕ·¬8ˆDMMk»·Ì­@ Ë°‚È䛹ÀI•?´Ìø£÷Î?LiʨéˆÉh ëP~æõb¡47 CÆ%%5yU—îJפ%½EùŽ6ÏzçQ¸QEDNò\±Ô‚ÄuŒÙ V<&ûÂûÄ„a(áÙäÀ@ PšicRcÎìBW…ÇaØ©ÒI6í:RÓ~ûã$6LÜxY¼U—6â-"þ3GÒíüaðC·ü0J"B¼V½å!ØQKžµÓ=òÏöþ=çýÍÇ l<¿Ϭ——`>YÞpàS¨Ç›0iˆJmOãÂ$ƒávîûÑ®L€·ÞwÛp 塚pN\bœ:¤ t˜d (ìò7ÀR‹îÏßyYj~¯AÅHì5M§>³áBgÀî¦àvqÈÛ«Àm Õ®ÿðök6¬$XnA“0G{kÓ¿„~ˆæÂ7»Àã¤}è ÝŸFcÕ9 ¿+~Þ5qÀ@˜š}“àʦÂC”%ç#@€áÅwU#¿Îð¼Û¿âý±ÞPÙ¼Z¾SRžn0uÇ¡B»—í„O•5»uöÈŒ¿¶·BºÖsåD_vzïœTÖwÌnÎZ?˜e7/ÇçË;‡¤ÀŽÜÜuõKÈáú›®T+TŸgq¤û;¡pˆŒiÔfưWØEAìEML¿Žù!x<§DærÒçaÏã,B«A‹NË’å1Ö¢ÈËŒO"ゕܘlâwðø«Wy])‘tíºh‡.×ÇçÅ9ŒùòÁ±¢w„´è³„F!»4¨u‚')Â#¿‘2] ²Äóêx”GD=”Áîœ"®‡A¸:\òáë“/£HpFù×Àø6]^¾Íî‡Ûô6b/'Q´Ý³ç/¤]ûñ2ižp´=â¡ ©:Ø«6ÍÕ£4r:3|¯8™´Ieù¡“a/)oìù®Ú;5ðᣠ‚Uñ½b=Q¹¬4Z¯jØz'xX˜$Ík¨F?¤ÂÒ¼*ðƒ¯ÄˆâJnBF8îÃòfº¿Óc7·®–S“¦!~ú­AòËöðÈ…YØ j9xq€‡Ó³ðÉôž€ÛåÁì³!>ÄÛE;‘w¼T„+]Ì«šxIQW7N5• ê4‡ÅívMŠ'ŽÖ8¿EŒ&éªÑ¡ü'&Ÿ8ÖÂå¯Àý.Òáý.Òàj€SÚ¨hþ1VÿFet]ÍçÆè€14CËX5îiD($ÆçôÂdCaX÷ÞÅ‚}õ5;\u‰+)Vò›>0…Zz\HHQ 5”Ôã©…"‡3ó9¨é Œ:j y¸$$ —°„|aï´Àlh”¢ïdlwè‰Ô¼ä*z4.tÒíýÜ.¹“Š€¥AT2©Ö¸lÂÜñ $)D³µÉu/Íêª|Q¸ñ˜ØÑßÜ4p„iµ+¶à ,ùáJ-<ã¾>JB¤nñ°é(ýûý¾!!HÒ–?læÕâ¶ïçáX$!zذ°Ê9×PZ'µ'†‹„E'b ×œ¼ôºESÍ™:>Ϋ~ã @Ó÷ÆptìP­L°Š¢ÛŽwᜌ ë´ñ9†PM ' ņ„W~}ñ‚®P©ý¿AJQ.Žçâf ?‘ý6–/ïñ½,L+lF(²ÔH‡#|Zˆj€¸jÜÏdÅ “ƒE_% êOW6yÖ ê |wŽ Ô¬Ž}6óŽ‡ Ó¥ »¡ï"-öC\×™²ÄЈX]ÎUamƒ»‘öùbíå;Ÿ'ñþHKwÏðŒßN ¢ºI<â„p¥1€ñ•Íump >±‡i*õ­ù5rÖæ  ¡ƒÊ±\«.°æ.ð\©ÁÜbƒÌ§ÄÐ]•+fúùu6":îtàx=þ’‡?ü×Dþúø?î™Ò·5¬] îç7b,7u,†]±//àÈìÊúMç ôI¼h•ްe6@žñãyî½ ŽµøÂ× ›œ>ì™ &ÀÍiÀÓý|óH“u¨* ØsÌTÞr€ßWÉBqVêªiÞ8 ¶‘m# çfFí5+7èË}$ž÷RVëöÊ«ÓÑòÒÎþ)ØPôc®MZwzý i|ŸüaP1šyŽÖmã­Wmç,ÑÆi uH@3‰²mz@H/\näÖ:AØ Æ¥žq¬²²¡mÆ(„q !{íðo0à€®HçXQXyooÉüÓ‘_X”(¤µ)ëWä7ô£,Óõz™EÊ<: F<™¢éÃ5"qw%E?øöÐKâwùýt;ßÅ ‹”‡óô#f ‚GÉ– A(’ E^;ÃyØZÂÛP N>t[$gLº^~oãü“øšy¿'^Öí¹æ`–$kfŸ;,y¢ŸŒÕ#Á¯y%¦® ȸÊ*v<†ðîa1†ÍŸç)RìäÎD›!ˆèÜÛ62)ÈšÁS bå$5í·;s¥YìmÍÞ[BGPüùÄÅŠ5.DeçVû)ö‡ó©‚¸^{WÂq|ÿ ­Ï‘ëöŽÇÑ=¢sÞªwh{þ~F'9ÂiDßææŠÉµ¯Ùþ¦Zr«DÑÃpáwñGÏ3‰•<weŠ&™Ø ×rË‘[ÛR Å]ª‡rpL8‚µ(Å! GAàæøš4¶h8õÎ;)üà5C^^o‰£GDÊö莛¸h £ÃEŽ!¤׊AQD§ð‘.ÀôWITÊ®ƒ™< yzÄEH/t~_Ù¶ ‘iC··Xz_”Ûë,Q/ Þ Fãêž*8€Z¥^#òÿä›™NëgÈgnL@ÖJïá?í–-ÙlRÝ<2Èë±,R ;z¸RGG¥1«¡oxš¹£0)Ýtµ:¸8DeL]˜íÛ¬k¾ * ˆ@%1®yLißðo8a=a’{Þ@|⃋c)SUàíÊáí‹|Y=fÁ BiÕzȧXX“µ(˜¹Zn±îÜml-Ñëΰl¤œè~¬1¨µ¥šÐ9÷gZ:s¼çÞÛ5Âû^þ¾èqª|6|Xj÷(s—ÊÕÚèÄiÂEqå)0"^ó¥7¼¡²•­‚a†oM@/úíÙ‚¿œ¤/¯X…¼UÙ¾'܌м/£„@Œ[Jl&éŠÀš#CÑ|úÆFÐ¥Ëä&½âÄoÐmwÉã7# VÒsL–ŽPófð=Ú¾Öü~LXñù:'uïY^V… f@Òc\&m‰§ä¿«Š ƒ6šÉÂ2>¢ … N°àÅË€À:WL‹ÞTç…ZÞu!ºbŠdüq q½ozØØbä¬&%³Ú±¬ o^Zç'¦¶Äp@ZãEã ò<Ð@Óû—äþ?éIwÈø°ŠnÔa©ŠГmLÛÄ`_„’›FÇ’9¤Í§¯Ô¼°XBAÚ}ÁØØƒ‘õ˜]eY>ˆ 1NÀÚR³²gß¾Þ^_Í›¼§É§îÞ~I®Õriû·ŒûHðí¿·ü±´R±m:¾_lûäy4ßßsíáÛlÇtðn8šíW&Ÿ»xÏ´gÛþY`€[êõ¼bj ,M¾&|ÐèÉ)î¶L†…PûvG À~ªˆºðìKð"¶2ìf} |;kí™»Ê|ºkï¹Q£*rý3áH4vt¼|c6RG—é¹öÉäÓ}Ù ^¿g§¼‰ÉÔ¤ún3M:ìSN~Þò·R2rõÎI]†×߯h/¯hžw„êÇ€jgi2Œ‡›ýÆËð*°µØM] Óª•œs‰æ³ŒääðÍÇ XmøÑ2vGá®Eè\ ‚0‹²ñ~X¢¯çkòô¤³¡ B…Ô(jõ†cô´öhN2æ¢ ØÄk;9†¯Ò<±©×A€l8 '&víIûà%øO5Øþ0-+ÐøÂ cˆ/ïð¬ÛCŸóðœ`!,AüË<óÎŽV:“bukï†ÂcÐê hÞ.¯…üÖ¸á9\§`ÕQó´Mæ´Òæfû ¤ÚÑ¥ó‰ãˆã´x~—l//¡<óÏ<ó§ƒ l¯Žrši;=áN¤jÒ»lˆÛ r£]âî™j j”wò†   hórª¥¤Ô‚´ ïàHÀ7™€ä«qvLÌ ïpÙ””1á"xe{ø ‘(õòõ¤³TOáø"7tŸ—Àc(`ËB |à}ßuðRO8(cÐyÂmŽM¨ùøàÁ²m9>#GÏÂ`±å|ïÆ&jIµ_üd‡…÷DÇGŒlmó {ìÞ4yL€òiòŽ./“"¸“ž ‹ÍÕÕy¹ kÇ$²×|eÿá”aÖiã|'Û/ö:÷…Tn(ëm,Þ_þ!C¦‘u0qd×sYºvw8NB ^Ÿ,¿ü8¿ü8]¢Bµ/nEÅ…‡míø¹˜&+°d^n;Å¥š '¬Á8"­œÝ®ñÄ3ÌQ šn`ä†À*‡|5yë/üJDþƒáˆ–©£)Š@² o}3¾jìü¶zdg£a"`Ur USÑ—ýአ´Øœk¿Ò6¨Pü¬3øÿ­%šûlÊ çÎ û µÊ\¼q{" e°†!;ÖvœÓo2\­ï"KFè!ÅÍ™ª;©ÏcöãØ¤‘5F–Ä;ÀuUŠîÛš#ÐÓ6+‡ô´vÊ@™ºTØK©Ã\ÁiïÃëIu‘âIŠ9XÑ´¹¨ô׈9¸ í-*lTtwˆ©Ž(Ú :ôû²LƒbA(ò_‰ ¨7åÑ‚("œž>BŠ‘8üüÁ÷®ý²à”ç`EBs&jòXÞÓÙ¨2îQ°‘¼bX: l\˜ÀÀ‚Á‰Ôþ?èÉd!e]ªí½üªBA¦àïBØî<ŒÜZ¡`4¼„ÛÞ¯¬p&§Eín³ûÞ.Íz4Ïh0¤Ê$Ù–TwÆ:"AjÐÛ†ñùêÍ!åúÇHÌšù·â¸wlÚßÄq +6°?? N1Rò—øÄÞhf=1A(ïɳTy|ψ¢lK¡µÉ¾r7`º-x5ð@$8³ÇÂ9á(S„xGA 0BîhåÍëåœàêä1ÐÚk´ýßÔþ?É‹]èH}ËàÅK…¿0|¦ “ðøñ F› E?ï€øTºÂgåM®ï·BŸc|»x)«Ò÷¼W &¥{ØNp¾0<oTB±`ðhk¢|HÇŒÒj3bûÎðgl¢KF bÓN©*¡¹Þ82A~¬“±n±éá‘` xb/-!x‚%¬x¥j¢í³á«ñìâŸC“áDD©^]|Q ·ÕLrS ª§E›Á Ézyøýœ&¯±œ(Ð;C§Öþ=ß=qðtró=í.ßAˆ(ÞÁKÌ}á²±ð£û˜„‰M;òlɼtÓ€_†§˜o6:wÓA¾t ²¸ÓP–>.2Ðpi~#2è }þoãü“÷¿s>Ej®×p;ûb ÁH×¢¡üâ›îâkC áÞò•Á‘~äÃ2ry0±÷bóÏ” ;ÔÑ&2†¥:ß+ˆ ;ÄÛAG“„¦óHœ®/¦“,•탣¬Ð)¨¥õ"þøÃ+(›`@ûõñ+ô‘Ï–³øü3”Å¥tÑÎ?E@9 ÆBBótôÛ„ÃÌZ¥_uÇÀ †laùÆìH¥ßæSv[³½·øë57""`¾ׯc÷³è¹×RÕ]÷¬×H Ôõ#­b2š·-DиÄï3> ãÿ´ÐH5ìñPñ~ I*왼XHµAñ·¿„Kv)³íˆ$Xв¿ƒ¢%»ä #þ2£ ô{Çjƒs§r:µ.¾/l}‘QY®howwàa!EÙÓî|Æh,u pOßྲྀ6Bªœ±€2•&šŸ÷5—H™¼!·Áü’XADº«£2]3gš]zÅPò¼ejwØx ôÞhM¦ÀIù>ØžÞ¸ð×ä@ÑËÔ+h7E|®Œ²ÙÛˆ¤siýñƒ‚øZoÄoÆDX mÓ)*Ë]‡ZàÈ«Ú[t-NqÎó8ªð HŽÑn6‰h­áxC䎬¨œÄG¼ŽÂ'^þSiá·?aüüSýþ;Í£iÑ“ Íó‹â|eþO¬ „ +m]‡¼R-ez+‰Pß(øÍCІ‹ÈÛ…nD©)xN°äxm·ØNïǤ~ok?| .µ]1©ÈC~ÞÁäºqG 4<¾fz@ÊÒd7Íe­8¸Êˆs‰ôkõp°º{1Ïi¶–”Îr4ìržý罚Éßç?òÉ©"Þ^ ð)·—D„\–úÍùy…îaX´€( myWx1Ú¢f#Á¬„hKSy󇫥˜i±ÁÍ8%!ÝÊl»ÅZ˜M^hÈ Ws€ÿ'OžgMÝß,9LÎÄxp¼ƒ­÷cÃ’5½×–ðjÃÏįÓCA H$5kVñׯt€Êq6®Ï ýت@¬7tN¿çÌ4‚G k}ü$/IÛ;"}ŒJ;Œ/ÁáÀûñ• ‰LŸ)[ë‘", U1LÞn뢜¦þ#­êÈO©@-±,¥â­½TWmÅ.ˆYè$W˜Ëp¡—J¥ç •W ˆˆ-ÈTf;Ŷ]2«Ä=¶€ kû¹ sP F¢ÝRï¢ÂŠþP8rÕñÀ›D‹Î6˜²hf@uç¤ z4kÄ€.™ ¼ÒJ…º£¥|ãµõ&»Ø2îpå¾µ¸ïØžOÒþ?éI©JG4RbTÑ1tÄZVa:®õ¼Ò-ˆ'Ý8ÃIï…4e÷àg8/­7 ¡òråpàš©„ÞÕ丼àí¢„Õž ˆL­etßVjè*vĸFŸã_ ·°¿³E³gqÿ‡Ê‚"TDˆ¶Íå¡íh¨h@¨?÷ôPžG4 P”ŠÀ ›}¦ÕžásZª³Dè×Ì.ÐJº”‘R»OÀáʼ–ʱX˜ò‹àÁÎSZ=}¡®Ço2[ÞAÖͼ…·HaûåCÀ!®‘;¶q›6wNãù– gGÖU}>m)¶P9Id0–o(ić'4Д$ë;H<ÄÌ xéÃuÔȦÀ{27·½Îg£ãM›Wá¼¹6ÖÏÿ¸vPòÍc㪲íP;ë&hÀQtî"ÐMª¹[ƒ®pœþ̳kƒ»17ó?òE6ÆÎj)9ßÊŒškáE €ÐG¯yk  å³C•ÉíJºÖèk ¶üN8¢ƒCÓ}ƒ7âY¦XÓyHwÇ/bä;s–EÜP·Í~îM ¨F9w‹ã­ T!õðû¨%Rí¤gŽ[ç^1ãN{sz˜DB" e;oŒŽaBHA8~%Z¡a’XVÎZ¾åi¿C¡)WÁ­7.2žQf««9ÔÛ‚@P}CcååŠN»hv°aߌ]eœn¨ò3Hˆüu+3IËoË®ñܨi!÷GjfÓ,`Ê»v5yÆÅƒ¸¨tAÛ°ûíÊHc¿ióf°t ¡‡ÉÏ ‘9(bž!U0Â&ÀXÍ^HuYIJpB1l^u€ÔéVC"£-ЇƒâÎ'¦Ïô즞úʈ;—O`œ[Î[Å@Xp\Öq';v6ï(®[q¼ºÉ`jXýý`HfÃáY¿·Çõ ¨C‡§Íü’%A¾[;ê{ý&KeâÆà¹Ð¸Â[M'x‰T ì¥!­r^0rj`8¡¤n­¸xJ6U` ¿"¸)lÎ(`ÁCá:0Œ1-%U¸eø9 d=®“cÓ…4@, %ɵۜ=? 0¢Ì ;x •l ¿ÐX\FÃ6vÇ‘èòëI_úqDf*`xy våÚTr¬­ütGÎÌÁ:9Øâ*°"ÔySË„JIR ƒƒ×ȈìBÖ.Ï“"¡àc=ÚÙ÷ , Š"qdºë8¢e†@tåýÌq)Ý¢:KWCS;žAƒ~¨M¦›ãôù“Ã.¾ÇÈ ötöiþJ“€Lyß³l<€_šm¨W¬‡@5{t‘`ð‚4‘ì3¦¢~~5·af8J°£0Z€¥`$ÀìnkÓ«¤@á wçá[(rÀÖí™C¢ïä_ÊF:‹Ì¸B4õqt9 )@pBî`I ØtÖ¦äÁÛ¯=ïÄmÂc,ʪ†  á ‡W.ÿE®é S뤉 †gx êU6ïBN¦÷…GŸâ;}a§’2ˆ„Ñz2ÏI—ulA¿‚಑*¹”[5¼pœK*1 ³vÃh¬Sp ÑÙôÁäÀ*.èìüÝ“€£€4tÚ|˜¹uÌE§6<ó¬å2“Ç2Û¬V©Õt€&)UàÇ¢ šH¶r¢ð&úî–jäL*˜¹Lý‚â©3ºÒÉä0°1kòtl§ Ë Wtî¼:Ýήn!³àÚÐô^#€p@'±NËÆnÙÔ]“áü’}sÃàTácÞïÊÃBŒf¼¼üFðz’RK`—xŽ"‹º˜ ¡¬oö˜V<@dà6“¼Q¥D9¯øLt_4¿ÁwÍd#›¨ûÀD€¤DT«PµùÔ6-;9>rÈ'qkMËÎ/éG8¦|%ú,B‹íh´0K`Üp)#ãY‰A¾k -a@I×w“ G¾Éu¶Š›wnjk}äÍ|9lí¥^Ù¸^šYÁ§+Òžú ¯l|SulÂçe=±¥ÊNS†¯²aÜt=„Ha@ë"g\)¦ÏLÚ}Er—ÊÖœÓÕx§K„o9ŸÉ%ê8Æ'lȆpüå“ù‘„$ñÎpY™˜Â²¨ã¹ÄBÍfÆÂÉŒœpcâ„<.8ºm¥BôÇù'Ö<0ˆXhª¿»òR˳“çЂãÏøÿãÔÍPå…†þu¿ö„æi®°›Ôˆ¨›£cŽwŽÅ×hªµ'†>Ç‚Sz™Ûƒ;Vïš­½°„„k3åbaÍ’[‚{(âØÄÍwøƒõ-Ê æÎ€q§`úR5!êwGò5é_„—xò•(¶oŒ)-:¤–½Ún0y³Jƒ¹- ±ÄÙã%@bH ƒ·gî»! 8u±CŠälŠ˜Fñ€†°GúLKµÞ!¼¸ûaM¡7„l +Ž-©¸ßx›˜kYk“Ïxˆ®"ˆB ¸L|°O8#¾ð«<ÿ’ŒœÖæ–.IZ4Ëú ê¡æÂ”«+½Ãøÿ$­BÓNiþ>P ì‡çåÁ"›'ŠÍAe86—Ë9,. û$RèÓ¼‚€..7ÏPSà"oÃØé'‘€"¬Å´ÜÞœ¯Áxˆ„¬—üïägI£K;ߟž†¥+¯Ä¸ TfR™º;9Â} ìŽ9çdLp„GËä)¹…¢df¦Øn—Ük¼ãtˆ^¼z&ÂŒm‰Š-¥N¬ u?ßõ)’ÄU:Ç?aF 6áç°ÁìíÞs v >£fBQPñ›0èÊß¡îÈUo‡_"€<¤‡Þ¿¦¢‰WÙø-:N,¹pkäYUæø R)5«[ðÚ­ \ìL.áP€»À‹Æ]ÌÝfJ7´ƒV!Ó§ó‡“WmBDˆg¥Ö ¡}2É厥T•‘Æø¬!UWƒoØÈxÈ`"àG®pˆ@o±?ÄÅŠ0„kVàdã¦@ ¶åf÷2l^Ù¯Q( à8OIѰ‡V^šð©¾¢ CÉǦd;ÐJκzV*àà Ëdi©êë–ÕÇ›6²ïÇxΦH-) O˦ì,ó ®Kö$d°#Y¼¦ ˆ€’6µ«ÕÖ*$ÒìË(¾ÀY~ŠŽÍ¦^̦I–]YDå0ÅÌO>‚{.ܨ^<ÐcÍ{€™¸ì§ñ]Éó„Ð%BSXP†ˆ•@pªº.°IpXKÂU–.CÛBAh°ó‚e:«oÛ _,iZCÏn53“c7…. ëåÄÇ$(Yâ a§¦BYg˜0 D« |ç¼·D%è¯!‡GÜ£jÕï68ˈ͘ö)”†“xwá¦ÍéfFöñå¹<óòœÀá§>ïÉü’(7ÚäA(ò9zA²SöÊêA8Öl×܉Œ~ø Ù0ƒ‘J7+1@_—•8¸àLdÀîSàÃWF4`$HåüeºÁ­¯Ö³¬G|¼â¡y×ZçïœH´ %©è!'ˆ<_¶Y\EF¨z\V8@´¦<ë|ùóáÐ…eg¿8Ü‹±™!XÄUZl‡‹èEÙÝüýGÅf¬ëjDÊÉÕ“*…ñt&R .®4]ß‘(÷2'ÃKKasÂc=-R ž".±H:Y5°,ß0ï,ªª­g¤íƒX ’€—§B ¬@ šÉrç”ø0QÖØad=J‡¸jM‹Í Õm8oŸ”ø##OçôÉ)DÑTS¡Ñ0;Pí q¼F!¼…:K#l2(:N郋9×gñ¸jìÿ9þùõ?÷þ1רÏïƒ 4ë³øÜ5° €ïI $h*¦Ì²Ô‚K6¾xÀSTá–»øý²ýáL4Н þr²+ H:yV{Í]ÒëWA¢·Ÿ[õ‰C›^xÒ`ZAöxV0w$øÁ[/ 1£ ó¿:T,ó1¡©ŠÈòDµ²Ö¹»VBy¦ í¼w¨±|¤j˜6ÂÌzÄ(»‰ç/3*ÅÜÆ+j%"÷Adk‹ù¹ÉÎC°…„|…ÛŒsˆfmµÒº¯K‹.A Ôb>ØÑ¼0/®­lÍÝ#*G­|ëLçÂÂ!¸xP[û™CEH¥h§ñu“ÄáTt¢;õšÍDËÁ¼ej £9t'Ǫ0Dmm: ROÈjîêÃ¥=äðÂJá.×Hp8½¤œ\i C„5sH-‰&ÛFxºŠ¹"6¸Ê™…  ­~™%…8Bö|Þ‘–aCó‰·Æv¬UHšiå••äîY÷ 5Y΋?…Þû¿íüaõÏ_íü`ý\sÚÂÿ82³N‹?…Ï®<¶>ð¡«¹Š©Ç'‚-Õöq„Ù’zÅðûab5–q-†s˜ðo×s‡ÛxRÓŒÝ þ<`TQ‚"RCüa˜t‘Ë–MqÇ…¸éÆŸ ¢Ž“=}f&÷@eÉLÝ|Ì¥Æ^Ðl …eVµ„¿ï…å"péÆÃè Lö“7“.¦Hä^9±a,˜ôãêì·Ðc´Ç+¬Úp'¼\…í)èÌ] "‘dh£^Ž «ZQ Â=—yjÜ1-$h¨UÀŒW•¾²¾e‘£EÚ|Œi¡§J³*†‡À {ã <í,úcAäý±d"„¨þJ1œEÔªrá&(èz ‡Ýüãß hÓ$H;ç"A>mØòýBOóÃæ*RS“í€ZhÑ£ Šï =Ì`ÃP¦ãðì .ÛC-‡Oé^Ý»víÛ·nÊ“$·­„‡ g:ƈFÐ’:³‚pÈpùçóÿâô¾ §½)'ù¿áói•…VF)aJ¼ ¥ûç|&©M%]|K—cÙTåoïøÛK­^ŸÑ—.\¹råËùLóœ[ªƒ9ùBøƒ€h¬ ·oăµoš×wôëueðŒÄw% Š€Ö"ªñe!n¼» I<“í\)ìƒBt`Ó»Âñæ–ÒúÊÿ¹H€JèØï˜šÂV››‡’xÁŠ1¸¨R’©ãx )K˜*œzÀ0/×üYÃä_ÆG¯V,~ã”Ü]osï]žÖ„aðë¶Ð*ÒNž]u‘n?Jšk숗UÂ]Ìvpu†À~!" ;L0)­h#„f>Œé£É÷ÈÅ@mªî¼c™€»àPÑǬ‰f~Íeõ‚rpôä |º)%¹cÑ{ÆÐ0¾Gù¿áó@O¬ë Ý…‡¨‹ÒqÕqÝm–ËG³ÈOý£x<ìCï)çÖ ;§>g;õ¼ÿ=ÿ±ãù×8迈á¼Jô_ćŸyûÄÿØú7†¾È}å<úÁgd—ºó¿YÃÙ?7³é×8ê¯Ú/œJô^;­ûÖ~ñ?ö=þÛãë@U±U^±uTGð`‡¸’\Ašø¬º»?QÌŠ S‡Þ=Ð*@è7H20 :ÂkœZŽ«MwÖRؼT{"9\#$É|¶ÿ @Ñ‚'ç$ð7…;ç ‘'¼ŠNûAÇÕÄ­^1†’¾.Of‚ »XÍþQúôÕv]´|½v0PÖ[ …\(ØÖö8Ãôg@œq¶Ö4L¹ª Jb "’ˆHÓ£ A©µ€÷P¤q-ª\K)¥îœaa|˜:XD˜ƒžx6aªøeÞh.ÿV2¥5 —ƒTL‚‡PÐŒ7±Æ¦J–7O¹NÏ8@¾0â½7¼q"ç%bÁ¬4\ã‰-ú=Í;§«;^Jgîç–ÆÛ.¯Xw(îX{ƒÆ+—Õïš(V}òÌä•TµA#‡nrµM\‚ƒx±QƯ_|„šƒ°±UËN¡“éŠÂƒL¯_ì8>ýcàgBJ¨/ºRÌ/Å=笻a÷e$Æé„P¸WöôÉÚ[!C{Iš`I«C„³žð¢'û‹Z}õŽúe׋ùÊ&Œ ATõ{0pÉt5û¾>ó£…^Ù©ï!6žÉ5Wlx൚‰£žõH¹¾.²-¥°NÞrÍrDøîuo¬ã­¬äTëÓOPy7]8w“¡Îd?X:Xš}Ï…¥ÎSÎxA£@ð=K¬* ÄKü&p:ãu÷àD€ªð_½ hšD~J4há5pvü~Êä"ùËÿZ^ûOywÕ¿b?ÅÈË(Âîºp(¤ä)´ý½øÂ3¿uîéqž01«”1÷8à¶beY£œïâ‹"´0)œàk }T`à ޘ_(„Z†6-”è@ÔcA%A×Ò7@¸ú/Šš¶Žë¦pMH7¤qKúT›·_|t{3D.¥Ó}ỬB¥JxͱÂF»fìÖ,H Vn™¼ïiÉPÈôÞ2ÃjÉ@z¥Xhgæ¥9½0¡Á@Ew^ŠÈ ¡]¬q‰€“4š‡ÌÇo…‚£’4~5Þ:ïÅ69­pü€X °|¹è3Ðdr4 "¤ø-Œ™fYSöœ4fxЀ~R Rà×€–xz`æjÀ+ }äú>O •5FXÔ€>H%€),33V€TÕsPåà=(Œ1 ýDêQ€€æiO( EYг$°zœ4f|Ѐ~P–Fd¨&¤„°ÏÍ(g ž4 ^Xe9ôY“$3ÀЀE>“ˆYÉÀ>h ñ¡p œŽ'É¢)ÌYÜÓ°8 ÙÐÐ`LÑtN* £©òm@|ÐÃFÁLp8ÂÌ߀„°ÉZcJehW––ÓÃFŠ@h¼°„°ËRó#ôƒ¨W––Œ±,À<q6 +ˈKy` `*ŠrØ·„°(nËàɈæ„èlLÉ–8óD?,M)4ÈœÈ%±0ägAPv:Ÿ¥€D%€¼°àw´²!,å€D%€¼°pD%€ÿÄ2 23@0"5P$%4!1`pÿÚýܺ™±ë,»ZŒJ&ËIoŒ»XOûË÷ØŸ™™PÈ«8›¹g›½!iqŠºZñÈéùoÿ Á’´ªQ+TÞÐ-]€¼»>$ÃëdC§ƒd­Tà„zäAûÝTƒoo²µ¾ÊÖû+[ì­5"ÁMHšÞý'[ôoÒu¿IÖý'[ô•oÒU¿IVý%[ô•oÒU¿HÖý#[ôoÒ5¿HÓºœ¦‹ßÏ­üúßÏ­üúßÏ­üún:ÍHd]ÇÞtçu6|Qs¤ü™l†°Q¾Í¡À²·fCž]Jtñ=LjÐóú]—Rû<çv]Œ ò×§b¤GŠÙ>!<ëØ½DB½ƒç}äŽÌwD¾Ø.s»>9<âýú¦gì”ùõZ®0„Y´iÆóÙñdŒ!£be‘"É*ßf$—Œ –1;e&“þßY±¢ÜW!¡JÕcG‘©õ´û2š¦ñÄ·8Iu˜qÈ–u3ÎHrjúâê>Û¼Ò…: 7ÝŒ0ãrqÒLIá™Áëfƒ`Ž—ÙI,%6BJŒ-ò0çÖúßXsê: Û•1sbb¬Aõˆ>¤4‚åií¼æ‘u×ïZŸNŽºUm"êZà«îÜ$÷»Š¬T…b¤+!S1§ LT…Gƽã$+!XÉ ÆHV2B…iÆX:5‰+‹4)pœúfÓâ‘ÓÏ÷9¨oéŽùçoüz޾É.s»>,ñ-µ#6úZ#¦lÇš„Ó„8Dq3Á†ÿÇ×Ç×Ç×ÇÔ>£´ê-D ±O\O\ODk–’ÿP½·þ ¨µ¬‡~ÍXñL7¬2Ãëp$kDØ~'®'®'®'©­D •Äñô£2üO\O\O\O\OB”ÙŒO=íÁ.;0[HšŽ˜}çüR:jÖ²mËÙÕ_€ú®5‡ ºm{Ù6OÖé²­WM•Ë;×A~™I²¬wgÅÛ²¶ì¢:yà;:«ðû~ Þºóüçvkmmð¦Çuøõ êiC êVÝÝòÕ¦ØXÑ%Fùsë}aϬ9õj“¨bDF úÄXƒêDiÑÛö§´¼LÝåL„™é 2û7Ÿ5Àââfï1‰¬T…b¤+!X© š95Š £O¼¾2B±’Œ¬d…c$(VœeP2‹ÓÒwvÍjQ"ˆÈæéÆGeߎžxŸµÝCoTwÏ;oãTu¿¸óÙñ - XN¢V*x0Ö·Ró+Z[@Ä6Xü±wþd嶇õAM¸¶Hl†÷–½Ò¤ ÆæÂu×5,{I6LhäXÖG$çøUþo3ï¶+:—„:\håª|¥©ð3Á”ÒG^ž2D±¢åxpqñFE¨¸ø`݉|çšw&EdȬ™“"£¤ßDœ¼›÷¬™“"²dSÁœäËs {!w§“˜$è‰B©öå¼›“OŠ § 1“"²dVLŠÉ‘SÒoÚ3&ECɾ¥äȬ™“"²dVLŠaÅ<ÔÜbåDÜœb$øò­!!¥L6%˜sýߎžq­èÕ3|¯Ïª‘w4Ëk³­é‹lç;³ã‘ÓÎæÄj­ëÔ?;­¥æàœÛ§­² œîÏŽGO<ÂnÙZïOø ÷YÓM·f[ç;³ã‘ÓÎhmž(Úfâ£í`ݬÕ‚z°OV êÁ?X'ëý`ˆ¬5‚&°DÖªÁX"¨m.†d>»>9?¢;³ã‘Óú  ÛÇG{O>‚BñÈéýù—´Kî·aw¿ wý¨å\§äßý7.T›„tê½:“W áÎÉ\˜'}À|ÇÌ–. %Ǹã°ÒN‘x‹È²+Ö•—!²5<¦Bp‹®*LÓÖô¹9wg± <²4뺄Ïz&~@ÙBË.Ú‡ã#§ôGv~­°†oʈášùˆéýÝŸŽŸÑÙñÞM×oÑÝÜoÿÿÄ ÿÚ?sÿÄ ÿÚ?sÿÄS !"152@AQa“”ÒÓ#03BRqr‘’±²Ñ Pbs¡£Á$St‚¤³Âc¢âðñCD`dpƒáÿÚ?úm‘:µ·)tª¢”Û¨ÓMúhóĤÒزA¤¨ºh¥ZöÓ¿@¤1<[ueËÉìÆ”åàvÅw©µ§ðÓ&–fr-‡ ”«³…i–\M QR‡þtšgoo7s-¯F…pš-×<Hâ?ðaÌñØöÿW ÂV“i*oîÐÓ² ˜n„yiööãM0Û%µµ,Ø,¸í°’‚nÕ¶³D'ƒårɲ !ÊÚHSôñ-¯WŠ(â ͦìÙ¿¡¼ºBBÉâ)V:q­!†•¶CiIôný£~øÜ©»và¸â¾ Ì©¥q߇›Kÿ¥h­J½+JBª¦øÇ*+Ç0…JŠ.s•W ¤§y4Ò­ê‘MÂ]q«•‡¬ã¼®0 [ Z‰)?Ȥ³ ‡›ÞY˜³_5#U·Î¿Ó­¾uþ˜Õmó¯ôÆ«o¦û™-%)¦jfjTN Ýó #$„) -­ ™y£U£œ„j´s‘ðVŽr>ªÑÎGÂ5Z9ÈøF«ä|#UŽp>ªÇ8Õcœ„j±ÎÂ5Xçá¯ù„Æ«þa1ªÿ˜Lj¿æ¯ù„ÅÁÉKÁimN“e*Vìõ‡ j³Ë¦5YåÓ¬òéVytÆ«<ºcUž]0•:ÕÊ÷ÑZÓäe«+ªÜFpNhÎNôMº¬ ¹F¥_CA„”¬“ŽÆÕ0<¶š‘K§…9µÎÆ–*­¨Ú§Š—DµXY±|›DVîòµ¥)ç®âSN¡.6±E!b ˆi-tÑ´„\¦‰-ã–gC‹Û)! ®ŽõDŽY™.T´Öƒ§½âƒúMòQ/±EZhö/kŽˆ–m9bm´Ë"í«°„”§ *ôC,~–œKl¸^nÍ€BÍjkJ÷ÆÿLÎ UˆA@H¦'F'‚5ÔÇ"×F5Óü‹]×Oò-tc]?ȵф•Þ_ë [ýª~¬k§ùº1”ÀÊîŠL ›–ñìMñF¹woáåÞA¿„k—yþ®]äøF¹woá C¯….(_D4 7K¼E‡‚JWÇ  D­±œª°ß0^u.^]-O¡ã€”€”@öæG–ŸohÊ á™>êGå^ à üTn døSLPËäÊ©ÿ ü4ü;B~Ñ¿|nlšÛó&Z]ËËFøµZ 1¬H4ë.Ì_­Ä¥ú ä« ó¤ç„yiöÃî¢Ô«¸$Z)ªN x÷´Å]rñiui¨]±KX ]÷ŽË«p-:hÊÏÞwGy»îŽów:1Ýæîtcº;ÍÜèÄÝ¥»ŒË‡¸9Ã⇒•¹j¨8°àï‡wGy»îŽów:1Ýæîtbå.¢ÅâQv¦\¼)³[~œ)Mè•JÒ’\mµ–T-Z8¥5=è߯¼PÊؼq).Ü­A5mj"€âAJ}mDµ…¾™¹ FU¡\Wd‘¼-Bniö&æYKX´®ÂódÛVŽûCó3OÊI<âׂV)B-€4R°‡‹“Ó,XQ墵Y®Ušh4à g(8SiÅ'IP“dmsΜÞ=;ñÝæîtcº;ÍÜèÇtw›¹ÑŽèï7s£6VæH'°9ðŽèï7s£EVݲ«ºv8䉗³'/r§‹fÚëÃvH»¦ŠhÂÍ¡aUMSj´óÃÉu(K}áBO‚Û‰ZT{ºVÕ/UM0µ·•æet4†Ú zPLkéÎIŽ®5ôç$ÇWús’c«}9É1ÕÄÝ2ÔÒ)2àÁ¦qÇNÒµ–f]@²¦™ÇÞÑ”SÁ2}ԟΟ aøÈÜêðf˜þ¢GçòeUŒ”þ~=¡?hß¾7*žyV[O|ÀoÃV-)Ç„¸C­© ¼" Faeå+;˜[”¥+µ„CN6 ´(¤¥IÐD)j!)H©'z}•[iÔ…¡\ èùÙ[ŠhI¸k÷©ë'æ:€¬æ•:ì"¶Õh-!cÄ`7lZ)·æ‰vÞrÂß]†Å4˜a´½žúœCi Š–Í<Ô†Ô]YJ›ZCKPB…*ƒ4xá }Ë!z,¤¨ž: î8CÔ—VÙu)JpÇï5¿Þeÿ¬“+~ò?¢ßÏ[ά6Ò¥)Z†œAª¶”¡ C¥Å8¡jÃ-)ÅS†‰˜VÉI 1²W@Oc¥m4`·–ËV”Ê ÇzE óî$ÜX¿miqÍ©#xÂU=u± j–¿[–{©U ]<8±mÔìY·{*T·\R0%U‚N9¸ˆ”” &è!»œ&[e9‚+XJиމIgœºÓIB”4`!HNN™y#¿B›¡ô¬Õ3ž³=dj™ÏYž²5Lç¬ÏY¦sÖg¬ŒªÑ“D—P妰«iÕ‰$™4›s(Ò¦±¦w‡õcTÎzÌõ‘ªg=fzÈÕ3ž³=d93úÙ[ÈtL—¾@ÈÎß¡ßУŒœ¶êÉfñ.2K ÄûÑ >ŒˆdË*¶%Xu»¥§NÕ$A²ú^rU³Áx®Ç÷8˜J‚R(;B~Ñ¿|n„yiöö‡%Ý­…ãB8Ç)(ÊÙC9Ej%HÄ'iß(zÍô#[åY¾„kŒ¡éo¡ç(z[èF¹Ê–ú®r‡¥¾„k¬£ø]\k¼£ø]\k¼£ø]\k¼£ø=\kÌ£ø=\kÌ£ø=\kÌ£ø=\kì£ècª}”} uQ¯²¡Žª6[Ó󓋨U‡Š- ¢R1ÇýÐv”ý£~øÝòÓíú ?hß¾7B<´û~¸¼ì·ÛÍ“·»¼§«Œ%ЧÔÚ”…&UÓxhNnnv &¢y¥…´âšRT7ÁPÝòÓíúflŽÃ}²nlcyuu[UÑgz‘›<Ôº¯ƒ¡2ì­¶“˜´šíA6÷•¼0‰Y{V®n[µ@+E$hÜ‹,¸ûÙDN=VCŠ"îÒ¶Ã@ÑæŒÄ"a.Q¤--íPÂÕ;ÑLwñ¦‹©a™¶›+/¤YñýüFp@RBÒ¼ùÆå§Ûe(S/Ò€%xéá”Ø›RÙ¶Ònl¸·«Ð¥cSMC†‡U3'.eŠ-×54SUY²mWmâxÛ^%+RRí¥(8+¤Z$ýû·'>‘{eÙ… eK£O…pŠ÷CMLKÞY½m¬]ÚhƇl­èÀÐÃE篿JÿíÑ »q—óFÒìï”Ó~•‹9:iÁ*âЗfUë-f¬àIÛ$áˆß0¥¥hk7ìsû®º|"DM.Ý}À• ’¤Ò^ð+×¢|ü0·ä–i¢åÚ*7!@qã]Úo›[w¦®„iHJM04ßÿf&å­!$XBˆÚb¬k¾x´@y.‡ Ïdl"‚\—P q¥R8|z!‰’èeÝ€VEðó§m+u¹6ÄÎÃuÇР‡Uâ4nž3 ´ê”(Ð¦Í EÝ«ÎSÆö˜-¡õI²â%WC™¶U7ó”Þ?TöÄyiöýŸ´oß1„Ù.*Ò¸Ïû9»-váu>Q­OùnG–ŸoÐiûFýñºå§Ûô~Ñ¿|n„ÓÂè0¦Ú¡Cÿ¿ÿÄ,!1AQ @aq0‘¡ðP±ñÁÑá`pÿÚ?!üÚY*ÍÔQн—­Ãk‹åœä,éáPrFqØniEuBí„'/´Ô ìeÙan]¿÷O4‘ÙóË„ؽÀÜ[ f†ÅÕ) ?LR€UEÙBïD}h‘ˆ”ÃÈ«;º²IÌ}†Ï8­¡ N(á\±5s /iCS›éQ  Y©ìP•ÄÒ)Ÿ`*C­ Ä€yÔv¾^T2Md@à,Ê RG (d[Їuf3qjˆEªaZr‘Ä‘² Ž´eñLÒï1b9YÕ‹×òtþNŸÉÓù:DÆ”d èC "ß¹ê#Ä•*T©w}$8pá÷½Ÿèá„! É€¥wv‹wÐâUUUUp2œÛ–ëàÙW°i±êΕ”J¤ãS[F‰™J‘‡‹WöaªPµ‡q`!·"ŽØEìˆä­I˳eлXf0Åx;PQ-‚ÞEÂÆºCçWôv§Ôò0{¾•šGVXz$žFê;_-:Žä &_z0 “éVÚÖÛoH˜±nœ¯Ølò‡ëÑèö&7=¹ 1}Z&ì[½Êi{q|ƒän£µòóa³È{X;Èø¢@–Al(}€ùkHzSEçÐðFê;_/-$ëÂpÉ;Ñ3˜¶ÔˆÏ†6¯°ÙSùvX<ÈÌiExаÛÕH^:“ ¸·§ÚÿWÓâà.)@Êü‹©Hêá ÔU˜`ÐÊ&Œ$+Éi»£ Œ­Št‚5W^D6C½ýÔa:ÀÑ1RëXÞi_  9DÃ…±›°Ö·v ½,™‚ÓÁ5á/³¯†Y8úd¸ØßÝj(N"]„cb¹´OI¨•,«ßùcé(þŒ0Ç)§jpQ²<ºÎªÝ[«_a²Ž ‰Zˆa.w(…‡¡Qn¼èõ‹ºðÌ™2dš!\,éÎ ¶ÇZej-²ˆØbgÓ‚Ý»zëg ac”Ÿªc‡!Ð ¹2зîžÉÎ ˜a",têɶb˜0ˆ¶ï°PÍѯºlY›çv¥}‹J›Ëƒ’•< ®ÝwZÍP‡$ˆCc¨5»ðqƒ ºÀn˜ÛCOu†AyžØ-·¹råË‘ J·ì§¥_·„‚\' •Ô²nEõ½>$´ ÖìïA|ÁÀ -ö<‡õ™Aü˜›íEíàŸ@‰ù˵òòÖ¶D{M=¯çMcQµ}†Ê$§´Ž@P³¾ ŒE'G(XA`Ú÷šŠAÀû“’9á\¹rëÖ†¶5·Î–|½< —.&sÄ‚Ø÷C:T¤öqTˆ6 %qMõU"Vu“š gg‚e èR ‹.k¤ ‚tÅ/šÝÚHn“ jƒA€³•™¡ Ö F`¸.\º\V%ð®oD8oFØñ.\¹réD˜³J+ðÂÙ Á¾'ÐÖ‡Í|‹”"~V¥°ìÜ2g ×Ê‚BÄ8!‰“•¯°ÙI$7(À@8”µe³ðEÞ—Á µíI@ä%ʘŠL°kâÌÍd$|@”ÈÃÿî¼®¿Çúñ£dWeÚù|"ó¯(€ª•¤BX›WØlòeÁ¸ñ“P"åñ×ÈÎÛõ^÷OãýüŽËµòøBbIÌP&æy pÄ釤Œú*Ø€p Kr]t€Ót 5+%ÔåÛ‰*’ÒÏz hµ®»-4¢d•-̈˙š‹5àïG¬]׆$H‘çðlµ'6iµlR$:ðjÕ«0G!¶\‰T¥¶Ñ€’ø‹ÏB@œˆ 7 d-5¢!j“rQʰšR?=Ù:Xñåe3±–{fœnlÌø‡_¿`Á€ö Í“¯"wšûÓiâׯ^½r3‰vý€tô¨®¡øKÝP:}yƒ/Kw @Œ¬‘Ñß]*ovPÈBAz’µ Ú¦öp *S$ß”û ž@ú‰¬ØwC¼—¿„Iü ü‡eÚùy[ä¢J°T¨J¶¨ÛI%f‹fÑš2†…~U² ͯMäø"#µ:6/2­ PˆÒ½ž)aóÔW©òð6ÕD{®Ëz :![Ã¡ŽŒ„ ³Œ¥g Pû8Ú[JJ%³q¸,¥­LQðA”¤Äéóê‚°VÜ j[þY=³ïp™ð¡O®*k@‰V´»Js4ç"ŽZ‡SK€eˆh@[í7(QJrpØÐòH¨-¦BPI&Ì1±(*Eš®¯`Œ.d%–ÌJ†–Íbø XÌv§Š@è ÃLi§z%Â\$ Xµcä†ÛÓæ4)?29¯‰£l».s6Ü $%Þ4•‘·@Ô«Gœ€›“INi Ü´æÌ!º…$À0‚žÑ¦:׬Ô"`›ú¿Smõ@ êÖè}É,Á$À±½hÍ‚,cÆÝ¸D‰$E|!M¾…º½F³™ÄH‘"D“› g{^ñf‡CP„¢ÐS¸mHÜð†èCCK&1Î-š_3)ŠF4¥ÕÕ%"+ñL ²–‘y9O°ÙämrûñYA~Ë%úþ;ù¬fƒëO0¿¡£ôy—kåæ>Ãg‘iZE½4}·}Ÿ $ËžâCP»2M]~ƒ%ù/‘Ùv¾^cì6yµÙèƒßEÆ1é'Ô.D~› [{z+áË`±äòóa³È Te˜dC„tJY®”ñpàéV­[I%°T¿á×ü> `áÄ/ÒðQ¥À³Ó¡äòóa³ðÅ|¼ÇØlüÞ¡gÖÆ™µXŒÉÓk,€]¹Mè/!Ø“¸óa³ð?^6ÿ /:T$D?ÈfÌP˜LÄúaš ÆNRøUÏ’ãd…ìÍc[p‰«„º„Â&»ôxˆ JÛI M…EÝÄb.>Ãe/Vh›¥¶©@8¨6d-†ô2a –©¤‰´“k韂 ÔZÌG;™;{4¥ÔCq(pŠÜ% 2]±C2È'´`¼ÅC —¢d‘C¾NR$&ZW€t¶I¢.A8Êo)Ø1(iC‰BéÑv&J1Â'¶„¤¥x-ƒLI¥ØnRte"6?zGÎrH躺˜êµÖ— ©…/R›Y$1Ë6‚õ_ °D”!4&èÜ*¿Á©”a,_»æ}†ÏÁÿòðI Ù öÜYË^òŸU2ïç}†ÏÁÿòóa³ðÅ|¼À–…vÁÜøip‡àÿàßÿÚ ’I$’I$’I$’I$’I$’I$’I$’H’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$‚I$’I$‚ $’I$’I$’I$’I$’I$’I$ ’I$’I$’I$’I$’A ’I’I$‚’@$’I$’I I$ $’I$’H$’II$’I$’I$’H’I$’I I$’I$’I$’I$’I$I$’I$€ $’I$’I$’I$€I$’I$$’I’I$’I$’I$’A   ‚I$‚I$’I$’I$’I$I$’I$’A$’I$’I$’I$  I$’I$’I$’II @$ $I$’A$’I$’I I$ $ $’$’I’I$’I$I$’I @ ‚$ $’I$’I$’I$’I$’I$‚ $’I$’I$’I$‚$’$$’A$’H$I$’I$’@ I €$’I$’I$’I$I$’I$’ $’I$’I$’I$’I$’A$’I$’I ’I$’I$’I$’I$’I$I$’I$‚I$’I$’I$’I$’I$I $’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$€I$’I$’I$’I$’I$’I$’I$’I’I$’I$’ I$’I$’I$’I ’$’I$’I$’I$’I$’I$’ I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$I$’I$’I$’I$’H$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’I$’ÿÄ ÿÚ?sÿÄ ÿÚ?sÿÄ,!1AQa @q¡±Áñ0‘ðP`pÑáÿÚ?ÿlL@ެÆF\H#cÔ¡Öd%jl1Ç*\N 9h~JÓ{¡á Èê„QB×þt˜‚è@™àí>üØhvS&PL èÝÜ£G%#Å¡|" À9]ó€uP`T"#ÜS'@“L<DÝ2áLÓ¬³çï$!ŠRš3A¿ee‘ ½jz#$U%‘ .nG“@ˆC‚:*{†*=ÅO¨ôü³;®|…¸­œ<—±’]‹^Ë¡‘‰k+C=@ÄábB8¬.PP š• Ã2dú!3€T-X‰7l¼gdH‘"Z–lÙRÉØ¨É¾¡“ ^FB ˜‡´ @7=Aæ;Y³fÍ˜Ž Õc]góÓ]góÓ]góÓ]góÓ[\ʇÛÀh"¸B;ž¨ö×ðþúþß_Ãûëø}ï¬å < ã¨mg¥ÌÀÖÉc;Œ“X#UW3¢:-]$HâáH"Ô†ËÂ,2Þ‚çÏrޏ$ˆa:Üm39ØeB®] èxµßª… †º—š UVUÝûÿÑý2ïÆ™6ž›È ü»ÿ¥üùÃük®ëÉúO°§ŠTz~Yš6‚)"  "Jæè¡glÔ »èÀàÀx e0äwU3N°~ÏŸ°âÕ/3¿«@ 6ÿãj¿péTÀr~‡F0©Æè Ý®áSàoÇØSÅ*=¿,Îð¦iÖÙóö/já—ô:ˆU®éè{ƒÍf$p_Xh †|M;«0’zñö*x¥G ÷å™Ý¢f²Az¢¸MrZeÆ2„Q i¥3N°~Ï3„-H% 4%.žÖü»ÈÅ)¡d—É¿¡O ûÓjy/ÿ—ç°¦)CN†íq"n‰¡VÈ79†Ì)H+zXLHÓ@tÚ jDžt'¦NýFøi…‡v‰~ÁëxHá ^ 9jAÁh¬lÞ„M¢Ùê쌯¦H@–k=°½Â¨ŽQIT*”Sj5,r… 0ìMÙì ±”r©qTX•—ã; „;ŒàYD*cN“K¬ª„ª ªºS4ëìùÒí*Ôbň&â:fì@@?A¨£¸"¢«•!±J•*@ gNƨpF•IˆLnMs˜ÊE«‡Lâ9aÏqÉjèì솑µæÕCTAÂ5#"ë…•’ÀÒä «kv›—ÌÜ2þ¹EªåK`IËæ/"¬@Ú޶e1ŠpÈEJ $ñ”yÁråØÝ0"‰  ”Š} æàSÈáDP º¯gN:tïZhóJf €\œ®¥†ÎàAÒ ªfîù¢ó-KÅJç[£¶ÆÏ­lÆØÐ}S  vÃ4ëgÏØâ”÷^7ösû8wôIÏèn ý‚52óTz~YÙ§ò@+"øýѼ‚öüç:$»É“ ¨ø¿=)­ éôÈCÏŠT¨/a¸^¬™2dÉ›bÞÕÐ9[…ÜÂ:¨A’dX+Žâаg-âœ#dO¯e8Fæ&`Uˆÿýf¨/b‰'ê PSe6×5:Ç™ó `ˆ”M÷Øk­úm?‰–öìÛ‡bLçs¸:ªÿDjF<)£ÀèDTÕgŸÔ¤\9#¾€6&ª/Q,¥91sײü.ý{o¤¾}.Ûz‘°a,Ìîj²í­ú` xë)•å®ju3çìt1¿iï¡óúý• Ùª\¼4Do'Nt®SÉqÏ`øû÷ŸJ|2ß`¦^hG ÷噪T¥7Ðä³"²œü4Àrx÷,DA#yŠÒ\~‰íHõÄ]@Ó=vË…R@M!®²¡îGL»ºfD5´G³1:Ê&¬KöÀÏ  Sj‘ÑNP"EEW*Bc÷ïß‚¹HZBä¯a6jŒ!"l[5ú•*Uk¥&¨hIfÅGCºj®KÈ9\îdYµR79ƒïåmÒ’n}]¼QÚÔDÑÇX¨…þÛšèEø{ƒ, ¶jØHu•ùÀίQ…GcLíôË! ƒ ‚8|ÞÓF4Þ›f”Œ99]mYóœFQ"¢#5"ÐNó,*ÀYVjê³´~¶ äÛºw¿nÚ— Èp]EF˜UÈqDÂ\ã3Oو줲@¸ÆŠÀt)HÈ®BÐf†Õ㈀›Ñp gq»15njT(ÓzÉcÐØzr @ŽÝŽñI{ƒoÁ;Ñx‚’™LšUò>,Þ€(FˆqœÁ   )WKBôŒ£Œ¤^Æ­Zµ‘Â!íhP›ÆšmðŸ(ωØe»:mq´¢Ž¢,†C› j´N¯‰Þè&¸š}k7B‘º‚ÅŽr«84Ë$±*Çr#Yz9'‹SD¸I§¼\%(ZäÝ–D‰ZPlußÔ,ý pP©’2!”ä½ @·­ (+ ®ƃè „ƒJ&ÉŒk"󎌜àˆãW•cÔm®q† ÎûÇ-¸ÙÔ™ "DÝALÓ¬yŸ<¾Ãd &ÌWðòhƒ@C®¼='p1’~!ëœjûÐ ë¬M\ózý¾ÅL¼ÐAïË3¼sS¬yŸ?aEBr?k§ÆQú¨Žà³:Þ(¥Ô (·”F霂ÄÊÜõPõ~™y¡ƒß–gxæ§Xó>~ÀÒ¤1øÓõ–üÿÆòî9 °aНþ jsCø ?AöË9Hôü³;Ç5:Ç™óö,í›12‘Bd “TFz×c‰]ˆ@äïêû ›6èß_)«å5>™×«ì@¤ýWƒ` 8šE&+}š™S”AïË3¼sS¬yŸ?á©•9Hôü³;Ç5 8ä1gt¬aT¦eæö1ÊiêŸÓ€„4Ÿ4 l8a‹L…ŽîfÉPE¦>´yŸ:|;i“xÜä ¥¶Í°£õNcQb…Vì˜g¼ËO ­<…©Röxs©1Ü å¦ý( e•«. fÆ oŒfiÇ,‡Œ@®A‚³I¥]å8¦´À0`Ç#IIÛ)ùä!ÆRŽUz–Jî]+2XªyÜ ´io¸ªB«$ð¨xMè”ì—PðÜ%"Þ¡" ("¥´uHÂZ¹ ±…´F²Ž‹¨Ç† CÁ÷jx^gÏ/ðð‹9Hôü³;î¶3_ëáÚ0±IKU4akI÷y©áyŸ<¿ÃS,å>ÁïË3¼sSÂó>y‡†Tå>ÁïË3¼5Ûß¡ãþ` Õ^7:òÌÿ¡¿ÿÙnut-2.7.4/docs/images/cables/SOLA-330.png0000644000175000017500000001256112640443572014464 00000000000000‰PNG  IHDRÄÓÓsRGB®Îé pHYs  šœtIMEÙ ,Lâ IDATxÚíÝ{LÕ÷ÿñ—¿4³n1Ýš&Û@Ž `¹ œ¡ši;¬v©Õ¨µµçÐ9/xÁ q˦hÖnºL4Wµ‹6Õ.k‹6k5U7qi]-‹ØêZf *Äzàýûç'?Ž\ΡµöÎó‘œD>߯ßó=ŸËë|¾ßÓÏÌLðýª!€@ˆ D€@ˆ D"@ˆ D" D"„"„BäÎ §UtB¹ß2ðõ‡Hxx¸ÒÓÓuû·O˜™~üãßµ”Wxx¸ ¤¸¸8=öØc*((Е+WºÜïÖ#>>^sçÎUUUUÇollÔ‚ £¤¤$mܸ‘ô5¿ûßþæ×¬ýöŽÍD  wÞyÇ«ìàÁƒúæ7¿yWäÓO?Õ¥K—TZZªM›6©¥¥EãÇ×ÿûßNûÝÚ·¤¤DC‡Õüùó{<öªU«4`À>Þ\.W¯sÞ¼y¶lÙ2khh°††ËÉÉñ:.îþLäö‡?ý&,,Ì®^½êÕ»*ë©mé·ÿß=wò`úôéÚ¸q£Þxã 9sF»víê´ÏÉ“'•ŸŸ¯?üPÍÍÍ’¤~ýúuy¼šš=üðÃ^eÝíÛ“Ë—/ëþûïïtmÙqáiçÎúío«W_}µ}ëö}Ÿþyåææ*%%ETfff§ãâî¯%|‘~óï|Çk=¯«2ÇC¿½Û33³ÂÂBs8¶eË–.·§¤¤Xqq±¹Ýnkkk3·Ûíµ½ã¿GŒaÕÕÕ_úù…^°_þò—=îwãÆ ‹ŠŠêÕë~å•WlñâÅL lMÄW¿éêÿù*£ßÞ…5‘[–,Y¢ÊÊJ-^¼¸Ëí---êß¿¿ú÷ﯪª*åææv{¬Y³fiåÊ•ª¬¬”Çã‘ËåRVV–_çqýúu}øá‡Z³f^yå­X±¢Û}›ššTTT¤èèè¹bÅ UWW«¥¥Eo¿ý¶6mÚ¤œœ¦æËô›îÐoïâLÄ×ö·ÞzËFe–ššj;vìè6Ñ[[[móæÍ–––f‡ÃÆŽkÅÅÅ>¯‘ÃÃÃ-66ÖÆg¿ûÝï¼VÑ»º¶Œ‰‰±ŒŒ ¯ëØî|øðám3f̰ӧO3À™ˆ¯~óEf"ôÛîõ³®–nÀO|ì!€@ˆ D€@ˆ D"@ˆ D" D"„"„B!„B@p¹'PN¤«/"BI°Ž€™‰|úé§„BZ°Ž.g"Aêž`8É®œ(£¬/•³~ff,°"”Ûàr@ߘ‰tœâõ¥©Ð×Ç@À]Î.\Î D"„"„H0illÔ‚ £¤¤$mܸ‘J¡ýAˆøoÕªU0`€Nž<©’’UUUiß¾}T í?ð‰UIC‡Õû￯ûî»O’ÔÐР9sæèõ×_§‡Ðþ`&â[W9êr¹¨Ú„ˆÒÓÓµnÝ:566ª±±QëÖ­Sss3CûÃ÷PÒóÏ?¯ÜÜ\¥¤¤hàÀÊÌÌÔý÷ßß¾›æôí2_íÖDzmïÞ½:~ü¸¶lÙBeÐþàrÆ·+V¨ººZ---zûí·µiÓ&åääP1´?¸œñψ#ôÄOÈív+55U;wîTtt4CûƒË\Î D"@ˆ D"¤9zô¨¦OŸ®!C†(11QK—.U}}=­ƒÌc BdûöíZ¸p¡NŸ>­þóŸ8p -ZDÏBÈæ1ŸX½~ýºtáÂzBR0€\9v옜N'= !+˜Æ@ÀýÞG}¤¼¼<½üòËô$„¤`"ï¾û®²³³µ}ûv 2¤½œçPÖ×Ë|ÖDü°ÿ~åååi×®]6loG9Á:b&²}ûv½øâ‹Ú·oŸbccéM9Á<b&Òqz×Qyy¹¾õ­oÑÃÐçóà¦D¾>ö€@ˆ D"@ˆ D"ÁäüùózöÙg£˜˜=ûì³:þ<CûƒñÏÂ… 5bÄ•––ª´´TÇWVVCûÃ|bUÒ!Cär¹ôo|C’tãÆ %$$è“O>¡‡Ðþ`&âÛ¸qã´uëV555ÉívkË–-zôÑG©ÚÌDüSSS£É“'ëÒ¥K’¤Aƒiÿþýúîw¿K¡ýAˆø6cÆ 9N-X°@’TTT¤ÒÒRíÝ»W7Íéëe¾Ú„ˆOQQQ:wîœ×5q\\7Цý©ÖDü3tèPÊívËívkóæÍŠ‹‹£bh0ñOEE…~ýë_«´´T’ät:µ~ýzEGGÓCh"¸œ@ˆ D€@ˆ D"(!räÈM›6MQQQJLLTVV–jjjh„Œ`"Û¶mÓ/~ñ :uJ'NœP||¼æÏŸOÏBÈæ1ŸXmiiQBB·¨CÈ ¦1pO Pss³vîܩѣGÓ“’‚m TˆÜº×Ã< ââbzBN0Ž€»œijjÒŽ;tìØ1½öÚk^+q3Êúf™¯1@ˆ|ëÁøøxn ƒ^ –1¿ÉÎÎVEE…<jkkµaÃ9NzBF0€X?~¼²²²TQQ¡oûÛJOOWQQ= !#˜Ç7%ü—3„"„BüqUàý· · 8P.—‹Ê¡ýAˆøvûAýùÏæÎj´?ã'>±z››7oêá‡Ö믿®ï}ï{TíX¹Í_ÿúW5ŠDûSÌDz¯­­M<òˆvîÜ©ØØX*„ö‡XéàÍ7ßTttt§ÄMsBã†@ݵ?˜‰øí±ÇS~~¾RSS© Ú¬‰ôΑ#G4`À:íOe"_Laa¡-ZDEÐþàr3„B„B!"W¯^Ujjj—7‰BA0Ž€ ‘?üáúÙÏ~FOBÈ Æ10!RQQ¡üãš?>= !)XÇ@À„Ⱥuë´råJõïߟބ¬c B䨱cºzõª¦L™BOBH æ17%Z»v­Ö¯_¯~ýúu¹çPÖ×Ë|@ÅÛÝJôíwžúª`y+€ððp!*Á2ø°€¾7<˜‰ D"„"„H°9sæŒ222«””íÝ»—J¡ýAˆøçüùóš={¶~úÓŸª¬¬Lпÿýo*†ö‡øÄª¤Å‹k̘1š1c=‚ö3‘Þû׿þ¥ÚÚZ >\111Z°`©Ú„ˆ®^½ªªª*=zT'OžÔ€´jÕ**†ö—3þ‰‹‹Ó‰'tß}÷I’”––¦òòrIÜ4§¯—ùj">M:U/½ô’W'z衇tîÜ9zí.g|{æ™g´nÝ:566ª±±Qyyy7nCûƒñÏŒ3ôýï_£G–ÓéTss³ ¨Ú\Î`&€@ˆ!€@ˆ Dàÿ ˆ/ôîê{HùM„’`÷ʉuÁ:¸œÐ7B$11QƒÖ˜1cTXX¨ÖÖVZ!%XÇ@@ýíLkk«ÊË˵zõj%&&*//ž…Œc ÿ¯¶¶V<òˆÎž=+‰çPÖ÷Ë|B¤—êëë5zôh¹\.Þš’‚i ĚȢE‹är¹äñxTYY©œœ=ñÄô$„Œ`ñ+Þ &hÉ’%ª¨¨Ð< I“&q£\„”`Ü”@ð_Î D"@ˆ D" D"„"„B„B!€B!€¾*¿~òn÷«z^Bà`!⥩©IkÖ¬ÑÈ‘#5xð`%$$èç?ÿ¹Ž?ËåÒ¬Y³4tèP :T³fÍêô]¦þtÚmÛ¶ÉáphÛ¶m~ß‘#G4mÚ4EEE)11QYYYª©©ñ{»™)??_ñññJHHÐúõëÕÓwŠõô:n oÄÅÅiÁ‚ºråJ¯ëõ«ÔÝZ#ˆXÈÈȰ_ýêWVYYiÇêëëmÿþý6mÚ´ö}.\¸`ÉÉɶ{÷ns»Ýæv»m÷îÝ–œœl.\hß/,,¬Ççjmmµ‘#GÚŽ;lÔ¨QÖÚÚêóüžzê)ûûßÿnÖÔÔdúÓŸlÒ¤I~oß½{·Mœ8Ñjjj¬¦¦Æ&Nœh/¿ür·Ï×Ók踭ã¿ëêêléÒ¥6wîÜ^Õk ñÕv_Õq¿ªçí+‚"DlÿûßÿzÜgÉ’%¶uëÖNå[·nµ¥K—úÝ!ÞyçûÉO~bff&L°Ã‡÷ú|¯_¿nQQQ~oŸ4i’?~¼ýçãÇÛäÉ“ïhˆ˜™]»vÍbbbzU¯·;|ø°¥§§›Ãá°´´4¯°óxùįvêê¸Ûãüùóæt:»|^_¯‘Ë™ ÐÖÖf¶bÅ {æ™gÚË“““­ºººÓþÕÕÕöÃþÐ¯Ž¸nݺöÞññÜsÏu ‚îŽãv»íü£M:Õïí>ø ¹Ýn¯}âââº=ÏAƒ™ÇãéTîñxlРA]žëƒ>hóæÍ³ÚÚÚ^ÕëíþóŸÿXff¦%&&Ú¨Q£ìСCíÛG§º»uÙÒ]Ýw,{ÿý÷mÊ”)ã÷ÿ÷õ&âïq;ÖgOo<¾^#!Dš››mðàÁí?/Z´¨Û5‘%K–øìˆ·fUUU^å•••^³×D"##ýÞÞÛ5‘´´4ûè£:•Ÿ>}Úzè¡/µ x{½öæ‡ö è#Ftäþ†HJJŠ›Ûí¶¶¶6s»Ý=®ñø;ñç¸g".\°”””.Ÿ××kdM$@=õÔS:pà€êêêÔÚÚªÏ>ûLr:íû,[¶LEEEÚ³gšššÔÔÔ¤={ö¨¨¨H999>Ÿ£¸¸XÇWDD„W¹Ãáаaôÿþ× ***äñxT[[« 6x›¯íO?ý´6lØ Ë—/ëòåËÚ°aƒ¦OŸÞíóÍš5KË—/Wii©>ÿüs}þùçúàƒ´|ùreffÞÑz½Ý¢E‹T^^.Ç#3“Çãñ:¯•+Wª²²RG.—KYYY~ŸOKK‹ú÷ï¯þýû«ªªJ¹¹¹~ÿß§Ÿ~Z¿ùÍoT]]-·Û­¼¼¼^7//OW®\Ñ•+W´fÍM:µÛºÿ2¯‘5‘¯ÉñãÇmîܹoæt:mÙ²e¦ægΜ±™3gZLLŒÅÄÄXFFF§wì®.WÂÂÂìñÇïö71‡² &t{~°G}Ô‡%''[vv¶×꿯ímmmöÜsÏY\\œÅÅÅY~~¾µµµõ8 رc‡;Ö"##-22ÒÆŽk»víêÕ¯&ý­×ŽÞxã 3fŒ9KOO·£Gz­ålÞ¼ÙÒÒÒÌápØØ±c­¸¸Øï™È[o½e£F²ˆˆKMMµ;vø=¹yó¦­^½Ú,>>Þ¶oßÞ«ãvüíLvv¶×̳㾾^c(êgÖçš /|b!€B!€@ˆ!€@ˆ D"@ˆ D" D"„"„BÚý_1Z½ù%<ï0IEND®B`‚nut-2.7.4/docs/images/cables/mge-66049.png0000644000175000017500000001512312640443572014656 00000000000000‰PNG  IHDRèV¢ sRGB®ÎébKGDÿÿÿ ½§“ pHYs  d_‘tIMEÙ /“ jtEXtCommentCreated with GIMPW®IDATxÚíÝ-tÙàO924‹ ÂR–e\Ø0‹¥°a6³Y·Ìf4´Y %–°ÊlÃbæ‚­TÅ‘læG3wžçŸnÏh~î|ï½wFRçááá!€Úèt:ñÂa€úÐ   Ð   Ѐ€ bÏ!Ø^§ÓùéÿÏ¿wdÓ߯ú›ç^çáá!:NøŽhGMIåzW·t¥Ö]<˼üoO]l›þþ©šÌk€ë½ÝLq×,ìŸ m=PH·lò;Œ iØ… 4wôüÔ5½nfí©¿Y‘?¯­¯úûMÖuà¹uoó7·9¥ú( 0ªÈÀS¾îvÚòïWõü¿Wýýsë^W¯Öu06©e›¼þãu®º ˜JÍ4Å]ó^ôò…3ÿÔ‰,á¸éè2˺w½oÛî£4Ï6ÄM.ŒuÿfT ´¥6¶™€Þ¼ájêPÓgŠ[oØq(=u­«š‚/®¢F¸e®¨w‡|Ó´êPo¶éH<õ4öòß´µ“ÒyPéK¹¸6yûÀº¿{|al²n ud“¿[õ¶£çÞn•µÞ¬ûÔ³<Û§–¦V;Ž€ÀÌ\χ)n¨! €º™ßÓõaHõâ}Ð-çÞ³4   ¹Z9ÅíËæL}ú¤> h'V¡Ô'õéLq€€44”÷AS{î…S&÷{аe8?W@ŸûtîžûÖ8í ‡óraõmimAûÉÛ†ÚÉP\ öðð Èj?BZ8 èÔ/Tš{ÎYíGûÑ6šhÏÉh^/N'P›Ò¬%ÐBK§j“Qv¢ýh?åÔÎ:Í2¤rž¼4T?úy< BûÑ~Ѐ€ÆèÇ(HûÑ~Ѐ€ÆèÇ(í‡6ñYÜ-ïô  +íáÚ£ŸMFAÎöÓæöÓ†ößä}4Å £Ÿzõ¬Mïi?ÚÐýi?Ú(TROq?îÉzB Rȃd¿Ú´S=Ï ¤Ö~¼e/í<èõz1 ÿ6›E¯×[ü÷ÉÉI¼{÷.¦ÓiE¿ß‹‹‹8<<Œˆˆáp“É$úý~L&“öôª‹ÃEƒ Â9ï5> âÓ§O‹ÿ?ãèè(""®¯¯£ÛíFDÄááaŒF£¸¹¹Y„sDÄh4Š«««8==ÍüÚI܃VX~Ð~êÐÉ£ÝyðæÍ›èv»qww·øÝÕÕÕbÄÝÊ€nB¡)ê@mª§~¿çççq~~Óé4ƒA¼{÷n«õí¥ÜðŒÞÊqÖ= óäÁòôöcë‚w8F¯×Ûê¾³4:H+Š”QP„ÃÃÃè÷û1s­'É€6z`Wyprr“É$ƒAL§Ó­×³çd4oÿšòzM‘E;uØ^í°¹y0ãììl1’ ‹·bµ: Û0r®rÿ¶=žM>f_œ‡]ow•ûžrG`mèúú:ƒÁOo³ÇquuwwwñòåËv´Â @y0ãþþ>ºÝnÜÜÜDDÄýýýO£âñx?~ŒápøSßÜÜÄd2‰OŸ>ÅÕÕUôûývô¼¸ª7(´Ú3Š.;æŸ*6ÿ€’¹^¯'''ÑívãÇ‹ßw»Ý‡¿üýFûòÐÂ3Ú.÷8™â¦Íç ïökƒŽÓ|ÿ¼Í Í£¶t€:ЀžNÐ  ! ¦( ¬”Ü'‰-‚¨þø;îh?ÿŸ…q=È#襋{þcŠ Ý½¼y0N_9?ggg¿| Æt:^¯÷ËdÌ—ŸÀI¦ýHáƒJVõTŸê½êÙ–3¢Ií¸j'Ž÷.öG»Ëwœ²æÁ&†Ãá/ß =£×ëýò¹Û777ññãÇÅïîîîâêê*NOO3ï_#èº7æN§Sè€Ú´Û< q~~þËïºÝnÜÞÞ.~—õó·—%û}ÐlhEü´-–CxîåË—q~~£Ñ("þšö qtt´ÕkxH €¤jEçÁÅÅEÌf³_¦½çæ÷ûý~L&“­_'©´‡Ä(:f³Y\__Çx<ŽÁ`½^ï—éíe‡‡‡Ñï÷c8æzݽ”ONª[TÙùÈúZ©~uŽw•û£ÝÕ+z½Þâ»ŸŽŽ£ãåÄ–Åd2‰étÓétíßµ6 SolU—M_+åÎÛ%Žs•ûUå±ÐØ~D= VN_Ï߆5Iƒ899Y|/R¹ ª<¸¸¸ˆ‹‹‹Ÿ~7ŸüÖ«Ùlwwwí èU'ÅÈ@HçɃ»»»¸¾¾ŽÙlãñø§'·c2™ÄÅÅÅâïæ÷¥—Ãøææ&&“I­|òû)ILqϼ§¸ê9âlbüøñ#ºÝnœœœ¬ü÷Ñh777Ñï÷ãää$ºÝnüøñcñ7Ýn7†Ãat»Ýìçæ¡…Ift]üqrǹ˜ýÒæ§ùþù6+\(ÏôÆ=ã ýh?ì‚€ hЀ€~†3hrøºI„³€®ædx¡ãöSÞj¥} ha@ByàÛ¬*j(EöÆÔ¦ôí¥ÔÐ4€êƒµnSø©äA2_7)œH)^8çúÙKå¤oRʃÆôº.œ)²iO°]0–UãÛžâ Ò Ýuè hZ;òE@WÚ›Ka[ÚV›šºŸ¾Í 4  @@Ó6Þz„ö4°[I}›Õ*¦ÐÚ%•Þ½{÷ÓŽ~ZßÑÑQ|ýú5^¾|¹r|tEÛWÔ€ÚT^œŸŸÿÎñËÿŸ;==ñxœùuc<ÇùùùÚ¿y‘jCày«¦‚³þ¤”“Édm/Og/ qqq±UHßÜܤÐŒÐéÈš½^ïÉ@ÍòûM,O'ЫzpB á¼‹<8::Újý_7Ù0Uw:ž{½6u‚tøÓªöU[«‹‹‹˜L&ñ×ô÷ªûÓó‘ðl6‹ˆˆûûûèv»?ýûýý½€ncï°Ì‚â)îöí«cºÛ}­ò¸èüj²s§§§qzz½]ê©éìápÓé4ºÝî/ü8°×½Þ²džâ€¼ypqq±Ñ{”W9<<Œ«««¸½½}v=¯õSÓâIŒ ç÷–{F<í“7c2™Äh4Š^¯ý~?îïïãË—/1 7Nc2™,¦³çïqþÇ?þ§§§?=ñ}}}_¾|YŒ¬g³YÌf³è÷ûOŽÈ“ù ’Ç=§:MÍ9Â7Ž»¶¥Ãç˜V±¯Mâ.c›ó|ɦy°ÎÝÝ]ÜÜÜ,Bõq˜N§Ó˜Íf¿ôüß^¾|¹xB{þÁ%óuu»ÝxóæÍ“Opw:´ZaÐÚŒc* ÛqÜR?NÉ|’¤F@€€4h@@¯àÛ¬ªåóÎÑ~åÙKédø RɃ)ž ½sáÜôæó¹õl³ÜSëyn¹<Û[å9Z>¾ElÓºõ4íx4* ]l›¸çgÖcVTÃZ÷úYÿ¦ÌFMZá¼î:ÈÓVÖ-»Égt?µì6×ö&˹.Úy<ö\lÕ_l›†`½ThZ8ϯÛ2®<ëÝdÙUÿ^Ö¾¤zîÛdÏÅÖœ‹mye¬¿ˆíž¯Ã·ÑÆQTÂö©½U×çªÙÈ禖·™ÛÕy«âxl²_Ûƒ=[õÛSë}*àªhyÂwùß³^૖ɺ¿¤ÀU‡]ÓGn›Ìè­ú›Ç××sëÉrœò,Û”ãñÜ~m{ ö\lÍò)£AìòBɳ¿¤ÒEwÒÖu2S çuuwÕ>–5S¸Ë)ý*GYÇ`ÏÅVŸÑAQ÷è˾(²l»`¥ˆöVô¬Ð®Úêºk§Š<¸ËãñÜL_‘ö\lÕ_lule+OsSVý¨Ós+y*E½u¨¬°È³Þ*jâ.Ç&ï(Úv;ö\lÕ_l«¶/Ë“Ýe6ˆ2Â}›mñméh¤¾Þ]v¶ëò0í¶ÛáƒJj|Qì¢AT±íÞ÷IYí¾îÛ¼‹}*ë5ó¬w—ç¶.íj“íxQ寤V š¶Oy§©6¹?¾MãÏsω´;¾›|&@Ö¿Ýeè¬Ú΢Ÿ )zÝyÖ›uÙ"óÌc¥>åÙŽZ<ŽîbËûÀTU[ž€Úv~“QLJ¸-E„ôº'o7ýÛ<å}@©¬‡eË\wžõ–¹¿»xíUíì¹[›ÛnGç¡â±þ¦Ц›çã:ëòQŸYÞ›·.„Ÿ{qÞi·MÞ+¸évlúžÂmÏ@Óu:êx> }Ý$Ô€ hЀ€ T)÷'‰ùd(êÊ[üÛE-"µZ´·ËÅ2Ô¢ÕLq@ hЀ€ e/åó$oõ×6|ýú5~ÿý÷øóÏ?7^¦×ëÅçÏŸ£ÛíÆo¿ý?~üغ“0rmÿ®Û…‘|{Ã0¥í)jEŒ&‹ZOÕûÕÔZÔétÒA/÷V󸾾ŽËËËØßßËËËÌËO§Óܯ? b8fZn6›Å›7oâþþ>ƒÁÖ¯~~^H/ÖH…üTÔˆ2ïúŠ ¹"ö+K­or-Jj]ƶdÅFD¼zõ*noosmËíímüíoË< ¾»»‹ãããFñòåËÆ÷þ is-Jm=óå‹XO–ŽCkQR#è"O@¯×‹ïß¿Çýý}t»Ý­ÂuÛžb¯×‹»»»888ÈüÚ×××ñÏþ3.//s…sQŒ¢ÑQHk{Џ¦çËçYOÖûÌM­E{.§_F£è÷û[Mqçé!ŽF£xýúut»Ý̯}||ß¿ƒƒƒ{–@Ú…åÿ-bÔšgóeëto½Öç/•)nSšFÚƒv¶ës®ÝiE½nr‰@*4ÔP÷ Ë˜ºxü@A–õïbÙU@l»¬):¨_ÛöÚÌ3Åšç5w±¬€nÑŰM#ßŲëžNÜvY÷Ñ žÁœåÃ9–;ÜYÞ–´íkîrÙT™â¨±å€ÊVËä‘õœ¶}Í].+ kÜ€‹zÛSëyî5ÊZ¶Œåòn/PþèyUH•ùžÞ<¯¹«e4¥_ˆ»á]Hu´[çk®­×Ô}Ð5¹¨óü{YËiuÚÜanâ~¿hC£ÄèÔ"u¨i<ÅáâÚd¤[Ʋeƒ<Û TW‹²>‰=¦¬5bÕëlú4uQËn³¯Év.ªü¨ÏM?Ô¼.½¥<ëjÛ²EoGÞvbôn¤”§Õé¤ÚVOêrÜ6i+e~“V§Ó©nå½Àu)¬»º÷ÛÄeÛÜNhîuÛ¢ZTÞ²Mú&°²ëЋ/:÷š3’ªÛæsís^CòÖµ¨Ùuh“åË>Ç/êv êب///ãàà z½^L§Ó­×óáÇØßßýýýø÷¿ÿ«Àl{Œ¦Ói¼zõ*:N¼ÿ¾±”âǶík“6–廆iw¸—ú¾ô*îAgíÉuß ¨QÜÁÁAÜÞÞF·Ûß~û-þóŸÿlµžýýýøóÏ?ëüñãÇVëùúõkÇd2ɼì`0ˆ‹‹‹8<<¬]¯µÈvb¯¨Ñ&êV‹¯wy×±6»Üž"÷«Êå˨E•|Ýä6W·žëýý}DD|ûö-¾}û¶õz>~ü¸Aüøqëõ|þü9Þ¾}»u¸ÿñDZ¿¿£Ñ¨‘=V£hª*ØumcYf6™)Øu ®Ú¯]÷:Õ¢ÒGл|’¯¨F3âìì,~ÿý÷øüùóÖëìõz‹€ýúuÌf³­ÖóöíÛ8>>Ž¿ÿýï[“/_¾Äëׯsâw=z~n9#h!¼ë¶Yf{,òmHE†k‘A¿«ì¨K-*ý)îTŠäññqÇ·oßSÔy}ÿþ}ëe¿}û¯_¿ÞjÙ7oÞD·ÛˆXü/Ð.//£ßïÇÕÕÕÖëùã?¢ßïGÄ_žmëööv±ž¬ÎÏÏãíÛ·1›ÍrmP}-ªK•µ=nY=:)Oq§4Н}CJdZ‰4Û`>ÀG›lƸNSÜÉY†‰µˆ&Ú«ªQVý6«²{i÷1åe«8_)¶êŽ»x›•Z´~¹"kQQçªNµèE•Ç®v´ÈžëªõdÙ·¦-[׆p¦ì:ЄZ4KRÖ·&íbÙUË™U¨A@oÚ0›\t7Ù·¦-»‹"–z;¡!Ý„‘óªíËsý”¹lÙdz¬:´Éñ(sßöêtq”y‹xŠRp¤ßNоšP‹¨¦‡{»Úiš¯Ì"£ µãzÏÛa)»³³Ë¶ò¢mežiÝM¾Ù ìZ”Ò6›MÐ;ï‘?÷õvu\ÖEÍ Ë<_‘Xæ²ëîéæ üÔëÐ^[vÞ/ßæ³pW5îº/ Ô·5©žä©mµç”3b.cÔº«e´êZÔ /ÚÚ¨½÷n{¦·A-R‡4Aë¹¢× j‘:$ ­çª× j‘:$ ­çª× j‘:$ ­çª× j‘:Ô¾:$ @@£× €€ÎÀÔ’pµH-ª_–±æÂh{0 gP‹êP‡Ú| ôŠ c¹a´ý8j‘:$ 5 µˆÿq4  @@VÈSÜÞTÔZ„€^â-@¨E¤Æ7h@@€€4h@@€€4  @@Rõ_gÜå`Rb–ÉÄ2+zOæ_:Á ‰M¼úù"¹+£r`°Mr+Ü9ûÚïp©üÜ•1ê 49ºŒ–ì°©íJ¹Íú1…2‘²ßuØ¥ªœ‘ž¶Mm’j´Ë,¹‘˜»ác?ü3gˆ’ýÈ,R»„ „(Ù3m† ¢Þ @€) DéŠ"ÏA² ¹¾&—S+³òB¾ L‡Âq^‰ ¼”}Sx… jŸÉ¾ñðD4çÎÌï´ÄÌ!„OVö#^ETáçbÁ,Âutî~p»ŠäcÅ\½âÂòö#£™6 ’DxD Š„k+ü?RxΈá0€ˆ¦^ÈÍdXB18Kª°3Þ†¦Þ?ª·Èº¾?¥î"[Aˆ÷6¾â7F(†±§ +HÛ¯]ÞÙ²ÆíÿdÉ JNa¼ 1‘ˆHÀél¾âÝëòn4@¬Ý ð¯o]×|í˜ÿ£ão)ä¬'Äì l :³ˆË{ݱvÃMDF½›ßlªþmÞZ™œ‚µf…ŒütóùëÔý‡Ž¨ˆµ‰}…nÏ)ÓE …ѵ|:°p[ᘎÄD ã"¢Ê ÙC|Ô‡:!ý?l¸2 @€ @€ @€™óH*û“^¦+šÌI…ue„Ý(Œ3%—éE8i9ÒòC8½~œ^¯‡7 3Áƈ b¤¿‹áÎ&LÃ1Cò“7÷óÎÁv 'àu:¨.òSYàŸY \ÜÔÚñÖpyqÁL”v¯‚+Ó\ºX01‡‹&çRœŸEVé4ü¡ÉøƒqxÒío¨Q7ÚNÈÖz­P¦ÁPg]Gø—Çžgó¡¾öµ‹xøáÍ\6£M³¼~íÌ"6 ~øÄÀŸ*i•…ŠÖ‚º?Ïçr¯¨,à‚²lü™™äO¿ÜŠYhg„ÔßIV']Wö93ØÕÔG8§ºZçùç_â7þ‰'n]ˆR m»ýng [ûnmliêo\^ž¥bî_×.ž”Ëuçñù}f^DvÙt„¦… ȳ@+P(ú‡cl?Ò©+VT’““ÆÓOï O§ o?á¾.” |"bðÝ?ì6µŠæææ¾³õýœ%]^¸@)÷³™gé-óJ˜òã/*'4÷twÊŒ£LN… ”B1 —êýÄõ–¾0Ûwãt9¸öÚ>ÞyçcF†‡9ï¼rZ9½õ;é=üJ*2½VTä¾¼·ýÇÀš³õÿœ<\ |UîŸ]’é¸in in «U>PˆQB»F°î-ÐIÒSc¥ØßzœGûðûݬZ5‹¼¼tFFâ¬[÷å9é\0)ÏŠq`¤·ƒÎ½[ˆGFˆŠ»_«Sý#Ñ%M­]ï‰A;Ó›©GY(p»¬»aVÈq×Â22¼.‚s.Æ_<iDQñ(2AÆ#É×*EQ”CÉ8È8H%M”i€4À4FŒw´ðî¼^X½z&yyn Ζ-‡dRŽ Z-ÁåÏ¢èü¥8=^\ÁE“ó„Úú9s„óS—†]1=À²Š›±$¤PÒj(„ ‹²¥¯$C#QžúË>öídÂ?_úÒ|ÒÓ`›6ÕÓÓsœª­”*±¼lX*Òr+Î'3ÝÃÜ ÙÔ×Ïäå3—””äh:¯Uù3ošSŒ&ÀW8OV¾hD‘FiDPf %Q0$ @(°¡5+uöóÈ«µ4¶uSU•ËÚµóñxHÂF”—_þaƘšï5¬1jdeK\w:ñ&ð™Š\€ü®öÂËþ*`ÍŒÿ ,'mÂ] ËpêÝé"#4))ß„W•· ”)H4°d aÍt„P!¨oîâÁÞ¥½» ŠY»öeÃÆ87ÖÑÒÒÃŒ ‡(#–ÌÊV–´_«x·?‡Šâ|ÊrÒЄ¸åtL§ K¥Å+‘Ü â˜ß£¯\»¤SCÈ(žÊDÅLÛT ¡ë Vœ"b´’B VÓ Q[×Ä/^{S*®û| ·¬ž*†BX†Áª¶žyæFFF¸pJJ"JJ{ VVñ!íêLAZ^Ë*[øÅÖcW–––f755õŸÝÃJ}^æ!ÔŠK¦h~8ÜÜé™¶w#–wM%Sâû,°Ïaƒ¡±¡¶Ž‡ÿð6CÃan¹©šÕ«ªPæˆÝ"H3‚’Q6¾]GCC;S>òünûÞ& ­Ð1ìäXqmdz‚•Åø=·fFnøD’0CÓ¿´"ÍöÛŸ"£(˜q@ZQÒ†MxÔ†4MCh‚ç6ÔòóçÿÂÈH˜¯­ÅÊ«&"ͤ‘ƒF™#<ñÄ&Âá0×L°ÃÀVŒ²2{ª”Ca"‰y<^–g£Ôø²,„ÐATM™’‹J6°Ã“ž¼©4bv¶a…´ò“ý!@ÓB×@ÀÏ_ØÈ/^ÞˆaD¸÷_/`ÅgƒH#Œ2,@iŒ lðw7×±kw™^Ù!ËpÊV]O dÒ«‰s"‘ Õ¡L€ KJ &Ÿ¸,œxfÏ Ð3µäìr#0‘±Ä£Vñ $Ê4IÌ~ìèCͶ’”ð½‰g^ÛŒ®<ôÃÅ,œ›kÁša¤1Œ4†QFi„‘Æÿl3áp˜ËæOµ•’+ ‚²†=#n U6t"™)#FE ]èR[r2ð˜¤%…¬ñºTMÏ c(Ji®Mבñ¨Kš@é¤@·³2 F®3L¾ùÐoÙ²»ž¼ÿþÐb*+|HcØ*;íl&„@ !µ»:Øôn.§ƒ«‡&ý–µ=ž+…R1¤RHÓJ`VS¸4˜”—Æ¡Îðô3KZª@:ÅÅtFm™ªÑqWI„4hêìçïî³dfË?‘‘‡Ãn¿ç16lÝEn¶àw^ÄÔ27*>„ŠÛ^[ÍŒ#ãÃH#Ì×mfxx˜es+ÉÉHC³ ¢ÙÖ„J %eUsÉòÕö²nù>”àà1V‚……>tÎAKÒHieEš®±aw¿u€˜aÒÜ=È·o¾Ä2˜ô ³ú»Rw¤•é•9<÷è òÁŒ%WER£šíaÁþúã¼òZ™é^6u2±(MÓš–Ìôš¦!5ë¬ti–¡í’˜fºÈ3 ¨)*JÇé¥Ó5³ !ãȘAX¿ñc6×w úhk¢®¹Ë6LJÂ\ÿíG¨klåÂ9<·~~Èøˆ}K§ÈYÓøåÓ{“ýxäÙ?óȳÆírRU^Duy€ê²æMÊr£é:šî@ê„C˜)qnƒç¥;Q<%77£¡·÷Ä)À¡P(Í eEEé%%ìÝÙaIZJš"<¼©‰ö㮼rW_=™;ï|ƒIE9‡ƒpÌdõw§®±•yò'àq+Kb Y2°=¬¡l¯¡iüÛwæñ/×ðQÝqöìïeϾ^vïëfWý1vÕKÞ£(ÇÏ ‹+¹méT4]Ghº5ë²g™ oçù\ĽŽéÀû§ !rPˆ¢€Mƒ’â ¶nm%7ÙÑtœ§··âò:¹ï¾Å\|ñ^ýƒ¹k~ð+vÕ7ávëÌŸ™Mí‡]TWféK‘HJViBjMR˜ãä’Å–/-Fh:B×9~Â`Ï>öìëaÃÆ£lÜt”u/×R]’ÍÜÒ,Kâ) Ê–jNšÓ®ÜDŸÀºif AQ0M@É?ëÞnäP×0ç—Ï÷î]HQ04:nçñÒ{{Ù²§€hÔäÞ‡ö''½ÔTfR35ƒšÊ fTfRç±`m9£i(%JO޵èV2Ìò;Xº HiIO<¹€ËçNfÁ´Fd„–’HzÚ¡ æØÄœÖt•¡øý.„&()±€÷†YsÇ ÖÜ9‡C$cåPƒµ|49”OAÔdÅ…5¼ùÁ^N^A9Ö6±¶^}»#y­0ÏͯïŸÅ¬ê,ºåeM‚&-ГV5÷èášÕ¯ÒÙfáôRþmÍ%#šLdÖm}:éi‘¸ƒŠŽ ,”ÊP¤´À„ ¦ñ½{1{V¾eHšMÜÐÐ×í¤0×O±;'﹋QÉÎúf¶ïû˜Ú} ø¸‰ãƒœ|ø¼:5“=H#ŽÐ,Ï&WåNZÆÝZÛÉõk6pb0ÆìŠbÿúJ\fÌ‹À¨wÇê4ÀRj:šB™ÖfU 0g~{Y™.¬¼b—Œ:;à Ę^Zhk¨ÉÉðqé¢Ù\±lšÛ‹îôÒ50L}c{æŸïù¿H)ùòª (eØ^Õ$è¥$*Ùñ?¿ÓÊm_{—‘ˆÉÌ)Åüú_n"Í©0"É´Äà?ÎÚœR 4q;TŸ’X……MƒÌlbð×,àúC–œËYãz¥0MÍ©äç Ó}|)%<Ÿ[–2â)±kÕÉBWH¬jè¹Wñ÷wïÀ0‹Î›Â“ÿº01c#ãP¿iuí46}:ј™œ0ŒÎeGa5 êë­i椢{ —(¥èìégã®mlܾ½õTWNæÊåŸáæ?Ç?} €»Væá–w•¦#4;A¥¬`®¦‘»r¥à²…3yü­FW1Œˆa刔vº5×Á¨‰©(}ÌZuʘáî…8mmCTWç&·cÇNâ­–.-Ȥ¥«Ÿ÷ö7³yÏa4¶ùÑ]ûê1L“`°Ý{í×ùÂò ;v5».–¤ß=ÂCO5pãe yðŸVñÞÎT•äâw)””‰Y¿ /ÇîŽÄ]ééûÆnii9^,”--ƒZ¢.NB3êmM@ÝAËh¼°…ÖÞ±I)Tìcå5“Ù²µ?ìÂépð“G­][¯ÌÄã0P†²¤’’‘¥‚o¯û˜§_±²ùÿ¼áR.[PÿóõMd¤¹ùÞm˨)ɲfj6¸4%örAY^{Ë w8°¿¡¡a|I+¥dY(PßÒ28ís¥€ ÅiiHÂ:Ë–sÝç§0~RÁ¾ý½Ä¢QÞÚ¼4`õrorý‹DfÖqC±öG‡ym³¥œ5×-§³ç8ÿð£Ñí¢á(^'Rš(ÓD)‰a<ôÖ67ôòlm;?¸z*Y^' è³€wžŒròšÖÖcÇNœ <š89x°?™#ÊË2¸îúɬ¼vYÙn¤ r¬Biéè`Õ2/™i(k(Ò­ÖHTq×}Gyw×n—“ù5SÙ°eUULž’Ç+/ fb€ò‚ ÌèJšFœû_?ÈÖýø32ÀW”±jÕæÏ³ ”RH™²‰b/²tt÷átÀí+œH3ŽPZRÆýƒ’Û¾ßÉÆ8ù9Y¤{Ý4µu“›—ÆêÕç3^ >¸€«/¬@šq¤i‹Ü÷â^¶éeåÊóyãÍ\>½`L_?jDíÌÀJŠ÷M$;j;XúÙ’DZG)‘LŒŸÿÜ®¿~Š=ô©äõÄö¥R©»G ¥äÚ…Nòü¦»ºgí}’/ÞwœãÃn‚™VgW]5kWVátèD¢’>j'ËçaIu1ʈ‰Dù?ØÁÎÆ^n¼q6é>S²pR–m|hîÐÒ14·j* ~ו–ñÃD,©3utÔ½[?h$ñZJk•Å”öëD┣Ÿ‰Ê@ÀšåÂÚ_2ã(ÓdOƒÁ÷Œ—™øÒ¼Ìä§?½‚/ÜTƒÛ¥£€½{Û‰D ®˜;M„#î~v;;{Yõ…9,X4‰mÛŽ²¢*]ˆ¤±·6ö´Ë¨øH øÊøVJ•ž~ÿýÖ¯ª1  ¤@Ã^nI.Ø%¾—bi-³$Ž`”äZÞ ¾¡óƇNt‡5} ù¹ëïfqþ¬`Ò‰oï¨mE‚Ëg—3ó¿³…ýÍý|Á†=1¡¡®ƒ¿¿~¶ièæºnt]+ñù< „ãBªã[²Öëì uûöæÏ  °6MÐ,èÄð”òØT2¾Æ‘)Inz©FL9øm.~»ÉK8jYÊãq°êÆé\³²MhÉd— S±sg+s*¤»àŸŸÚÂÁÖ~nºy MBšP[ÛÄ¢IÙø\:=ÃqžÝÑÎÖ#ýL« RYâ•—w*¥ÔGÛºþ|Zà£mmue¡ÂÍÏ>{pÉܹPV©+ÁZ¨;MÁžÜ HÔöõÎ'·­Ë¤wpT--åÎ;g‘›ãAJ+4ÂÎ ö08eiUoýj3 í¬¾e N²ŒƒbÛûGù‡ù¼´§‹—?ê$+×Çíw,&;ÛÏúõÁ”æ7›Ú:“Êwš'Äcï¿ßºäXÓ &”f€BZS®ÔeÓ¤*Sí%c àã¶ÑON,Ïâ+kç0cFûjL)> µ;Zð¸t~¿åM݃ÜrË-ž” Ž;ÎPÿ¼fÈ„ËþÇLæÍ+'5Yÿó· Çî?ÚÚùÀÉdã;Óü/ÄÃ<÷ûúÀ7¾>wtE!ÑÞM!>:u,öù\Üzë ®ºªa¯W§fø“ ¥€ÚíÍDb&M݃|ñÖ¹,^<Ó½÷¶ŠIjæ”±|E5‹¸!ùÝ3ÐÝ9øë¦öŽoÇ6.pCCC´<øÇ×^;ü»U7L#Xì;g`eéòË'qçš™ø3ÜÉ ?.¨5Bcc?==a„€[o³`eJÉl’ƒQ¾ù­åf$“Ý«ÿ±›C íjjïºCæYŽ3>ãQ^\ôö‚ ƒŸùá.D¤À&˜wHíøS¿ÞÇœÙELš›„L aÒ.TÃÙèðf]ÿýs{yñÅýÜ~Ç</.Ç4S¾+Á4%R‰de'%lÙò1/þñÃâèËZ[[çc:ãþ°PÚ—¶lm¿·¥Õꌩƌ½fJg­N©d§o¾©úTØ„SÆmy’w•‚;Z¹cÍ<–,™˜,cÇ„ bTU@}}'¯¼¼»ÞŽ+Ï{Và#­­õ(~üÓ‡w22bÚpöÖ†Liöß'{âä¦lcŒ©Êì뉿»:‡Yqi… {š8OɃ<ýôÖaŠKZZZzÏÄsV`SwÞ×Ö6tðWOîëY3¥pL%¦R`å¨ÇåpÈÎñ&={¦8W ††büòïõÆF"Ë>nmm>Ë'nnnÑ…yõ³ÏÕ |ðAûi½˜¸~ŠÔ¨Ì1› ®äèF˜T  mÔë©c,ø‰?¶i¨·gàâÃ-݇> ,œ%i¥C…Ë}î×ׯ¿\ÏÏO&ÿIÍÒj¬lSJÏѳ5BjRK1ÐØf]ïèä±õïŽôô ^ÒÔÚ¹å“ÂÂ9<˜v¤µóÍÁØ7¾ÿý÷ˆÇåI'²¥9&®G'c¼{ØÔ ÈxíðáÖ=ø—x_ç {NÀmëêêzž|â‰]c“–TìÉr>m;vL->.´b÷®ùÙ;rx$~ScJ}|.Ç9?M[\\œëPÆ¡ŠŠÜœÛn›IõŒëI'ÎãuzŒ¬S`SKÍ1ÆQÐÓ=ÄK/~ć6÷ Ôßmëüã_{ÎÀ¥Á›…?ÊÊKï±þMUU>óæ3{v%2ÇT]¤€Ž™Bž˧Â*Ãá8zí7Â0Õ†úÒÇ]-ì'…ByNÌ—<^׋?WÃŒù¥lzu[ߨèSˆZúlnNš³jz>UUùL›VH(díO{Š—ÇÂöõ…Ù¶­™ oÖ14í°¶±µã¹OzNÀBQ lÎú]wךô°é•ýœè?¯™¬=ÒÙÙY^žEÜs’ê:—Î ¿›©Óò™2%@¡‚yù>ÜnÇБpœ¾¾»÷´²sG Gö¢†€u“µŸÖ«ç 0aBá$ÍÔ~êR ¥¾|´­óÅñ>[^ž¥¢®+ÚBP3€ä¢qF†ŸÏÅðpŒ¡¡¦)L`¨Hm£Ç0Þ;ØÝ}êNܧ<Î9i•Ì”º·i¼ÇúNwøÒ\Ú|¸oàœ:óWÿ_àE²çguIEND®B`‚nut-2.7.4/docs/images/bigbox.png0000644000175000017500000006104112640443572013441 00000000000000‰PNG  IHDR>²Û=£'sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœì½w¼%×UçûÝ{WÕÉ7uTI­dÙ 6¶Ñð,K€s'° ŸÏÀ<ð`<ÆÀÀ03<=iÞ¼  ÏøóxBÂ8àlã '%[–%Y²b«»¥Î7œ\U{¯÷Ç®:á†îÛ·»ÕÒqýúS}N¥]»Î©û;kí½Öo)¡@“áç~îícÌKL¼XX\\|Ë+_ùÊË[Í¥ÅV»ýhs©ùÍ#G~iaaá6à¡›oºÑžë>(°TA|ÖÂOþÄOí2y1Ák1/5ÆTL µ¾·Ón_ñ®w½“ƒ!âpÎ/ÍV›……×j5uÚ‡›­Ö7Ž?~K«Õ¾xôæ›n,¸çñàõ¯ûmŒ¹&ðd÷:cÌsM`0&Àã—ŒøÚíö¿òÎwpðàAœ“1òsNpÎ ¶Yëh·Û4›MÛj·w»½‡ÚíÎKK _ètºß¼ù¦÷Ÿë{/ð½…‚ø¾Çñ²—¼lÚæƘ×¼Ú³%%»5ˆ¯ÙlŽßJ²[k}¸Í¯·ÛmÚívÒéöõ{ݺ½Þí ó _H­½ëæ›n<|®?Ÿ“‰‚ø¾qíÿò¢Ë2¢{1æÅ&0¡'6Opë ¾û–––žóŽûË:tè¤d·Ü\› ý6k-Ýn‡n¯×ï÷úOöûñý½~ïÖv»ýkÝÝ7ßtãü¹þ <³œë8ûxî•W…Ƙë3²{­ Ì¥§Û¦')»*q­FvË-½#"DQ‰0 K®V»Ð9¹Ð9÷*çì ½^Ÿw½ë׺q’H“ø¾8I¾Öíön‘{n¾éÆÖ™ø¼ L> â›P\vÑÅsƘ×c^g‚àÀÔl^‰È2ònùâ-ΦVïÞ}>sssT«@Ö Ä¸ÆBÔjºRqåKœs—8ç~Ä9KÇüâ/þR+MÓýÖÙ{’Ä~¥ßï}Ik}ßÍ7ÝØ;ƒ÷^`Pßâ’ ÷Zëû-gëÎ9Ò4ÅÚ!1=öÈ#ûö=¾w J7ƒÀ´KåJjjªZ­UçjµF}û¶íjË–ÍT*´Vë<¹µ(c(•t= ƒËs—»’{K­Z"M-ÿúg~vÉY·WÄ}Û:÷eçäKÀwo¾éÆäl}>žÞ(ˆo2a8‹¤0´ø†$”&iàUÄVcëHK§ÝF)R$÷Üý­¦RªF¥v­^jÕêL¹\ž™Û4lÙ¼™jµŠÖæÉo¸¾r› ”Âh=¥à*ç¸ øI­眼õmo[@xLà["ò%­ÍW€‡‹ÄÉGA|6Œ4M3W×1ÕZ#"ƒJ "„(ææ’~ŸÅ8fi~”’Gy¤ ´Œ·mµRŠ¢Òl£Q-ÏmÚD­VÃŒâ‰Èn=ûΉrÎÍfË÷‰ÈÏ8'h­í¿õm÷+¥¿ Ü“-÷Þ|ÓÓ»ÀEA|69ñ ]ÎÔ¦¼•¥”ò*…ߦAyK1ßç÷£€†B5œuôÚmúJiŽV½z¨©Dµ‚(ˆ+åJ%ŠJ³¥RXŸžžQõz¥TF²ë±×^ ŒRê àŠÑ~ëÛ~ò7ßtã[žš·ÀÙFA|“‰§$F)NÎ]Ý…cǽNAˆÒjЕ½ÑÊ[„žý6¥Ô°Ã#¤ˆP”)+­¶(^·K¿×cIÄ>x¸é–ÖºE¡ ÃÒL˜éjµx—Y#rb²sΡ´&Œ"Œ9éŸÂKÎðÇWࢠ¾ƨÅ'â¼{kÓgJ«õ§µFi…Ö­5 Æ8οWk\)?fЖQžQJÍäÛÒ$!Žû´Z­¶s®‰¢mŒ‘À˜šÖf&ŒÂJ¥\XˆJ)¨”õe](^'ñØ2W׎ñmß¹»›¤é”³ëdÖ0ïsbç² 5$G­QŠ17øT µÆhƒµ¶f­­å„h­wy“$íuÚݶ×Bèi£ÑÚÌ„¡ÙR­Öu©T:Ù%܆:Vài‰‚ø&O‰ubíøßB"Û¾;ß§h[ ÍñJdºåÐiUW"3"N‹â…·¼†=y¿AòóÍøñ>ÐÞÊT*#_)+¥Ë¥0Ú”_#sÕm³Õ\XX˜ïˆH[œ¤&05c‚­år¹\«Õò¦ â› ÄW`#Pֺ̢ò®®BxÕ5W1=7g*µÆ–^b9¶°À­ß¸›N¯µ–P+©„f¾™v%4®dLUkf ÔI\ÝuuL©ŒC½µ©µ'À||ÏZ‹ …öägPj¦E3Zœ³ÌÏg~^]rÉ¥Û²¦ Ww‚P_ #M“1W× ­™©×عkµZ$Iúmo‰9G·Ÿ¨v¯7×îÅs~Âñ8F0,¶[ˆ8ÊF/U"³T -…¦j=£ Ò¢QÙ?£Ìš} ŸWì'1²Ù`4ÌÆZKh´2(­±iJj-a6ÙŸ…Œ‘]añM â›L<%ÖIšŽçã®Ú‘e"å( Ö˜­Wñ_zíVmqOuºÝ©f§ÍB³Åñ¥& KMº½FÑ®Ff±š¤…¥(ÐÓF›J>¾§'-ïâêÁdÈèâœÅZOÖ(oºš @œÃZëÃnVÿø â› ÄW`ð֎…³¬66·õŸ$IHÒ”@D„©Æ4µJ•ówìD)iš‚H­ßï×Z6Kí ÍÇš-–Z-Ò4î—C3_ M¿V£r4ÔŒ1*‚,ÄÆ˜€ W£ˆx ÖÚ‘>Ëè ®î¡ ¾A6Æ—Œ…³xnç‡õÊž)% VžÛïw1&ÀÚ­C‚Ð07»‰Ù™i.¢Ì­…$é—¬µÛ»½.­v‡ÅV“Åf›¥¥ÝnÛjÍ|%0z¥d*QØ(EQà ‚€(ŒÂR©ŒÎHðèÑ#Ë»XX|„‚ø lCuL¬À:í$Ó<Ø'Ù$…ßÖïõ¨ÖBÜý¤©#Ib´²”¹jEѨM±uóf‚Œ•RæØ‘ƒ›¿qç­4“„c©£o¬6Zc«Q¸T¯”L­\ªEa0Íò@Âø& ñM&ζ[6ˆãUg»ªä/ëµøü±y_Ρ*³?ùñ1==EbµjjµB­VgÇŽLÏnʺ$cäk­%Ib‚ $ CŒ‚È(*Z#·¾(;#½þÌb{‰ÃVhTJÉÔt#\ÖÅ‚ø&ñØ0Fãø¼«»ëqu$7ò?dd(><¥V«ñŽwü2Ý^~¯Çí·ßÉ׿~+Æñ Ë/ågqÇ·™ÚVDlóØxl_ @)‡û4–×c|„‚ø lË37ƉÂóÄzˆOey»’Ymƒ„,›:Ý.¿û»@£Ñ \.†~Â"g6‘Ü3Uƒ±B­5ÎŽríÊ7RÞO¶{œ´yÛÎB¢è27ÓívØ{ø•0¤”£½jfÆø¶üœ7ôo&I,Ö"½^Ÿv»I­>ÍÔôtv¼øeô$@+P«}Jaª›0ÕM°í9¸$^N|£(ˆo‚P_ ævLˆTiųvogë–YfjUŒÑtû1š§Çtû Ö9¢ÀPŽB*QH½Z¢2µ™j¹Œ QTÁ§› Ƥ©õÖ @¹†³l œ³(åg{!ç:àÆiNû±¼3€ÂÕ ÄW`#XÎ’ÏêVË3µ*[fêÔêu¦"ÃuW]2ÈÜè')^< Â~šrßÃ3¿Øâx«E/N©”KÌ5Ì6êL׫LÕjÔªe*¥Q†!iª ‚kÖŠw‰ñVßhøÝòÙâîÃ_Àv1ÕÍèúfLu ¦¶™ ¾ª`uáêN( â›L<5™Ö!#c|«wd¼+Q`ke¦ª¥ñÌv‡ 08—’¦)ý8¡ÓíÒnw˜_œçÑ':,,µ˜o¶é§–j¹Ìl£ÆL£Æt­J£V¥^©R­”‰"!Ò!Ú„ˆ„ƒ‰¥53f‰Î£G”¢Ýï#ÖÒ¬K9¡DUA|„‚ø lÎŽººëKY[ iš$ "kS‚ " `Ól…™Æ4„!à‚€$é“Ä qÓéviv:Ÿ_àÑ}˜oµYl·‰S¡V©0]«2Ó¨‚µÌÇÂtjq(SÓ˜n›ééYšÍ%êõÝn‡^·Ëá'Ÿ\õV6þIxº¡ ¾Æxð²cEìëÏÜðá( /Í<šýæ±$I‚µà\:(x^ #Êå2›7mAĆ¥K÷pÎÒëõétºæŽ½Óï÷ ˆç\y矿ÇKÒ+pâ@yi{?ì¨VgÉ`h‘q‚U–~«U+•å6…«;A(ˆ¯À†!’b­:íÌ‘£X|¢<¨­ {ö\H§Ûåð:|ð!>õ©ÏÐët×qœ(´ ãú´Ÿ`©]õÊ^mÅvŽ‘vŽcÛGIÛLj<ˆ]ß+\Ý EA|6Œ\¿nàê®—¼~âSÃWßîD˜™™á³Ÿû|öŸ?7. ƒúج‚›ó¬)ËÜoSÝDiÇóVíÁÂíïãÈ—þdE²t¸‚ø&ñØ0ÒÔb̈«ë,÷ø~dVZnñeÌ£éºiëéüãèê,ºÔ8•Û.Æø&ñØ0¬`¨ÎâD1ר±ufŠ›g¨ÕëT”%}Þ³Æ27zq’-)©MYZZâÀ¡Ã4Û-Z–Ú=œ•R)ËܨҨUhT*Ô*e*¥2åRDø²çÆ„¤i’Í{ÔZ¡µç,¥îÑÏpô!Ö§Ž 2‹®Ì¢*³¤­â£Þïö§ß¡ ¾É…p² ÝGfñy·s0Æ·ŠQ´|ˆ/ ¡ÑÔËÑ scÏEϦÛ놥쇵1ÎAÇôú}:½.ínŸCGŽÑìvi¶;,¶Û´{1J•*Sµ j™zµJ½Z¢Z.S-•)—J´{]DkÎÛ±“Å…y¦¦gXZ\ R©Òlκ6‰‰©lÞÂÂñc*ÿGú_ß¡ ¾†ãSu?±›ÉDå­crÃ9!MRœSX›†Î:´ñÓ¦ÓÔªUÎ B“¥¶–8ŽI’„~Óëõèôz´;]Ž?N«Ó¥Ùî²Øé[ǃ·ßK)ÔT£cDZQŽš µ’Ãh&@ã óŽ£pu'ñØ2‹oT `ÔâÕÔ[G³ŸF„ Ou¬ Ú'AJ#´©×ÊTÊ5¶ƒu>õ-¯«»´pŒ;ný2ÚâTH]Bœ­~ŸÔ ‡d‰ÔA*pùöF”@ÅäÆD¢ ¾FjSD¼.Ÿ8K'îwž¢ô<’‡Ÿx1R•Åðå¸ã¶Û(—+DQD¥R!*GTÊ5jõÆÔÈg†Çú—¦Xk‰¢á̯Â+‡JQÒC•f%&[ó ¥Ù-›Go â›(Ä7¹8›c|$IJª†|§°uâµðrùùå§=r˜·½íÇyòàAZ­>ò0<ÁÕÏ}—]~¹7ΖŸR £ô˜»m»Ó؉íΓv°ÝEloÛk®Ö›‘Ïï̈úxz  ¾ŸMq2b[ñɈ“ìñT–±!YHJ’$Üyç7(•KLMM±mû6x";K£T ¢ʼn|‘4…h#Äò…/öÒó+º`™ÿú{8úÕ¿Z~ù»Ââ› ÄW`ÃÈkeŒb9Í­‡÷#¶•¨A! _9튫®&‰cZÇxòÉÃ$iÌÌì,Af×Uy©¶¬MôO­ÇðUTVl.\ÝÉDA|“‹³éšy‹O©1¡wëî?D”qQ•ÙÄñÄÑyž8ºH"£!4f¼±Qr0É1¾ÿ¢‹.Ái’f©j)¨‘d¤ 5èdî²H¦Y°!äRÎEÊÚD¡ ¾†Öz,-Â(ÍTµB8'tú}_â¾}O')q’ê_„'ÀJ)àXW¨UJ”£årDÉY¢P*—Qh´† ˆ²zÖB¸ "Öë°d9kùD‡Røc3´îþº|]ª!QÕÑ¥ºÜ èÞå·8:ØXŒñM â+°dc|㦔RŠmsSìÚ<ÃÎm›¨7êI—ªë27’Ô§ ýÄÒS¬X¦keº½>‹KMÚ½.­nv§O³Û%NS”Ò”KõJ…F¥LµR¢Z*Q-—(—K”£ˆRR … 0(¥Â0‹óóÒý.hcؾ¹Nsi?%U¦»ØÆ˜€¸c• SK¥Z£ßëî©pu'ñM.κ…b–¹¬ëQg ŒÆèˆr(HÅgn\pÁt;‚°ŒÖ~RÂÚ˜ ˆp·¤X눓„$Žéöcúý~Ÿ£ÇZtº=Úݘv¿G«Û£Ÿ¤Šr)¢V)S/•ç˜oYÜáyœM©;EšB5 pLÕê B’&<¹ïñÑ;(RÖ&ñØ0r¥c$›7]Uz~}m9q8›’$>#ÃWpóõ8Œ ±©PŠJ& ^ŸB$Eë¥òÙe›Yx`Ó„$qÜ#NSŽ=JÒ:Îü¢X^h’:Á:¯deå6ã¸ìÚ”âÿ6FC Ww‚P_ Á èeÝêÙ§Öðx‹?_¡µ^aP:çPÊûü^ÁZKY¢&©P­JAÀÁ½­ž—ÁÂÀo.̛鹹,Ÿdx¹S»‘OgÄ7¹8û³ºz9­rÉuÕÜ]΃úÙûìÔ¢ˆ0ŒˆJ%Â($ŠJìÚµ‹Í›·&MrU–|^Âèñn)¥VZçÐ œu¹ºUÇ7¡(ˆ¯ÀFà‰a$o-z[OÊšEñu2†Y@²'°Rñ¿ùkìÛ·£ ßøÆ7ùú×o¥Z©°yËÖ¬¬R›0(^”Zƒi\óvjWý·qq×où%iѼÿÓ,}çS+îsp+&ñؼ«{¢2>ÃãÖÓ–VâCåro9¯¹á`ii‰ÿòîßc˖ͤiJT* rpÕà:C>q ¥4JyÀQ(¡*º2;¶=>öŒ߸jýEE <#Q_`hñ °:Á­wŒOòÿr‹o¤=¥/Åk°6¡÷HG’ô˜žÙÄܦMäµÌ%ËúÈ¥çý¹2"Ý u– EA|“‹³îš-wu{…r ]©3ã„'Ž-rðøÑÚÏZ{í» A`ŒÎÚ︈O8«7¦…u)Z>`Y87ÌÜPø1º¡Åç³KdÄâ‹ÞCûþ¡ƒ „tXõ¯Q ·ÆoPN°Và‚ø lk¹ºJi’ÔÒîÅH³ËÞCG¹çÑý$ÖϘZ74œ£){Ž4}0rEå(" Ãl ´Ñ(£‰‚j–‘!JE¤©ÅY@i\“’[|Nö-œ¿‡Þ¡;Ð: IúˆøÚ!ijgǃ²U>«[¤¬M â+°a¨Ix²ÑJqþÖ9vnßÌ®[¨7H{çUƒAæ†sŽ8µ$iJ’Zœ;wì"ŽúIJ?Ž™_Z¤ß÷ʽ8¦ÛëÓíÇtã˜4ók£0¢…TJå(¢R }Ê[R.y-ž<Ã0dq±I×)vMÏÐl-13=C«Õdjjšfs‰z½á¤û}öï},¿­ÂÕPÄ7¹8»á,"^›ý$³ºË÷(¥u7$ò™¥ÓigÈ’elÄ„a) u¬M|ž®uY®®Å9Gj}p’$ÄIBÇô“˜ÅN›^ûênqŸV·K³pàÛbœĉèDZd¹ÃF„ÓÓ«ÝHA|„‚ø l õ ÂX²×uÆñå°Ö¡µ%MS@Ç AbÓqBjŒ‰C”Ö”JU\6öçœÅ“½ˆxR[Z<η~yE³ÏÞ¬¤Xc­÷a”Ë] ‹1¾ BA|6‚,ÕŠÙˆeXñ çH2†ª’bðææ)•Ê¢\6Þ uÃx½áõÈT[àçY;`Ù( » +ޏקÖhÉ:ç(,¾ BA|“‹³îꪑµ5-¿ ô"’†ÿrËçÙ³çBâ8æØ±ã™E(\qÕs¹ø’KVi#OyƒÑd·òE?Hé¼çãÒ..éá’.’½ö}‡îÁï¬Ò(âø& ñØPúÌ0¯<'±æJ¥×_iš277Ëý÷?À§>õÒ$Yó~ÖÙáªÈD[.§våW=~áŽ÷-#>m»pu'ñØV¤¬­…õŽñå¶•ÊäT?ó;œn¼ñfŒÑ¤iŠÖz ‹•Çð­ÚÑÜßF›-,¾ BA|6†,Žo°z‚ãÖ×ä‰2¢ïknÀ«^ûúL}9AÄ$}œ´d-ÚómŽò—¸µÇûÖ89ï\A|„‚ø&g]e<€ÙÏ”>ql°Ö TŸ&Asp~‰Ã MŒVhð¯J¡µÂdÄE!i„6Ó‰÷–ŸBœBCêrN† ­#”Ò8ëåì¬ý0ŽÔµ¿ñ^–nýkÐ1!èl1¶·´âÜÂÕLÄW`C_×lÌ¢ ÓYìt©.5é9Åûòèãû±ÎeËØ¸Fk¶?òd&a–±~=± ‚ÀÇÚ%£µ_LˆÑ>+Ú0ò!,Ú©0i.Øs‹ LMO³°pœjµN³¹D¥¢Hk5úµ=t0?¥pu'ñØd4sÓžVšKvleçÎíìÜuFƒxážµmz¹!"XçHSKj}¶Ý».ȶ¥¤Ö’¦ ‰õÙi’ÐêõHÓ„8M|°rš§Ö,')q’dõ;'‚Öm_Ð(#Oç,­f̱ï<‚RŽðè" :`-a»—•¢´”«•ñõ(ˆo‚Pßäâ))‹í]Uz~å6­a`|ý ch4¦è´ÛTÊÒ4% £¬æFˆs>»ÃZKFƒ6œóõJù‰À¬}VGjq>y—ÔZ\šr|þß¹ÿ> ÁZpiB*B?#K'¾a×l-ë€OÅxð»ß% "”Â0Bk0AÄÔTjujLzŠL€txö¦¶UšAlŒ³1ΦHãl‚ë·°¶3ìÁx„vA|„‚ø&O‘«;’º1šk6x·¾n ÓΆùº<øÀý¼ôe/!î÷YZj²oßì߀«Ÿû<.»ü9ƒós!Ò¼Íå)mµ+ßDýy?µjnG¾ô'ùªC0#¼]ßAŸüV@‰ä³ºŒÍÈkû­€Œ¸œ£'ƒš½%Ùn·i·;ÌÎΰmû6¸œGÚeß@z^ò:2n ®˼ï$ßxº¢°ø l£³º°º‰¹^‘‚!iæ,:´E„óvìdïcûH²5aË–-”*•¬‹qDˆTD³¢ܺûÊŬî$¢ ¾ÁM¨³Œq\N<ë7”†ä7êîzkïû^p A`°©Å?{«´Q^ÉO–eÒó2Ú]º|t~?¢(3xEiúïí¿[vKñM â›\œU×,—ž½Šˆpt±MeºM­ÕALÈ‘Å&K‹-oÕ‘-Yn­¢Â0$B_37¯œ¦ü$F„ˆišøZ†ÌDt€Îú1z»Ë,¾ÒSÆ0ÅÝ_¤R.Óë÷(E%â¸ÖmS‚¹M,-ÌÃÑÉè L â+°¬ZeMD8<¿HP«£ËucË]>ÎÑùyœóň$‹›Ë-A¥_ºïQŒÖh­ Œ!Ðm4¡ÑhãÕš}¦†!ü{­4ñçäYÚhŒÒã‹)¥íÎ/̳;¦Ðw`DHEjE†!N)ŒÒáHÆGáêN$ â+°AdņF\Y­5Ͼ`;»vídçÎ4¦¦h?ÿršÍÖXæF¾øÂCŠóÏ¿ˆÔÚÌEõ©mâ—Õè°âg±Np6O}³8'Xëpb±6%I=gq©?Ǧ^¦>uŽN·G»o¹oïˆó,&â“Ñùk —mkˆB®î„¢ ¾ÉÅY)ðé]Ë u¯Ö‰ôB+…1†0*‘v:hm°åR™$ñ’óI’Ö&bmšIʧYyÉñfÁ=8Î9CMsq;VHÏ«1"ç°Ö²t|žÙ-›ÝXVJáêN â+°a(=2¡Àê1{§TPœaXÊú0Þ¶ˆÏäP*HÏûÔ¾#¹ IDATµ =ænlè°°ø& ñØx·ódÄv*ņ֦<á«_þ´6 Fk?c« »ÏßÍöóvùó•WiYžP2ºZ¿êÍ”/{õðÂùx£­û>ÆñoÞ”ï*fu'ñM.Îò¬.Øe¢žË,¤ì¸õÅñ­zÜ`2UÑj6ù¹ŸûŽŸ§Ñhp×]wñµ¯ÝÊôô4ÛÏö /6䃛Պ¢çºº™hóe«ö£»ïöe¹¡"em¢P_ û“ÔµÕ)n½ü;n¦É üÎoïõz|øÃ!úý>àÃ`NY5§x]½‘œDó¦N¹‘O[ÄW`#PˆŸQ=™A·Þ1¾å™Ë׿ïßRq©W^Ëö»˜™]³‹«Á¶ºgàâúlÏpÉÒÑÓ݈DUÖ& '%>¥Ôy@MDÊÖ/bàðÜì°>pHDŽgÇhà* Ü.'*lZàlá캺øp“]n½¹ºkw×gnì>÷š³ºkZ¾®Hþ$ó|­¼«®”Îj|€³–rµJÜëth`ÊÄw¡”zð~ùŒRªüðó€wà¿…ðe௲Ó~x5°ø{ùÔz¯·‹ïß××gëŠ'½_¾ ÌgÛëJ©_‘÷7¯Ž3J©‘o¯·SžöPà‰bÔÇšÝÍNf·‡K[l' àÃE²#É ­0 |ÍÔgiD¥8f8¨ü#çdm1¢”º-ëÐÍgà:ž>PB>¹¹1ÍU{v²s×.víÚIcjŠùk®$MÓU37$«qÁ…—Òï÷ÑÆ òns>|%“•r©CCÅg›KKŠoõ©a†³Þõp,.,pß=ßÌäéq‡ ެ/*Ëé³pôÓ›æ¼^Äñ=UXUÜ'{RJÝ ìf€‰È½J©ÿ¼ øß€¯?4×s±õèñ%0RŠÞŸ“Œ¬ÿ ðï½À/ˆÈ¿Á»Çw¿ üÅz:SàŒâ,»f‚ó¬A®É·úxÞ‰»áÃM´·«Äq‚8…MÎ)ú};”‰wŠ4u䣠±Îec}”ÏÔ0Ú uˆ BPš Œ¨V«ij|ÖˆF¡ã KtyÇ W÷ìb ¨gïó1‹vöÚ~Ï5e¤w=ðÞ⻸X]av¬Çâ; ìQJ•E¤ì¾1²ÿ"ò‰|E)u ð5àEä—”R-¼ÿ^`r pöäúÊëãS¸LBÊóèPÄ sb=ù‘‰ä®lækåQCñ{•3ñ(­Ï!Üü,ÐÆ/øWQýߤùÈ—]Ï_ W÷)ÁýÀÛ•Rûðó =¼1õ\ ‘Zvü&¼‹ûNüïÕ4ðÄz/¶âûÞj{L)Õæ€×:XDRJ}¸C)õ0~àñ—ÖÛ¡Ïd®n6¹1†ñ9SÑ-ÎTø¼»;H]ó±‚üÀMT*´Ö$I‚sŽ$I2éùËÉ8/{Þ ˆyxÊžëN =¯W_áê>eøàÿÄ»­G€·ˆÈ1¥ÔAüLîr|øOÀOeà?ï_ïÅNJ|"ò¸RêÙxs2¾."G”ÿ)þaàîUÎyKöò\àÛy(L§gßÕun«ŒL…®7W× ¢!Wí ÏetX¯×ù•wý;–ÑÆpï=÷ò™Ï|6K®U‰üÉ‚: éù•Ý.¤çÏ&Dä VqUEä“À'WÙ.ÀÏ–Sƺ&7²ø¼O-Û&ø™•µÎy†ã<‚ø& cÄ÷w÷ÿ][¯×¯mLM]ff~~aëÂâÂyÎù‡Ê‰ÀÈ{q>×Ñ ;s=Èu¸\M7{oŒI¿ÿû_ø@†ýN»³w~aaï}÷Þó×7ÜpCÿœ| 6„qW×÷q’ÒOzI‚î÷Ylw2Ùy—ÉXùçÂe1}Æv‰ Mà³5tèÇû¡‰Mý3e-â„8íHþ\åÏcöŒIFÌ^éY8¶0Ï¡NŠ>rœ~s´“Ä1j±KjÜ‘E¬³`nzL¯H΀ÅßiâÝï~wéòËŸýó33Ó”+• ¬µ¥'<ñ¬ÔÙçã$ã O5Ëx$“Ë¿wÉâ0n ‘–«vŸ¿{ד{ö\t8I’…ÅÅÅï6[ͯüäOüÄWò¾Y‡‚ï{þ >~þ¼|Ïž=ʃ΢äï»ï>¾ûÝïÒív±Öz©ogOðÞ«Ø®öÞ9Çž=òÆ7¼Ý矹s¾Ã K‹ÌÍÍýÞ>ðÁ·¿ùÍoZ÷ÌLs%ùäÆHÊšsŽ»ÙÏñŽÇŠj­ÊîúnVhH£³É ­ríW—ìÁ¯»÷‘d®±Ou붛ܱphDz>/~Äàâ|[I?†ºH>+œ¡ ¾ âÞÿ¯žÛ´ù¦F½¾"abnnŽ—¾ì¥tÚmß·¥¥EšÍ6Í¥%š­&Í¥¥UÛŒ¢ˆF£ÎôÔ)Ï3{ö\ÈeÏzjðlÎJ¥ª¶m‹^ñÂk®ùðýÁ‡>ôOï˜Ý4w͉4Ë´Öìܵ“Ý»wû_é‘_Ó~Ón5Yj¶(•"f¦§™šš" ÃAð©sv@vήýüÔëuÝëvþöÝï~÷y7ÜpCñ Îî>7w¹¸¬ðÚÉRŠ4MI’ëÀÚ”0 ±6A„AÍ4Mp"cê,ÖzÔÊ`]Šs`SëëïZ‹…µÖÏþŽtPJ‡h!&B˜ˆ´u;¿o¤÷CkV WwCx÷»ß­¿ÿšõ·µZí„Ybõz+®xJ ¹Ei¦Ônµi¶Z$qLcjŠÙ™iªÕª?Vë{{2ŽQJ1==óÂÿÓGÞTªÕwFQiÃ7V.—¨VÊlݺ-s?F’Ë7€ÆÔôÖ ÷\ôz`y¤v§2W׎Nj,Ÿè8…YÝœ@õpe–pôð”V¤6A'6`zjšéé@3¨Š¦|ßP„tøŽ|éO’Bï´qáž‹^_oLmÝèùFk¦§§™™ñ!KÙÐÆFQ*•¨T«ï@¶o¸ ßïóï|‡JµŠQŠ©éivïÚµáö‚ `fzzMâSJm~ÿLþ½ˆÞðÅÎ ”R/‘ÏžÂ9†ùÒoþFDÖ•d}Α»':äˆ/R'<É×…[¿þ®¾ú**Õ v‡Ã‡³ÿ®ºúyLMÏ¡TŠÒ‚b8f—«¸lj8¹‘aÕ›UJý^}èóÀ¿È©Åðœ(¥fðy­ÿSDÖ=a¨”Ú’%'¼ˆEä §Û—™éé×YÜÀÞÇçøñã4êuœ³\z饔Ëå7(l×"l¸k-·Ýv;wîf×Î]\tñň-*~|ß~¶nÛ6æÚ–Ëeî¹÷>žý¬Õ º¬³gkü° "W(¥~‰,])õ¯ð Ë À_ŠÈ·”R¿†J}.ž\>…Wù¬ˆü‘Rê×ãÀóð3ÜŸÉöFDþX)õË@GDÞ›½Oð–蟆Aü‚ˆü)#Š5J©k³vzÀŠÈJ©ÿx=^ô`vÿ øéìXQJ…øÄþ˜ZvÍ[Dälè³=C<¼éÜvj²ó’Y|ù¶q—·T.ó¹ÏÝBˆ€ÖŠmçí Þ˜Â‰Ê´ød`)æpÌÈœ]|ðnšßp¸¤‹Kz¸¤‹$=\Ú¡äá±ÎË PÑÊ8>¥ÔÅx1Þ7‹È‡”R—oSJMá¿ËÿŒ®>€'ÇóñbšŸÇßïÁ?›ñyª¼ÄÒ-øÔ­¿ÂË,Íeû>—õ_[³÷?÷Ž—àxß•õ5žf³ã®>*"ïÉRP/óôÑly;`”RoÄ?ËIvŸ×ãňŸÀÿ]}G)õ.üßÍÅÙ5UDFtûG°ößòºðؾ}\ùœ+ëJ)6oÞÄ£íåÒK.Þp»§Õ©ÅÅEª•ÊŠíåòÊmgÏ>—¯ˆÈŸgb§çŸÅ§È¾”YT/Æ?4Á?0¿€'“?TJmÅ? ïÄ“ÙÏâ¤”Ú üž€ÈÞ_ƒ—Îy+ðZàKÀŸdû÷×d²Ùÿ |/§ó®Ì=ÿaüÃzþáüfÖî§€RÖf5;æÇ€ÿOr¿²ìš_þ»Rjú4?ËÓƒ°ly%ÉmÄëËC—ñýÐK^ÎK^þr®½îz®ûÁæÚ_ǵ×^Ïö¬ÄÚ Ñm•¼Ü!™*‚ù{Iï½óèÇ`ï')ùáÑÛhtd®’²uÇŽìàÑ jVKY{vöú¹ì^ï‘ÿ]D–ðù£oÀ[ÿO›ñÏß&üó€Oãe•Þ l~˜³ýŸÄ“Ë¿ÃëнÿCŸ¿ðÏøoã õû²6¦Föÿ'à5À_¿ª”º/×þ]|Åö^·s:Ížmü/'J©+•R›ð„¤€ðº] þËøœˆ|¯øð~à³ísÙëgGöÿ#þÝŸFË­ãÿ¯¡ðZŽ—àetþRDÞ)"¿ˆÿuü6ð;Àø?‚\‘úóŒ³ÆK÷ŠÈ?¼mk6Vù|Fq–Çš|°p¸¼juÈS!¾|D-'©A¹¢r~ å'Ñ´FM„! J¥år… Ã(ЍT*~–Ø ±(S3¨0 ªT : ï„Nœ°ÔiuzTjÕ¡Ÿž»º+?ÏvÖÍÝàÇk•R×dû^ ¼'ûÿ?&ã¯[‡EäƒxÍÊüY“lÿmÙþá%àæFÎ׬|o‘[ð5³lß뀛Däã"òly/ñ~ žPþ‡w/°OD¾;rî‹€%ù}¼jJÿ|ƒ÷tnÁÿ€Ÿµáv{å÷üü;Î;o•£×ÓruÏ;o;?ú(ú"•J™8ŽÙ¿o?/|Á N~òÆð à¿*¥^…ÿuúþAù' †ÿ2;ø/ÿV¼dM>¸ëðØò_î|¿dçŽîïàI ü/ï¨M—ÕÉå[@#Ó%¼_/àV¼»°ïâh ÍŽ–ÿpm&åÿb¼¶áÉ®ù”C@¬ujœÛ<[X'¤ÖbEhwû¤Îa%M,6 9°V°Î¢µank'–~7öçv;ØÔ’f©©³¤i¶Ø”Ô:›’äë©#¶›Zb›úsóñ}yÀ«³hßó$×ñV¥Ê&DÊf{£ ™ôÖÈÐÞòçæà0ðJ©ÿ„'‘ŸÎ\ÞÇ€ë”R\Ëð;I”Rù³˜·›;ãNDÒlgd¿YߌwkG‘·•°wÏWJ•ðèàUÑï•Ü•]#e(šã1`‹Rê*<¹ˆ·ù,ðîMœˆOC¬à«–ÝñÀcìj§>uǽ^íØhŒR­½Ò±öYFk޵n'4ÑÞz3c 1Fc´_ ÆD¾ mŒòjËÆgv¥1örT*1P ”¡¹4Ï]wÞ:Àœ¥¹1;ÁÚ˜N+¥1;;î4/suE¤©”z+ð>|•¯ï?—}¯¿Š÷ŽáŸÉ?N+zOB·á ÷‹§pÞÿÿwñEü÷ð®ê½øÊe»ñøgJ©FÎý2~lù«xè]"rô ü]­[·n¡V«ñðÃeëÛ¸þÅ/>ívÕ'>ùétzfÆø<ÍŽl¶L+ïNø€Âñ å•ï‡ñ5yŸÎÎ]-€yð~$emiiizrôȑϽö5¯zÙšö¿^FD:˶ø_Õ3VÎ2»V"rjq\ÙXHš‡7dú…ÑhxAÖß S¶^qÝS EXŽË.ºø°1f‹1˜ŒLŒ 0ÁÈ{cV¬Áò}#çgmÝß½®T*é~¿?pOÌ·¸xç6vïÞÍîówa´áöÛ}ᬵ27Œ1\|ét:m‚ $wrÓ$&CÒÄl£M@šZŒ1>pYc¯FR›<;LÀ!v“;nýòÄ7Ì,±Ö¢µ¦1;ûQú’jÕ1 üÈ—þå‹]㻞‘ƒN§û®q­ŠˆtO~äŠó"‰GÖËËŸ;¥T]DZ«œöTÿ>þ‰O~vvnÓKsiL5MYËy'ç˜åÌ9·œˆgV `}ŸÚ”'Ÿô)qÖYŽ;fŸ‘ê,k=L"rÆÍî>¸"’,[†nu¾-e Wá üÁœU—XBÃm E) ˆC uŽ¢N:Ö—$ qœàœx¢ CÒ4õ˱C‡ '5"ƒµšÑ1¸Î %«È6À\Ùs=¥=?„Ž*¨°ê—¨†k,ÝýŽ~åÏ7(zÌÕ]ó&V#½lûÝØéeçÅËÖWüØ®FzÙöÕ\èg4ž‘ÄWàÜC)%¹jÏpãø1ëÜ–…¤€Ï q€öq|÷Ý{ŸÿõN?F˜&Xk¹`Ïvï¾Ð·¡Gfƒµ 5¤éx§Â­Ï¦zéê΄2cao²ì]‘¹1AxZ_¢Lí-w6/?×ýx&CicN~Ôé@žÏÀÐZ–½vjq|ž Fu$Çã=Ê›Þôz½s›æ¸çÛ÷ðµ¯ÝÊÖ­ÛPÊ—–D¼2‹[½‘)„¡éLôìì,žÉSÇO« v®û°ž–Äw$hü€sæ;çºÏd˜Æ ÒZ×|̆I£-sueÅ1§ ¥††£ÊóØðBwßým”‚Ã_=J˜KÏgpNpÂ@Pz=Ú ± ®;‹Û¸~ ·pý¶ß¦ÿäpÒÞÌm¿Ð=0puË׿ñ¯œ;Ë¿%ˆ#A 'êžE<-‰¯À3Y× S7ÖÝX§·<Dœ¯yáž=XÚhÎÛ¹›@+v¿‡©é)D¥•ÏàÈ œd5sõè5Í;ßK뎿Ei5˜ùuâ| ¥˜ž£¹¸àƒYÔˆJ:­×O3<-‰¯$鄯žë~<“!iò*|`êÙº™ ʈkºìˆSãóJ*yiÉ|Ï°ŠÆ•W?c¬MÑ:R|!ñtÐ¥‡m SÞ†=SF³çâKX\X`jzš¥ÅSÓ,..E%zqŸ4Ž º!Òï·<®Mvó«GNýsúÞFIì‹Àl9×ýXާ%ñmJ[wðšêÎu?žÉ¸¬½ô¤1ætcÇÖ„2ž»r,m½"C¨kš‡:QÅé)‚ J™©ÂƒµÊ‡A‰&1©MI’ž¯Ÿ‘$ôKœÆŸçñ¥”…öÒKzÄ{“ZGlxKRF˜®„ìž©‘Ì~"0æ²üß÷{¿ýå_xõçOç3û^ÄÇ?Ñü,lzé¹îÇr<-‰¯À32²ŒlsB’ÚA–E’øL‹$ËÊHR *Í$©¯Õ‘¤ ý$¡§ÙkŸ~œÒ‹zqLœ¦$iŠK½ Ñ…¥0¤…”€R…O[ËÞO—Bf#Eàz” hãCc”Ÿ  `îÒœïS›ž^~OŬî¡ ¾Å`ˆ/ç+Ž»ÙÇ‘žã`'åøR‹Ï}í[>+ÃhŸ¡}=ÀhB£yàÑG£h@Xå0`ªù<\fAÕAøzJ#A µÉb ½ô<øøB›&(­ÏÝöó÷aŒÇr«Œ)æPƒÿ)»ñM â›\œõæ¡BqT¬¸pÛf.ؽówï N궃É$ÄO”¹Ñn·0&¦qFX1Æ„éy_V2Ĺ´A\Jš¦8£uIÒgRôÚ b}ÂàX*,˜ÚÌô.TÔ@•èÒÔ`é<~‹÷}<»£e™ºEAñ‰BA|6•ÛzÃl bªZ¦Z*QŠBÄ9£O:É‘¦ išš4‚p5©5 —Érk“Ø¢”E뀈TVXÜ:?£»\¦ªvå›Ö”žO;#JC+=ù§…8D3ƒ‚ø lJ²ÙŒ±YÝeLqJ²T€,› 9ý‹_øIš&i–»›Ç W?÷y<ëòçgäÿ ó)ÞÛ  5ÚZañM âóÐøº ]¼bE“@`PP~íƒÖO|9ѬE–Ió¿ùk´[mœߺë[|úÓÿ<µÉëìŽÓÜh(ÞÚÝt¸þ®¿¸¢O#Ñ0ÏD⻯åw'™ã_À£ >¯‡÷à•Ùúo¿{îºsÆp–]3‘A±¡5®t*á,¹düme—ˆÐl6ù½ßýò,/4ZÂgæy¡Rçg5Fkn,G÷¡ÏÓân\wž¤»€í.ââf&e(WªÄ}Ÿ¿¯Æ Øs_DèñûøR àUÅßÊêz}ß“(ˆÏKƒ¿’ä `f@—øS¼üwµ0>¼Ç8ûåÁÃë `ö3%È@{~¸†RŠ]w=åR‰ ˆJeŒ1„aä';2ºÔJáÔPÁ%G̬´aJ/Ò_8Lµ^¡g{46m¥Û­Q¯OÑ’¸ÏïÍǘï™dñ½ø-\Gp-E°õÀËðRö(ˆï"à?"‰Ø£¡ôì£Êϸ€qµåË ƒ”µ°^#I‘®R1€mÛ¶aL€³)Ú ¥á†g(m#3Ð놷¤tz}Ú‰cV “¦t—Ú´»}ž\èЉRw”^œP .ܼ‰~¯Ÿ;æx¦_ _G7“"ØŽž~ ¬TmþžÆ÷:ñýPrKŸ‚ô8˜Ù|û꣞Y8Û®îÊ«,»â©8‡¹ðd˜À SúÚQ6Së|øŒRôú=âÄÒûôâ„n¿O¯Óé÷éö{tz ~Ÿ8NHÒ%%Ž…Ç`PhåËi% D¨i‡JcŽö(×ëŒTþXåîž¶øMàRéÝtîBÏ Ê}½~ŸN¯O§×£Ý‹éôzôã„4MÐJšZ¹L¥Q-•¨”"Ê¥R±©QeǦi¢0"ŠB´öö:‚¥Åyî\U™¡ü¼k%³TWij<,¾ÌƒIÅͿ߳v—ŒáôŠ]O¾W‰¯†/ˆ[x?ˆ3 *xø„g<Å-wu­³ÜÿøAͱž°÷Ƀ|õŽ»0Z†(«©šÐJaÀR{‰JQkÔØ±i–( À01Zûœ\'„a䯜ejäËÎÉȺ%4â,(ž^]YE…Le]žEWgI—ÒyâžÑ›|¦¹º”¤ùYH½ž‚_ñ\à{•ønvKçN¤ç«éMàrV]3µÊ¤­ÖŠ-Óuvn™á³òO IDAT‚[ØÜ(3k,¡1>¬dÌ /ºœv«…6&›±°iBÄqòÿ·wîAv\u~ÿœsºïcžü¶dYÆ6ƒ L`½‡]l^¨˜ïn’-0¤L6©J²qœªü³„líaM±<¶x²¶qªYÆX~È’%½ßÒŒ¤™ûìîsNþ8§ûö}Ìh4ÒH#é~UWsoßîÓçÞ{úÛ¿÷k]ßkñÖ&aбF…Anî£k-‘ªÛb8xí ½õ“ÈòDPj{ïÈsßh_R˜ËÝXÜpL2™ú¿n‹@-× ®gYùsç"ñ½ø7˜†5G~Ôº2ZÄ7@{/Û¸ö€gÂÿÔ¡ƒ\ž`|tˆ%C •K$QƒR!ÌHn&ãTb™+C•šö̈£ˆ8iÐlV‰cMԬǚ%ccŒ/kU=²¾½Åett–žWƒËPÃÇ.ZÓ `Î6,æß¿¥Áþ>¤-2‚qü‡¨Ð¾¦OÐjyÎá˜ÄW­VyuãF:ÄÁƒ‰¢È%ë´³‘îxÝ{Ûàà_|1—¯¼œ›oþm¼2úÌø2˜©G@OeE0ž>ý°ä±¸™ŽfAç2¼Ì–ßÐ]–j®¥çÉÎZ#fÉ $<ò?eÕªËYºt)ͨÉÄ¡ vîÜÅ[®»¾ƒøÒcœJ,eG™¹ÀŽ ÓÇßaìâ¿ËmýelcC¶Q¨lM¯þ¾ã˜ƒ¸>ÔÛ~z³ãõ×_g×®Ý:t€£G§ZÓt‹KŽÅ9BÀùçŸÏÅ_Âïz'+W®œõœ³ßÏþs&''i6›Ù$æ‹ééi6lØÈúõ¯ðÔ/~Ám·ÞÊ;Þ±`Çg½ÀM6Þ~ªí ïƒé'º(]iExÉÀ'€ož‚9ž8FÒ_8‹ïèÝu¼À¢¥T*qË?}?[~³…òÀAÀÞ½ûÚº­ lVº>4F´í“Lí¥±õitm‚¤:‘ýMª‡ˆï -<æÌpn\ |ä‡í÷SÇöZÓÁ2DùÚópÁúÿâ̱'&''Y½z5Q0¿h­Ùµk7;vìdÍš5¼ùšk¸÷îíÔ!2ÌH|kÖ¬áÑGãío[Ï÷¥ wkV i ÆX¬‘kqBE]ÇNL⡇bÙ²qV,_ÑõþXyÏ;Øò¾gæú¡çµ$`ù7¯CaÎulkÏckÏw&—Ü)/Éÿýß8òíÏÔ9- –©óO{¿C{'uÍ•÷fbPך@P«Õø»o|‹ÁA” ÅŠË.cÉØXûX–¬Ü”ô•˜ó]7’ßüŒÊ–Ç Âc Åb ¥c– a/ZŠIFÙµ},—NCØ-|û/JßcËûºZ2žv\òÕk(^d!Y9Øh+6ÚÚuˆx;¢|-ÔÖþû¾øæ…œÞXùž7Âx×ö$‰ùë¿þÆÆÆX¶¬û})VµZ”ºßc ÆH¬5Hm‰šÝJ˜µ–_ýú9Ž=Êî¿¿ç¼zßää$?|øafJïN’„J¥âà X‹1`lj¼v1.6«Œ1ü¯¿ú+þâÏþ¼ë=%£QàÆžÎKî9Œ­>ƒmv/†^…åˆá›ÀÔaú‘•ÀÊ“:§D¸ÀÖ[+òZ`îo^û=Î,/kmWª™[?–ÞùqÀlb÷7I@˜\ïxë=¾RxƒœïÞ&¤3«È@qÉŠ9|˜R©Ìte©qÔ¤^¯£u‚N†—,¡êRKÕµ”Š\w\æT`ð·¡x­¬9@–‘K~ß=ŸúÑ8½Xé$Bɨg’ð7¾ñM&&&ë¸q¥˜®THbí9ÅøªÛ­Æï÷Z©™{¡lÚ´‰'ŸzŠ÷¾÷=]ïõ¼<Ö>³–8vù½à?›öx(?ǃž³hÓõZÍ›7sÁE V!½…¡›‹9òã¹í/bé§ “_ݳgô¹‹.¯·ª:w´«¤­1èè¡áW(E„(Uôá,iY*C#jG1Qìzh³gâ0u+y}ÇÍ„ÄÆZ‹‚‚bº1Z¾`ªµº­:KgñƒEÁ›0GâB²æ¹ä÷]èVõI¨\¥j®°ÖòÚk¯ÍºO¢5ZëéµqŒÍˆp¦œì«W¯ž;ñýfË–cM8IÚ¤»t2ù‡"_²'Ö­[Ç­ÝÖ¶m²¶üIàäöÜcO€¸NŽLNcIaŽþŸ®ÝÅð-ˆðb°É*ˆÅÎІýúe!Ô¥ 5¾è¨]܃özní)R†¾ð€Ï»• ¥Dk£(I’ÐhV\¶F³é²7¢ˆF³‰MbŽÖê$ÚzL)Ô"6– GJhcIŒ i&®z³Àz¬©E.z´ÜºÑïÞ¯·Z™~„¿üzóº¿übiÇü¿±Àà{ÿø—bäƒV ÜÐÆfò[`“¶ÝEqbðÝ€=‚©Þˆ sYPLÖ–ÿx´ÈÍùm»ví¢Ñ˜Ýj`´&Nâ6>1Ùóç¨`v‚™˜œì¹½'ñíß¿ÖÁ,t/QÔŠ„Ÿ[#ç­Û·wm3V%¬zâd7…ý,ð3Qºf8¿ÑÖÖuïŒ#Gnµ@‚þˆ•ÿxƉ{Æ\aÒqžÑÚléº^â‹M3މâØêDljÖI”$$‰ÆbULXÐISÔj *õ:ƒe×äÖX JÄÆhHŒAIA)PX ‰¶ÄÚY‹¤€¥© S KAIŠI¥™0T °€0X i&šjÓƒ »š‹1lËþøÔsÉ‘X“'ŠÿÜ, +®¤³•›JéDŒ}Â?ÿŽáÛ73|û‚OмúhçDØ´yó1s™4퓹Ùý⛋B‘Ä1Õjµk{OâÓI×\;fåšÜùýÿ"SëNoç $ñ)«”³8¸WŠêNàó$Ý7=9öq¡þ xõTMðŒB›W×kC¢u¼oâp$Ke+ e^}}»Ý°iSJQ,*)´µÂºï¾%餤`Se¥‚Ê$´`¸ÈpAQ ÄkC %Ã¥Hkbmˆ´A A!`¡’ÔcN)åP¡­¥‘†ŠŽà¤ÇŒ+ôCÐÁê‹Ñ«{¸¸X\|ÍÆÝkZ ¿ßi0ðÿ€‡Ná»ÇÇà¼ð”ÿ'lû61GæšÍnë¼LàB‚ l;e›Äg²à‹ MÈúõ~ s‘ˆòõi…–-À?•“;ÉÈVE”ÊeŒ1'QaoWe§› Ö˜DÇÍÁF½A£Ù@§9¶J"¤3C%Æ5ùq‡J@=6L7¤€RВЦš -‡4C­™dýB% ¥ÀXçT‹õÉül³óÙâ‚^ðÏ—thlj 9z›Å#ý ‹÷³d”ÂA»V)Û%@#½x¥çI|Î.“š±#ȉ¤~.‹Ž÷ÚðF }‘ˆ"rìcé«Ïs–D¶[k‘RQ(À÷«mAó³Ãw*IlÝJq((AA ´µÔ›)¡O# ¼„f-„ÊÝ"‹¡bºc- ŠJ’øõ'³==é°YÔtËb”ø:ñF·Ÿ» DAÿXßã¸E!%RÊÑåœéka²îÇy_†Œ-›sæÆ"ÅÕ6§êÊÑÛÓÜÆpmti_k¥Wõ\˜ˆ”aÓ@_‹ÖÅb‘cz¤Ü¨þ¯[vCÅ‘:*J4°(XÓLá œ-öÍ¿#­³Œ‚’ÄÆ*ÏäÝ=õèˆ.\Ü)k)®È›oDù-ˆò[vž–YÍ##ÃsÎÜ8Þ°)87suJÀ ôTæáá%ˆá›Áå5~á4ÎmÁ`¬Å…Uº¾´¦ˆf³A¥Z! ‚0dhpˆÁ¡Az;IÚU]ka¬èz±áµ-¼øª«‚¤„@J'ÕÕcíÉíŒ@.qãXnœEw3OÍ7¢à¤=‡ûn+ÿ9Š™£ÿÎn\ H›©¹±ô“ø¯ã?s–"•J"…rê­§µ9{D*׈4B^´<œµZ‰‰ÃLLô(OØáh( h_u¬«Å ëëBgîº3Câ³’CÈÑ[Óâº?æÀzn §Äg­qÁ‚ ¥¦ZZcÏCL= p*·…ˆ¡÷ +ÖáËvŸM0ÆfY ÖXd BÒe„"³£¤!Òg&öF×o·¨o¤ŽˆCD€·uº˜¾NXŸ=œ‹a^ìÄ· XêHÏ Â‹÷€³SÿëÓ:³Nø`äôù‚Ÿ£‡°Þ“øœ§ìØ.ÿ˜ÆmN#ñÙä¨aäè©RóÎÂ6|©J(ïµÂW-IMÃãSS)MãË3e`´otç8½î¬–„éãµa,¸B¦Ò¥~içT‘8Rׯ2>Xh,]­¹Ø‰/g³ˆ±Lƒyè–=°¹ëÿx:ñŒµ³rKo‰ÏK{v+ñœf‰Ï¹ýu¹ä£ ËW®êÙÓ1™BKÕõÒžtYûÎ ïÉ/µó œ áHPZ™å©ö¼+Œª‡ÑpàÈÍ_4Öy„ta1Ry³œÁåpZ'Í•CÅDÕÅr%ÖP$D~ ê5›çòÅnãskÚL#ß(®×#úœÎIõD›Æ·Pç°³j¬3H|Ö?zdŒeÿ¾ýLž X(úЖÔìn•zH–,鄜1þé!¾õré'AöÿñtLäTÀ¥~N *![Ù ¹ d¼]N ‰ðÒ^&e íNí±jO*ñ¥¼«­% d*%=©i+š†Ìh#D­FJ?–õŸÇMQ)PÖ ©³ªº­¸Ø%¾€ï.- pÙJ‹®—®ÍIc3]ÿ•J•½ûö7›K%'æ´m4õj¥ËÆ)„…®ã%ñõ´Édß Œ¹k÷nŽäê+ßÀáCi4š4›M¢f“(Šh6šl½í3lZù.ŽéÎò±6/Užâû;àoÅ&°¸³¸®±Nr’Vvk,iÿZ‘J‚ÆørPÆ7évcÈ™¬w^%ô?ãqŸð¨±­Ђ T謤SES©Õâé4Qݺü\%r®JÈ6B ¥$T’¡B€±0ÕH¨FšZ”éžk0Wz>•#5¶„Ž‚<ü+àéÓ<§`süÒýÝk­Ù¾};£Cƒ,ãè‘#4Sމ"¢(bÿ²ËÙ~÷áõ]ûѺÇOcí¬Ö[ÕÅ×½šá·(—‰ Œ¨0`tt´kâoªìàpu?###½ÏàÇ>MÄgqÊ÷sÚôÐÙýîFX¤´¾rJ¯@¾Ì¸‘Á¸È®¬SN5QB%‹ËÍÅÛælfæµ³Ó%^m½Ò«àEåT% Òk¡j‘F A1Xbc³ *åì¡bÀTCSiºJ,…@R êqB5r¹£Cƺùå¾µœ)`±K|)΀yæ¹¥ûú—R²bù¥Ô›MŒ6Š…¿¸c.°¼¾½jÓÓÝwgã/8–sc¶p åò¼37N³s㜂ð‰ùƤP^,ë  ºÿ} ›'®$‰{ÿLâÐT#B _5× —HŽ@G4üQN=qÁÔÞ!a\ñ‹‘RH-rd9í«¨•$Ñ`%D‰;¹ Êv¤'…Ë޵A…‹åP¥É²¡"(AA)걦ÒLPÂ9@ ‡ë1ËòÞÝtbg¡œHM]éóžûab¤F'ɬ™½˜¿ŽáÜXRr¢n—*ÑÇÉEöÕ*_´ÓK¾g7´%Çx#H zZŸÞ&²Àæv¤¹¾îÿ#!$i••)å)pýÔ¬pΉ81¾&^B)T4bM9TL&±¿±®(’$Zy9=%%Ƹ’U±¶R0\ iÄ ÍD“wlb,Ë‹˜n’x•YàrŒ]6I×zÏ…ðÑñ´Á\œ' cg•›Uâ[8é¾åjžMíãäÀø ]Ê{œ*jŒÍ* R‚Î ìÄ[•Û'Û!1jãÔUðE,pUY´5+½ÖÙ›ÖR.(¾ •ãÉMù¸:7'§ªWÁ¥™dv¹¡‚+"ZµW…}—XSk&>:_ºJ%† àUöVø"RŠ,‚Áv°\Kà³Ð—øN"f.>9§ÈI|=N1ƒÄ—:7Ž”2ãf=´ªèá>ÄÅû{½}ÏõB‘Ö)îì™~&òf8`0”D¸ÂÅ@iK-Ö®ON½M³$ï1ÞáH7~¨$5/Î – ˜¨FL5åâñ„€z¤©4úHáKÅ;#PÜá¡ÍÛ»¿G¬Ö¦±|yæ³¢ãŠé/Ô“„c97NòÎ^AÒ½‹X{Lãà‰¢åÜX°Sôᑆy(!³×>’/SaDç<ºJN…Ô-5=µ¹<_pÖD*7“ÅÍYK)pDiK’¸£¥À…¡ k"m)+ã2Y‹X:PÀb ”`°%šFìsê}VR’T¯µ#\1ã{©L—’p÷wçðâË/õWêIÃìΓsŠy87\ó–,)eÉÆî«º †ì‹Mmt–ÖÅžÆÐµ:›‰ÌQ« PJ†AU·»î@èRÃ"m¨Ç%¡"J ‰ßHA© 2+J=qÍ ‹¾wB!T¢V|§‰#·Œ¼rÎèV|_ï€û4Y%ê•ÉM«1COÒNÕµmße'›¿þRâ;^ç†ÍZº- ñ¹(ú¾ª{ªà.]ë¥0‹2»à[ÎŒüþ­íÌ•µõë=pëÉH)`ÏÑ:’ Cc¨ÇÎd"„óÔjc2©-µÃaA9µXfûC§D×=IªƒŠž©gRz¾mþnð4rÅ"2U9÷‰ò xýEz2a[×ÿ‚)8Þ8>'ñ¹G'ÞúÖ·Òh4yå•W¸å–[xî×Ï191Á}÷}–‰ÉÃD&ýì1jµ*wÞyRHvîÚÅc=Òu?¿>Vë4@ŒE®(©Ö“@ÈVè†SQ[­{õHpup;1>X¤ÚLh$.^Αš«÷,… öü’K¹œ-ÎõfnÓÙàÒpœÎ÷Z¹ÇÌ"ñ9_Çv™ö|X4Y.sÏOÚ'¾“‰Ô±–>ïÄ?»ûn¾ý­oqþùç³jÕ*~ñ‹§yÏ{ÞÃo¸’ZµÎæÍ¯òì³k¹þm×sÃÛo R©ðøã?cÏž½Ù3ñW )zÜÍf“øŠ…"…¢‹p/ ø;2”Jeþþ;ßaïÞ½\yÕ•ŒŒŒ0<<Ì#>Ê¡ƒPÕ,³ñ{0Ÿµ¶6ãŒû˜+Z_¬'Ÿ/áìö>"4/Ö8»YË‘a}˜‡²Ëþe;^¤ã|¹ølÑ G-icðvòòê¬wn f 7Z^V7÷Dù1]º[·OJ—w ­mÊ•R¸rö‘6Ÿ¯»t Ÿ¹A&!÷oÏó‡µt_Ë6ýw32ì!*¥(—Ê”J%^X·Ž‡þ!ïxÇ;xó5oâÅ—^âÉ'Ÿ¤P,vœbV­ÕHdwŽj¾On÷œmV®Ü0À•£¿ûîOqÙå+Ù¶udÍš5Üzë­\÷¶·A~ÌyÒëµZõù^³íc~ÐIBµR¡^«øïÛdD•©¼ÞYàâ÷dö:å».¯nÍ⥭tõêý¢¤ÌjÀ˜œÉ,Óñ—+2ɬeãs‹¸ýæ'rŸ£µðd¬+q¯a¸P‹5””…Ž8¥€r¨H´!PŠb©D†FJ‘¿S÷%¾y¢×µœõÏ0¶§Ä×âÑÆïºñF>þñóÊz×6äÑGã‚ó/à£ýccKÛ™%ûL"Ž:Ñë÷užØØÞêõçŸwÅb‘ÁÁA¢¨ @Ç|ç;ßÎ"«/½ôR–_ºœï~÷»Üu×]œwÞùT}]gˆî«[­VÍÁƒNkû»³Æš*PA@X(´¤»6é‹,~/ géU¥mѶñ¢mÛ eê\°Þ~'rN/ÕYm—{o¯¬À–ÚøB…·ù9»žòǦä6Q2ϲ‚ÑÁ…B‘¡r‰B±ˆâ8"Žc1<:2ܨTSÁ¸O|óÄÁƒZ:>þ§ƒƒC­Ð€\8K/‰/IFGF£éùàÙµkyþùç³¼Üßùßeõê'ؼy×^{-ëžoq¬aæZ‰Ö¯öï¿;,¶ŒŒd¹:³¥¬½öÚë,[vøÀؼi3•J€m[·µí·{÷nV­º‚;ﺓ;¶³{O{5÷VÊJë“[k©T¦ÿÜ}÷-®Žõg&fUϬµèD;„_’ÂW`6Æ Uª»÷:«³tø5ÚUæ4w;ç™Õ¢ÓΖ#Fۦ潇SUe¦ÚB ]6†Õ®À¨R>™ÔúhÑJ¡DIéd) ”ŠIœ˜„F³OO‘q+'$Z ¬ÍgÅ÷UÝyâs÷Ý·ã~òÓÇoMÕ§QÎ\¤äé§Ÿâ·nº‰Z­Á3Ϭ`rb‚©©é¶ýÖ¬ù%7ÝtÆžzruû ¦w=¾é©©øÐÁŸ þðÿ`ï?üä'ŸSJ}ehhXÁìÎ(ŠX½zuWžÜÃ?z¸m?k-«W¯Îåï¶O¢åÜpµÖ«È: Ô8¢ ü, !¥BH¹X \( ¤DkM`4FÇzzªNÇ*iF~mÚðÜÖ—øN/¬{þ#7ÜðÎKÇÇßÇvn¼öÚëlÚ´9ë²ð‹/v•ŸÚ±c'Û¶mÏ´ÌRc²hdqd‚ ;½{·9rägºý¶ÏÞ~ëOì¤}œ¤”  £T€”Ò‘ Imxy]׿þgFÞ¢²b:ösNákëáín2+0P\êY9DÚÅâIÑ!’bAŠr2>¢¤tö> ÖZS¯ULÇAǘDc­UÖvLd&d×ÍS™eQâH€+òÓŸ~9Š¢FÍø)E¡3„h>èÉ/"­èa!dïžÝ»¬ÓG޾ûÎ}ìÑ)j>øàƒÂ{k©T\U*—ëåR)‚4Ìý×¾nz]yõ5•ê¼ÇÎZ´6bzzj0I´Þµk×#_ÿúCgCW³E…«V]±I)u•R ¸@d÷PAî¹R]¯ƒ ó½Üñ~¬çõìÎz­¾<=ŸœµäÀ´³ËXðeÞ‘¶øÂÉ®¸©°Y¡P‹ómI#ÖXkY6Td²#„ Œ®E)¥PQ ÂPH—{ìmÒÖh£“$’(BkÝ2lÛ®UÚ-¢Ëo=|eýÆ zB?HþäOþíEãããVJª‘ÑѪ¢ÅB6÷KurIÛËv™µ­jBÖZF¡Þ¨—ëÓõ×GàÚ -§¹^ „«V]ñªRêê#¾gŸÝY¯×rÄ' .ä`¥™…¡@ZÁÒ²ñ´(BP œ½® $µØ©,ç h&†@*Båó…å!ÖkL’h•DM’D·ÛsæJtÙ¾n¿ÁÁ¸V­®ÖÏk×oÜð›øúX¤8WŠ÷q¢È²<ü ôh=Î…¯ø|] ¡«¦’äíÆ>Š9‚P:/l© …@©€ÁP¦!6Ú$Zŵ¦«Í¦µ ¯wσ謵ıÞÅÉÁ8Š¥…ðüÝ­_úÒ—n;±/¦3}âëc^ðV Zn]Gu‰iW¥7ð¹„R hõÑ(„ >#cŒQ‰$ŽSÌÚ®Ðó°ÓÅuÑ[û–믻lû¶íÿ|ýú )\”î´{÷îOÿ7ÑÇ™ˆ>ñ½øÀ¿þÉB žë«ç_;¤[ „%” ŒE×k”… , ÃáÊbY'yÅ¢Y©¦Í§z7À:–¥ýíÄb_Æ©¬©ÚºiýÆ @1R(nVJ­06I½Ê•üà/÷Ñlj>ñ¥Ø¼å7?~|íßtðiÿX>ûQÇáœþg,Ä͈²Ž°ÆË%Š¥RJ¤”Ö:ŠE³Ñĺòá3Ç ŸCb§Å¦$·øõúfL{´ÖNßsÁ,öi­ ï{î¹çÎ,}ôç ¾øÅ/þÖ ë^üñÞ={†‡†eÁ‰87~ýì³ÛkÕÊeéøR¹ž…b¥B›Ä±HItWœUæn§«bùUžèÖoܰçD¾›>Î=ô%¾sO?ýôžËV\¶dÇÎjÝ /°dÉ(^xãKÇç9bûÓZ—TÕ›Þc;‹4×6Â1‡7b‡Õ“Ý+ë7nèKf}œúÄwŽà—¿üå–ïAð‰ /ºpr``àÉ(Žžššžzillì½8Uøªã7± ÏÖzIDAT«˜¢uÖÛcö}g%ºýÀÚÑýjýÆ Ó³ÐGóA_Õ=Çpùå—_¸uëÖ}½Þûȇïx·RêÞ PŸT*›]Õ]»­Z©¬œõd³«¯ àyrD÷òÆW¶ŸÈg룹¢O|}tá®;ï**Ü¡”ºWêƒJÁœˆof¢³Àk佬–—^ÞøJ¼à¦>z O|}ÌŠ{îùôJw+¥îUJ]—#¾­ÕéÊå3Ý$-’[ ¬}yÃ+‡OéÄûècô‰¯9ãÿø3×ax¿â²§V?±´V­^oŒ5Ƙ ­ÍÔ›ßrÍÚåË/ùÅ×þöo¿|ºçÚG³¡g°h}ôÂW¿ú•Ï[¶ô«#ÃGwíÙ5¸ïÀ~: M:ïðÑÉ+.¾ä¢«—¯XÑîcÑ£O|}|ðÁg–.]úM­¢¸Ùöx饗îàÖœî9öÑDZÐWuû˜–/_þ8ð~@ûG\¹sçΧub}ô1üK“_‘ yIEND®B`‚nut-2.7.4/docs/images/note.png0000644000175000017500000000467612640443572013147 00000000000000‰PNG  IHDR00Wù‡ …IDAThí™YlœWÇçÜ{¿ï›ñš'^â’4i I I´MKÓB[,ˆØá PX$P„Ä RA< E<€Ô"¨AE ÑJ‚ )…¤%±Óbgq¶Iܱ=žï¾™ÉÒfqc(ˆéhüÍŒï=ÿsþg¹wÄÌø_}¹ ¸\yÀË-ÿßFFFlxxØzzz¬§§Ç†‡‡mddä?Zä¥V¡Í›7Ûè}÷°yÓí”&‘Ÿ8Ʊé)¾>^aÕ‡>Æ–-[dm}QyI±ÑûîaË[ÞD:þ ³{ž&˜`ñÔ_Z–2zß=Üÿýÿ‘H¼¤ ÛÝËÛ(UŽ1û·hp8¯ˆWœާí|Á­à¡‡*6ù·Eã%E`ÇŽteõ¿ï"”¡Y É!KèZÆŽ;ˆ1` YPËâçóe1¡T*‘'”êuŠ Šú":8ÈÌÌxÿ‚åMDœsg}$ iš¶þ§^¯S«Õ8uêÔ£7_œ8ôÞõ¾»8:y˜%Y( õ×½ˆ íí%~û‹ï°hQ'í%²,EUi±IÎQD R˜Á²«îÄÌìBœï=µ“»x÷Wð•ïlå«kºðåmpß,'ï½’ooÛÉÉÊI¦?†ŸÍÐŽ1 8' ç0Wh‚h†H(`¤iJŒñÑ:Sæ•Ex•·ß~-í7¯å³»+LÖº¤÷úoäó#25º‹h‘Ú\NŒÇH3¢ †Ã4;­@R4—!š¶\Цù!q¨OùÄGÞ‡ŸÜÇ]OŒ3ºíà÷ÜÐ×ͦr•=dz¥L=_£³##š’GÅá)'DÄ74 ÚÖR ¡âÞû…@š¦¨xœOpÞ¸æ5ClÙü>÷£ÇÙý§ý˜A=ܶaíåŒÙZdfÎ8õü!Mp Î+NFÞZW4\œ+ü*†sŽ‹UßySH½G4 .ÒŒ×^½ŒÏ|üN¦NMñàÖÝì;LGGFðU¡˜:¦g u‰"ª¸sKlËøÍ0.¥ÿÍ €shBðB’ÎQ*•èì˜EŸsãj^·fzÝX´¸ƒ$ ¯fÂ\Q ô¢;+4«”EâÉ¿`‡s…‚´•3ò¼„S¡\ÊXºd1õzDD)•‹æd(õºàTIÒpN\h “°á¢üŸ7€#˜Bð IÈó³2ipÌÕëDEQçH‚Ç©’f"Š:OŒJPä…ágI­0ÁÐ…P­VQUTç„Ä'Xš‚å¤Á“ç9 ˆF„àI’@š&@£[;‡ )¦ Hùôâ–7˜Ÿ!ä- -(€#ˆ Î‘G!$‹')1òQUœ:b4P-òFÁ{ŽWªL<Îuë×c{÷íçØ‰)Þ°n-‰KQ ì’Ì«¨*¢EµpÁã4áwOŽMùÛÓŸ¡½«ŸrgÜu„o}ï7ìÜýÎ'ì?x’í;ÆydÛSìûÇ!D¾ûý_òÜþctwvòÝûD´0½ht/¡óÐÙQFÔ£>Åû€ªçèñ*?}èÏ™W¼˜,]ÒMww{ëyQwcÏN°áú5-yÞð¤ÍQ›àÁ‡cýëVrݺ«yzÏ?™«Ïíç.¿pªÕê ©¾LßÒææ~Po7=‹;ùÉÏçª+yjÏ?y×;n-6óŽé™YNMUY±|çkV/çÐ䩯 ÒЋÓg^*• W\чHÀ¹„<ÏQW£gQ™O|ꊉqãÍëáÍ7®ãð‘ ·Ü´ž,KQ–.éañÄ1D”÷¿ûNùÕüyç>bžsÍ5W3Ðß* ñÒètÉ&&&xÕ¢®F 8çˆéèhÇâ4fEq$iËz‹SV£/dY‰›oX(!À¦á›ŠÅE*RÌääy~Ñyè’ŒŽŽ2Ðß[ߘçsÀ¥Á¢jRŒªŠáq Mê Æ Á#háÀDv˜£··«8³¶šMÑò-ÏÑ"¹A³ú©Jh–ÈâTÇ™5^<¨ÎÆ$j\ð Ó”K.£Ï>û W !FÀ8ý·(fJŒŽ<*y.…Æâ=³Â8).M•Æ:F)ZJ’dalݺնmû5·Ýv; ×›£®ÑˆH1©šiC¥ådkÎ 4¾+ŠYliAC‡4EACãÅå’(411ÁÞsåRÊ\õ(&ÖÈ3¡Ù|Šûžˆª5Œ*($R|ÖœóÅTQ-½IT³†cŠY貎”Í;™;wòŽ· Sp,DÈ4=+R$¬§˜™´ñÞéd´¢DDæÎÚKEÀQ f9Õjõò4ff÷Þ{/_üü]Ô¦`9xp’¼^㯻ö0yä8Ý=†ÅHG{]]m¬\ÞÏ5kV!ƒ½EŒˆ‘X?kŸ¨µ9ÌŒçd÷®1*•Ê‚(NU–óãŸ>Šå9ÿr;ËVÒÙÑN[çbV\µˆ¾¾>¦§§9yªÂø¤ñð7ÆÔ‰Ã”Ëím ôqõª!®]{}}½˜>ÔO8ÆÁÉ£ŒŽóñŒäàä1î¾ûîV4Ï'¼63‹1288H½^GDذaÃÃÃôõõÑÞÞŽˆ7vµªJ<Ï™EU©V«lß¾¶¶6öîÝË<€ª²|ù«[û8çéïïgõêÕ¬ZµŠµk×ÒÛÛK–e Q*•Λѽ^¯×ëV¯×™žžfff†Z­FŒ±Åëf¢5Ÿ›œ÷Þãœk½6sÄ̘™™9ëâ×̨×ë­C¼sŽ$IH’„R©„÷þòïFUµåáÂk…qÍÛæ¦6C~æó¹w›Þû³¨!"Ä[£CS“$¹< ý/Èÿ÷¯”ÿ ò €—[^ðrË¿Ýë¹ðdvIEND®B`‚nut-2.7.4/docs/images/hostedby.png0000644000175000017500000000764612640443572014023 00000000000000‰PNG  IHDRv&)oæsRGB®ÎébKGDeR|ž„‹ pHYs  šœtIMEÜ Ôíð!tEXtCommentCreated with GIMPWIDATxÚíœy\éûÇß“tÒX"„(i™ÈZaš&MÄägf~È a¬?f0#[c²~åK„±B´Èh”ÉÒ‘ìM‹¦l¡i9–ó<¿?Ž%ÕwfL™WŸ×ë~ó<÷}_÷ç¹®ûº—ëÜÏÑ¡är¹H Þ:x{{ë¼|­[Ú ãǯÑÒÛ ñËår±ÿ;¨¤¦£ÿ;!•ËåbU ¿2 ?MCãÔѯREí Qã%îGEÄ’šüSs“·Â ¶¶¶4kÖlA•zì“ÇÏÚw†'Ÿý-òÆõaÕ¼mΰûÎpçzò+÷£#b‰Žˆ­v|öìÎÎÎüñÇeæëV’y¹*­ÇØ;t¡Qãú(sU„Ÿˆ ¡q}z:v)áIOg"3¨MOÇ®(bn–ú ý¸y:¾RnÀ žÚú/˶y¯}…o_OâÎõd­œŒG™DŸÅÒÚŒvÖæZ¯75kŠÕÕ׃pqq&77‡zõêU_Ãîô?@N–EÌM|VN$ãQ&KfùРa=R“îsãjc§}LØñ(öo¦µ9i©1¨#ÓzUN–’ }gpótdËšÄDÆiË]¹ÅŒEcP檘;i59YJÚY›|¡\~i©‘¯Ü«å‘œÆØi|AËWs“ }g=Éãê*%%g<=ÍyöÌòµåªÅâ©G_ü¶ÏÁÔ܄ԤûÙJN–’éóFã³r"ž^.ÄDÆ¡ˆ¹IrB†u °wìÂ7Ë'ÐÓ± 3 µ9›ù’ñ(“˜È8܆;1cцü€ÛדHMº"æ&9YJ<½\´yå!'KÉ7Ë'à³r"v}:GÆ£L]zštŸŒG™\ŠŠÇ°®A‰QåïÆÝ»wqrêË÷ß÷#!á1^^^ÕÛ°í4CáËCØO³157Ñ.ZŠ?S“àìÞ›æ¦MØp˜%³üµÃpéù»xî?Ô‡€ ‡5C°RœǙÚN`ùüóu0¬k Ì,škåÛ;hŒ¨ˆ¹Á« tèlñÆttíÚ5œœú²f3¶¶-¸ySÀÆÆ¦z¶,X¾kNjÒ}ÂŽG¡ÌUzI;*•*FMò`‰ÿ ÂG¿˜;•*R“îÓÐX³ºµëӉ͇|Yâ?Ÿ•igmŽ©YSNý€3'¢+ôبˆX”¹*¢Ï*4ÍÌ„Fëc×§û·““¥ÄÙ½÷ÑÅÅ‹0À‰ \qs³fÓ¦(FŽUnÝêjØþ®ö܉Obÿö`öoÀÓËSsVÍÛÆíëI/ʲ×zujÒ}|gndó!_FOò `Ãab"ã´:cÑl쬴CjLd\…[úÜyJëõž^.ÚíY‡ÎÄDÆaX×àl‰Î;‡»»Û¶ Ãݽ#j¶lù…"°üŠUuÊÍÉoÅ'й9y¢(Šâã‡ÏÄ[ñ‰%ÊÜŠOc½!>~ø¬Äý{‰éâ­øDñ^bú+ò^¾÷øá31öׯÈ-–]|ÿV|â+m·s/1½LÙ·âÅРóâ8¹bhÐù¿]?§OŸ ă½DQ\#Šâ10ð3qÈ!GŸj‰û¶ÇyÌWúlÕvο !!!¢LÜ»{„X”¿TT®µŸøÞ{-ÅcÇŽUhX]µ ÔÄàþ†Žrfè(gíõߥÇððp<†¸³y½3C\Û äç¡#Ñ%ær:))*Þwv®°- € ˆU’t¥Rmª­§Gccc|ùRPPXeœ^N>>>4mÒ„ÆÆÆ„„„ü#mFDD0Ôc0VôÃÃÕ¡ õó´ný/|òé§H$ÒrehOUé±=zô`®Ï<²³³Ù½{ . µE<=‡W¹G®öóÃܼ5‹—,¡³M—7®§ÈÈH<»³Ú·;õD]‡ŽŽ‰”‡*½Á…˜ÀJñ¶*@ÃFpprÂÍݵë7¦-³bùrÞ³íJ+ÓL2…‹/Ò«§==ºÛ¡Ξ=‹¬¶ãÇC-øùù!«­Çõ7ÈÊÎfõêÕté܉žö=X»v-YÙÙ¨Ym=¹ºbmeEGë•à&«­‡J¥âæÍx L½úõËäRY•I …‚¡CÜY9φ][ ÎÏCÈWj=vó¶+ˆ¢ÿþìØ@üõë©Õ¯Õk•V­ÈU*ù=íwvîØ€yëÖ¨E 0ž]lmYèû—/_æƒ÷âàèÈU…‚´´4BCC8qµ pòÄq¬:t ­¥%ÛYºø;¼'Nd´×–-YÌ®]/zü™ð0&O›Êâ¥ËKéáHÐqÍ>¸{wŽ-—'OŸV(«¢”•ͨ‘Ÿ±tV<]L4ÍÏCý< yŒòlÃöuê^d×¶…8ô¶Ã¢µ9Ë—-+S¯ºÅÊ­*œ >I“†FÚë~ŽŒóþµZ`ÏîÝ´23cíúÿðnÇŽôéiÏ“Œ'šº§NFg®*\ºx‰ ÑÑ|5k¶¦þ®]dgg3}Ê­üCâ5f¬æ'®nÝ5z eé o?͈Ұ}û90iâ„2¹lݲ¥BYaúÔ)ܹ}‡ïý ü®ÖõèjÝ€Îê!“é¡#Ñ¥iƒZ¸ 0ÁýƒVHjÕFY ÁqÐ>rrrÊl¯ÊçXûž=™¿È}}FFF˜4k¦]aÖÒ­… ¾è…EÏÀÌÜœŽ:ñãî]Ä]½J@àn&9Ÿoç V«qsŒZÈÎÊÆÌÌÿ~ 33###­¼ Œ*|v±\.ÅçP*#ëu˜ðåd7æðÁüt:ŸN§ðN]®èGC}$‚QQD•¯føøp[1ë›oËlWRL²*@ÝzõéÜÅ–vV0nÒ´D¾Ë 7R’“™>e ?îÙÃÌéÓ0¨S‡ÿ;W7w.DG#•JéÕ§/}úö#êüyÚµ·¢µE[ŠÔº¹‘œœDXh(±± >îÉÁ´mK¤Ò ù‰"r©Œ¬òR;«Ì¿Ëq×ùõŠ‚í»icaÁàþ Ñ—"ª4© Ÿ<¥ŠãCHL•â¿y+":eò~>««ÌcEQ|mûsæú “étôÁ'ãâ:ˆEK–bhøIJžtµí†¾LF?G'NcÐGiåM›ñm-Û±{çðñð|=ûm~ym—æW—Êʪ š·0E__Fê½{xͲ@] B"¨A(*5ë·Sô8tô( ŒŒ^Û¦Ž\.Ý=¨AõÁÆukùå¸{—´BGZ I-=Ôèñùü;\O®Í¾ŸŽaÚ²åkëŸ>zXã±EêšÈSu ìÚÀüO 4Þª«¦¨HÀ{iq‰úì9t“æ-*´™.@~Aaµy°u«V°Ï.A`åºôìÓ÷­0ˆµy ¾Ï†Í¯ž¹š4~ ¡!\Oú½B9gBC¤ãØÉ¡@…ºHàËÕ÷¹’ cç¾ý4mÖ¼RöÒ¶°ú6à9ÍM[2õ«ÙX´·ªVÜ*ö6±L¾Åa¾Ê<ËîÛùÔQŠX ¢P-2mc&W’ë²9p7M[˜VZÏ=¶è>ð“ŒÇ|ï;Ÿ8ÅtСSW[æ,XLÝR±ìßm @bÂ]¦z@÷^½I»w‰„½A§ÉW©8r`/Ç@_¦³ëG¸ŽÌÀ€<¥òµyÅó?j5Žråâ¯|éõƒ† ã[ß¥ì ØÊú•Ëøñh0fm,عe!'óôIN?ÀuðP¬Þíˆý»m_áU<Œæñ4#ƒÀ­›9Ìû~DÖó“„é95%™«—~aÝP E…|%ÏGqÏÿ€@LZ´ü¯ìôxì±ÃIIJbÒÌ9$ÿ–@à–MØõêÇ¥m+ý·0sÂX¬;ÛàõÅ$fNKLÔy¦3F›P¨VóÓÁ}lݸŽ/¦ÍDG"A¾f:R)ƒ=G”›§ <ôˆ|­éééDEþ¬9¡p!ŠüÂBÎE„aÞÆ“–­ð_»Š?lâÃÁCéÔÅ–£örâÈa„hNS–æ ˆÝ¸½Û3q2‚Z öRL¥<öÀž@÷ÒÃð)_o)àjz3ÖnÝF£¦&ÿµt ‹ÞìvÇã“‘´±lO쥮]ÑqyžþJ»»u¾·m ýÞÞº#.ƒ‡QÌóÔ±#(ssñ[¼ðżt:WÏróŠÑËaòµ~œ?w–˜¨ó´mß»·nw•kŠX>ûÜ[ÓNÐQš6oÁôohœm-ñþdGöï-“Wñ[X¤æç°ÚX¶c„—æ ~ø©“¤¦$•«g•*“Gb¿!³rˆ{hʪÍ[1jdü§ìóDž–ÌÅ•˜hÆMþ?lºÙ1{ÒøqͲâ=ÅyïÔ«W¢\nn&Í[ðõÂ¥ädgQ÷y™òòŠÑ¸YsÚX¶'äønßÄg™«|ç!_³A­¦·Ó@Ô‚€TW±TL›—bM¥y½Ì[*‘"Š/ô*‘H*ü½6<ø[±õ¤ÀµG­X±INý¿ÍúG³ÝŠCO¯6ú2§ƒ4gˆ *UW*•–¸îí8€ûi¿séÂynßdþŒÉD„W˜WB†Ó@â±H$RºØõÀæ½î\‹½L«Ö´4oý\V¤§±vé"ÂN±~ùwèËd¸ ö(“W ùý’üÛ]¶nXÍ®üIIú­Âç =qŒÄ´"â™±bãVê70úK:ÿG³Ÿ:ƒ][6ñŸï—ò»ÆMš’š’ô§d}6ö ZµnCð‘ƒÜOKc€ëGŒž0¹Â¼—ѧÿvlZUÇŽÈ èfß‹óaôí?ðÅ"ëËièëË8BÔÏôvèÏ„™³1|§n…Ý=G ÊË#äø1Ú¶·Âá}ΖÑÁ^FæÓ§´°´cÎw+¨chø—u®#—ËÅV6=j"ÿ"¤(.h&Œãþ»Œêíí­Só~ì¿Ú×Ú‹ß„®ñÜ·×SáÅ_è”.Póç"o'Jÿ¹Èÿ½2 :褀IEND®B`‚nut-2.7.4/docs/images/bizarre.png0000644000175000017500000013635512640443572013640 00000000000000‰PNG  IHDR.9.“Í`sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœì½y€$E™÷ÿ‰¼êìªê»{Žž{€KD‘E”uÕßz²®Çªïº^ì.Ë*¿Uwu×õvÕE×ÛWQåtD®n˜ƒ9zξ»ºë®ÊÌÈxÿȪêêîêžæ„î/ôdVDFddVÖ7Ÿç‰'žG(¥8„K/©”J !^ ¼])õ6!Ä'õ€RÀ·•R !âÀg€Ó€GËåÏôds˜Ãæph3<.¼”?wW–÷×mÀ=@p¯"|X |XSÞÎas˜ÃaÃ8BýlSJ}Mñ(p-° Æ'´8ðA t„Î5‡9Ìa–c¦ÄUOŸ¬-»Rñ°x Ø |ˆâK^Aàëø6‡9Ìa‡…™ªŠéò6ZÞš@®¦þàŸ€×ç)¥\àµødÕ ü5ð²­ls˜Ã 3•¸zñÉësBˆo¶ÖÔïUJýzB›‹7§6¾ú8‡ß½åûF[[Û»t]»„ d QJ¥Q¥Ôö_Èç“ÃÉÑO~âï Çí&Íá„ÄŒ‰k/=üæŽ;tû<(¥|†BªXͶZ7“cÆ—ÂóÀ•6…B‰R©D©d ]×ÑuÃ00 C™–© ÓT¦ax†a*Mh(OÓ4©ÀB¸š®Âšæ®@ØB£¤izN 2•ˆI!Ä0Œ*Oå=å夔)e¶X,ú²Ùl¶pã?Þ0÷Cxaޏf1~}ûo·ÙviåA ªL>Ó’× ©­sl›\¾@±'_(âºî8"3 C×Ñ ÃÐ1t¿LŸb_Ó†a¢é:†®¡ëš®£kB€ ”ÿÐ{þ0”çyž§”’€¸€«”rWÞ—Ëf?zí®8æ_Φőr‡˜Ã‹Êö¤œ†´fBPã˦$¯:ÇhºNC4J4¥ÒÃ.•(9RºHOá¸. …ªŒOÕì—Ëýñ) Ý@!ÀMóø¤e:†aC7„îïë>ᕉ±L–µûÅbñmO<ùÔ:`Õqøræ0 æˆk6CaKÏ«S*Á•žrñ\…@¡ ÍÐÐ…@×t´²$S5~Í„ØêÔME~¦eaZæ8SJO)<Ïé’X¥Ý¤6®nâ±SýQs,b±Ø’Ÿüìoºöš¾cÿÍa*Ì×,†Ç“^•NY²Á7¡ë˜ a…!R)\¯²UH®RHoÂVë)\²²×_=¬|®7ÖG¥¬Ò—Tà¸Ò¶qliÛH§€²K(ÛF%ŒJ”òÉPM}iQ€¡ MÃ2:`h¦&°t© ºF@׸rE «vúä†BˆÀ´w±W]uuHà¯ITö£Ñhëe—_~ÚªU«SB¨|o__È.Ùš'U^褅®¥•«RžP£$…ã$])‡ßòo¥¾ø¬†PJñÃþðÜDcÓMÍñùó:ÑuM„¦12’dûöìÙ³‡b±ˆ”þÛS–ß´“÷+oß ob)éììଳÎâü—O `ùä'=Òé4=}½  ÝyÕë^{Åñ¾)³·ýúö»GFF^Y±GÉ@˜u¯zm]ª»=„ã¦m;éM}\}"œrÓœ«n máÚ3W‹Åá—¿üÕçÛ´i?cD4‘˜j?×Õb\×A)Å;Þù.N_sÙl–ýï( hš†R MÓ@ðË!Є ¹µE…BáIW¥R£; ÅâŸûì¿ô…ÇâE㦛n2Î^îMMñòÌË8455sÞ¹-¬_¿žÔè¹\\>G6›!“É’N§ÉdÒŒŒ¤1 H4JCC±†b±±XœX¬¦¦F›šðä˜$V‹p8ŒÞÞqù¯nûõ·_ÕëÞ}¬nÀl†¦ë…Z‰ËuªMQ•ºÆýQ»UåýòVÕl™°­ôéóc~§éKÕ9×c«žcâ8Ê$‡Ð™Ö5tÊf:¥(JEoq̇vÓ“ÏRxî„€öŽN†?v¸÷Úu]FGF¸ëî»hmifppˆÞ>j¹sl¢²&㫬v©Dÿ† bݹ粨k±Èå ËK¥Ò7«w\/Vgžµîñx"~°u]£¥¥•ÖVКðgkü}Z;ÿÆð‰P”UL_½œv0†A4Úðöoû;ÿðîw¿«÷H]䦀R)ÿÇᡸž=Ž<*à“ÍD™Hõ¥>‰UûFµÐG…àÆHt*›Š8ý¶¶‚‚Ò°… ¤ 4M ëÑQMÑý¿›ÍGùÏ, Š…<†i{íoöïÝÇÖçŸÇ¶–._N>›ÇSéVl~Ò·zž-Òß& F†‡ikm£§§Ë´N?üA½xa˜¦uQ=IëP ”"NŽ„ fl¨‹H4ª'ßüýLÛ!Zð§¬7*¥N/ë²]äài¥ÔôŒ]¿} •RŽøàÊPß TyFq¢d5ö§Ê¤ ¦ ‘J¹ª!ƒJl•6ú84‰m‚äVÓ‡•ÄP ]Å(‚Q ÁÔhèBCÐ0 B\BP™´p\‰®ëGà^ûÌå:¹\¥<"¡‘pÄ? òT„BÔ– Áh2É3O=ÅðЦa¢iâ„xÎ4 ép:èéíåÑ›(–J òøObÛö îOA0:㎿Øüè-6<°¸ˆÌ´âµBˆ/”?Þ|øÈ«æ|ˆU{¤í¢öÕ# )Ô¾HlÓ©}ã÷&±©Ž ¤çaKá¢ËóéY©(z~]®Ò«öW×u0ŽqU$®ZÁ XXV @À -‚Á `P(D0$ !]˲Р¡Ð´âÁN)„¸ZñºšÏoBœ6Íñq!Äa‹—Bˆ…Bˆ¿9Ü~¦ƒ¦”zÁßJ6›cïž½,Y²„P(Dkk+ ,ࡇ9¼AùÑS !ÄBàËÀÇ•RøQ(^%„—ë/B|¤,½ „˜/„x³b±âo…Bˆ· !Þ'„0gPß"„x¯ðQÙ7…— !NB¼ª\fýÀ'€bùÜg !þOåÁBÄÊ}Lq†¢ ø(p¾b-ðcàWåcÛËc¸Za”Ë^%„8SqI¹.xÈ7Z0  :±â”œ:j_} hj•m:‰i%¶q’é ¤4­ÄVÛWÍ廞bgƦä)òžª–ÛBóÝ<<×qÑÃ÷RUæòM)þx§÷+«ÌÈ{ž$•Já8B€t=3’¸Þ üLqRùóß/«w ¢xˆöÅ àß@?Sb¦amêbÏ::ÚÇ• !°]—c´”h~ˆÿPJýP)õ ¥T^ñ àûø*äSBˆ×'·Ÿ®¶gÿ€+lºú‹ðCõè5ûà}ÀÏ Þt–ëCBˆ?*—=$„X‚èÍø1ÌÆÔˆ?;Õ¼øó2¡=\…mccùÚ¯/ŸóàŸyÒ™¦iƒBU‘¸»TGíOU¶q6ªê½iû#ÌzFöi ³¦© õˬ#ɸE9FiR‰²Ä%q]ãHWåÄšV¶)N$ª2IÕ–y ǃ” C—¢AjÒsA0SUѾ6±PqâgBˆw–_´ÁüùE!Ä'Êõ–â…ç !”÷ƒBˆW !~.„øW!Äòr7–ÛýÿùE¡ !¾ „øÀaßÀ 8,â’®;éA€1Õü "Ö*ÿ¼B«H$À{€›•RïþÿíþY!š^¥ÔG€ À’ƒÔׯL¾w÷)¥þx`±o¾¨”ú;`%°Ÿà~ ÌÃ'?Un»I)uWMÛWâ«ó¯Ã'¯UBˆÊ[óò9ï¯s΃B)5ŠÐŠc>w.¶ãÔµŒDÇP?¾É’Û¸¾™Ðg]«o¨¯ªÆ!åxkD1wœÄåW­ª8Î)Ö«#m•Ë2lˆŸÆÒ /ã]Ÿú4óÏŸCÍ[îK\Bä¦=ß¾¬B¼¹R „X|§\÷WÀ{!Þ8åòsñ_®×à?{‹ñŸßßÂú{áÉ_‰ÿ²½ƒ±ðU_űþÏ!Þ­ƒâ°^%]] yî¹-444TË”¡DeQëÑÆãøÏò€oâÇ ûG!Ä)ø±¥**gH–÷ J))„pñcO+LÌ ¾2gÆX‹J°Å“I­À˜~ p'pp7~B‘+ñ“(Æ›A(_Gÿ!‰àg•kIMs΃Âó¼´®‰¢”^H¡Ð\&K(ž˜^=Wv$ õLM@LØÖö=£~kÈwŠûu<„9vû\4*K¡ì#%q•·Ÿ-×qq]·l„§ºõ7þNÆ39™ml}p½H×¥5¨!¥{(ĵ_ºÿT¥´«ñoÑûñŸãËð¥vðŸÉþsùTùóÅøÚÈÏ‹€=J©/ ?·Äûñ à7åî¯À¯þG)UyNëiˆ6ÐÚÚÌÎ;Y´¨‹dr˜Þž^Î?ÿ܃7>PJíBÜ|]ñQü7ÂG”R!Ä¿ÿ%„¸¢\~cDöB±~ơݻÿ¾$„¸¦ñV|¢; øG|ÒjÄ'¤7!Þ^Óv>A?‰¯¢þP)µõH¼\×Miš^õž7 ƒt2I0–¨+½øDq(Fö™êǤ¬©%¶© ³¾¡^MÛT(HK»Ÿ®Ð«îòI\•Þ5Mà)å/ü6q³‡•ïT”™Ì7Âäî¢÷駘wæYH)aì¥:üð6ü¤5à?ÇIüþ•øË™*2§¥”êBlÁ×<®Æ7s¬Æ7—¬ !–'áGB~¦Ü¶òòŸ«_üVñe¥Ô¦CïAqد’åË—³t‰dx8I¢5Á²¥K•´€Rêf!ÄÏñßRJ –ËoBlÀ·!ýQ)å!@Å3ÿÇø*ÀÍøä18]½R*+„8£|®ø‘] À§{›ý¾¨}_LÎ)¥þ§¬ûŸ<\&ÖWçÏ Ëçþ,~*·§ñí^R)•B¼8èWJí(ŸçÆòy*ã›æ§YÅb1£ëºSq‡B#—K”Dæqh†úš>§•ØfN˜õ õÓÝÇó°UÄ%tߢ¬ºqMü-º1N-‡ª¼íïT>æ²Ùšö:RJ„3&.¥”+„x/¾zðkà<|ÂQøZ@?>™= ´âKWüø°¤Lh{ñÕ̧€àƒåߌ‘_ù´ê~!ÄWð õÎt¼3ÁYdmíØV-”R[JºR¾Ø[óyÿ‹@)µߢҾ‚ië'ôyyûlMýæš¾î¯)î«ùœ*¶¬žš6¿©scݤóLÿŒ1808ÚÚÖ*+(\Û©¯ö1AŠGˆnZ{ÓAˆ¨ž„Uíû Û4†úé Ýã<²"qù$1m{1Qbª·-w¡ëúdâªÖdô:&Ûe”dãR<ÝÀVz¥]¶~‹ÚëR—×ì?Äxs„–Rªê»$„è¬9þfü"ø’Y¥\]žprTy0J©‹j޹_; l×=☋1‹ñ7|_é‡?þIUâR \Û®oÏš1ÍÄPOý>jãš,±•MÝÇLDQ[Žå¢35Ã/›9ÕÙ"|‹U…—ôªÄåÕ™y¯a0ÛT§œÉg¯ë$Ϥøþ÷Xé÷°íFµ¤Uïó¡´=Ö˜#®Y×qE©TÄ(ÇØrÒ*ÛxÕnzUp¦jßtÛÌs&†ú™ –D¤6&q)|{”®ë3&©ºuåÃô‰KJ‰ò@ˆ²ZXö–Wh¡Z”‹Ø|?Ÿùùs¸%Ï.±öŒµ•ÞŽ¸ÁûÅ„’¸2z¨ë æÿéxc6àRÛíìÞ¹ !@€ž¶UdK´…u-Ý'„i õªQMïepC½wÜAÔÈ }L4Ô¿H­"qQž&glf±BNÓÔ$"«Q5uÃ'.!º¡×Ì(VúåÿŠùÅ‘dµ½¦ùÚ^è´ÁËnØpi× ºÀŠø¦˜Go¾èîjí’¸rÂêR‚÷8f¤aUf©p]—iÁ=6€!­!öN[È 5¤Ñ0h jÄ,}‚i‚A|Ÿªƒ/:r†útO„QõZ÷å!Q5ÐÏ„¤ênËÐu}œïÖÔðÏœËMðz~³µð¦vu/ZnØpé À—n¾ènï„$®9;(¡U‰ Àc¦ W)zó.½y(k§ Aƒ%h05¢¦FÔЈ‚°©Ò5B† ¨ûAú¦5ÔLb«#uÍÔPÿBá•ïIUâ‚qªâAIªŽ4V½¿e?® 1Ž}åõÕÆ’Ïç«ûš¦Í›³ !à‹À2à'$qżÂV„¸éxc6ÀRî¿IéÎ÷? ·4}ƒ2¤RŒ–£¥é@BP%2Kó#šÂ—ætÍLªk )¦ù¶j „‰’Óê'Úß^(ŠVái¨Î”ë†1s’ªsLå ÃôCJS6ÐOd+¥Ò“x®‹ç)Òé4š¦¡i~¬·ü’QEãƒÀÐ ¿Ê âø.J¯/~ß .ýÅ I\aÏúźЎ÷8fnÙ,?&¥œ_utŽl´¥ ïzä_@®kè—44ᇨ©›§ü覵*¤/!½‡¶ †åÇÂ/_)åGˆ¨Uûf*uU¶e»–Y&®ªDG L€††¦X¡Bhcáp\ÝÐñ¤#³Þòµ¯~ù ./|ó† —þ¾w¿àD•¸æpì  mT×4Ç÷eÕJ…ƒ;>#(ÀU€'vè…'49N™QªN¨‡@Vþ¦jn÷müšV6Î{Õ™JªfÅ(ïÃyî’Mgª*¥®Hé·E¤UÁçñƒ˜Àº9âšåð<5l&Å¢ÞI”r -QœhPšë¸hº?ƒ§”šVUô7c$U讀UUÑ_ éRÙ!bœÚ¨€B¡0Φ:®tÑ4íÄx³CÜ|ÑÝ¥6\º?dÎÂ9âšå}†iŒè Y³Îð;J˜¸R¢Wˆ¥,qMšQô?LOd© UΪ]Çs~ òãgõ²›†bÖWÕsޏf9LÃ<`cÄ¥ré9‰K•NPúY±'Ù¸BRu‰M)tM«³ägŠéD|‰«ºa }‰kv¿Y˜#®YH4ºÇ4Ç|¹d~ޏ”Ð) Ë-“¹§ªQPgDRuˆM)’Ah¢JZž§j}SkGJL".C×®D1»¿ æˆëDÁ%ÀµÀüàkÇL0 c8#./›F¸J;¬“/j(ÀÁDJßþ=IU„™K]å­§< Ý@þr¢JÆìIçVc³ŒÅz—'šöæh_Z˜#®ãkð}Tb§,>~¬Nîy^Ê <)¥  iD!‹ FÕNHØz ™{Ó0jÜfFVµÎ§RJ,Ó‚²êY‘5㎟˜tFÓ´J´Š9â:Þ˜åˆãÇ×½Ñ_¢Å_B3Ç–¸2@PUó®KÀÉR0CÇj'$\1f÷sӲƅ¸™Ž¤ê-ò¤D×5bš%?þü"0y©¾÷¾c;!œI•³ sÄu|q3Юò£2÷Cüuà§q?f°Kv*h¤'e5Z^Ð)P˜åv.G3‘Ò¶mŠDfDRõ¶²œàUÓüEÕ^YÅZ _—¸|5Sq\CÊœ˜#®ã‡3¿Æ+*oô#B?¬î1Ãhjt4zµë-åÎz½Ô̱Å玃iUÇ܇·)o+D¥iZM²Œ²ª¨(/œo¥Ÿè  iþ¬¢ðs ÌjÌ×ñ†ŸºLóÒw€L#‚++u;¦nväñžw½#÷¹Ïý›”®¬þv,åÍz_.§FU,•J˜¦‰çy3 QsŒëú&)½Æ¡uZ?.5Ù|Téz ÄA“Á¾Ô1G\ÇïÎQÎTfƒ_b´TꎩÄ NUâ`xs—ÔÇÛ¸tÃ@—ò $U—ÐÊFvM÷ã›ù)ÈðY×qà²íÒ¸ˆºŽô\4MŸ#®ã=€YˆàÿPÉSqcÄuL%.!„#Ë‹wÝsf=qyÂÀ+ßÛ.a²,9jx› ½UbzÕJ\ã”IJژÏ×_è®i¾Ä¥[ú‘] ÿ"ÄŒ‰+›Í2<4ŒíLžÐc«­Êágýð$•xFžç‰„™¿`!áÐìž­Â_,Ú¤r¡ìÝc¥cÄõnàµå}…ïÛõ-ü$²G𮹵ox]Η¬Qí’O\îLZSGúÒjˆ«lßš«…B¾n¹nèþ„°æˆkºJ¥;vìà©§ž"—Ëãy’JÖc)+Ù§.ûì—IéÑÖÚÊ5×ü+V¬8V×x"á|àíx9åÞ6^G0Z+{ë'´¹ø3`-~Û#MÓKãŒónÌÙº·§éãl\†aL^hÍÁ ó¢K |‰É4­ƒfù)‹X¦Ik[ñx Ë P(0tÏ·3Ód°/YLI\®ëòõo|ƒÖÖÖÊÍ:"èëïç+_ý‹ººøÐ‡?T÷M¸Av_²øˆôD€Ñ¦3ÿÖo#LáÞÞøgÏþ‚ÉùûDÓ›V×Éä7¼šŸ>C3ÍkÆ}Ášœõ“VxZq¾XBÓ4½"Œ#%]Ó|r¡ù~ZBÕDRJTy¢CÓ´ªÇ|8âÜs×ÓÑÞN8áùç·qÖº³‡Bô÷ðÈ£rÆégP,éëëã¾ûï'i¨Œiޏ¦ªøï[naÏž½´¶¶Nª“RR(–ÆMëŽý•׫&õ(O)Å–­[ùéOÊ®yäúx°ožê>Œë:ñº„‰*u£rO®wú&¯óÑÂhzP0øŸ¿:¬°žÓ 56áwàÌ, êK^9€í:ÄãqÌ––qÇ(¯­Tzã¶RŽ/³]ß¼"HW¢”bh4ÃÓO?Ãò)òùù|žßßuŽS"—+ u‹Û7<Œ£[­æÐ MM~?3È©øRG]âÚ²e+Ï>û¦iÖm$ÊoŸ¨Æ/­’—RH˜ÖVrï½÷qÙ«/;×qbCXÐø—€‡ù 3]Ѝ%®=™;A½(½–%±,kl‰ÉŽ‚ú¢„{ö¥ã€R†1‰¤FZÒ“8Õ¥;Â7›Há¥ç2˜Kâ˜!ìöì@¥`y-L>'§ôräX…%.øÙG •íÊ™g±~©¢.q=óÌô‰F­÷°Kš PÎCçBÕ]ú^…RŠGy„³Ï>{\¹ô£(õÈAGÿbµ$‚¹@ÙûQîÀ¸JB¹T„NEDÎ/]bøë ÔQ[Ÿ¦á­ ‡ÃmUâ*Õ7Ï&(1¦¶;ŽCÉ.ašÖŒÉªv[‰.+WJGÒzÎ+( ‘wžëa» ×õ°‹%J¶ƒ,f0 yÅ‹ž¾Kx˜¦UÒqÕ+Ü»wß´ü(B^yëÿçUþ å)?§Ý âmÞ²eq¥K­Ï²ì—OÑäÅ Ø&¬‹õ_W¡2÷âþrüÑÂBk|£¿¯ÅÞÄâ_Ýv4×Ïw¾¯¯سބ‚Òj‰Ëűœ€3ž¬<ï ¤%¥¬: R^þ#=E÷®R£ÈÔ ÞhÚІ÷ÑXÊû¶3]Ç ˜Xf€p8Äi¯¾œþ¾ÊÒÇü†œ`¨K\©ÔôIr ÝŠ}ËóM/ÊCVUE¯ª: ÃÃÃ/dÜ/6ØÀ•À§ñ—ú ˆ*§ÒÁZü Ð~UÒ°VO(+(Ì.Šð†¦G FÌzñâyÎû3%nĥʎ¦e#½òP^%3Éä)þ9p€rjTh|¾‘ö‹„B¡ÁPhLâòò™Éá ýn " IDATN h®^Ê {E„’i£»ôbÆ'¤|3;ˆ‘êG/Ž™ƒ*vÙÓN?ÞžœzÚid2 D£óXµz5=ø¾üå¤FS|ï»ß­¶ …ÃágBš)YU¶ Æâi)¥°m›á‘dÙçË÷®ŸÊñt2ÔÔ,;Kð‚ˆËWÝñîe»V­Ä…sÄU° /Þ˜t#"ç"KžþýX Æó¼t8®ºAzùQ4·ˆwŒcriNÓ΢)MI„,¡Ùy´R½0Š™Kbd1Óýhö¡M ¼öõWñêË.ç;ßú&_ùÆ7øê—þ“«ßð~ðAâ Hºw좷¯§Ú.sù•¯Å0ýÌ?3%­ZßÇÊ~-qÕC©Xdoë*ìS.$ôÜ}Ì~~ªË™“¸^H#Ó4ijjš±çü&a `ªZ5Q‹ %^þ{á½À1‹réy^:Žx”£°z…†,a âRz)GÀÍ¡k ]9hUBÊ çFÙ$Ff=Ýv”|ÈúxèÁ?±sçNþùÆy䡇øÓŸÀ.–ˆ%ÄqæÍ›GC"Nû¼y¬;ç\L˲,¬@»dÏHª÷¼{•p8šVõãš)%9¥qñ[®ç_ÎkçÝ··“ùÞMãbÕ y„oÏ‹s‹¬N FMÔWƒø.ð§c9ÇqRápX2>š€gOôIxÓNP6¦¦0<]–ÐìZ1ƒ–O"2IÄh?zz%mÐNÃ0H46ÑØÔHcc#Í--ž¦›âÍoýKaZ§®9Ó²Ðt@ @0Äó¼ºê c;3"­©P1›hÍV×ú(_ÚJÆðòV Ë49u^ êô«˜3ZÓa âR5G ³Æ(_¾}Ëõ‰K–!"ëÁ“³°ÍäóùT8'*Ä ½„ K9èn ÝÎ!Š)È%é!¼‘>H‚:¸D}¤¿iÓ´hll$ÑÔHSSMÍ-²©©É ƒ¶®ëE+`e ÃПlïèxÌqœéTzßÛÿò­#·ýúŽk=å}PJ¹^JRŽ—¨Á†5ÝbéêõWm\éz—&þzȘÛÇÏ÷äY·Xr÷㛉Ö#-àÖ[¿7çQ¯°Ö þh ÖU›=³Šµ(K\ý t´Æë*åÇþmú¶·¼yäæ›ÿeÿðíIÇ© Û£@ @¢±‘Ʀ&›hik¥©© Ó´Ðu=išÖ¨a#ÀŽP(ôdKkë“¶mH¥R{ßýηÏÈMàª×½ægÀÏ~úóÿûøˆ§¼ó]Ç ªá}&¨Ú¸t å¸ÔJZ³íÏcëÏ¿ÃÅ÷/f鞇êö5‹“ÁŽC}âòÊö£´6Î?Gùw2;‰k%€rû ¯³àà¿×€t]?ê6µ@(DSc#‰D#ÍMª¹¥E&Ò²EÃ4 š&‚š¦'‚-m­,\°X¬žžvïÞCw÷®WÝü™O?~$ÇôÆ7üÙÀ?øá/v÷£E¯ør×q£õÈJzòU—ÐjÂ5Õ»®[%·yÃÛ™7¼}ʾæˆËG]âò”ªþ-Túž¥W@OA‹]® ü5ÇQ6M3Ä^hûP(DcSñD#Í-ͪ©©Ùmll”†e•LÃÈ›–•3 cPhâùX´á±h,¶µX,îO'÷¾ï¯¯Ï¼ýíïøì‚… ?¾pÁ¡¡PP(T ÃQ¶É¾åÍ×ýøÀwoùþÙŽã|¢/¼²X*ƽØhR5³Šõ¤4Ûž~BÂ0 àX,‚s³]L)qyÕ¿zÈçótwïÆ.q—p$2阣•å¤ÖxÝÅÚ³\âú%p™–¸üwïgð] Ž¢Ñè­‹–,ùèžîÉA9"‘‰ÆF45û¤”hjtMÃ,™¦Y0M+c˜ú€¦Ûb±†M‘hôùR©Ô300°çï{ï‘\±}Ì&“Þùöÿo#e‡áo|ëÛ§:Žó©b±xY!_h:TJUU“‰K¡pl»:A`?…™í:¤='3ÄŠ•+d$™œÝ˨O\øN¤Þ†×LÃ`ݺ xøÁ«!i+R}Ûÿ¡¸ã92~JS9G-*}-;Ú Žoü Ö÷·ÏÁ,XØõ¯¿æ ë²™ÌjÏó"†i”LÓÊ›¦™µ,«OÓ´­MMƒÁ]¥Ré@r8¹÷ú÷¼óXÇ¿9.Nï½þÝÏðÍo}gY™Ä^—Ïç[gòüV´ M#.?ù†¤h»¸ñV²F„|0A)ÚJ.ÚL:ÔÊh¤…"‹žú%‘=OÒÙ9t*5,im\jJ]¾½½¡Á!6?÷¦eÒÜÜ<¾½R˜w|;=BGGǤŒ¼¨±¾g)q|¯üwBàÊ×\V„Ë.øê×¾©¿ÿ}u<Þ쓃¿ÇŠ›nºéOç¾âí–ë6I)ÑUÜSª yƈç1*e®çŠ+®8j„úW׿k'ð.€o~ë;ó×¹¡T,½¾T*Îós0–(K8¥V €a˜ºÿ3«FB EÉŸ{5v´™R´™¼ä]œ«üP6ù"^òñ=OÒ–é'–ÜK"ÑÈægžñ<¥>{´®íÅ„ú6.o,är=„B!ºu•“—ü(MJQ éd2“×½)ÆÄçYL\',Ži¼zºÊ¦¦æÿ<ýŒµ_Ç‘–Dø+£”@C€æ¡i aûž{ïÆlÒ<6Ú–~Û^8x$wË-·3™ÒIÅb~mrdôe¥RaçÉe¦i6¥R)æÏ뤽½¥‰&:;;Ù¿…\žµgžE¢1Aÿ¢±cñj ÉAJ»·àö †{‰ŽôÒ˜&æèš'„–ÃÏ5ð›ä°ö/¿»ëw³>œMSºCL›÷ p]‰ç9õœŸ •¾çˆköá·¿ýmL —žX¢4±Dy,îíÙ·X0]»Tj4"¥;É41ÊRp–Pœ¥e:2{Ï}øb8h}ö¼óΛ‘½íë_ÿz££Ô)©áÔ¹¹|f}¡PZU*•æs¹˜ãºbÅÊå¬\¹"_,²zÕ)8¶Í³›·H$Èç (Û¶oCÁ9ëÏ&ްíùm457QÊôÓóÕcJÇ‹ëº-`T!ž‚¯þô?¾}&c|ï{ßûªR©ti"‘XØÜÜ:˜Éäú]×½ÿ‹_üüCjü¨¦ .ol½áQ‚š“¸^ò¸ÿþû£ŽRiJ\äÁ2 –(Ä3jBòÓ:#ìßà ýI)9pà]]]‡2ŒèèèèÇtÇoî:ï¼ó¨­øò—¿½@){õPrèÂB>f©TžØ¾ púgÐÒÜD6—gÏž=lÞ²•h´³ÖÅÀà0©ô(¦iÒÚÒŒ”~jcÓ0éèìdñÒ¥ôöô²aâÑK—.ga×B†††ÉeÒ†ÅE/¿˜äð0=î;õ_xÁù( ¿¿Ÿ¡¡A\WÒÑÙA6—£T*áIIggÅR‰ý{öR(Ùt-^ÌšÓO_°sÇŽ{ãuo¾É0Ì/Íïlûüõ×__]ÆtÑE—|B ï.)=-—Í "FFFÙºuÏ<û] çsíµodݺ³Èdzyù…^ðñ|äoù÷ÿÂK†¼¦ôãRGYU¬ö}Ï1‡c+Š=úðÃttv²dñbšZZ°¬À”Ç뺦 ?f¡ò_hÒ“ yRéCƒƒ$“IR©Q2™,š.hok'‘H02šä¤“W²êäU„B!~ù«ÛX½z5…B‘B!ÏúsÏå¬uëxò©§Q –-[JCC”í;wÏe †‚ 9ÄãØ®ÇHr˜‘Ñ m­Í¤Ói/YBç¼N:çu²sÇvžzêIbœ¹ötB¡vvîÜÉ`ŸŸ²/Ÿç¹çžeÛ¶-œ|Ê*VŸr óçÍ#•J‘NÒÜÜD0DÓuöèÁ¶mZÚZˆF#¸ŽËÎ;YÔÕE!ŸÜ{⦅ÚÛÖöñl®ô]Ë7~àèݰá¾{_ùÊW®pù}OÊ—ås9!ËÄÞÐÐÀh*Ãþç—iïèäÊ×\AW×B¤”hºF"ÿˆâ“/Ãý”ª¢çÕßÐà'¥L¥Ò455“JR*YÔÕEÉqÉf2 û©´‰ÇãìÛ·—Baü„ÎØ"ë#}Is8ž8síéŸîÞ½÷jÛ¶— àJISSÄb1b±n%ØžôH§3 ô±ÿÀúHÏåIR,YصugŸÃÚ–µ„‚¿»ó..yù%xJ¢i…|‘ÁAt]'±yËVV­ZÅi«Oå@Oƒý,X¸€yó:I§3Ä r¹,Âô÷ÐÙÙÁI'ÄîÝ»iˆF(JÌël'ÑØˆ”’;¶³hÑb¢Ñ(+–¯ žhäÀþýÜwß}ØŽË©§®¡­£“¾¾>¤ô—{æsyötw“ͤI”Ý"v-Äul Ó$°pÁ2™ 7mÄ“Šîî] ¢Ñâñ8«O=x"¡#xÏh:>°àž{îÙ\pÁ4†ñ)Çq®L¥ÒË<)ÃRJ¢e;á­?¸Oz$ ²Ù,™Lvø¥BZ0ݬâ×É'Œã8lÜ´‰sÖŸÃc7ÒÛ×Ë›®»Ž|˜… ðÀŸ`p`€¿ø‹¿à‰ÇŸäU¯~5ßøúׯ¹GŒI\/™{9 ¬Ú,ÿì¾°VÓÏHO^â¸n(_(084Äàà½zD7tB¡±XÝÝ»9õ´Õœ½náP˜»ï¾›çž{ŽR±H0¤PÈÓÛÓC¤¡þÁA‰8 vñܳÏ"]—… çÓÐÐÀîÝ»ñOGg'»vn'Ñ‹Ž„1L“|¡ˆëÚ4&ât´·S*ùîw¿K8& ÐÒÜÈ…_ÄÊ“N¦X*F˜?>¹l–;¶ãI‡|>Ï®]ÝìÝ»)=b±b âñ§¯]‹eš¤RiÒ©Qz{xúÉ'"6ÿý§>6î>?ðÀà“å?.¼ðÂFÛq®+ —E"‘Å‘h$¡YZ8›Í\W>§”ûÞcÿ4=&,¥¨H[‡" )¥Øºm+@À—²öîÅ´„B!îüíoÇÛËjÂÚÔéyGÄapÇ_úÒ·ÛK¥ô飩‘‹ …™Åbq¥RJ,‹–¯\I×Â…¬Z½Šîî]˜f€W\r1¶cóä“Oã8O>ù$ñx#ç^|1mÚH÷înÖy&,˜?¾þäó9bñM--d²y’#I’É$KwkháœxŒ†††fÑâEƒA\Ç¥§·‡Õ§œL4ƶ‹H©ˆÅãdÒitäpŽÖ6 ÓÀ.•°K6#É$›6mdÏž½Ì_°€Ë®¸œ®®E8ŽÃàÐÅB‘åË–Ž„yôÑÇÉe2lß¾ƒ;ž§TrˆÅãÄb1ÖŸwšÐH¥FI¦èÙ·'6õ‘L‘ËæPÊó,ÓʇBÁÝÁ`Ãï‡G?y°{ÿÇ?þq)Ù7Žþ7}üaÄ0Î1¦*mÕ‘¸¤”ÍŸ0ò\úⱦiœvêiŒŒ$yæ©§ÐtƒÜz+ííí¼æŠ+ùÑþ—¾þþqçð·*E!Ÿ¯“£~'nºé&­©©sI:—<{4™¹°T*¬)•ŠË„- Ì}{÷ÐÒÚÆ%—¼œùóç„æùç·1^'ÉäóæÍGÓ4~ø£7²víZ”’”Š%6mzœ•+WX¬X±ÇõÐtƒád’§ž|Š–æfŠ%‹#„`4™Ä´ àœõëQJ±iãFJŽÃº³Î"  ‡Èf²„‚AR©”O½}4ÄbÄb ž~úib±Ñh”B¡Àþ½ûiˆÆ‹B7… t]ã•—¾Šûî½—]»v¡i:á`ˆööv 6mÚÈ?N>Ÿ#Úc` ŸX<ÆÒå+ɤR¤R£ìÛ½›M}Œ$‡Ée² ”2,« †ö†Ãáû›[[¿|çwn>Î_ó £X*ü4"£«icZc5áEYÅáä0ëׯ'Ÿ/’H4V—ô¸®Ë=÷Ü]õœoŒF¹îºëxðÁñ¿`ÛOÃX!®ñçȤÓÅîî]_= ×:‡ÃÄ뮸╭í·=ñÄSaÇqXwÎ:Ö¬YE"ž ÑÐã¿ø9ýý457ã¸.7>J4㤓OáŒ3ÏbëÖ­tïÜÅÙçœM{{;K—,æ™gŸ£dÑ5îÝ{èZ¼€l6G:•fp8ÉI+–cY&çœs6¹|žzfùŠ&Rzl{~ +@k[+Ë–-c÷Þ½hºN¾P¢gÿò…í,[º˜H8Lÿ@?V @±X$R*Ù$MZÛÚˆ„#ìÙÝÍ©«Wcš&ù|Žýû°{w7@JIÏýÜù»ß‹ÅˆÇã,_±Û¶Iú$µsûó 222L6›;wuS(-††1ôhËçÐ ôèìgÑÂ.V®\ÉÐжc“ÊxìÙ·Ë `Y‚–E[k+Ï?ÿ<±†@qàÀ>6nÜÄý÷ÝG±X$ Ò‹Ç9yÕjâ±…BtYÝëëéaxp‘‘$¹\<”a™v(ì …£5¶4ÿ×Ýwß}ïñþ_*0úûúÎBlˆÅ+`zã<@__ï¤ >Û¶MN¥4œL284T×l¢ª˜N§e&úÒë_ÿº¹Õï'(FFFÓ ý vi–eñøc›( ¬ZµšŽŽV,+ÀŸ_w›·lÅ4LŠ…û÷÷°ví™è†ÁðÈ?¶ +âå/¿¼êÕ—²wß>R©4û÷`ɲ¥œzÚ2™ƒ '9oýz"á ]]]Äã Ý»÷²mÛ6ÚÚÛhnJ°|ù ÁÉä–eáy°þ¼—aR)Á mmm!ƒœÞyÜsï}ôìßK29®]Ýôô@)E(&ÓÜÒÂ’eˈ†#ä R©QÒ££ìß»—¡ÁR£#ä²9`˜¦†C‘ð¦–ö¶ïþþ÷¿?êÈg3 €w¼ãí}ÀÊ_þòW[ÈeW,Y¶ô­¹l. …¼H8\ð*™ªñ| Iyx^Y¥db^E@Iü€å2Ï á«‘ Ç.ižë„0Øÿo}Ë[¶×»1‡i±c÷®o¬X²ôtË4ßÛ9!žç±eóf𛛉'0­ X±b9ù|Ž@ @©Tbp`€5kNã@OëÎ>›@0ÈŽ;Ù±sË—-£µ¥•€eaÛE’ƒC4%Œæ³è†É²åËÉæóØ%›ä°od¿ð—˜ß‰e™äòy\éQ²mÚÚ:hkóŸ;×qЄ   i:ù|žþ¾^º»»éîÞÍÐÐB‘0±²$µnÁzBá0ùl†ÔhŠÔÈ{víbxhˆÑÑòù(0-Ë ÑHô‰–Æ–Üõ‡{p¼¿›Ùñrí˜Ã1ÂÊeË~»`a×åíóÈår(¥8sÝY¼ö5W‚ð#ä‹6š®USqmßþ<=zY½úššš°L‹žÞ^Ááp˜§ŸzéIV¯^…i€`ã£ÒØÜÄšÓÖÏçq\‡@ %´ וxÒÅ4-¢”Rd2)víÚ]‰SO6›EA$%ÓP¶IY ¹tÚŸÝKùD5<R½lß²…äЩÔ(…B!†aH+LE"ϵ´´ÿß‚“ÿÆC=t$CO¿ ŸýoÖ¿>,„ø…Rê=ÇàÜK€M@;Ç0IñÁ0G\/A\³1ÿ2àMG«ÿÆGo'ؽùÉÞÏp¹i Í-ŒŒŒðäãÓÞÖ¢%KÐ5… —/Íòûßýž¥K–ÐÒÒDsc+W]õZžzêß| <ºÏS„ÂAÁ Z©Ä\Àƒ=HG{+ó:;é.«zÝôôô ¥D×u¢ Qâ±ËW¬$#„F:=Jz4EÏþ}l}šäp’tj„R±B`º2‘hÃÖÖ¶¶ÛšÛZ¿úÓŸþtFéÍŽ..Ö(¥žB¼8·,…ÅÂev+ð+àeÀÀ>à5ÀW€w6ð`ð [ÞÿW¥ÔBˆ›?(¥~_ިܾ ø"ð7Bˆë€7·?Ö XÀÓJ©¯å{Ì×Kk€Í„–¬¢ýÃÿJïW>Ža˜$ FGGùÝ¿gí™g±~ýÙƒAA‹€erþùç žâ™g7³yËfN:i%`×vY½z5Ý»v°å¹Í\pÁôõ »{7;wìäñÇüÅ˺nÐk K°jõ©Äbq”òÊŽœ£ìÛ³›gGGI‘J¥pÊ!ÃuÝð@®!ÛÑÖ¾# ùŽ;îèŸîúN0¬†•RÏ(¥îîBüßüà›ø£bø‰…ßDð æ­À·ËÛ}Àÿnúþ{ï&ÇU¦}ÿN…Îa¢&(Œ²,YÎQ–-lcc‚ ,¼&.°À²``… ,óy Æ``÷eI»€Á`Œmlã$ƒå ¬ÑH#iòLç®tÎ÷GUÏô$i¬,Oß×5SáT:ÕÕu÷ó<ç ø5&—àg ÝܬãK[w !.¾ŽŸºú @À‡ƒí'ŽÎíOF¸j8dÄWœý¦°ÿg_e¾iJ¥Èf³<ÿܳÔÕ¥X¹j倠¡…¢±èp"ž|6’ˆ7¯ó^z¨W GÛº·mÀ÷ªá(Aq.°C)5|®½PJõëkW ¯ ×PC ‡!D=p pÄ‘„ïBÌ´ Ð2àßÔµ5‰«†N>|¸W)UB¼xx9Ð á»3ܦ”Ú*„hÁw[H¿ \+Ú‚}¿Æ÷¼ÿ`ŸâV|­Waàv¥”+„x#0œ‡ï¥³b©Rjû±¹åñ¨W 5œ|Xü$Xÿ"¾ÏÕø~YgÕwâWú&PÆ'¡ë„K‚c~Ôâûn%ð½èSÀwñ¥ªýÀûð‰ðVàAàkÀ“Àeø$wÌQSk¨áäÃøŽ¡ü'p=¾ òEà«À¢ íãøa;s€ŽÀ¥bð~|2»Y)µ ß©ôàYüô9›ðCŠ.B$ƒ¾~¬”zKàϵX}Ôîð ¨W 5œ|°ñã+Èã{²ƒïånZºó¾*øÀB˜Àgð%2€û„± ?1/ø.|)îoÓ̪ó¯{ø’ÜqAMU¬¡†“›˜P™k8øñ‡àD›@ _"Ëá÷ŸUJ…CÀÿÁWߊ—ÇW7+ýU°‚ภF\5Ôpòávü¬ÿŸ ¢_êªØœtàÏ”R^àºp°8 _²Z œƒ/]URÑ|?×¥Ô¯…ßN Œóï'ˆ_BA_o;êw: jÄUC '¾|XSJý±jÿUë(¥Fð³¦ü¡ª½ú<”RCø3Œ•í> ¯j»:×Ök[”RãKwCÔˆ«†N2±‚ÇÍ0®”º ¸íx]jÆùj¨á$ÄŒ$.!Ä?â»ø'Øþ.¾ž½ ?©™Â7ômÂÏ_]B\ ¼?Áþ/•Rß; 㯡†f!f*q]ˆï;RÁ+ñ30¦«ñ Oáç9ÿG!Ä|Qò>|ýúÛA Ùj¨¡†ÃÆ‘²qýH)µ[q)¾·nßÇc~FÆ3þ#t­j¨a–ãpl\Õpw!¶âçøù‘Rjßä<àaüšpm‡q­j¨¡†QÌ”¸²ŒOSbâ笮à&üÊ‹ƒÊ"«€z¥Ô9ÀRüàÌOñÖPC 5ÌXUÜ |Rq?ÐŒl¹¹ªýn¥Ôîªm ø®¢Ø‚_÷÷G`¼5ÔPC 3&®¯ài~ßvõ!¥Ôú Û˜³¤”Ú¤Ûø?À[ŽØ¨k¨¡†Y—Rª|dŠýÛðC¦:çgÀÏgp5ÔPC S¡æ€ZC 5œt¨…üÌbÜvǯZ"‘È—=Ï[äiòð†]!„£iZAÓ´¼R*‹?A“Å/m•²J©¢Rª(¥,J) ®ë‹ÅbÑqœÌ{ÞõÎc^$´†ÙƒqÍb„B¡¯ºŽýf©”¦” J!•BI9æó¢Âÿ‡!l°ÂO×t/™LHê¶;~å2F„žçy†ã8a¥”Ò„p…ÐÍÐÊ‘²BˆŒŒhBÐu}@7Œ^©dAJY’ž,:®S°,«”Ëæ2ý÷·\P5ÔˆkC)µÔõ< ¥P£¤U½„qmrŒØè(Uú|5î|ð—¥r™b±@±X¢X, ë:º®cš&¦ab†Ì`ÝÀ0 e¦2 Cº.C¡lžÓÌÏo½ÝBxB)®@x \®Âà¡Ùš.ò(rBˆ¬TŒh‚!¥Rö+¥†¥ò¥EÏóJŽí-Ë*år¹ÜððpöúÏ~ºVJíE¸f1”R)éy“ j*ò9Ð1UÄt0ò ™&f:M:•F*‰c[‹eÊå¥R‘‚T蚆n†¡ëB7 ¬ë†a :†´º¬¬kcû ú®¡ëº¦¡éšÐÐ4 „@¡”’ %¥”þ€¥çyÊu]y÷½÷í*—Ëùº×\µþ¸< ¦Å(qÝpà ÚÙçœûíT*Ù‡—êšžŽtîê¬÷<)JI¤àáI…ÿ¨¥ß&%R©Ñã”ô÷Ë ]J/8_‘J¥œW¾òƒ†òlËî. CCCCß}Ã^ÿ«ã÷QÌB(e{RN+Hrš‰t5±M<_7L’IƒD"(<©°m ×qq=×WY]'èG¡–A£ë•v¥‚kø†a ”(„&Ð5Ý'¾1²ñéc$è·kš¶rýÿôÓ>ÿÅŽë?ûiE ' €Ÿüôgg\pášß×Õ74ÎmoC×u4!XÐÑÁÜyóؾm]»wcÛ6ž'‘ÒÃó¼iÖ%>ÑU¯ûËæ¦FÎ=ï\Ö]²ÎˆF#ó<)‘žìÈf³˜áÐk~sç]·½úª+ßx¼?”ÙVµÄ5œË£!¤”ÁŸÿƒãÿè(”çïWÒC¦ tM  áKIÔ#„O3#¿ÉÄf¾š8JNRâIH\Tð£‰2Ç–šŠÈ˜Ð6ñت?ªÚCש¯K·®>mu°ë8<¢¦qà 7èç_°æ÷uõõBˆI477Ó2g®¹¡ÁaŠÅ…B\¾@.—%›Ë‘ËdÉdÑ0‰xœD"I*•$N“L$I¥“4Ô×3§¥éÉÑ£±X\èºù†;~õë¯~ík>|¬>€YŽ@âò "ÝÜÊ/»O‚«Ôä¥WúKÛq)Û6¶maY6¶mS*—±ÊeÛÂõ| \yžôPRùK%Q®/©{žHåI”òÀ“£;Ò¥ÀsAIP!=P6"h×Ph€©A$d ™$¢aRñ8±X”p$B8A™b¢Š¤Æ¯OEf¦i¡Ph5â:¡`œuö9ÿ•J§z aÐÚÚ‚¡ 4¡U­ „ЪÖšæ·ƒ~µ%ž<ð ¹aÄ㉿úÆ7nºáºë>4p¤n²†iáHOŽ©}ŽƒTŒý1a9ú§@×1ÃD(‚W„%xJáN\*𤿬ޔ¤s°>¦;ÎU Ïuqϱqú-”k!œ2BÚ£$§ ….4  !]`jÒa]ãíËR¬]Ѩ!Dø@âë^wM?ÅS]Õ߸íH$2wíÚµ§¬{Ùº’¦éåÞÞ^½\.)×qlו–ëy–']Kzžåº^Éu=KJ¯$…°iŽèº>4222œËY{>ûOŸÜ<ýhfŒP(üŠ©$­‹b±H8&dš‡ÕO"‘0[[[¯>w؃ªá€PJÙžôF%.ÏqPÊ—T@P¢òÉK'1&Z5ÙU,UÐǸ¾+ÇMÕï¤}Uã˜Ð‡R 5eêH#ŒŒŒì| ˶ !NÁ/¥¾?8wpM0®{”R2Èã¿_Òl¾¯”:'É!Á¬!þK3CµoMA£$ãûœ–Ħ L&Œc’ÄÆôçK +éÛÖENÁ°TÄL–ˆÑ1€´9FNŽÒ¨¸‰¸®øƒ‚yU¤çá8¶/ zˆŠŸ™"@ÅtS‰R¢wÿ~²ÃC„B&šÅÃÔIŒÃ ²¶,¡& t] ›Gkð sÜ  ”úºRêj¥”Ä/Üñ5|²Ø.„88øீ÷á¿øKðSõ¼åí_ ÚW_®½øz°þ÷ÀÏñ kÞŒ_˜sE¥]ñ%àÀ2`}@Z_þ 8¿^Hø¶’â“p~ÿ—ÿpgpÍO×Þãx‰à€)P,ÛŽìÛ·MëWñÄö,s¢-Qæ¨AXŸZe;†úéIo|Ÿ‡j¨?HMg,3ìq͘¤ª$¬‰³ìâÒ4Í÷«"¶ŠäˆTA¾:A¡ÇuÝ)F)Féæ^"8!‰«Á-lºõ¼Ø5Ç{³Ø20DK {K‚?mïe‘45æDuæD š¢aƒ†ˆŸi!bˆ ê37ÔW¤®qö1ÆÏ¡êg.mM„§ù3w‰ |™)IMEl‡ˆŠkÔ8?ɼ5~G¡Ê0?kÄUÃì…Ð4«2ƒ`H{Ò19G’s$;²“³i@ÜôI,a â†FÌЈ™‚¨.ˆèLM›ÖP?æÙ~ ‰mj©ozCý!²à =¨§0Ö‡¡ëSKR“ºÆ‘×Ô³Šâ®J%$|À6bÔŸoV¢F\³‰Ks^\fɱð:d„4!††&Ðhè4üw\Tj8úÊÌ`fÓ“Õ„öâ>‡j”©ÑëWJ¯&®IVÕ¤¤›!?ÌÇ“¸¸¸®‡”. š†QI$(ü«TÄ0L„ðÇPõ¬ýO~Ôˆk–C×´Rµt¡;G'é€,OaM—vtЄÿ'c3ˆûC”¦#G B ¤<¸ªXMRSµF~³bœ÷ÑbÒÅ%³Ri¼½Q7‰KˆÙý€¨׉‚÷á§~Þ„Ÿ®æ˜Ù/‰DWÈ4‘ÁK* ™YO\RèX˜„<ßî§”šÚÆ5Ó%þ¬§¦ihBCÊéˆk¼cÄ$⓸f÷¢F\'>|+X¿Hqø¹äg MÓ†Cá°òIMµô<ϯ5*Īâ$TÛžê£ë:®ë!„8!êˆOÔˆëøâÿI•ûª¼ „Çø™xž— ‡CÒó< |µ(b)˜ñƒú’†«UåärB¦éÇ/2s±zFÑódP¥zqMæ, ÅÉ™™uMÃò,¨W¸Ž#®Þ‚—Afà¼ýXÂ*[™P$*«½ç#^™‚7»]"-T•LÐ!žL€ãÌ<+ã¥-¥$š®Dæo+%QˆÉ.ÊOè8£6.!&ÇeÍ2Ôˆëø Ü Gne!ŒÑB;¦=ë( “É G#¯š¸LåÍz½'ÌQïyDZ0Œº1ÕnÖÕËJd‚®icª¢§PúT^ ¾6Ñ@Óýào!ÄÑ«¸|’`ÆÄU.—Ʊ]þ‡+%*5²üÈÆBhAI¿n_,¥µ­ Ó0p•Yƒ¿–«òfTñ)ÏqS‰ëýï{õ¯ÿú㉠ä,'.}LU´lÃ4ƒT3W '.EàȪéúhrÂ)gµQ)5iFª$®Y^SB\J)vîìdãÆgÈfsHé!¥DJT?–Ü7¶]Ù§hooãõ×¼žE‹“<±ø4ÊUrøç£ZÂñ’¸„†;.C„rf½Äå¢ã¹°mÃ0ð\÷Åg…`¬ø¥®iÀ˜×ü(qM°s•Ëå)}¼Fgu½F\Ó5H)¹ùæïÐÐX?úKs¸PJ±gO7_ùÚ×Xºd úÐH¿'¾„Uî^pûÆöŽ×€Öðð%üÊÛGBhvðKîÅsýT ³^Õ¬¢mY†;] 5–¾*3޾óét—µw•ŠSºWˆK¯×ôÄõýïÿ€;ihˆ ²€Ðšðg¦$=¥xöÙg¹íÖÛ¹úê×MjŽùv^ú®C¿­×KúÏ_…;ˆÌÞ3®©Jâš7á¬SPå+Øó®Àí9*_ØHè5¦¬’°4×}vK\RhxS®eYº>ôb3¡Ž¥Æ1£üC~J¥©‹TWÒ«ûôƒW¼ëÝêÉ„J•ú©‰kë–-<ýÌ3˜æÔö(ÀÐñÄ$ù÷×…ObšÀ¯y:5î¾çn®¸âå“öGÍ‘eÀ÷^äMØÐ¢räç ÆÏh{½ÿÆTßb­ùˆð²:ô†oáöLj?HDÜêt)„\ B³;NŠj‰«ŒašèŽ3ó@k|›–c?âš®…È“¸‚°j©…CÄc1¤ãJ&èèè ®®3d²oï>Ïó5º«x©½/S×3Ï>{à³c$UEZHäHªòQ9”Rlذ³Ï>ûPÇò þÝ`4£JQ¥ç'·«)Þh@„ømΞ£6´Phü¶pg½›²Ê˲,4!F­5Ý@×54Ýᢺ‚{°T OzHOŽö#*á>R‰„xõU¯fÞ¼yèºà™Ï²|ùr°wï^üIÒé:òù™L†;:Yº|9RJ4sv«ñ0 qíÞ½û€' H‰BŽ‘V`p”),x@Ãó/¼0‰¸ÊNrðíßÅá£øT°¾•#ýkf4‡I½ñŸP–!‡1ãÓ´ú·€CqýÈÜï~Æ¡Á•‘m•máÎz JÓ6×¥¾¾žH,†ˆòÆ–ž#©‰KÏÃv}·+!Àõ<”Rt÷ó³[~Ž']ÇEI‰íI4%°õ0åP%#NÁŒ“ΔXôãŒ?·Ïå8ãã@3LC\#ÃÎÃ/•}þ ‰Ÿúc´BI@h3ÉLÙ?Ð?i_ÉMw³ø™Éì¼t!cÄÕy®½øN?h!sÆZT¼Éµ=EüBDdÀ ÄÖ\É⎚ïNéžï¾2¶YÁ¼8JYPO&¨ªêÕ®ãàº.žëΘ¬ª÷9NEšö]ƒ\ÇE]ñì Åq"IìHв§àJŠ®¤à*Š®‹ ”& IDAT¤ä*t§ÄEï"OPÞ~ø‹ëî=vïÇ ‚À®7=q©ƒèx*poPÒ'1Tý­ˆÁÕ’ØÄòLS\l6`°³õ<½õÓãTödæWãÖâhuWW á‚h4J…¸°¦6Ï&(1öj¸ŽƒeY˜¡ÐIkº6'Èb*ð½è]¥HÌ]ŒÞ¾”¢«ð«P¤¡T¶p­2f>Gª4BS~ˆyÏßE"ä¦ól¡yÎ+ˆ·Õ>)#=£ëˆ±Bš³ðZà#ÀÙøßáÕÀ\åöN:X«»´¸ÀW—ÿx´§ëzo4­w%Ë“ÃMfª%.ÇqqÇqfLVÕÒ—[‘¸„o\WžÇ–‡~‡;ÔƒÞ¿1ÐMÂ)’Œù†i‡‰„"DbQ’§,aÑâ¥lÙ\)‘©fu1X8DâR0NU¬éÇò ùû5âªB/P-nÝ ÌÅO\"¼¿ Ÿ1®£Šp8¼7n+kvü  ÏA·sèN DHÝ-£Ù"ýÛFs]_UtwŒ¬f¨2J)G‹]á»™¦Áù#/ 4Eda3‰UÄâ <é‘Ïç°Ê–eaYe,Ëbxh„}û•ܘåU¬á‰Ë4Mšgì9_ÔX ªP…ŽVmeë㣢ŸéTª+‹Žn«âd›ÛÉ͵Э,º´ÍuМºS@/g0ŠÃùB¹~ôüøb9©T¥¹ÜØgà:ž”8®3c²ª^ºîج¢çú?øeË"S,Èf3  PÈ3<4tÐûSÌP ²>^ ñr Ç á"y9˜-÷?:Vƒ …ÂÃÑXl4ðDr $í g[hvÃÎ!”‡P.ºg!Ü2F9‡^Îb‡0óÙ^ôÒxòEc4·Î¡¯·+¯¼’ç_`í«_OÏþý¤Ò)Î:ûîúͼì²Ëèèã›_¿qô܆Æ& Ó˜RU<io?ªçy¾íW)ÊåÒhn!å²?›ëÝtŸ5‰ëx`–b) “¶ŒF´Ô• ßc÷˜ÆBI%3±X|Œ¸ŠY4·Œ4c9ó@)t'iÑð Ió,4»ˆfåÑKÃ…aŒl/f®Í>´‰ƒ×^s /Å|óÆùø§>ÅWþãßY{ñZîûýïQR±é¹çÙÕÕÉÈí# !„ ™LÒÒÖÆ+®| FUŃ‘ÕÄ*Ø*Ð:4Ý/{6Õl»¬r™+®`ù%W²íÞÛX¼ë‘én§&qMµSJ5ªþ-Túž‰¯×K+ªí[Zý›@˜ø7`óÔ§xž—Åb’ XÙ%L·ŒuÄ%”ݲ„¼2†&Ñ¥‹æ–BÊ¡†Ñ Cè™ôlÂ;ºN¯oø#Ý{ºxvãFÞõ¶·188È“O£‰¿½†7=âŸLƬ. Ó¹CHßÅutˆK©±_!u”®q‚#°oùÄ%bg""«v_<Öƒq'õ‰kåLéƒ!¤GÈÎQeLMbHÝ+£[D9‹–‚Ü"Û‹ÈôµïÐtÐtt:M]}=õõõ444ʆÆF'»f(¤sþ…f81L3D8&ù~‰Õ†÷*rr=wFÖ~äG%.¡ùÇMAF–U&obaØ#dšÔÅÂxšŽ6•ʨjÄ55qðr¤‡ŠJßGñ'2|‰ËíF«{ceÿuÀ1÷þ,‹™X,6î i* š•C”²Beû`h?2|4]ש«o ®¾Žºº:ê›¼ÆÆF7‹9†a”LÓ,„ÂáMˆ]¡pø™Ö¶¶ J©î\.×ýÖkß<ú²ßzÛí M3þVJù6Ûu–x®+^Ì,áÄåÁ «TEÏqÇIZ•U˲¨söòÝM#ÄÒuÜõàzZ¦±sýèGߟõŽvÓWàT:W*—éܱ‹R©€’’h<>±z´2,Ó0®Ð@pÀl—¸–(§-ýjÐÓ¿~{<óη¿5û¥/ýËøñà÷cSòÀ0Mêëë©«¯§®¾ÆÆF›šˆÇã~€³®¡ëÆ]Óž G#Ï´¶¶=îyÞÞ|.¿ç-×¾éE1é^Møð¹ßÝ}Odx8ó1…|‡t½Žçj3!«ê ôƒAiS—$S~qŒˆRä~ñ|æîvæn²/!Äìü©Ÿ€©m\J!• ™ˆžý=è†àâK.æ±G×OòŽ÷”$ôŽObmžìC·ÐÐØ0Å5âš%®ó0 %/C$ÖäÏiº6}ÄašÔ×ÕQWß@}C=õ ^cS£‰DmÃ0J†iÂáÐp©TŠiB;¥±©‘ÆÆFÚÛÛ©¯¯§³³“;;ÙÑÕù¶þüçŸ8’c{埽¢ ü3ðÏ·Þv{(W(|бw»%÷TÇqôƒÞgU5«8Õ¹Ž3f°OÚ9’ƒ[¦í«F\>¦±q©IÏiif``M›6a˜MΗ„îý>¥ÁAZÛZ±íɹý+}ÏN‹ÿ®‰Kt|má:`ïñP8B¤_ìKiF`Oj ¾±^544y ^8± Ó(™f¨ …†4MßEŸnnn~Úqœ}…Ba÷Ûßzí¸iýw¿ûÝÿ2wÞüSâñbЍ q”gÁßðúklà«ÀW·lݦ=øàÃï³-ëý…bátÇqŒûÙT ªŒó•õQmQmOÑ%„ ‰‰†1Í0ý}½!fç3ÓªŠ28Æcq “~qËÑ2Kʶ2E4)°ÃMd³“½°ý 8³ZU¼Xü=)RR}ƒjljòêÜP8l†^ ™á|(Bl'O644X*–αmûENµqi¢*]³§é:ápˆH$Š® ¤)=ò®Æ°¥S†IïÝÌé+–ÒÖÞNoOÏìÎð`ZãüÔ%ÂÇà:îŒ<ç§ëCÍnã<øduÜ «‚ |÷âKÖÍ?ëìs^oÛv½ çC!sP×´mñdò©T*õ‚U¶ö t]÷ÁäÇ¥ÔŽ[¥•þõû~üàæoçM–í|¸X,o[Vø`çV&¢ªUE]7ÈIEID)'æ`'[°SMcMdMäÌ$EGaKEûÖû™·÷´´¶!`Öæá€ª¢<ŠÒИq~¶êŠ'®¸üRuÅå—~øÌñƒRjÉÚ C»èþûïPJ[ ”jP‚züt¾–*‹¥Ó‡ŸY³fÍQ›ýÀ_½ÿà€oç»W—-çï-Ë:;ŸË…Êå2®kŽD‡#”ËÉDªüF]3ΫÞnÆÑ\‰rŽ+):’R>6´ÔÐsÔgzˆf{IçzH·ÖóÌ“OftÓ|ýѺ¯“ Óç}Ãü¡êô3Á,7Î×0o~ó›õX,þŠ“H%¿ä)ñ%˜œ«])áï“P(Yîïï{ày`ƒ’üøŠ+.}àpÇwãM7§S²Ùìù¹\ö‚r©¼Ú²ÊÉd*‰„D¾P¤µ¥…3Ï<éy EÚÚÚbÛ¶m¬»ì2±}}½8‘ÎÈ …þç°ö#‡{1‡zh+eˆ+Kºî ¡ej³¦øáOoûÙ±LªyRàŒóG³Ü8?+qË-·„RÍÍ …JˆEB°P)¹ÄÂW]õšxàþôÎÏf2X–E8|PíÌPppï½ûÞûîè{óµ×^;p°o¼éæv)åªá¡¡µ…Bá\˲NÑ„˜ëyNtO÷^ Ãàì³Ïá’‹/Á44ž~+W® \¶ÙÓ½‡Á¡a\×¥¥¥•¾þÊV™3Î8h$B×î=8ŽÃHf@üúF/fÙÙ˜J©õš®}ñg·þdÛÁÆðÎw¾³5OýÓà`ÿeéT]ÝÒeK{\×B=!÷666>ðþ÷¿ÿ%{šD‚ò€îGcW¹^Џå–[BuMMk¼\±NÁ’úÆ9íH¥©`:Íè}Ñé`éÂÁ·‹öôôÐÑÑ1ãq(¥èÚÕuÙƒ<¸úÚk¯}àÆ›nÖ%årùô‘ááKJ¥Ò™Žm/D#s Ã06½ðŽã°â”•\¸æBæ¶µŠÇœ¾þ~4M°~ý£ÄãIN9e9eË¡l•‰'â´µ¶Q*•Èçsx®Ëœ9s°m‹Á}ûQJqñºK(KbË–ÍúH&ÓÝÔØô‰OþíÇîɽ!Ä;ÿâÝ·Ï¿¦®.-êÓÄ Vœ²¼=OP_—~y$ù”íØê?øá¯³ÙÌÛ¯»îº“;ÕÇ4˜Fââ¨J\þÄcMâz)â†nÐ.ºäeŸ¬olþ ŠxE¥óÓŸ.Z»–eË–‘Ïç‰F#ˆ “B>_@mmmÌ_°à ùÝ\Ç¡P,R(dÿ¾}ØŽCksë§?ú7Ÿø²ë8 Íp¨®T(hñdœËW ‡Èds¬^½Ó0Y0[¶neÏî.æ´Ì! cè&-­mD¢q,˜OÈ4yü‰'ؼe3u©:ÚÚ[I$S  QÈ炪íûöíÃ0BþL|2ÁàÐ v©Ì9çœ#t];ýOÚpϧ>õ…¾¾Þ{þôØ£X–5­?ÝÅ_òª|>wQ÷—¡ÁA::æ³lÙ2âñ©T’D0©ÝËÖ]zyï~w,eÿþžÙøsÛçrƧsñÅk‰ÇãÄbQÊå2---úÚµk?úå/ÿß³ÿ÷ÿçå?þøKF}œ>äGM=«¸jå*ÇaÃãsáš5<±aû{öóö·¿ÇûíísyðÁûèç­o}Ï=÷<¯¼òJ¾óí›ñªª#«šqþ%‰¸/g•K,]¶œHÙ¼X(|&éùÅ&ǦT,ùÉöF2d3Y …J)Bfˆb1súi«I¥ÒôôìãwwÝÍÂEu´ªž ‘H¦xøáõÄq:v ”ð«T;ÑH˜L.‹‚D2I(bÍEa•Ë<õÔS¸RÒ2§…å+VP.•èíëc$“唕+ÐlÛ¶}û{XwÉÅ´´´R,–èëë§k÷nÒ©–m¡ë:½û÷ÓÞÞF2™$“ɰuË4McÝËÖ…†ß³cÇöwüŸzdîܹŸüÄÇ?úXå3q\>¦ëÚ%Jʺb±„çI4MÐ×ßǽ÷þž?<ø®zÕ«xõ«¯òk:j~Ù´óÎ?ÿ’ÁÁŸWþ>1` p€qòw% ³|ª¢çy<ýô“èšFCC#{÷î#Ž „Æ=wÿމj‚<€ª(¥œõÑï'+æ´¶G¶oÛÆàà óæÍ¥¥¥X,†nèŒ*ŒJ¡i†®£iþ þ,¶ç¸K%‡†èïígphl6‹’P*åÑtÕ«O£½­•íÛ¶Ñ4§•5œmÛüü¿ ‹cÛŽã²öâ‹9uõjtM§§g?û{z8û¬3ioocÛ¶í„B!„ìéîÅ4B  i‚‘LÏ“ôöö²páB¢Ñ(®YÃÐЛ7o¢¿¯K/]G4fëÖ­ìß·þ¾^¶mÛÁÀÀ;¶ocñ’%œyæ™Ìmo£T.‘Íç‰D"„#Òuu Ï牄M柲‚B¡ÈSOs«wŽæŸÂpîyÞX rÀ T@]×Y³f-ÃÃC<ûܳ(?þñ™7oW]õjþç~D__Õ5¦6Î+¥(•JŽäMÖpìðµ¯|ùsŸ½þ†UB×òc$<Œ¦ hhh ¾¾Çq(—Ë ÐÝÝÍÞ½{éëë#3’!PHvíìD)ÅÒe˸ôÒKill$që­·³dÉŠÅ"º"—ËÑ?8ˆišt,ì`gg'ç{.‹.`ggÙl–t:mÛ„C,Ç£XÈ’ˆÇèííeñâ…,Y²„þ¾~tÝ'¸X|æòË/¿Øóä{s¹ìÏsxžI$¤Òi6<þ$>ô0º¦‘ÏçB<ûR!-ò¬;¤”Ô´1¡k4××ÐÈ0çžs¹|ººzr9?²Ãu]î¼ó7£žó ¼éMoâÀu=„V•XU9 N¸F.—µwwu}ýÈßj Çêó7\ÿ¦n¸Á0BæÇùëáá‘ùû÷÷Är¹œ$—Ë144Èüùóiž3‡t:E©XâÔÕ«Y½z±h”{î¹—;;Ée³är9¤’òE¢±»÷tÓ1®¹€M›¶02ÿ¯ŸøÄÇgG™™—n¼éfX<2<|A.—[[.—N·m{QÿÀPs(dêñx‚}{÷²tÙ2.¸ðñ8{öì¡o Ÿµ]ÄÐðàÇñÝqÇ´´´qÎyçqú™g²wO7»»vsÙe—¢µé$S JÅBר»w/¥r!æ`Ûeš›šèéé¥\.á9.§¬\‰®ëtïÛG÷ž=´´´Çhll`ûŽÔ×¥ ‡L,ËÆ4CM£\*ÅÉdF˜?oår¥ÚŽC.—åû`pp€Å‹sÁš Y´h12(A¶ß^VŸºŠd<ÆÃ<ÊÆgžeÏž= ’©$éºzæw,¥Èd2d3#ìÞÕI_oÃCä y”TÊ ™¥h4ÚÆî¯oh¸ñw÷Ü=£Œ¸wÞyg?pÓÑ|Ö' Œë¯¿^ý¿ÿ÷½K”’Ô76-_}›Î8н§{RŸž~Òq}ý}ôôöL9;9Ö·O\ÙlVŽ ýç5W¿î³‡{S5=üý?|zµ ]Q,.(—­UŽc-0t#Ž„E4eÓ Ï#¥¤¹y—¿ürV­<]7ØÕÕEgçöìîííóhŸ7‡y„RÙbåÊ•¤“ NYq ë[cÛHéaÙ+W­¤lÛ”Ë%6¿°‰p$ÊÂŽ$—-#‹!„Æ®®.@púgàºý}ýìÝ»—Ë.½”ºtM4MChûöí§·§ÏsXØÑÁé§ÆúõëÂ4MP°wï~‘(uuuDõ8¦éN'yßûßÇ]¿ý-O?ý4Ù\žpÈ$d†Ø¹³“-[¶°~ýc$“¾ÊƘ7>™ÌÙ‘ ;¶Ó×ÛËÈÐ ybA…L³‰F÷Fc±‡êoºûÞ»hÚž—* €÷¼çÝ@Ç/n½õ†R!¿ £cáµù†œÆd,³AÐõÄŠÕJ!QP½´®b r*5šÃÈ“)¶ma•KQ¥MMýÛ¶m½»§§çï|ç;j¶­¯|Å+?ÐÔÜü­‘ŒŸæâu—pÆég‹FI×¥ ™? *˜×Õ×#•ä§·üœ9sæpÖYgqêêÓì`ÆÇiok§.✳ϡ{ß^¢Ñ®çqß÷‹ÅQJbè‹u°eëVæÏŸK&“áô3Ï  ³«k7;wv2þ|Z[æÐÚÚ“OÂsºvuÑÙÙIgg'…BÁÏæ ilÜø©TšT:ͪSW“!“a{Ï~ú{{¢”išv$í‰Æâ445}çî{ïùÃq~¤'-ÄKÈ^wèØyéB 3Øú‹¸ò8Žæ„Å«Oûæ¼ù þ:žLR(X¶l+NYA]}=ÑhŒP(Ä]wÞEÛÜ6Î8ý4„¦‰„yþ¹çX¼d ¦aÐÕµ›Æ¦9$“q¤”<õē؎Ã%_D8Åu^Ø´™æ¦&öîÝKGGÑX ˲xúÉ'‰'\xá›6o!™J’H$Ù¿¯›îî}´Ïm£­µX4ÂÓÏl¤¡¡ž†Æ&ÛÁ ™£.;;) t,ì7£¬_ÿG› ³s{öø!:±XŒT:TŠd"I©T$3âÛ¤2Ãà ô÷W“¦²#ÑH_4ÛEÿëžßßû›ãüøNz|úÁ+6¤=¯•'«aÆ(äó·÷ìß÷ÎùfG<²mÛ6lÇåŒ3Ï ­e¡pŒk^5»÷ìA7 ²™ O>±å+–û’¶ã`;6»vídݺKä´3NÇuº÷ícëÖmtt,dþ¼¹èšNCC{÷ícÕÊ•”K%’©$K—-§P,Q,س{7‰D‚ó.8%‹—Ʊ¬2¶ã`˜&«O; ðgª…hŒ€L—/[Êoû[†‡éééaïÞ}(¥FI*NsÖÙç‹Ç)äód3#dF†Ù½«“Áþ~F†‡(ý 3†i:‘Hd ?ÕØØô£{ïÿýÿÏç4P#®fŒí»:ï^ºpÑ߆ùù èáp˜®]¤SILÓdþüù¸ŽËœ¦&4íís±-Ƕ‰EÂd2y–/÷ë„ìêìâ©§žbþ‚y,[²„¶Ö ÝÀ²,~ìâþž–.[F©TD×5’É›7mæÜsÏ! ±jõ©„Ì–ågØmjnÂó$žë ¤G4A×uŠÅ"ƒý}ª·‹þþ~„Ä Ò$uÞó‰D¢äs92## °sû6ûÈŒ W“”‰D†b‰ÄÆÆæ9? …ÌïýöwwÕ’ûcÔˆ«†…í»:o^ºpÑr3d~lî¼ùH)Ù¸q# Ái§žJ¾XÀ²l„®S*—iikÃu]†‡†yæ™g¸àüó™ÓÚ‚µX½z5éº4Á½¿¿r¹Ì²eËЄΊ+(•ÊdFFH§ÒH)™;oÿ?{g^×YÝë÷Ûã™ÏÑ,Ù–dyNâÄvâÄa I€„¡À¡J–ö2·p ôr ¤--¥p[ÊJ)Ð(ó”„Ì$qBÇŽ“ØÖ`[¶5žyÚÓwÿØçH²,Ùò$ÙÑ~Ÿ({Ÿ³Ç³ôóZë[ßZ]ÝÝØŽƒªéÄb1\×Fº.ñx )%ù|Ž}{÷Ò××Où|EQ¨ç7µ´µ±rõ à ŸË’Íd:ÄîgŸatt„l:C¹\B¦i® e¢±ØŽ¦––Ÿ˜¦ùíÖyïÂTG¡à§r-úøN \ÇÍîþ¾¯Y±²ÇÐ×µ-Y‚çy<µíIEaÝ9khinES‘H×ó¨V« y^våËPU•r¹Ìƒ¿ý Šfpé–K‡B\uÕ•Üsï½hºŽ'=*–ÅÈè(Éd¡4UÃsm M%‹aÛcccôõõÒ××ÏÞ½{©T*(Š2‘#Õ½¼‡D"¢*ä³9²Ù ÷ïçÙìÆFGÈf2T*•z}{Ï …r±xì™–ÖÖ_¦ùÕ[}ëÑê^ï…ÿf`TñŸÀßJ9Kÿ²S{íK€åRÊžîkpœžç½ùàÁ4]»¸µ­l.ÇSÛž¤¹¹‘d"…nä ~…çr©ÄŽ;H§Çyéå—ƒ”lØx!¥r ¡ø–ÙsÏí"ŸËS*•ˆÇâx®Ã˯¾Šûî»áC‡ˆF#ôõõÓ××Çàà ®ë¢ªj-Û<Éê5kI$“HéMˆÔ¾~rÙ c£cd³¬ZΡªižiš…X<¾»¥µõf3þÒ­¿¾uä(w¡ù{àeÀkðÿfoÄïvþýšvÐ+¥Ì!V‡ðgÄdðË=¯ž”RV„Ëñ;M­ž©½Ú¥”»këA°\Î IDATÀüÎêO !n•Ræ„­€*¥F.›ÅqüVŠªJÓ0Êñx| ŽÜ ‡æœu~†Óì«¿¨[VÀà[FÝÀ‹ðÅå˵m·ß†€?Æïíû£)ç¼CJù~!Ä/·O¿¨”ò>!ÄýÀãøÍŠ¿|Ø ü‡¢ž,~³”ò½§âƒÎ•@¸NŠÝý}[W-ïyïÁÁÿêÒ43•J‘ÉdxüñÇÐ —_u%®ërèС Wo```bªØþ}{I$’4·´²b•_¯Z)OäHÜ¿Ÿl6ÃøØ8…|n¢ƒ´¢(Ò0Ìj<‘ØGî…ÃÏç¬ó]ÀK€/×\ß_Æ·¨†¥”ŽâÅ9ð]ÅJmýãð¿÷ºøà[l¨×ÄŽO»¾¨ß\XÛÿ߀zCÕc—¯=ÅÂpÒìîïûáªå=«tCÿ»Î®nÇÉçó<òðÃüö&Ä& “H&èìê&™L‡)—J~g&Ëþ²Ù éñqò¹Üd#UEÁÐu+žH‡#¿ G‹-ëüÓÀjÖQ?¦õ.`ø¿5+ëà6)åÞZõ–©#3B¾CQ^¼_|âBˆ×NÙ¯ŒoÍ­îÀ~àjà“Sö™Wá 8%ìîïûûÕ=+VºñŽ¥´¶µ¡**ñDœD"‰2)Šä²Òc£ô÷î!—ÉNSÈOΩB ë†H&‡Â‘ÈÖp8üÍÅžu.¥ü™â%ø±®ƒÀ»¤”ãÀ¸â…µ÷¿„oø"ðP®¯3üí”Ó~ÿïÿ÷¥”·!þXƒ/H—Õöûþh¦À´×ço“RZBˆ¯á»’óJ0å‚)?§u«Vßµ|åÊ+Îß°‘R¡X³¦üØT:¦T,Nìë‹”î„Âá‘p$òx8þ¯;îºó xû‹!ÄcÀ¿K)oXè{9‚)?§ Çq^;ÐÛûTÿž=ËëAs`"¡3žHŒ…#‘máp$È:_8Þ‡ïîµÂpJÙÝßWXµ¼g“ªªè†Ñ …žÇ?‰Æ¢ šu0‰”òÁ…¾‡“%®€SÎîþ¾ ~Âc@ÀiA9ö.g2Bˆï!:fÙöY!ÄkNòüïB¼ådÎqª „+ à,Fñr`U} Î \¬š²¿8ËÜ üB?cO pœÝ¼ZF¼⯄Ï !ú„˜º“"%„¸È !žBœ+„ø!Ä?Ô¶¿_q›ÂBÜ „B|SÑ(¥Üƒ?çñåóýáf#®€€³› €ÝµŒúßþ ?Û~*ÍÀ7ñÓ 4àUøø\³¤Þ ü¸x)°ÞâëjÇï6œÖOrÂpvÓ ŒJ¿ûÌ{ð­¢[€wLÛ/ ¼ªŽ†?½çWøYïÃÏŒÿ¾kÙüHQëçˆ?5hgpœÝô Bˆþdéo®B„¦ì÷ø™ôŠ?H©‰Ý7ð3å¿_›¼ý[ü9Žï«ŸúOû§™#A:D@ÀÙÍcÀF)åMBˆ8À;ku¶ÃO6½8_¨n¬í~)›—1Ùñ€_¸°¸¿öþFüêgpœÝ|ßýû;)å€Ã¦LI)ß?ååiRÊøõ·ê¯àj?!z€’”ò¾S{ë'Nà*œÅH)·· !NãeÎþÏi<ÿqX\g9RÊOŸæóßt:Ï"W@@ÀYG \gpœuÂpÖW@@ÀYG \gpœuÂpÖW×ÿÍg”ëÿæ3'R`. `A2ç17Þ|ËfEQÿ}óÅ›“ ¹ù–[Žô°Ò6P[ÇV„RA—Ræ‘ä@f¥ßy\JYð<¯(¥,»®[t§\)WJù|¾466–ýÔ'?tô 8%µˆB|Ú¶­HþÿjKé·?®-¥ô·yÒCøûKDí?O ªa p CwLÃp“©¤×ÞÑ®ýÏ~¨¹Ž«Kð„.à(Šâ!|QT[JAAr%‡ -£Š"F„à ŠRÊ‚ë¹e×q‹U«Z*—Ê¥ƒf?õÉOX ò æ@¸5¢SzòÑšA¼¦ló¦ï3¹M‘È0R†§“KÏ“”+ŠÅ"åR‰R¹L½)±¢(hš†iš†®ëhš†®k蚎¦ëhšŠ¢¨RSU©¨ª‹Fe"÷ÚÚZåÏy“'„px€ƒŽ@8B`KpÂBXª¦äA䀬”2-„“RŽCžç夔EÏ󊻞۵ã#þ½ßLÀÑ „k#¥4\Ï›U Ž&^3m« ÔûLÛf†¡ÓJâIIµR¡\*S±,,˦Z©`Û6š¦¡i*šª¡jšª¢iª¦ MS…¦jЦ©µmþ¾ª¦¡ÖÖµi뚪¡¨~XW Šª ( ŠE!ž‡ëzÒó\Å2·üê××_{ÍËÿuA¾ €Y „kQ#-Ïuç&>³îsüâ5ýx]7Г:ñÚ6×õ°×qñ<OzHÛFJ‰DÖÜV)ë‚:ãú‘ûjRCÕTU9Lì´©b§iBSU±rÅŠÆ­<ò¯ÿ›Ï|ùSŸü„7ßßNÀ쵈‘Ë·¸|ÉËÄ=ו¸ž‹çz¾xx®+‘ÒÃsÝÚÒóE¥ö×Åó$H$¨ª@Eø"¡((ª†ª*€àñ›&lºê[XS·yžÄs=lÏÆuTWÃÓ¼ê!ãðufyúñÉdªqÃÆ k€gì‹ 8‚@¸7¶çzâolaãK¯Â‘àJ‰ãͲ”àzò¨û9®KÕªbU-ª–…])c[ìJ×uð<_¥+ñ<Ï“HÏEzž¿¬‰¡ÿÚé‚ëôÀs¡þžç€, "QèŠ@SULM!¤i„C:†¢bê*ÑI,"6ˆ…C„BaBáRŸ.|L¼VUEÑ4-±Ð_TÀᵘ‘T]Ït鼩?Ô—²öÞ,K&—R((F] !"]‚ãIÜÚÒ‘“ëS—®”¸þöúr†ý&–3î_Ï©ï¹nÑÂËÚ¸VéØ;î£ M t>²6Ä«6­™4¦6൯}$ñHÔŽö:©ëzÓE›/ZrÍ5¯´MÓ$N“Ng„eÙŽÂB8žçYžëZ®ëÚŽëV]׳<ϵ׳\בÕrE”J…!˶?üÉ¿þ¿ùùø9S „k#–çzÔ]4×¶"Y"9!PSElRà$S…îH;Bä¦;ý¼ÓSÎpìa˯5åÜõsW3qUŠb;¾Å8>} åQb±8š®sÇí·ßðõ¯} &…(r¼Ï:ŸÏñë[oC( ×¼âdÒY¾úå¯`;ª¢ ¦¨(ª B ø9'® ­m-¬]·à Q©Vÿø£ûøÛ>÷ŸýÞñÞÇó…@¸1Š¢]×Ï •HËžAD¦XQµ×rºˆL}Ÿ£YlÓÎ9å‡û±›G9Óy!˜¶Ø€ª)¤4…˜ªU¦èÂÃõ$Ofª]_ɇGùÝx ijjžç{*žw©Tä‰ß=ÆyçœK±T¤céRÇA_ À÷€AâI‰¨ÿƒâºìÙµ‹R¡À†‹.bhhXxž÷a ®€Å‡'½´Óñs¹lךAf¶„f¥ÃçH‹MÎ b3 Ûä±³[lÓÎ=Ë}ZR`ï†zP°<< T¡ *R†`}¯àHWs¡ÇAÓNÁŸIͺKg2ìêí)9Ãÿ}á ²¨ žHÏÅuýŸ«V±ûÙgÉf²äs9 Co<ù›:{ „kq3*8Žï.ÚŽ3w·™b\GsûŽm±Íäîý¼Ç¶Ø º” ®+)IÈW%£%AKD£;¬£ «“Óvmꃎ㠪êI?èz¼Lzžç'q첿±nn‰ú\6€PüYǶ±m›B.‡aš!Š'}Sg1p-nF$ø£{k2Æu,·o2Fu :ŠéönEÉY¬­Ã,·Y,ÁÉøÛ‘çøûƒE›°¢ÐRqådš–B}ÐÂvìSbqղ݄H$ºiNLŸ‚IÁ:ì½Ú²R.cÛ6ЦṚ®•Oú¦ÎbáZÄ(Š8$Ô³çmËžÕ:ÌíãHñ˜9P?imÍÍí›Ù²šYDOF{X£5¬ÒÖh )Ä õ¸²áËícš C”ލŸÌÁqMV†BLLû™»k8}éoV5­VgLúS&ÔjН8eµXœ5ë!}‚ïyA \‹!Dµ^! àHª±Ÿ¡ZÂ*­!•¦ÚOLWˆé‚ˆªÒÄ ê1³ØÍtŽ8Çœ­­i¸ŠŠçy™íà[JǧiK¦šœ<Ϭ ¨GĹ$¥)Â55È/„„+`ñ2]¸T§:ã~–',: ·+¢šBTWˆj‚ˆ¦ÖaUR!MA ÖjMÍæöi±q ›nu;ýáXxB«§Lˆ‹ªis©™®¾“>E¸<)§åÉ©]¥bib]U”š•@æÄ?áÙO \‹EQæ$\Ç“·=òöÜ4Mè hB )Â5E  _j™äµýkù™Hü¥_ý”Z¥S&ÊF»^-vBŸÂ§J N«Ø Õ üaIÍÕúª[\µà¼c;xÒ« ¬_¦È÷ÿ/€J¥‚®ëþ=hS¾«±“øˆg=p-rE)J)'þ%W¬Ò1Ž858ž_^™“’˜Ó„P¦ Òóü×\EêˆE?.Wï)¥ôˆÔ­8Ĥp {¯P,àØ~kG_¸|‹W°¨¤UMÃ)ûsvEµ Þ‰†µŸxŠ:Q™|i=š«è/¦XK3íS òºæÇß<é©G- áØö„hÁÔ¬y…¾ò¥™}öEB \‹éÉ]U)Õ-ŒJÁ/r¾ˆ‘BÃqlÅI”RN¸Špl‘šIØ$ (~GéMéY¿&µi>r2V,nýªª?Ú©(Êh¦Î/p-rTU=¨éú¤kTÎÂ¥hØŽDSkõøçà*ú‹£[Í=Tk®¢”>ªxĈ"”ˇW®Qj—¢ˆÅm×™ÂÛ뀧€OöÑw?u˜¦¹OÓ&…K–ò~qöEŒ'4ÊB'âúÓ]Eq|Ö—'¥ßÍgÆz\3¨PžÁâr]¡(‹û "®3÷_­­¿ hþx¾.ÅöëS,.·˜ ,.T,¡`:‡[\s)qøRJ‰"E©5¿õ&,/q¤f’Ê4‹KU5\ÇEbqAµÐtŸ‰7ö-”Æ·ƒÐ^5Ï÷1C!é:®ðŠYDµŒÔŒy¾3 KÑðê#xSb\GµºŽ2ÕÇs]4C«Y\uÑš2Éz u1«TOžRÒfQæ!®…æÿ1Y¸Y~ŠZ=òyU ×us†iJ×­ W!ZÎãDRóygŽ˜´BÇA7 <Ï›“HÍ´Íó<UñS#¤ôô3¦öûncµZ=,%@UÛA1o¡„3•@¸Ž+ßÇÍãeoµ?í’Ýóy®ëæB¦é¹®_JJIÈ)Sðóyg¶2)\¶m¡iÚD®Û±DjúÒó¼ZP}JŒ‹#…kªÛ8ÓEUQ©ºÕ@¸„k¡0€¯x™ŸƒWFËëÛöÌç”Ëå\(dº®ëjà;.!·Ba‘ǹŘ.˶‰‡Â8Žï¡o-.?Û_¢N¤Wx~œë(“)K3 —ªâºBë¤?àYN \ ÃG€µ²ºYzÄGk®o›W‹kxhxÜ0M·Ï.½E w”Éé5¶e¡ÆD­x˜Ã”Ÿ)ûÔ'l+ŠÌ0ª(¡Vx~âúÓGa2ĉÍËz—ÏÔúN÷3éþéâ¥8ñ¦˜®yµ¸þüCïwþñ?ïN§è8‹^¸Ü)ÂeU-ôÚ<Ác‰ÔLÂæ8¾g§ÖºeÏÞžÌÇó<*•#µIÕêÁyŽ,ß±8ÐŽXYä  ¬§÷rÁŠ{NWvò°,Ü ö¡Éw'…ë“ÀŸ×Ö%°ø~Ž×iA(8S…KsEŸËåŠ)ÂeY¨š†v Wq6ë«ZÛ®ªê„ÛXÐO·´ žxzä¯_}TQÕ_ëOÜwUX^{¹;.€÷8ô^þðB  ø+àïOÕ^ün/{ëá[&…«gÚ1—çpšzé)B±«á—3U¸ªUtMÙËDkfiô--EU'šdxS,®Ã¤KB¹4ó×¼X…ë÷]¥àÿã]ïÅ´5®Iþ¸±¶þz/p§J,”„N×÷þ%†—þ1ÈÃã«Þð?3SNÒün„¹ª“‘Ïþù[÷ž’{™FØ|µyDiuq —ê„kXµª¨ªzäDkæfuÕKרªŠPD­Ë7««8=ñ´N]¸Œ”Ûþ‰û®úø)ù g>IàÀ¦Úkø| \uVÜs½—ø“Ú;—Õ~N ©7ƒC–·û9[Óñf[¨I„¾¤¥ß¾ç”ÝË4¢!gj:4§ 'ßuþ¬ÆSt¼ºÅU©¢iÚDcØc¹†B¨ŠŠŠrb²¶ª¨èš~˜«¨›:ÑH!$©T#±X”öÖÒÙ4›6]HSc#š¦ñØcO+p]½ÁYÿíbäúÏ\vÇãpMeÅ=ï¦÷òG€N]"“†äAZx™ŸÌý°†ß÷ÍþÜÜ)»éÔêÔM ÜE?ÚŽ7ٿз¸tÓó›\¨Šâ'“ 1¥ç¢@â!êÕX]×sñ<‰] Î#®ë"¥$‰pÝßHKk …BmO>I{G…|žôø…\‘­[¡Z)S*•O§9÷œsý‘M}QƇ€÷|æ²;~ApþHVÜóuz/ÿ6°?®¤ý€9¾¸aþƒ´ú¯µÆÉm^¼#½Q½>¼Â!2ß;­¯m7òi!DwÝuQìEB™©L~íŽãL$‡Ã¸®;‘P:±¬‰”tÁñ&_{®¿Ý±ü¯N&„k÷hޝÜp®ëa#üÎBB£¢…)k!Ê¡6J"I!• Øš¤{è;¬«ßOF» øïù* B€ê™Ï\vÇÄT§@¸fbÅ=ðXíçT ozÇFµãS‡m¹Ûñ²7¾·AI½@¢ÄÞH÷/~sŠîcFÊ¿þúÛÂáHw©¶ëhû$ž2ù§áØ6Vµ:‘=?“h͸t]\ÏÅž(ègÑ;ŽCÃ>@Þvqõ(eÅ èx”¢#)9%[Rt<*®$”;H4?L<ºkÄxä3—Ýñíù*gpÍðjàÃÀ…øQøµ@»t†ØYI½”À7Ó*ZB0Ž„© —œ§òÍg2RQ'ÖmÛÆ²- Çœ³XM}Ïrê—Àñ\,ˆ$Ñ<(VªäJ%Šå •r «C)fIäFiÊ Î¤aø9š[['ÎæýœaÂ5 âgÌ×¹¸{è°„Ùƒˆ¾üšâ›STm( M¼–•Ó’uqv1U¸Ûv°mû¸EËõ&]EÏqÑTÁŽout/J)GT×H¦a"¦I8%‹]ÙB|ã TEáé§wø'bQ7ƒ…@¸’µ‡[\ ¢áÍõÉ<5D‡ÂûÃáÈÄkYÎÏÇe ṨVÍ.‚tQ<Å.£ZEÔJ­”ÆŸÌg*uk„k¡X ÀáRÞÂøÅwÌçÍxž—‹D§W1b—qꯎȬ”¨VÃ)¢à!<Õ­"¬j5RÊ ÇÐrÃè¹C(ÎÉå–]õò—sÙå—ó/_ø/xÑ‹xäá­”Ëeöí߇çxlßöÛž|‚Ç{‰GgW©†–.]Æ9ç­Çu\ßòšƒ…åMkïæÉZ/EÁqœY3æ«Õ {_ü^ó’-üâÖÛYþø¬9ŵÐ7°Hñã[µÀ¼ŸŸ0\?ß7ãºn.‰zÔ'Õ9†W¥Ìñ —ð\4«€á–ÑM:¨NÅ*!*yÔR%?Š’BÍ ƒ;?Uˆo½åî¿ï>2é4zßûH¦R<øÐƒ457“L¦Ð4õ6`˜&Š¢ ¨*!3„'=ªÕ*–eS¬¦W,­ãM±¸&„kš1eYcáfÞ{õÅüѹ 8•ðäã?GcÆs.êf°×B1iq ÷¶|>Ì{.‚eYÙp$â2å÷Á”6SÓP…kaTó„„¡H4i£:UT«€RÉ!ŠiÈ ²Co~ç: !H¦R¤R)’ 475yMMn<·U«º^4MÓ5L3‡“ÑXÌpOz3Æ®*•Êœ-¬£”u‹«–Ç5m+ ¨V*8jÝ©¢ë:–ã‚tgš¾ 2®@¸†Uà[\JòZP~Yû™w …B&öÕ>¾ƒ¶ì3GæFÙCxÙÑ…¸=„¤ihðE©É%'‹9ªª–uC/麑Se¿ªªÛ›š›Ñ4­¿X,îÛüþàLçüéÏn¾é~Ì“ÞÕŽí¤\ÏSŽÖt ëh‚UGÖÄJU\»Ö€ƒš&ÕV¬ªE£3Ä?o`á®_ÝDç ¢Òa¿Í.IDAT%„à;ßùÏE?'+®…! Ä^Œˆ¿|+ë u3ïzÇÛKÿ÷Ÿ=ÌD²·þ|^®­( ©ÆFR)R 477ÓØÜD4#  ‡ÐTÍÑó¼owt,¹IJöå ù}ïxûÛžèußðúWý¿—%?üÑÏÅ•åºî5Žë4¹Î øi¢u¤å4;“®¢Š”õœ®)ay)ýºöÀ²_‰ûîMÒYŸùdB,ú.Ö×BñŸÀ5"~E}ØîÏÓR²f®(ªzʃMª¦ÑPsÝhljr›šœp$jkªZÖu£¨zè7MsÛÈÈð¹Àš›šXºlË–-#‘HÐ××Gooý}{¾ò¾÷¾çÑS}Ÿ×½éOðï~¯Ç¶OTªÖ«­j©ÍuÝ£ÞçÂDp¾Övª¥~3Ž:štIÍ&Zþ!p×Bñcü.?—ã×üº{Aï‡B4MKÕB MÓhhl$•j ÕÐ@Ss³ÛØÔd‡Âa[Ó´²®ëEÃÐ3 úÂáð“Í--Û–=˜Íf÷¿ëo?ržðÎw¾ó³K—u"gÚŒ§œþüŒ·½õú¨•6úæ·¾ÝQµœÿS©”__È–žˆhÁ¤«(eÆ^Š–u¤ç§i¡p]ÓÑ4x"Á¾½{ñ¼yž¡µpÜ[û9#hnmýàëÞø¦Ûî½ëN5“NOˆRCcƒlljvÝP8RÕtµ¬ëzÁ0ŒŒêžp$üdSSÓ¶mæ²¹ýïø£·¾À±3¿¿¯ïzç;â»ðø·¯ÿ{³ãx«T«o*‹]žçÍœd5õ8˜ªÖŠÖŽÔuC7PA,Å“ÛrÈ+&ÃZ˜Œ§j m`+ëöŽ%<8pÔxë[Þ|ç‰Ä…ÝË—¿)/PTuO4}¼¡±q‡mÛûsÙܾ·ÿáœÖü!)gɸ¬!„mûéäÏþôÝ£øÓ°þò¿þû;‰B¡ø—•ªõ–R±¸¢ÞL·ŽEQÂϺ¯[juƒQ2ÑÀ«P 7â´µP5S޵Pˆ5S”úD¥Û“T")V Þƒ®X•ꢟ§pLá5¯ºfð§ wrãѶƱ?ºóÎ{–£ˆ.)e#‚$Ie)EZ*d„¤_*ò‘ÌÈȶ7½éM§eôííø¶ð×7ÜpÃÿ3B ›Êùò‡óÅüKJ…bÜq,a;­­­D"l»JcS#¦i¢k~/µ±ù{ äH<ÛÃr>ú˜„ÛÁð‹ÏYXßùÎ?yk2•ø‹þ¾sRÉ”~ñ%Ç­(Š2òqÏ¿üàß÷«¹žïl$®€ÓÂwÞy®‡zµ"¸RÂe5)<ÉD(k"/ÆÇÆQ”£Ç»Ãá¹lvŽÂåS,ÙùôζÏìì>ÿùÿކÃåµÙ|æ’|.û‚R©¼ÞªZËÃa³A¨š0 “þÞ=d2Ö¬YËÅ—\L{[+¶mÓÑÑëzD£Qn¼ùfR©Ë—wO$Ñ “¡C‡hkkÃrlªU‹ñô8¡PÇupK.ãciV®\Å…]¤ìÙ³'u`ÿà¥(Ú£]ËÚBÀ1…ëºë®kŒ'RÏv,YÒœLÄX»f ñD‚D<‘J5¤ˆE£í¡Ph‹ª©ÿ릛nÚûì³{Þþ‘|èŒ:•ˆ¹dþÌ•›x Á°œ¯ øýã=Ö¶m2™4šªOÄiL5¢hÙL×ój-ìg§R©P.•) ä ²™,étÇv𤻯R©4Dc‘X,ǶlBá7l ‰²{ÏV®èAÕTúúúeÏî]ô¬\Íš5«‰F¢¸®Ãž=½¬Y½ M×xî¹]d2ÌPˆŽ¶v›šÒeß¾ý´µ¶a˜cãcØ–iš477c[ccãtu-Ãq\víÙÃÈð°~æÙgvþ¼¿·wÆT€%Ë:ßÅ/1Mà ±téÖ­[ÇŠ•+I&ÄqbÑ(ºnà86¹\ž­[yèæ›o|éøÃçU¶} \§”Ï}î ŸÛtÑ…9[I–“AJé×·lªVŪb‰b±H&—§\*ú] ¥dllœB!ϲÎN6nÜ@[[;Š€G{‚W\}¥r‰§Ÿ~†p8„ë¹tuv!„ X*‚”D"1t]gûöm8¶G4eÉ’â‰û÷í£P,ÒÕÙ‰¦é ô36:N4!•l`ɲò¹££iV­\Žª© DQ¶íÐÖÞN¹\¢P(266ÆÚµk±ªUvîÜI:=¶«±¡ùãûØÿþÓ’M/¿ìŠo9®óÓ4‰Æ¢FˆH$š5«¸æšWÒÓÓC4¥\.“ÍæÈf3<ôÐÃC?ùÉ/¸í¶Ûfų@¸N)o}ë[?¹fͺë×sMM³Ö”š !H‰'=ÜZõP«jS©”Èeó¤3ir¹<…Bžb±HSS‰DÇq(—Ë\zé"‘###ÜqÇttt°bÅ 2™,465ÑÛÛ‹mY´··‹Å¨T}K­¹¹™CC‡Ðuƒ¶¶6ÅoA&¥äÙgvrðÐ!’‰k×­# sðÀJ¥2m­­44$éïàÁâ…/|š¡ÓÔÔL6›#3>Žaš455‚ÑÑQÌ®¦ÁøXš\>ϲe„Ìýý<µ}{ºµ­ý‹í­Í_xï{ß[¸âŠ+b®+ïó\wøIª±xœXýéOn8õßøÂWÀ)åcû«÷–Jů$S)Zš›Y²l)±x‚a ªª¦ô[uù½ @"¥‡ã¸äsyFFG>4ÌXzŒ|®€"†iâ8ž/¸t <÷ì.×fóæÍ n»í×\xáfâñ(£ccŒŽŽÒÝÕEcC#®ë°{ÏnÖ¯_eÙôöõÓÓÓƒª*ô÷õa;K:Ú‰Å:D4aß¾ý´¶·ÑÖÚŠã8X–ų;w’Îd¹êÊËÉç ôöö£:»wïb×s»_|ÛÚÚ¹hó&º»º±ד$ñ !´ª*å*–U¥½½•±ñqž|b®ëÒ××G¡P ‘L²ùâ-%EÓ?ú¡÷ýéWêÏøŠ—\ñROÈ/xž· Pbñ8ñxœX,F8!N³rEëÖ­£µµ•L&Í=÷ÜÏW¿ú%E>Oþàá 8åüÍßüí„¢|<‰,ilj¢±±qâ§¡¡B¡Àèè({÷îepðÃÃÃd3 Ã$ a†LžÚ¶ ˲Xµj5¯|åÕ47ûò_ÜøK6nÜ„”’gv>K8l²þ‚ PŒÑ×·——^ö@ÒÛÛG4#²{ÏnŠÅ]]Ë0t3drðÀ!º»–’É0C&†aлgŠ¢²¢§‡pØäÀÁƒ¤‘ž/¬fÈĶ,¶oßÎðð0KX½v !S'ŸÍñ“Ÿü”¶özz–ÅAÓ5Ö¬YMcC–mS,Ø·o/}}ýìÚµ !ñD‚d2I<‘$™L`è¹\–l&‹eUÇn¸á+ÍÓŸóu×]§ ýˆ7†yI,íJ$’f,GUUlÛ¡R.Q­V]Oz?ûþÿ|ïMóþËpš„+à´pýõ×+ÉÆÖ+éýu¹RYW*“¹|N·mKèºÆÁƒœ{Þz–/ï"•JñÄÛhmmaË%S*•xäÑÇctd˜K_ðB–u.£T*ñÄã‹'Øpþy„ö=õÉTŠX4J__?X·n¶e±oÿ>ÊÅ›7_B©RdllÓ4FÓt–-[ Ò%›É“ÎfX»f •J…þþ>–÷ô`š!¿ê©ã¢ë¦aP©V(ä 2°w€ööZZZð<¥Ë–¡( C‡1:6ÆùëÏ#dš<ôÐVžyf'ÃÃ~ˆ)‘LH$I$“$I4M#›I“ÍdÈd2Œ b|lŒB¡·©©ùÆû|àõsyî×^{íÒH$rq$[fšfs¥R÷<åÖï~÷[ÏÎï{¾ „+à¤øö·¿/ÖfG²/*–ò—V«•õ•J¥Ëqœ†æ¦f%ÑJ±oß~:–t°qãñ8}}ýôìeyw¥r !T CçñÇ' sÉ–-Pkžºcç3¼ø…/À²-hjn&144Ä@?K::èêîÂ4ClÛöíŒMÌår<ûÌ3Ä Î_†n°í©§X¶l®ç±ë¹çð<µëÎASÊåù|%í”+UlÛÆ …°,‹‡~û ;w>MÇ’%œ·~=Ý„Â~€ü©mOqþùëæ~ƒëºäó‰ñ¤oQyžG¾4Ϧ3Œ 1>6J¡X‰§ëz) õfôö¤–øò÷ÞØ»Ðßñ™H \sF!Þÿþþ‰¢ª¯²mgç:Ë4ȆC!’É$±X”î€r¹L*ÕÀ5×^ÃyçžC¡PdßþA2™4‰Dc\²å¤”ìß·—¾þÖ¯_ÏÒŽv@ðä¶m,[Ö‰ë9<÷ì.ššYºt)Rz ô÷ã8[¶\L:¦X*£é½»÷P©VY»n¦aà¹.;vìdË–‹±›]»÷jh¤µ¥™B¡À3;w …¹pÓF¤ôx衇éê^Žaè”+FGƈF#t/ïÆ±,E!"üêW·òä“Obš&m¾Ƕ9pàÕª5áê%)Ç&›ÍËfɦ3 "36N±P!¥®ëåÞ‰FîÇ¢_ºõÖ[Ÿ^èïøl!®€9såWþ]k[ë'ry¿‘ò/{—\|1‰DÇõ(ä³üâ—7±dI­­m¤Øöä“(ŠÊ† )Øúè£lÜ°ÆÆF8€‚–æf<ÏcëC"Í›/" ‚;vÐÙÙÉàà ÝË—£ª*™L–O?M[{+ëÖ¬% ñÐÃ[Y²d ¡pˆ=»vQ,•Y½f-ш‰@ðØOpé–K¨T*ôõõÓÚÚJCc#RJúz{©Ú6Ë»: …ÂÄcQ~ûàÃtwu2<<ì[ˆý}”J%ÌšP'IµØT¥RñE*“!›É2<WÀœY½zõ9!͸£kùò%ñD‚|¡@sK+/¸t 6lÀq]J¥2¶c …Èf³<ú裬Z½†Ž¶VB!“L:á¡!.¸à|ö쥹µEQÈf3üîÑÇhïhgÝÚ5Ä¢QFGÇgé’²¹}}},ï^Nª!…ã8<³s'ª"Øtá…Ø¶M:“AU5‰ÄDþ˜çyض…ª(D#Q4]£R.388È}÷=@µZel̯¥G|K*é[Ráh”r±P©lM¤FÈdÒ”KE@A74Ë4Ã#áhøÑP8ö­;ïüõ/ð+Z4Âp\¬éî~y4Õð“®îî˜ S,Y¶lkÖ­ãÜsÎAJC7ˆD#˜fˆ¾¾^¤„\6ƒe{¬^³ Ïó(‹üîwÒÕÕÍ矦«ìÜù¡PˆD2ÁÀÀÏ=û,+V®dyw¦iR*–è`ÕªUT­*ºnLXQÀä ¦û­Õ …"{÷¯—¾¾~2™ B¢Ñ(‰D’dÊ)Ó Q(Èe3£{ã#£d²iÊ%¿ñ’®ëv(‹D¢‡Ðwo¿÷®ï.È—WÀñ³²§ç=ÍMMÿÚÙµ\CQ)—K¬X¹‚«¯z9fÈô“Lk8¶…çú…ôÞº•‹6o&²oß~L3DSK»v=GooëÖ®cÍêUèºÆƒ>L<§­½P(tX¾m[HOb:±X×uÉdÒôõõÕâQ”Ë%E!ùódŠD2¡ëärù)"•f|t”l&[;F išc†Â™H8ºÍˆ„~022üííÛ·?¯æûÍÂpB¬\¾âóYÚÙ‰í8T«UV­^Í\ÀÊ•+…B¸®K©\!=>ΞÝ{8ÄB!Ûæ¹çv±ÿ~^ýªkQT…r©ÄSÛ·³fí:B!¿ÐéŽíÛ)ä ¬]·–h$J8"`Y6‡¢¿¿Ÿ¾¾>öíÛ‡ã8(ŠB<ž˜HæL$¨ŠB.—#›ñG÷2é4ãc£ä2Y*Õ BTMsÃf(kFÃOGÌØOËvék>ø`ùà´#„P7á·³û¥”rÛ<^»EJ92_×;^á 8aÖ¬XõÓ%Ë–½~ÉÒ¥KeÇæÂ /bõÚ5,Y²d¢í– n}ˆD"ÅE›/"—˱oß ]Ý]èº_4—ÍñðÖ­t.[ÊêU«ˆÅ¢„B!n¾ùf ÃÄó\úúú9xð RJTU%OL$s&SI<×#ŸËMŒîe2Òãcä2Yª–_5FÓtÏ ùp4úlØ4ojnkû×ýèGg\9dáÏ…º¸¸x%ð)åioc'„X üVJÙtº¯u¢õ¸N˜]}{®STñ€aè[ZÛ—Ëeyì±ßQ©V8gíÊå2–UÅu]^ð¢—P©ZEeÏžÝd2.¼pÉD‚æÆ™ô8¿ûÝã ¢¯¯Ÿñq¿M—ªi©ç_°D"‰íØä³Y²Ù ý½{ÈfÒ¤ÇÆÉårí¾TUõL3TŒ'»[£á[ÃÑèo¹å–¡{`ÇÇk+€5RÊ]BˆW…ï/7ŸÚÿn¬íûR`¸ø2ðn ß!}=ðÀº”R>%„ø'à)åݵõÛñK5Ô^xKíØŸß.ÆïI™’R~ãt?ŒéÂpÂH)õ­ëïÀàþ4M_ÕÜÒÂx&ÃÓ;vð#!¸ð¢‹hii¡R©b[UB¦I"™DëÏ;—»ï¾—ñ±Qúûûýé-øoò…ÉTŠ®în¢ñ8V¥J¾6oo÷Ð!²é4ãéqò¹,®ã7½QUUš¦YJ&ã}†½=©ÿzãý·÷/ÜÓ9iÎH)wH)onBü ¿‰ð÷€oá Kðq|ײøðGÀà ØGñ~$ñè<|KîYüy¯€GkÇþ _ ÿ ¿Áç€q üðiàñÓõŽF \'ÅöáíC+ºV¼ùàþý·jºÖÒØÐÈØØÏìÜI*ÙÀê•+9X<ÀîZÐ|``€J¥€ixRÒÖÞΪD’H$ŠULä<8¸ß/8>F>—›ì–£*Ò0ŒJ"‘ˆDÂ÷!DgíZõQ½¶|RÊ»„ÿ¨ømÚbÀ+jײ¤”ןÖ'pá 8iz÷ö>¶²§çÏ~W×ôpcc#ãããüæ7÷ó›ßÜ0‘m¾¬³“D2I(¦R.‘ÍdÉe2ìëï'›ÍO“Ïg'Jè EÁÐj2™ G#÷…Œø×IÖù¯Ïÿ[ñM|ׯx1p¸Bñp!ðϵc꣞“¥ §v±âÇ̺€1à¾åÖ"„ˆâ»žõc”ÚàÀ`ðzàøÖÙr w ?ëqWÀ)aO_ßÏV._ñIMÓÿ±kùr¥»»Ã4H$0 “R±@6›a|lŒþ={|‘§X,LœC(C7­T*u0Ž=dD̯ßqÇw-àÇZ0¤”;…ïþ¸Ø ü±”R !Þ |øðSà˜[©ì~Ll ði)¥+„ø1ð·À«Cµýöâ Ú“ø±³ ÀN ¼ _¸N¬­÷)"U 8¥¬îéùZgwÏŸ­=çrÙ¬?Á8“!®g›û!0 Ý6C‘á ë|vjÁø„”òˆ‘O!„)¥œSw !Ä–Rv ! )¥5e[XJYž¶p¤”NíõaÇ,4pœrÖ®\õ€ëº/ª¿€¦ëN8 ²Î!ÄË€I)o¡ïåT×ÿoï^l Ã8ŽÝa†DÒ¦;WnÊÈXe,4Ö쇕RS”,&JÙÚh‘XXÐX*Ê? ¦1“IlL± ‘DÆx,žwrbq;ã¼z>uêÔûÞ÷ܳùuνÏsN(E³Ñ¸²¤­}CÇŠå÷Æ'ºo˜Ýý×ß)ü?"¸BÙYøEu!„J’´RÒŽƶ´¸~»¤¾VÖ(SWy:×sÍg/ÐÒ¿±éGÿË’ê­¬S–®2#i0Ü–T“Ô/iHÒ¾y殕tTÒ IÝ’º$)Œ’´)­s0mµ4|8µ8gõg"¸BÈO/0efoðÖœsxÝÕ°¤Í?ͽŽ_™õ€÷ÀI»ÓÜ›xEý-¼:~¯ oú% « ‚+„ülū٦€mxà\Å[‚ŠŽ“@'ÐefÓÀp8m÷ià^dúèOu\¯€zªª¯”®ò3SØŠ7Gà·uƒs)pžàO„¸Ì•¤\ÃÛwÒþ,^n÷†€¥ésßøÑJT\!ägï7؃? ¢‰·ð=.Ìû|vá·Ë$u˜Ù(~5õxhfŸKøØ1<Ô>u¼q»”•u\!d&ÝÆ½¶›ÙkIë`ÌÌÞIZ4Í쑤ÕÀN¼×±˜0³’6mfö¬°î: ŒšÙŒ¤3@ÍÌÎ.îþ^W’tôÌù1ì7³·eão}Xå7á_WIEND®B`‚nut-2.7.4/docs/images/simple.png0000644000175000017500000003717012640443572013466 00000000000000‰PNG  IHDRüiN—sRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÜ8ê¸ IDATxÚí½wœW}÷ÿ>ÓnÝ{·i‹´ê«jI.r“Mqð)¡å„ðNLè$ä¡;„T‚M‡ÐcB±‰ ¸wã.Y¶e«Xe¥•ÖÒ¶»··™9ç÷Ç̽{·jÕ%t?¯×Ý™;sfæîÌ|ηžïJ)!Ä Rêßüïÿ¬îþ P@Ø|E)•B\|0Û€o+¥$uÔQÇ ƒ6Ãvgç×|¿X„€7[€'¿þY÷Iþ p3ð¯~»:ê¨ãÂ8Jç¹I)µAq&p`` ð0p0R¿ÝuÔqjHøÉPk üH±xðS¥Tø3`!p¯ÿYX¿ÝuÔqj> Dj¾›@®–ðÀÇ€eJ©ï !ÝÀË€¹Àfà3õÛ]G§†J¿¸Vñ>ÿ˜þ¶ PJm¨ùžþ×ê³Gê·ûøâæÿ½uv0¼ÎuÝ€ 8š¦•4MËøÏ(£”JùúRJå”Ry)e^J™w'_, ‰áDæÃú›Rý®žÚ3ôÒ[À?—Ï÷/@+ð+௔R/Ž;æu¾Š¿ xø{ÿåªã8á÷·ßy¿tÝ—K%u¥(J"]  ¢ŒÀ!l!p…Ð$(©. W¡lÀ„£”rÇ º®kišVÖ5-§ #iI„Ñ5mP×õ]דµŠëº¹r¹\(ä ¹={ö$¯ûügTýÉ`Â×qêák×#Òݽø™²]îF)Ïáâ/+äWÔ.j¶š}•㽕IÚ*Û!_È“Ï(–K¸Ž‹¦i躎®ë˜¦‰e¦¥LÃÄ0ui¦2 Cjš&5Mp5!\„p5M¸á!ÀFGakB+jš–Q¨Œ¤”$jDÁ ¢_º2çJ7/¥,8Ž“³Ëv>_È燇†SŸúä' uÂ×ñG‡›~sËÓ4µË¥ÎZ²ONì±ÂØ63#ûdm\×¥//ä)‹”Ëe4MÃ0ttÝÀ0 ]Ç0 tÃÀ0t }ìºaè~Ãð–BèèºwMóÖuÝ[š¨¸“(%J)%QRº®DJé*¥¤RʶmûžL6ûþ·üé‡ë6|§4Š…bÚ4 Ç•rJbŽŸöøšã*¬ †ƒCA %%vÙ¦ì”qlWºØ®ëíSc?Œû>Ú C!„”šæ%B¯нŽA7 aè†ð×õ1‹ß¦X,¾uÃÆ ÀŸÔ _Ç)w¾ãí‰[ný”®¬s`xÃÐqQH)QžÄCI‰R]ÓÑ|âh‚ªÔÔ Ý·ô''öÁÈ_»M7t‚zþ>åýé aG!e-Ù©’}|'ã;ÆujÊcŠ@ @À \p:½“þ _øB h>×w¾ë®»®žr{ •²¥t«¤i[ÔÍê ^†£ÀU GŽ.))–Ê”Êþ§T¢T,Q*(‹8®‹t%RºH)q\‰R®GNéâº^§¡”D:”ס $R¹()Áï\P.ÊUåCøûP.BºhJ"”DHEÀÒ0u“¥ „‡B„£B¡Ð(Á§Ô ˜b}´­®ë¡/üã—¬ë>ÿ™òiEøÿø'ímíí¿ŽÇãÑ7ýé›…‚ÁØp"Á† (—Ë^/캸Ry½v]º¸®ª¾ nÍ‹áÛMc¾/\0Ÿw¼ãhº®’#ɽxäÑD2™¼ùª×½ö‹ušU”]WŽJcÛARûàyëuË"`Xè!E@‚£®GúK¥påK¿£À•jâ±ršsMÕNJl»Œ[¶qì2n¹vÙ_B8e.št¢ˆ.<ÄSš†!¦&0u¥ ,]Ã’œ=‡®Ž6”R!èìì Óþõ¯£4Ö|âµëMÍÍ‹_óê+,_qFÊqÊÅýýýÁR¡,]×.º®Sr§à8²¨k*«VZ×ͤ”rD)gD"‘ÎÈÄí·ýzèÖ[ÿ÷˜ ?¡”⦛óºX,vS,Þ˜ÝÙá©rB 4|¡Àîž]ìÚÕÃÈÈH•Ì®ëN±.q]wܺDÁŠË8眵¬Y½!®ôBD©tŠÁ¡!†‡†Ö?õäÔ%þÑÁonùí3éTò̪Mmæ¬K^å²FºOA¾J»±W“jc–µÇN×YT÷O~ì„åx­dÒÎbôw2´RÜxšu«—Ó‹²¿ó½ï~÷ŸÓé´1 ¡àt÷ºT*¡iùèGèìèॗ^âW¿ø%®” ºæñIS‰>ñVæ/X@,Þ„m—I&·çóùåÿñoÿrÔy`\à ˖­øeC,˜¬A$fõªÕ¬Y³†R©L>—'—Ï’ÍäÈd³d2)Ò©4©tÃ0ˆD"Äb1ãqâñ8ñx#±xñX M×}µÐûÔ²,fÍj;÷ìsÖþxk®GIÂK9jS;vU¢JxoŸôµ©Tu»×nt»šp,cÏÇè9«çwé_k̵=óÿ çUcÏÍØßåš®Ñ]C ]y¹G²7oû <þÔSìÝú(èš;×p\÷ˆ³@Û¦XȳaãFÎ_»–;wQrœÑýîÔÇæ²Yžßô<ë.¾˜Yííä²Ù%RÉ÷ß=ê„_´hñÏb±ðL‡‚A¡³´V4¡!„@hÂÓ„V³.Ð4o?ˆQu^Nßa M£!ÿÓo}ëÛ Þÿþkw×ùzİ¥ëVhŽmO$Ý$D›H>5†tª²DM øDò3ª×˜Hþê¹½Æèïü¼%)("puJ‚ºðȯyK Øã“¾¬4*ÎL×u1týˆo´Bá¸.Ï?ûñXGJV®\IÙ¶=ÿ‡t‘Žƒã*”ëE(\é"IC´––föïßOˬYôõõ7^uLo˜æyG,FÊeR©4`€¦ÆFŸè‡‡p8¬Íjk»†ƒäÞ !Z€UÀSþ`!D/íx“RÊ=Œãçy¼RûŽÒï)׆åÛ®J\9™Äe¢tC°)Hw0 aR ­~Þƒj(t ¨¤§ê—  1Ë 3ìEt¡h¶4öøo‰ƒgN¢Žë G!XåwVù|ÛvpÛË0ͪfQUáEõùT·íÛ·—d2E¡P@º.†¡gÅ;ª¡TÓ‘œ`×®]<ÿ ÓĶm6<ý4ùü‘ñ/ sù=À>àÀ~!ÄçN‰:xˆ)ß „øOÿë瀿?jWÓr®ãK×Å)ÛãH:µ­¦îSwÓiŒ!¿R“K÷);šI4„é:+G)%›íé2yWR’ B6Âw:K¤«ÐŠ„%±R^ª²`ùŸ€ð€Ë_Žýäs9lÛÆÓE 2Ç„ðJþÙt:ÃÐP‚®®9†A,gþüù<ùÔSG*™¢Óìë¾|N)Õ¼¸¢rŒâUBˆ !æWÚ !Þ)„˜/„ø[!Äÿû„Ö ö·!®Bè5ë–âJ!ÄÙBˆË…ï÷¥ûðQ ï_û ´Wö !>îk À£Bˆn_‚¿Ã¿æã@ƒïŽ-xƒÞáwo>TF"^Ü\üð·Ó÷œ £ðè§\ž=½äUã}‡ãÀ›ª“8¼ñH•\ J5–ƒîE‘¤ÄuŽ–Jï^ׯÆýkH=Y0h v”LöŠ ¸šQqh§O: ¶mOØîJ÷ˆìøƒÀ¬íR…³†_QJ]ã;<®­9æZà—ÀRê#ÀýŒå0¦Ø¿h¬Àœp¿RJ}xh\[ð €ü§RêÀR`'ðÏÀïýŽÁòÏ÷°A)uWͱ—³€«×Ë„/ó÷=â_³ö÷Oõ A5dê”ËӪ窖Àq¾¶oZ2™ON"c$Š´ãR®ÙYÆÓz¤ëâ5§O(_¥Ÿöãw[…Åçóo~=ßýê—ùæWþG³*ùÇddéumóæÏçÙgŸ%Þ`)‰„ÂClà-À À;¯!–Y?~Š/9•g¬”*û£®25ϽÒ+ÙÓì¯ôha_ ±jüe~’Î Wó[Þ„Wðóf¿søGà*¼h’œäج¯ÅT>FÍÿRy ë°…•(‰R`Û¥±ªô¸ðÜdá®)Uëix4Ä7¹&qd¼)MO["j¤¿£4\å‚òÂiUÇÚÑðš'áÛÁqïcüvÕØ{³,cl}„ŸoH±ßKH·ÌÒù ÷œO‡C!æÍ›Ë¶­Û˜Ó5×qNpáºc—ž¬”Ú#„ø ðŸBˆ ðÆÚgýjº?B\áK¿7#¼änŸl¿¬é f‚ï_÷˾©‘ö×ÿÁï´š€À}GdOãÀ¯”R›UkÒ4mPÓ„ëºR–'å¾OMpà©©ÂsGÅ7¹t¯ àHÌš{ç WÃrŽë á=|÷ÂÑ:R)oX°¡û„롯lköwì&7¸ßSEwWz‡äIGx€®®.æÍK¡P$ °bÅòc©ÎWHÿ/BˆÿõmõG•RþöŸ !ñm䇔R¶_A÷Uþ¡7ù¶1¾z­€þéö+¥òBˆ³üpÛ“ÀY¾þP©óE_Zï.Å«$s£âv¼Š¿û繯ì×fßp¯Èz`“m×oûJ¼âŸJ©íþu¾P£q|˜ÖQ"¥Lkš^”Ò(BÚ ô@hr›˜éÉw$¼©;‹£áÀ›Þ_Tr%Z á®ô$¼m—1ŒÃ“ðÕ°š¨”Ó4klöt$ r¹Üdב“’ð5ñso,òq‚_açÅI¶÷½5ßS¾Rªèó×·ÕÚâÓíWJíõÉ ð€¿|¡fÿ–šs=P³}¸¯æ{¸»âó¬9æw“üeÆ•SJm®Yßz°{$¥L M”\WFüP¹\–¨œ@:uηCuà)&ÏÞôÒÝó))œšFŽV‘ð`ÛÁ`h†¤žz ^â˜aZãFôU¢l“Ûý›vÄ 8‘…hÒ¥FÙHžÔ„¯ãäƒmÛi]ÓË^×4²É­U‡ØdÒr²ðܔηCpàU÷UÞÌ¢AvMb½‹VM¼ñ$¼áeΔÜBTÕò1<¦aŒqÎmà1_áý“wë‹xßYóX÷¶ËÑ “gvõ²ñ®ßV'ꄯãPÈRŽëù|Ó  iùtj ÑÔAÂsS:ðÔÌx‡¦!Ì,RPëÀ›¡F8†ðÒuýNÑñ«é3&÷dR iÝÐ=§ãà:.U¯]5ÁÎ[‘ŽCâ¦ë¸uý*nv¼¨–at/YZ¹ÈðiCø=¼ôOŸÊÿ¨NÙ#ƒ¶ôjqéÆŸ7öîÞã9„L“-çÑÙ’£=d0+¨5µeÏ‘Hô1’_ÍІ?¼ûâjšWøÃ'¦iš^¶Ý É=iG ð«îè(é;ítcŒ“®–ð´xÛ_¼‹¡¶¿èY¦ÁP釴¿~ýfO—„Ù†—„RÇ@ )t\_š¹®Ëú‡]›FÍÃ!hé´… Zƒ:³‚:ͦ€FH׎ÐÇ¡å×£¦qàMìh7µËFõž”Ëåjݼ™“{â¥Ð5­–“J¡ÕªìøŠOeÔaghF%jp̲Öê*ý;„V}¹,ÆŽX,8ŠÝ‡Ýg¢†  ¦ ÁÒˆS#¢k„MëAC44LÁAÕóéRagæÀSx‡ —Q ¯J¥j‘Ì™’{¼ôÇO¶Ñj_ñÔײ|2Î׎;Ñu­Ré÷ô"|“›VHqm­Gá+ç6×u«¤ÌCÄ'•"UV¤ÊÓkÖ„ d€%4L¯âŒèBxõñ„×NøQŒ¹Î¡;ðŽ„™è,/cIózªŠ*>SrOÖFJ…aŽ'¼ªŽ¢sü!±(‰º¦!„N©XÂ4-@¡R:M“§áÊÎÜtAèñ:]ß~Våj oHûè›J‘³!‡{XÇk4šæu‚Z§¡¨üh‰=ÛŽ)À¢Pè†1Ú ÍTÊ×,¥t«U¢”’H×Á‘ÊOÆÓ4èA4ák'Jâ:.…B¾F¥×p‰&„{Z¾Ž£è¸Z¹V¥7ÜœdsTÃk“ʵ£ÿ[cÍ”W¡w¦äcçû¿°ªÒW†Ç¢¡é P¿2¯]uÜ ¹|nœõeàºBÓœ:áë8<ÂkZi4<z¹®{ZßéO’Qå»RÞš{RéîºÞ€MCmL m&ú骫…qu# C¯Ô¬¾ŽÃ&|AúI&Z¹pÚ©&^‡Ÿ‰t¯Øõ¾½®ûeݼZûŠªïmŠL»ñ…bt]Çql„vðu„#¦iR,¼øo9Ú^)‰ëV„¨¨’m&䞬C¨$ñhš>Zñª’~ª¼ÚB¡0®sÖqÜBˆrðu¦½ª† }4/ ypOó*àjœ„—rÚ8üÁ¤½°Š6bt6/]w2­Þû[GxÝÐp§Nø“ ­xÃ[ç×÷œÔ*½Ð¦Y}ÁU1[·á…éU÷Ù¨”M­=)ï/5И¦ë•œÜ†¯QïÇ/?V¥7¼L;D±Nø“Þè·Õþ÷×ë-AuÒÁ0Í>³–ðùL]¥:Žãx¥Ô+6¼_õf¦hÆ šOxÍ×*^›œí@>Ÿ›Ìß‚ãºèšV'ü1}J;¡Üƒh¸â`M?¬®ioâÕ´;i D{-ËômVÙT]£#|’«JXn&C`'ÙWÒªk^ÅY9Ɔ¯ZcÔûü$càuCÇu\ô€^'ü±c»‹ùDpåÁÊÙ,>3I{ódþ÷„&’fÀR®ëzZj!pl”¦Ÿ¾\è”…é;î”’FËš´×jTúZ^› Ù«„Ÿ¤”»®i¾JO¾Nøcõð3÷݈¾â`M¯B“´ßu2ÿ®ë¦CÁÐ(á³I´b7=½ …æ–ª©J?]ú­®éc _TRk'“$…I¯éÞà„ÈÕ LØ0‚Lßé߉ÖéZ¾¸zŠö'5áÇI‚A)]WPN³”Ç5B§/áѰ5Ó7mä^ú™äÖ×jº_¢Zº¡1e©ör©46Ó¯VÂ{Ûë„?&¶ÜȯAy15áCÀצiR¾/¤ƒ ¬:퀠“§è6ÎG³ª¤“Jb™“{’±ðá P ©$ºÒPHb‚€ÏM13“м¾ðj&Ö T{ùÂó¨Â¦Š2zóTM? ,˜¢}’cTlðh!•N%Á€¬•(¦rO{Ç£F.\Ç©–¦:”1ðã‰ì¥Æzö»&¦(b'…|aòwrt¾ªKxÇqHŒ$H '°ý ©V UclMvQe)]Âá0³çÌ¡½­í˜Wµ=8Ûmdò¦š»ÐbR'Ö2àïP¶’É›Ä$íÃx¥¤«¾à6àŸ˜b(ÈñƇ?ø7ù/}éËÎØ1ñÎiOx[[C×õQsûPTy!ª"^ÓÉóèÇÙñãn&Aú„¾T*³eËf6oÞŒã¸ÕiŸ¥”þl&rt*èšyß§Úæº’@ À믾š .8ÿÄ©òé;Á-6:ÿuÀš¦½œ9î˜uþ#þÒIóv áŽ17¦‡>MÝ7“ðÂ'¼aš()i ¼ðGÇUª5뺟K/%Rhã4Oø™–A4¦!Ö@gGqt]§·womÓãOøB¡À×®¿ æÕyâ …7þüçÜvûm|þóŸŸ´©ãì¸ô’žò¬I¶=3ݪ…—6EꦱÇv|áR¢¯¸g•¹wì£+õà¾ôÙ‰¼2ÚÑÚ>ÎÀ5ì~{n&¿åxÀ4^=Ö9„Ãé©›Õør©„iøß'³nÕ 6š^ˆMÔL÷¬¼)Õ\×E:~Ú2×uQ@ss3ë.8ŸÎÙºÎÆgžåüóÎC×uvìØÉÓžfáâdÒY†††Ù·o/-­mþ‹tlf™’ðJ)¾õíï044Ä‚ó'Uñ3Ù¬?ažôÔTùÞ¾iÆ2ï߀ï}ïû¼ï½ï°/j ­FS÷Ÿ°·A‹@è ß±÷+_%FÙàNÐ$¢þôo… óÑÔWO–—;—©ꄗºUdår™9]]c5×õgݸ¿Ïvì Qq]—œã`ï‰;R¹L±PÄ•.·ßq'ÛÅFCê&÷më¥`E(!F’µV`ò¸þ™gžaÏž=˜S̹U)©ëܯç%ŠÊ¬™¢Z—Ûf ÆÓO?Mò-oáD›ôÐpè-¨üӨ⶙iÍÖ\DìJP%Hþì¤úwLcì Ö\û´'¼ª™m¦\.£ù•l§"ùTdw¥ë•£ömu×uI7ÎCÍ]ž\ ;¡lÅ(£‚ s$yG‘s$%WÈàâ­ÀåÊñ•ð[¶l=˜MX“dP)A4‘ð’é ðäOpÁ…cç¢+»áý náÿðòI¶=|ˆ÷áåcŽ ýr`¥*÷‚1Î{ï¦&J|¢éÏá/7ÆÛñ&×ô _®^·joã8Î!‘|Œ„÷GËyj¾ÄÍ+Ï!ç(ò¶Äu²dS.ÛäKYìB‘Í-$iÉ ²hã-ÄãqJÅbå<‰ãJø}ûöì š0¶~IR¥@úžÌL­µeëÖ „Ï•›w²èþãRÄÒ}é3àfƪ¼á ¯îÐß(h|ãAÛ‹èËÖ\€4¿ï\šßwR?-:߸ Ö×!ì:áÑG%¼m—qÇu™ì®ëçõ¤ž ³2l{ôœô0 @ê#˜ ìÚtiš˜–…eGBÄ–/fÉÒ¥<÷Ì3z_Âg2Ó‡=¯dÕ—I%:'*ÕD…ð¿üŒŒœÀ0¶,L /z dñ.´à[ð¦¢ntàeSµ×âWUb‘ÍIŽ£ÂkZ&P*ùs_ÚùӆךSB/gÐÝB9×Fw †zF}R¶íظŽ;c’{Q(ésõŠo*Zœ,­}O‚”„baÂÝDÎÂ4 òù¥R‰R±H©T¢\*RÈçyä•ð0|\ 0¯¼@€”^&‘T~ípY-À¯”·]ͤxø )g`â Òø§ Á›ËýfóàÅiÚ à[ÀS'é{? …j_8õ‰\Îa”³hÊA(Í)¡9EôR½˜ÆÈ&0sƒ©~ôÒ¨ ƒÆ¢;¶M©XDÄY7)Ù]×ËÇ<ã‘_ #F£¸ŽK2™dxxˆTr„L&sÐÿí'?þÁ‰‰ÃO)•Äñ ÷)%=½”Þ°@©~G NöX¯Ý?¶# ®@„Ï™¬å²ƒ´?|ú¤Õ^5m  AÒwþOR ¯z9‡^΢ázDv‹h¥ÚÚÛ‘RR\_ o_Ù\–[ó¿U[Ù²,æ/XÀù^H©\:$²™œÒ_š6ÉŒ±•>AR²^\u5­Ý«pü#»N †A´!:ãL»“WªèZìÊéFÌ-›¦ýÝÀÏOfE¦¡¡¡7T3ºÊÂkN ³”ÅRE4!Ñ•ƒnåðÄ@VvZóÛ§×ûçïø³ªÄºæš¿º£««ëÕ±†(šX–5¾6‚ ³4À?ÇŸ½ü%À~ô“ù¶mºX(¾¾\.wxïòá UUûÅ„s(¼\ý©`š&¡P¥d%F_>þ„÷“f¦JœI§ÓìÙ³ I&›%Ú›ÐfW´5|€5Ía,Ëšô'Ôˆ7ÛfûLZö÷a¶_æ·ïÁ Ã2øðÿ&ý ß|¨­­ýª±¹¡P˜Æ&O2·¶´Êæ–;ÞØh[–U0 #Ó¦i0 sskkËS†iî)•J}©Tjß{Þý®£-Ž[à÷¼û]{ð2#ÿú;ßý¯vÛ±?U*•ÞœÏå»5²TUé51I§¡ÐuÆÆÆêôJJжMR’‚H/ë.¾˜·mñíÒñ'¼RÓz&‰‚gžu&=òÈ„QuŽãÐõ¶÷“Ûþ,ù?Ü<)áO´J(>/àjà=@¸àÜ®9×¼óÝïþ^&^#• X¦U!óK¦i¾0«­m½&´Þb©Ø—N§ûÞóîwÕë„”üþë¿zo?ðQà£ßùÞ5;¶ó÷¥Réíù|~~¥Úï´„¯LÔéÛð^­zAÁvÈ›arñfJ¡FJÑV á2‘f2Ñv2X8R±ì±Ñ=0@C4ÊÈÈHú„~:/ýìÙ óìÆgE#477O8~保*ähëh§X,¿À‰wÚòxÕoNY¼ùMo8àw\'s§7_Åš{î{à  hš€¼*®iß¼Ú~Åe—ô3ò_óÞðIà“ßùÞEÇù„]¶ßU(ç—ŠEQ,«%± ¥À <Âû3Ï]K(ž{N´ÇQ¸Ž¤ä(òŽ$_´‘C}„÷>OÃÈ~B™~š†{.îâÉO§"‘蟟§œVÂ[–EGG'Úl­j¯KYɧ÷ìòhDR(Ô ØgלB¾Ž£€×¼æOæÎ;wåtmæÍ›w3È ¹ç¾öëñÆ.üèŠË.Ù¤¿ï†o|;,-òg§’©‹ …üšr¹¼8 ·X–¡¥’).^Ä’îÅK%ZZZ‡BìêÙ(Úfµcš–7ÃOC+®fPܳ…Ò@îð¬áýD²C489,]“š¦>ààË_ýê¿í=Ïá°vRJo ñA2íNV§]Ç÷Ü÷@3Þ,= ýå`ᥗ]~öŽí/N{ìàà ±|ÅŠ™j s7Ÿ½ûÞûÿóU—_ú™»XžJ&/Ìd3 Å3Ì/r ÃC ŽDX³f çž{®#¤»»›L&Íþ(—mïÝ–Š—öïç>–eðü¦hk›E~÷3䟺K…(9zA)õÃ_ßô«ŸÌä7¾ï}ïë4ÍÀF†G®œ·`®jll.”Š”«°,ãwŸÿüçG•ðÒŸÐþX ßJȯ.àOYbkÀÙÀx…;+MÖ¾oß¾ƒÛMù<étšX,6ãß¡” oÞ¼ùS7þì'7üð‡?píK3ù_Þõ®wE ˺¿½­ãÜX¬A(%ihh`åÊ•D£Qÿ¯eYÜsϽ™íÛ_¼îýïÿW á‘ %eM¸áèCÕUúS•è:ðà#¾=#\~Åô÷÷S.—0}'®”Û¶Ñ49sæ0ÁÀÁN¡P —ËÑßßÏÞ½ûpì²èèìúâÇÿöËÊ¥Òbôڒ©¤ÑÙÑΜ9séÔÛ Ö¬^®ëÜ}÷ÝìÞ³‡ÞÞfÏîD (•lw/A––fLËä±G%ÐcijnÁ.—Ù»¯Mh477³B8°Ÿh4Jkk+ÞÞ^–ÅE­3…Ðþò‡î×G>úñݽ»wßôܳwN÷¿-î^úç­m³Îèï§X(pƪUtuÍ!‹Ñk  ‡hiinèî^ü¿ÿýmŸÞ¶mëÿýøÇ?þ»#—ðjtÌ£ÏöÉgجãäGÿ7¶wt¼íP‹D£,ŠŽÍy8Ðï§*ë:‘š}J)Êå2¥R‰B¡P%x.›%NS,)‹ò"‘0466¾oxhˆxWk×®E×?ò(óæv‘Ëå(–J 6°úÌ3Y¶b9Žíð /‡™5«•p(@6›çÀC!Ö¬ñ*o~áy‰aºæÎ£¹©Ûqé?0ˆnhtuÍ%³c×.fµµÒÚÒL©TfÿdóyÖ­»H†¹pë–-·lÅŠþP8òÿ­ê‰?¿é¹ u¥bñ³½»÷ i‘H˜;wÒÔÔÄ‚… ¸àüó™5k Ñ(–eQ(X¾|YkSSÓ­ŸùÌç>û¥/}ñŸ߆÷¥û±’ðª.áOY<øÀýîE¿ŒÙsæ iÚ«Z@Âq°í2Åb‰|.G2™"N‘Íf±ËetÃ`xhˆöŽº/"²eóžíí¥¥u9ç®=‡|!Oss3Ù\Ž?üáÚ;ÛY±|9ùBžl>GS¼‘††(ùlMÓ ‡#ƒAÚ;:è?ÐÏ“O”R465QÈ(—KèºÎìÎvR™ ƒƒds9Ú;ÚYsæ™í;wî¸á¬³Îú׳Ï^ûýYmm_üЮ­†x/yù%ŸBüPJ©e2Y"E6›e×ζm{‘Ù¼õ­oá¼óΫÞ …Bâ¯xù—>ð~ã×ïð:¨ÓîÈ…¼¬ô.uB°¡– Ö³·wó.¤¥¹+˜t6!]׫“4 ÿ™»®¤X,’I§dxx˜äH’R¹D&!1ÁfϞ͎;8÷¼óY¶´›²msûí·ÓÚÖF±X$ rÉe—qöÙgSÈçÙ»·)g¬ZÉ¢… Ù¾};ºîÕ–/KCeH¦ql›¡!ævÍaç¾>w/¦½£öŽvvîÜÉ–^`o4ʺuÒÙÞÆSO­GöìÙÃîÝ»fûöít/YÂùç­%Ò9›ýö#]I8$ÞGþ~ò…­­Í,\¸€á¡aöîÙC4&JwìØñÁŽŽÎ¿¹þßþµ¥‹]{í_<ðð?¹ôÒ×Þë:¹+%/Éårz%?’Ëøæ·¾CÓÿü’+_uóÌÇq Ã@ÓÄ'Ã$¼T~>ýDéÛÒÚŠt†˜3§‹ Ö¬^ƒm—I¥3ìÞí ìŸ7o>MMìØ¹ƒ‘DbR§¬ øS sf/þóLvà®ë.I$x“.477ol¤!Åq\Ç&ÎÐ?ÐϾ½ûèïï'‘HP*•) ô÷÷£”båªU¼ò’Wkˆ¢iwÝ}7gŸu©tŠ}ûzI¥’ôŽDéÙÕÙkV3î<¶nÛÆHb„p8ˆeƒ¡‘I i:ƒƒƒÌ™ÓA,£`¥ ™I3»³“`(DKk3Û·¿ÈšÕ«°m›ùóæ‹5048ÄOú3Ú;:X½j-­­]cd$ÁðpÓÐÈeÓ<ú裄ÃQ::ÚéìlBÓ `š¸RÒ348ÄÃ/<ÏÐÐ0###D£Qbñ8±Xœ Ö]DC4ª%‰·9ŽÆÏ‘¸ÿþÛú€+Î=÷\3^kÛö›Ó©ä*Çqš]×цn¹õVJ¥2ÑH„‘‘RªÕS?ä°Ü¢ ±m›!V¯YÍg) ¼ú5WrÇwqÁ ‡xiÿK\uÕëx衇yûÛÞÎ÷¿ÿ=ÜšòÈ£^M¥õ×qâSŸúÐ °ìßÿýßçišþ%Ûvþ$—Ë7¦Ri=™J148ÈÐàéL†ŽŽ6bDÂa†††9÷ܵÌíšKCC„ýø§ ÏçÉç ”Ëe^z©`0Lÿñ¦8çw>›7oFÓÑp„îE‹(–Ë(¡Ñ÷ÒK ‚‚¦ÆœuæžÝ´ %]Š…"íííÄâ1úô3’L Z„[ÂÄbzzö  ‚tw/¡X²)—Š¡ÑÙÞNks ÍMüþ÷· …X&¡`ˆu¿œÕkÎ$™J …™ÝÙ’’õëדÏeÙ·o»ví¦T*mh ‹ÇY¶|ápˆt:C*•ddxˆm›7344@.“ñfœùÂØY˜Ö¯_o7ø„ÚW¼öÕù|áõáphA$m XV0W(d„®ÿ~åòî/ψðBP"ãm+Y©Q7C Ë{ót©T ]×Y¾l9<øÀ˜¢ø©»cˆ_ÇqöÖ©urá†o|{V©X\“H$^Y(Ö–JÅeRÊÙ€ ƒìÛ×ÇÙçœÍ²e˸àüóX¿þi::Ú9ë¬5$FRM#—ÏqÇ·³dÙÞðÆ7`ÛÏ=³‘ÞÞ=\pþùÌíšÃö;ªsµõíëC7½l¶|±€°èÝ·eK–ຫVB°}ÇNR© -BJÅœÙìØÕìY­´¶¶pàÀZš›$Ã(¿MKs3¥’MÁw ôóЃ’ÍæXsæ™üÉk_KçìÙ”JE2é »÷ìáÀþŒ$†|›}1==»I$8&söÚµX–E&"•L1ØßÏ Ï=ÇððÙtÇu”išÅ`0øR(y¸«½ã›·ÝïA‹Ÿ*(·ûŸÃ†x1Ô1^R%Õ¤ú¶ã:?…P×tÜŠcOš[ZxqÛ6¶lÝL$áæßüË´¸ô²ËH&“ôíëc6L&॔d2ã&s«ã¸áºëþq¾œŸN§_^,Î,‹‹„í–e™Òuééñ†w.\¸€W½úÕ´·Í"³aÃÓKE’#Ir¹ÝK»±¬7ýæ¢ ¬Y³Mh,œ?Ÿ'ž\O÷â%¸ŽCSs ³çÌÆ‘’Þ½}ôô즭­ŽövÎ:ë,¶nÛF©Xdxh˜H4ÊÚµkI¥Ólݲ•b±ÄË_q1m³xîùç«=öôì!‘H°wß^šâ¬\±œ'Ÿ\Oss–iá8.»÷ômˆ£›aË+wFXºd ÿó?¿`Ós›H§3\~Ù%¸®doo/?ú˜¯–Ç…#dsÚ;gŽDH%“ì©M72œ"“Nã:޲,«CÑÇÚ»æ|ïÞ{î9¡£,B>ÿÝP(üå@À–“ÈI¤oÿ¼ò’KÑ5hC¹¬WÍu]ü±1i¹o}Ë[xöÙç0tB>?F_—SdÚeÒéäŽí/þ¬N½ã«¯ºú³±xã“É$¦iq饗°pÑ""á0‘H„P(Äõ×ß@´Á‹;§Ri{ìq-ZD÷’n””lÛ¶í;vðê+¯ ‹sÅeWг{È lݺ…¶¶YärYoÊ2K§wO/ç{6¥R‰ÕkVyaófRé+–¯  5°qãs´´4£: Ñ¡p˜RÉ&—ÍS.–Ù1°ƒW¾âeD—/Eß¡c—m ÅéLšxcœ¡¡!ÚÚÚX¼xJ)6=÷,œ.Åb‘þèééa÷†1M“t:Ž÷=@<'sáEëH'S¤RIöíÙÃ3ƒ$††Éf38®ƒiZå@ 8 ‡ÖÏž;÷‡÷Üs÷-'Û36ÞôÆ7|åw¿»íÿÚf­©aü”Ãc_Ú¿ŸßþöV¡֯Ƕ½pâ7Ž­ã˜Édøæ7¿I[{>ú™Lz‚cp¼„/ *—Í~øºë®«Orпÿ¥¦aR™´"“ÉLŽÍå°’IʶM `Ñân–t/BAGÇ¥¼°ùBÁŽãǹìò˺ÁP"Á#øMÍÍ,˜?–¦fb±({öôR.•900Hw÷bz{÷ñÜ /ÐÕ9›•+—sÞ¹çòÜs›¼é—lݶ Wºär9"á0óæ/à©'Ÿ ½­P(Èì9³éœ3›|±D¹\&Ÿ/ 4AKK+³;;Y¾´›{î}æ¦Fz÷ô²{÷n¶nÝÊý÷݇eYÄbqbñ]sç³rÕ\Ç!•J’N¦Ø½k'CƒŒ 'Èf3¸Ž‹i™v …#çÄfÿ÷ÝÜÿß§Â366lxê³ÏY{OccÓ:@T¤»œ"d–N§IŽ\³ÿÄñ år™Þ=½“•ðã‰D!›IÿÝ›Þô†ŸÖ©wbJÜièæesæÍŲ,{ì1 Å"g¬:ƒÆÆ8Á@×¾öµŒŒŒ ƒýæŒ3V‘Éæ°m›];wÉd¸ø¢‹I¥“\tÑE8ŽÃÎ]= ',[¶œÎٳр\&ÍÞÞ^º»»‰D"´67Ó5o.…R™þ={Ù»o/ÅÒ%KY{ÎZ¶lÝF¹\Â0-‚ºÎ…]<êCòÇ™k(šâ1ÎX¹‚[n¹•r±Àž={èíÝK¹\æé OU=å«Ö¬ñçt+ùäN²³¿ŸáÁAF>¹¥‹išN L†C‘ç:ºâ¿H¥R?zúé §¤Pµ‰/ÿ}ãÏ_ßžûàƒ\SÈ"ÁPÐ]µzMŠêŒ2ÞÜq …’•xýèvé—÷QHP^èÍk[S»^*Jå’¶kçÎF%³fµö¬»è¢_ïÙ³ûkûØÇÒuÚX,Y¸ðsºþïì9]ä E¤”¼ò’W²¨{ ñXƒ?颋¦ BáÏn|Ž9]³‰D"‹%‘š¦Ñ³«‡}}},[º„ÖÖV¢Ñ[·¾H(¢1g÷î²¹«V¦ ì²Í¦MÏÓÜÔÄÊ•+(–J †ˆF#Õ8¿”Ç.#„F8 X”ŠEöïßOOÏnzzzØ¿?J)‚¡q_rÇbq¢ QŠ…"©d²Jð¡¡!FF†ÉgrHåb¦ SHhs8½y$›üöƧ7þXž¯¨gºÕQ‹æÆ&mVKë]sçÍ»¼­³“L&ƒÐ4.¼ðB^uå•ýTW¡iU*¥Øºeƒƒý\páÄbäòijjDÓuzðA‘«Î8ƒ`0€eY<ôàÃ,Z¼ˆööl»ŒiY^øÑh Òu0tÏ¡¦ë¹\޽{{ÙµkÔÞ‡ÃUɋLj„#äó9ŸÜ)ÒþtO#‰ù\©¦aH+`eBáȶP0ø»†ÆÆoÜzë-‰?æçkÔ_ñ:ƨõɹhÁ·ìßß÷°aš«ZgÍ"™JñÔ“OḒU«V XX¦E8JºÄ¢,î^„eYŒ¤RÜwï}Ìš5‹Y³ZˆD"\}ÕUÜÿÀý¸R⸒r.O.Ÿ#“Í2§Ë@¡ŽƒÐ=g°’É$½{÷ÑÓÓCOO×ùA8!‹Ñ:«E‹—†Èe3¤ý÷î];I ‘IËæ…n2äÂ¡ðŽ¦æ¦;Ì`ðkwÝygÿéö|뾎I±lÞ¼¥Á†ø}sÌŸÓØÔÌÈÈ¡PW¿úOèš7wLY³‘Ä0÷ÞskÖ¬æœsÎÆ4MvîÚI¡P¦­½ Ó4yzÃJ¥g¬ZE0D…eYÜ~û\~Ù¥¸®ÃîÝžJ¾gÏ …"Bˆ1Ùi±x Ó°Èú䮨æ#à FFÕH®*`YùP$´;ŒÜÓb_ÿÝC¿ÝUªuÂ×1 /\xEc¼éæyóç7„"QR©$¡p˜sÎ9‡‹Ö­#‰P*•Èåò¤3)âq¯PcÏ.´ÝKÓÖÚ†®kD#ü ”‚¹s»üÜôÝôõõá8š¦U³Ó*7tt:M*™$J‘J&I$†I%“ ŸÜš2-« †{CáЃV8üõ{î¾kSýéÕ _Ça {þ¢kZÚZ¿9oþ|C7L2™ ±XŒsÏ;Ÿ5g®©ÚñŽã Ó2iˆ6°iÓ³¼´ÿ ,`÷nÜ(¥Ð4Í#v,î“;†¢*±+I$H%GªeÒ4MS¦e•áp_(þCØŒ~óŽn¢þ”ꄯãh’~Á‚éœÓõ‰®¹óp¥$—ËÑÒÚÊyçÏÅ­CÓ4†‡‡}•|==»IúSSëºáKmà ñ®ãI¥kÈí©å©T Ûö¢]BX¦U…CÂÁècF8ðýûî½çž“šLB\¼ x¸])å§ë@ƒRj¤Nø:Ž –.êþõܹóÞ<{nùBÁËEoi!Ÿ/P(x¶³aUu<iˆâ”í{Ûó–Œ$H'Ó8®]ya1-ÓÃÁpp}(ýÁɘ¥vÒ}øWà6`)Þ¤«•R…ãpí À”R·Ö _ÇQAsc“ÑÞ6ëáyó^Ø1{¶_]«Jîp$L¹T®Æ·=é"9’ J#¥7W›@`˜† …‡Ã‘Ð3¡@ôÆ{¸û'§ò½Baà‹J©¯!Z€O×+¥zýÎàUÀ]Àñæ:ø>ðSà}ÀÿúÄà€§ñ¦ ¿oØì+¥~!„x+°H)õÏþú2`Þ\ oæ›äôÛ@o¦¤ç€¥uÂ×1c¬ìêjÓb,[±²{ÙŠ•d3™1OŽŒN§F7 ¡n( ‡Â›B¡à/éôNÕ,µi¿ Ø ¬SJ=>nßG¿þp °ø´ßAü;P> |¸Ѐ7øÂ-ÀMÀ«wk•R¯Bü#p1ð¿ƒø*Þä¦;€ë€sýs|oúª[€ÕãðuÌ›÷íX´`ÁÛ¶oÛúЖÍÏGËã&1 ]F# ©p8¸%‰Þ<'9ç;?ÛøÓìipkrþr.ð¸Oô—ùR÷rà&¥ÔO…YàxE@~tø„ÿ.Â+½]Á·”RwúÂküm•ºb€Rj«ÂžÎZý£h®:áë8$ìÚ½{ã²y‹Þ¬ q£i™z0ÞÞÿ®±¥ñëìYjÓ ÏW™ÿÊ·§_ü° Ø \ „h^é³¢• H¥”-„?èä"!Ä£À"`p¾Ohðg5öáàMÓÕãŸûC@ 0¿¦M¢ÚKÔQÇ¡`[ï®»j^¼ÓJ)%„x‡oKïz)¥v !¾üô¥ÿß©_ü-pðo|Z±Ó'wÏúþ€u¾ép õý¸PwÚÕQÇѶç•RÉI¶”R¥žCó z>ð\íq~N(¥ìqíƒJ©¼ÿ]÷û¡ ÃT뾎:Ž®´ON±ýP§~ÈŽ?n²Ø¾Oì|ÍwwÊΤ.áë¨ãôV¿uÔQ'|uÔqtmûI'ë8ؾ£qþ:áë¨ã8“Ý·­Oøo©;íê¨ã¿‚ÚN`: í¥2‹ ë“cºŽ¥.áë¨ã‘ýhç`$¯¾Ž:Nq’.ꄯ£ŽSG꨾Ž:Nb‚mG_ðuÔq‚Ȩd®´Ÿì<3Ô½ôuÔq‚%þÁHz8ÇL…ÿu̘b0JIEND®B`‚nut-2.7.4/docs/images/advanced.png0000644000175000017500000013070712640443572013742 00000000000000‰PNG  IHDR.:¨¿ÎsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î< IDATxœì½wœWyÿÿ>3s{Ù{ï®¶¨W«»H¸!WlZ°1-üB€Ð[¨É‹|¿|05$q€€Á@ @›ŽqŒ-l¹wY]»’¶×ÛË”s~ÌÜÝ»}U,ÉÚý赚™3gΜ)÷3Ïóœç$„x Wºú™Rª| ØüØÁh‰ms˜ÃŽGJ\Y \³mùšímÀ§‹•R-„¯>Ôÿ|Yq¼%½9Ìa³GJ »€â«ÀCÀkßÕìß¡”úCuC)Uò øWÿ ¬z•Rö±u{Ç?¿åz]]Ý;„¯åC‘SSVJebH¡ŠJ©‚”²(¥,JGMÓ,”Ê¥Â;ÞöÖÜɾŽ9Ì>ˆ#UB¼ø < |J)•Bü7ð¥ÔÏÇÔß¼¸h>­”Úqì]ŸÃ±â·ßq£‚¿´m+%éCêº!5MSšRÍŽšK.Zš¶SÓ4S‘UÒB0€¢_¹R~^JYRÇ)š³T.—‹ƒCƒ™¿ÿäÇ 'ç Ìáh1gkšÅPJÕ;RA.ƒ©74h$Œ ‡Q(¬ŠI¡T¢T*1T¶m£ëº0t]Ó Ã00tÚuÃ0Ð C÷Ê Ý[×t}¤Ž®¡ëš¦¡ëB·CB(”RnÏJJù§»·)à pÊQ ˲¬»Òéô'ÿ¿¿|ÓÀIy@s˜sÄ5«¡,éÈQãþ¤§–¼¦#¨©$¯Ú¥fèÄ¢Q¢Ñ(…m;˜¦‰išØŽƒ”K©š6ÜuUsŽÚuC) ! „pÐ4t—Є¡B7t=â·îî/—+oò©§6OÂÙØ#®Ù EÅ‘Î0é R±l””š&Єp%CCÓÜ·¦é3"¶Ñ7ù¹ëB€ßïÇï÷R1mi£$(ÇÁöD¥ªÌ¤jþF¶©)Ÿ®n͵û ðÇWþÏ­¿l|ó_?7ÛãÂqÍb(%qÕ-XÌÙ_‰-–eSªT(›&¥r…J¥L¹\ölTl[â(‰r$Ž#q¤D*éH¤·®…Ä[*%JÊ‘?%R‚rQJ"¤D(¤p(C(t¡áÓ\Rõ:†Ï‡ÏçÇçóá÷û ¨@`„ KLL²>™¡ƒþp8¼˜#®SÃÄuýõ×›6¿äûñx< ÎÐu­n(ìÛ·?æØ ‰”jø½.‘(¤)PÇ[WRáH‰RÒQ$“IûU¯zeÆð¶Y1 ÅÁþþÿzÃë¯ûÅɼ³¦”ΈäcÙH¥ºŽ/F „ðGÁ‘ [£¶³T G2z©À®3ɱR)¬á6ªõFÚ¨–UÛrlÇ%MËı*8• Ò*#MÊ6ªä€ã¾g ¡,tº]h:øu CÓ0ºÀ¯iøt_øu€®ñÊ• ¬[Ôâ’™@x3@f„k®¹V@·L‰H4ÚòÊW¾bÝúõú•’Åþþ~©X@Á0|©ìŒÒ´!»RRJ ¦ÓÚÀûÞûúì ñÐO?»ùç›Î;ÿ‚­ÉT}rÁüt]G‚E‹—°hÑ"öîÝÇÁƒ±¬2Ž#‘ÒÁqœIÖ%ŽãŒYw—--ÍlÞ¼™-/Ý¢þFGJ¤#çg³Y|ÿ«Ûm·]}ÕUלì›2k ”é K\ - ©\B^2fY»Ÿê6Þþš¥÷§¼25Ù±Œ)¯iCmWÓQø‚ÃÒ’SC„µ„9j9±zKÀkסˆîßâd”H$ ž|â‰W~÷¦›ÖQCDŒ!¦šíèD·:“Épë-·{Wœ ë×ã3 n¹åŠy×[:ÏE>ñññ|ST}}#‘hD8Ž£²™Ì©œ—}þs×÷Ç·áEãúë¯×Ï;ï‚»’©TRˆñ³yR©z.¼ óÏ?ŸLzˆ|¡H¾'ŸË‘ËåÉæ²dÒ2™,š ‹Å¨‹Ç‰Åã$êÔÕÅH¥R$S©UBÊQç ‡ÃB×›¯þíï~ÿí×^sõNÔ ˜ÍÐt­ì86U¯-Ë#®²MB£ÉIyõÆÏddç‘5¤4Üî$Ƙ6GÕÓ¯1ýU“´¡„ ä„tå^µR”EWyÄ'úñ'ž¦üìC -óçÓÓÓóÇz¯Ûbhpˆ»þx'©T’Þž>·wPëŽT«¦ºÚŒ{AåJY¦.ÀÐè7Ø…G\BôÛv0ôchâ©b™&ùb)ÂÑ¡hhxDFf,P#‰ !ä™§ž&‘La>4M+MwN!DxðK¥Ôa!Äà:¥Ô7¦8fžRê˜UP!ÄÕ@F)uß±¶54…šVÚš ÝÝÝ<úèvò…<ÝÝ=<ñä“X–uÔí !†BGü5B¼^qÈ !~-„Ø|Ô8ŽB\+„xî¹Xq­·~Ÿâ/Hß} žÒ6ÍiÔ¾Émò#±‰Ú7z}:‰M“Ø&#LGJ*ޤ¿l±;S&o+ÊTÍ·ÛVš;Jê8X¶u%.÷®£@ C„Ba"‘‘H”HØ]F£Q¢ñ±xœX]ñxMÓ‰Åâø|~tW›©Ìà¬ÀW›¼íÀ¿MVYñQàkÇr™5xn«êè]" Å"ZÛX¾b9‘H”¦æf.\Ä#>vl¢îHê !Ö7ßÎÂtøzÍþ&!ÄÚší!Ä2!DXq¶W¶Q±x†û“Þ9«ëë¼õµBˆ˜b©b‘wº§€ÏÖœ;áÍßÕ!Äf!DÀûJ~¸LþxЫ§ !ÎBDÇ['„X,„Xr$÷ AŸ@¸?R)±*ÖjßÄÐ8©kãúÑêÕ8¢šºÝ‰$¶ñmŒe¶T(˜”¥¢èŒH8Â%s)±-ý8WUâB¸ççYåÊK·RŽl{äé8é¡!*•2B€í8!¦•¸jðJ!ÄuµBˆyBˆÏ !~ „Ø$„X|¸Dñ^ïãB¼Î›‡ŒâƒÞŸß;öwBˆ·{ïí2!Ä-BˆÏ !®¯9ÏK„¿BœqÌ7±GÖf:::hiiU&„ TžÉÇà¸âj |Q)µx^dVï+røâ1!Dø+Ü\.„hž~éµý1öÎù=`Ÿâ¼™Þ4!D%‰#l«2Ú7ÚÆ4µ-êxêGsXšb"š`ds©k4«Z €œé³eg„Òlôa‰Ë¶íã"q á…†Rž Kñ[&–#)ÙŠ¡BK tŽí€Å#8ýàJRµ#ž_ÎÀ}wÿä€@+p/®ð2à­À[…Ëqcëõ{m]‹Ýø€Ꭺ¾HUõp)n$ä{”R{Žä~M‡c"®JÅBŒ}MÓF–œ¤€îªmK)UQJ)!„ø2®žné¿öŽQ¸7ú6 ¨”º8lžáþÉpŸRêJÜü’j¡p ‰¼87ØbWû{Ü0Aç*¥îþ |M)µ­¦Ýw`%ðvàkü‹VJ½hÇ%Ê™"-„V®ÇĶœ)Õ¾Q4y»¡^Í@bcܱ“Ù¸ÆæDÈXåQ̵—…~l\UKb´w¿¬•Gb)ØVwó^úJÞü¿Ä»?÷oØÍ«ÒAq$Ãÿ(2Ún|-°ø0®ÇÀ~ Ýûøÿx p)ð'àݸ¡Ûo®¾§”ºø!ð–švÿU)õ§šs4A_g„cú”,Y¼ˆçwïdU,6z‡R«Áÿ±X)„hVJu{6¡ÍÀ§€ÐéYîÍoz¼²2Ðíµ“gäžLµßôÚsñtzË4.QVÄ}ˆ9Ü—hnë[g½í*ã#ÍÆ½>I!D§·]U©';ç”RfuM”G†À5dò9ñº Õ³‰íHÇÑP?c‰m†ú s²OiÞ–cän£¹î(Ìã%qyË*qI)qloà}Ô^\¡Œc°^ÛGÛéïëE:6I!érŽ˜¸*Àq ÈôÊöãJï?Àý(îǽ}Õ÷çWÀ]^ùO€ow)¥òBˆ6àb!Ä÷-¸á­ª¨ušý•wüWqÃZ7“ÄÇHÖ%hmm%ŸÏÓÓÝÍ®]»ÙòÒ ŽWÿfŠŸ»€G„ÿ‰{£v*¥ò¸bð×…ÿ\Žû €QïñDïôTûó=Õï­cöMèó¡”*ÿ |Wtÿî—nð8°–2,㪇ëjšøp¶âëÀw€ß×dKšÚÏdضB·ªÎ†®“K «}jUÎDö¦É‰HMØÆL$¶±d7¡^ëÛd(9’JMWUô¤Ðã%qyÐt ©Ü 4|†áÃ0|ø†§.ü55e»}íÛï£ó‰‡ð) ǶŽ(Rênà§5Eÿ€kby ×$1„«*¾Î³Q=Œèn\ áÀÇqå à>ª¯Ö´[;íðà !ěޤ¿Óá˜?%k֬ƶ,úûˆÅc\´åB4í˜øðˆ¡”* !ÎÞ 4ã:æÝíí¾7ða X­”:佫þ7ŒH77áªiúTû•R !>‚›ríïQ ¿Žk‡×&¶Wºª>Ø·zýY ¼L)Õ.„¸ Wô¾xƳÁ}WÌcÎy‘Wþ/Àï½6¿‰K|Õþ==ÓûV.—³š®YŽ#…Ðtò¹,É1„1™Ú7Ê>!QM"YÕì!ªé$¶ñFöÉÛo¨Ÿ‚·°l‰YCNŽÐ©ÎáT¦B×þg2Vó0t}”:Xµ †¥®š2 Ï:Þq%®i#Ïzv%Q³ýV¼­g†¸DáWJU¥°ï !~æ}ðÁ}¿‡/¥¦çuBˆ€róJLTçÚšòÆéúz¤8.“¬ý~? ,@hâD«ˆÃPJe™`¨W)U¾=¦ìQàQoý·5åß©©6åþ1uðʾ^³ÿÆšýÏ{e%FõPJÝ…ûEW”¯¶÷€·¾£¦îÀcŽ¿±f½¶OÓ¢¯·/=o^ƒS¯ Û´f`3šX::CýÆõqçšFb›b ÒTP@¹Æ9ÚÚ°ªƒÃt¯tõŸlYÛ€¦%®©Ñk´:!ã QšÀRÃöãã2±†´ªÛùÉêNpì …«b.:Ä,ÆGþöƒ•›~‹=,](°¬Ê”¤4)‘¡žQmL.±MO˜j̹&SO§ƒU;ª8,q1ì:rµË1uªÕ ÃWcŒ÷4“1’V{Uœ†çòÿ^ßH,ìCZ?ýÑWÏÌà²N[Ì×,‡ã8TÌ †n PØs”ê6Ö“}}ß•ÇÝvt ¢„;€ðç/^²uX=%‰« –‚øòÉîÇl€£û¼°DnØäYÁ·s§ªuASÈ 1¬ÓÔiêÌ $ýQŸ6^í«‘ÐÔ¤d7FJc*R:vCýQÝ͉΀K,ÕùŠ3!©‰¥1·-C×k|·ª­"Ön c…×Ncᯎîê^´Hú¾+?ñÅK¶þœ¢Ä5‡%ÜQª* gÄV[vóóãçžú5A_#æ‘XÔD ˆO#dh„tµ¶¨™Jl3•œÞP´B¾'UrÑu}ä4µÍ Ü(çGs×pA±†¸4MvÌþ£ò€y1#üàÓ÷]¹î‹—lýûS’¸b²¼W¡¾t²û1àSö—Çi©þftÛœ²~¦Tô•úÊS§<ôë‚°!é~ |º}ÔJ*04×)SC 4幸ùÕP´¨ø##„¤y¶©)TÅi¥.!܈Œ—T mŒ*[Ë]U)8NO;2 v•P+úßáúQîH¯^ámâÓ÷]ù›S’¸"²Òóës#?<Ùý˜ ø¯ÎG•”-U{f—kû¦£0Eú(|d«ä¦i®§´ðˆ@àæ“RxD¦PÊ›¸Ìð@àQÃÖ(åõ×SQu]Ÿ–œ&²k¬»³IªÄe[Že¹õ€Âó kºëˆjD"Qé é鸛ò÷.þηnüÆŒ]^äøê§ï»ò À§qoÑÇNIâšÃ‰ƒ¦‹´¦ LÓýAh•â±ÿòåS uŽ_Ÿ•ض=,Q)¥\×dä4†¤&\*·žÏçºCTs@ÖãÇ襔¤Óéa•Òïóã8!³ˆ´ªøî>8wޏf9”dÐ0 J%7JŠ(À™Sœ¶šǶÞžRj8´ÍŒHjbS¸v2Ý0\.©PÚÔd[*–FÙÁtCÇvl„6ͧ!¾xÉÖò§ï»² 7ÈÀ’9âšåÐ]ºa £E)ά3üŽ‚ÒüX¶ƒ¡{ü0CUÑ]LLlÕ6&òœwm[ Ô°È@±4:r®»Ž±š³Ž¸< Qçˆk–C3ŒŸÏ7L\²”›õ—:¡#<{’œ@Ut3“¾”’žïÖS~Æ(ºE¥ââÒtÇFÓ´Ùýeaޏf=¢‘è!£†¸œ|ä,'.M§‚†¯Æ%bXU„饭 –U§S¡‰‰Ý!Æu‚aõ½ ÝнP8bv? æˆëTÁKpãsïþûDžØç÷õûýþ‰+ŸÛåí=Û c”/W­ªè.f¸ôŽ‘Jáó¦ U[k£CŒï \=«ëndV¡isÄu²;0^üð{ÛgÇœÇo¦PRe€rG@2h•2Ò7ãäͧ%L!s)GœŽ¤&Úç8¿_–¾Ü`‚jÜ÷A)· Çq0+£ƒ/èštl„GŸæ4Áq\DpC8ûUöNDüJ@{;'¸éd ”Ž£HÛÆ_ÉQÖf÷«a cx´ešøü~¤k¸ÎL¥.„ë¥káMù©‰1Œé«8vª ‚rÙ™#.æˆëdã3À"Uz™ùzì $OdLÓÌÇñˆ h);ñÙS–6¢>›–E(ÂæÈÈjXMt×S^sLg2å§XO\šfà8E„3›ÞpcޏNÖCYJ¦)Ð t€'²™t&eí|Å€rf½K„£Ø¸lËÄ!òIIj‚}B,)QR¢yµå ŒóÅâø$>º¡¹RßHÜøY‹9â:y¸ðÉìÁDVUË÷MqÌqÇ»ßõŽÜW¾ò¯N-qù”3ë]",á—ÌŠ…ÏçCJ9-Iy+£FmÛv üšææÅ¨%.…BŒ2Ñu…ׯe;Ì,ìi9â:9økàRì^T΋Öl4T÷í?ÑBØ£ˆKÚ³ž¸¤Ð½yÓ4]¯÷š`‚G2ÁZ˜®€T•¸¦s‡0+樈Uèºë¡éÚñPú"Äqx$p³S#‡nåy¬ŸBÄ¥+kÖûrU㔘•2†áJ;S’S«‘º®{“ÀÕ”S~J¥‰s½ê†Ž”6šî?’,Ö§%fL\–e148„eÛÞd÷‘é ÓD«ÿ †_Hé ‡ijn#ÏJ|hRÅ'Påšä¾#Äõ&àBo]ÏáfÔî{¡:¤éº9<åÐ Ä,·qÕÄ)3Í †Ï‡á¦›±Š8²tG5MwÃAÿÉÑ*¢§6–Šó’®éØŽÄç?¢,Ö§%¦%®¶¶6žy憆ÒHé ¥t“Y:Ò[Ÿ¼ldÛ-ÁÂ… yÝë®eÑÂ…'àòN9l>€ª(™þõh!®—MpÜ›qo¾ Ãàš&,Y«*Ze˜å³J¤6B\•ЉaSœb©UczéúpX¦PKe—¸¢Ñ±XœP(ˆã8žªhƒ›ònVcRâ’RòŸßûñxÜ5J8ŽCkk+7Üðï¬Y½š÷¼÷=ÖB´]ž8.'=U`ÌÓXpówº&3gt®•þ5JŒOB-× | Î¥²ëBº>ðÌ Ñ5]¿ÎªýiÞ½Ù )FF+å²;ϰL°–”„@Óu4á¦æš6*Ž’Õ(anÀD„@)I dý†u4Ôׇ9|¸Ö“J%âÉ'Ÿäü ÎÃ2-z{{¹ý·»®îoñˆ’ÁžŽ˜”¸~vóÍìÙ³——¼dó¸}R),˪I³ä~=îÚŽ&4„˜æø‘[)%~â Ü~;¯z嫯íO;/@ª¡c»´S Á @è(«•»oÜnU™`0QÐDPÐóÙ{)¬çh¬ý;Ç9˜à‹R÷KD–mÇI¥R€§VãêkÎÈÒ‘#™°¥ƒåÅ:šÀ±”Räóyz»»éîê&“ÍRÈeyä‘G(•J E¥øÃwcëÍ@ÒœºíÀl‹Å5×þýûÙ¾ýq|¾ñŒD£t!†c¸Uƒtˆ‘¢)ñûß߯e—^vd=~1B| PC·À £j‰kÀHAaØÝÓÖ?Zü’`0DÙSQ0g½&ÚHfkÛ²Bðp¤RYª©ÉªviZÕ·&ºö–39ÜyÇb›1›×P Ä)†ãµ0…@‚‚m‰6þòïÃÕv¦Íb}ºcBâzú驳¸»SC¥·tÿÉêÿÂåðÒ)ÚQŠÇ{Œ³Ï>{T¹T¾¨çäBŽ>˼õ¨ŽãÚºy½îLeFUfæ_*«Ñ‹@ln|Ô 6ÍCÓœÕÁP01B\³~Ð ¥—e[T*ªá¦“°F-ÛytŽã`[’¦‹_CQ Š¶Â±$Ž­°m‰Uq°Ê%¬J*&«Äâçþ—€cºóGÕqÊbýbÆ„ÄÕÖvpʃ”R8NUM”®#%Nu´Dz™‘éå®;vŒ#®L¹éi–ß{ñÌ/ãqಥ@«·õËﯿBÀAá_4O_ð/ÔºÊßÌÜ6º¶ÐÑ’oh‘w²øÖ4bD÷Öï/ ½;=äiçåY¯‰Œ²7Ú–…mYX–53²ª!-)%–]%.ã8XŽäpgÅ\+݇34ˆJwb ´Ó8ØÏ±š†aèø|@—^Æà`µKs×D…CCS›—”RnÂL©56.Y´#ý|Å«^Å¥—]ÎOòc¾óŸßç›_ÿ®{ãxèÁCÌ›×È®];Éf²tuu çóù¸üÊ—0-szµÐÛWëûX%#¡i8Ž]3¹z¸ ¨”Ët×-a`Ù¹Ìk}ŒÆÌ¡I®F ÑM= qTÄå÷ûilœ7cÏù9ŒÃ  ì5QÐ×U·>Œk;!RfC‘ˆtUrU+[‹Ÿ(‰¯’ǰ‹šD“šSB3KèåZi=?ˆžíAO÷¢ÉãµÅ2-l¥óp_û÷¸ÿÞûyæé§èëí%H&Sä‹E/[Æ;׬Æ0|ƒAâ±:Ê•ò´dU]Ž…ôæ}jš†ã8JUŽãP”‚3ÿêo¹á¢þæ÷«°oþòd—3'qìÌR¬ FMÔê^z¸¶¯ÿ=‘±m;…<âÊ y¦&.áXø¬ea‰¡, »Œ0‹hå,Z!–ïEK÷"²½'|âv0$‘J‘L$¨˜¦êêîáš×_'üþoxó›ñt]'Ž éŽí`Z&Jªa"*›3 ­)¢iHYõœ×°­šD³Œ]•J™º…¼oa€p0À¹K›Ù†N`âl¸ÇõF½1G\'®}Ër‰Kø"b—€ëý±Ý™b±˜ …£D…p©Ÿ²èCZNÍ,¢•2‚\/dzPé~P'^ª†B$“IÉ$ÉTƒjh¨·‰„cøüŸÏ(>_Îçóõiš¶;‹ý9žHì4M³½\.ÆãñH)ßê8Î*Çq´awË:²ÑBoßôƒYîýÑ4c×Þ+ºT*¢v?[;J\yÜûì¾ÉH‹Ÿü÷æ$®‰ •’^&’æ…t]'ܶåìU¬…'qõ‘|3®Ù‹ޝóë °ßþ`(4êaÇî¾qº'âi…B!’©Éd’T}=©†y$’ tÝ—õûô¼ÏÈ>£WÚîx¢n{$ÙS©T::;;}ü£;“ùJ_¾ðË_ÿÖ/„ø[…úÇqÖÚ¶­ÍØð>‰Ê7ªï¸¦i(5ñ­¾ü寜™Õáp˜D2E2•¢¾>¥’õ v"‘°}~_Yšáóûc@€X,Ê¢E‹©¯OÑÝÝM[ÛAÚÚZ/ÿ¿_øüÇ«/¯ÝkMàà†›ÿçVCÓ´÷Z–õ»lo´,KŸ‰á}&¨¾ëBÓÜ,<Ñ[ql{¸Íe]OA×S“¶%Ä,7äaBâ’J ÿ½P¨¶=K%.÷¢}-huWWoÀûaÝàÀðùŽ9p8!™L’LÕ“J¥Tª¡ÞJ$’¶á3*>Ÿ¿à÷ûòºft놾+‘L>î÷û÷‹Å®þ¶üí+ïxÇßüóÂE‹>µháBR©Éd‚h4Zyôæ|Ë›ßhß¾õ£ÿTóûý礼•÷‹Ås,ËòU ìhP=Nóˆk8™‡‰Œ…¦ëH7lóq1©Ä%‡ÿ&B¥R¡µµB>j½®«è“¢”gYó¼ }¹f¹Äup…Vÿvp?¾ß=™ŠF"¿]°pá»;ÚÛ'܉D<õ-Eª¡^¦Rõv"™´ Ã(ù|¾¢ßïÏéºÞcø|Ï%S©'|†¯µX,vtvvüä'>z¼æYž›ìÛßöWø÷Ç·oúÞ[LÓüP©T:·R©L¤ÁM‰ªJ©y9Ç´L„„B!üÞ`€tJ¦…´LÎX³†ŽÃ‡Éf³sÄÅdÄÅÔWggB.ºøb~ðÁqû¥”ð¶ ²é~A}}ýø:^Û3µœføŠ·|p0é¸÷‰Â¢ÅK>yíÞx~6“^Z(uu Û0|EŸß(úýœa]†nìH5Ôo7 ã`¹Tnïëë;ü¡¾ïDJ‰'e0éïÏÍÀÍßþÎ÷^kšæÇJ¥Ò•r%8“ã‡%.]~ß}>MÓ°LŒ`F# øÂ”Â)Êázrá2á$ùÐ<–=y+¾{inn¡P(ÌúD0­kbrŸ7¯þþöîÙ‹¦ãˆIJIàþ[(öwÓÜÜŒeùàzÞõîê¬$.pÉë+ÓÖ:A¸ê5¯Ê«μáß¿^·ac*—Ëu¾óo;Ñ_÷é‚G.®®Üyç¿ßŸTJÕ™BT„ifÇɼúÕ¯~AØxß{~‹›yœ›¾óŸ¯0Mó“¥Ré¢r¹Ö 74s¹lR.—ÐD¢±a—MóB8ÂäϾ3œ¤m JP´%E[Qð–ål}¨“Æžø+b±8{ví²Ì½×÷bÁ¤£ŠÕ¿‰F‰ÅÜÕ1Ùl¶IJÙT•\&A‚ׯA ‚¡h×Ö»ïý¼¡©ï]vÙe39ýæ7¿9¿dÊ™ôÀE¥bis¹\^].UZ,Ë -^²ˆ–ù hH%ºÆúµk±m‡gžy†eËWËf±‡={ö`øüly©;·¾µ­•h,J%ÓKçMŸÂ_Î54ZEÑ­”ºÔW~ñ›[§E÷þ÷lQ0¨Ÿ×Äã±LOOÇîÝÏßwûí·ÏŠX]“K\rzǺcÁœªxúãúë¯×^z饛„ Ô %YŠË€¥šáw=^½ÈGµó'ƒmÛtwu1Á‚™wBÐR®”¾ºõw<}Ùe—=T»ëÖ[oÕ»º—‹ÙÍÙlfK±T:Û¬T–K¥yÅbÁ—JÖ³fÝZˆ„Blß¾ýû÷£iuñ¥R¿ßÏž}ûA)›šp”C4¦P,³páB …"–cS)WX½z gžy»v··/Ž|¹ê;ŸùÌg<Çé­o}ûû’©äõ 56íÙµ“J)ʆY·~¯Ãë¸ë®»*Ù\þpGûáä#ùãÌoÔ‹ “×Hšð ³Ü8ZãOúÓ™Rèÿ¸åâË^†T)ªé"†—Æcá¢ET'ï×¢úžÄëêX·~=ñxÝŒúà8é¡4­m­´µ¶âɺõ×þKçäsÙóËåʆJ¹¼$ &ⱘ&4 Ãà²óÎ#‹±}ûv~øa²Ù ]¤3æ/XÀú ðùýòyöïßË’%‹Y´p1Ó$—Ë340H8& P1M‡†hnl" 34ä¦÷kniaíº5óúþãÙgŸø·O|â±Ûv?¿ó[{wï˜r*Ï‚eË^ºdɲo år…‹.¹”d2I<'‹Eiin †±ò¬37Þqë­¿ÜuàÀ¾·|êSŸšÜ1ìEŠ£r‡8fÔçg©;Äi‹ÿô§o™ß²àÇŒ#ñ³ojj¢©© ù˜ ¥éëwËÕDµp‡J¥L©\¦T,QÈ( d2YŠÅ"•J%¡:⻽=ݤRõœÁù„AžyöY.¼à<*‹­È T,“gņÙà‡DhKX}Æ*ÇfÿþV4a¬OpÙå/ã`[>ôõ©6l\@gw7¥R‰@ ȲeK)K ô°lÙÐÝÓË@ÿ ¥b™+®|¹?“É\—¬O½nõúµ;z:»¾ùØ#NÎÈ6í÷îß»Wè†N0dÇsϲ`áBÖ¬^ͦM›CD£Q‚Á¦i²yó¦5 ,xⳟý܇>ûÙúö<ÆS“8 Êá¿Ê;‡{®9â:ðȃ®¿ìò+ŒÔ.03EuZKÕéS:Û¶1MÓ¬P*—Èård†2ä yJÅÅBÐOC}‰d‚b1O[ë–-_Ζ‹¶ÐÜÔD±Xâ‰'Ÿ¤·§‡`0ÄÙçœCWoé¡ Á`˜xBá[.¼ÃgÐÖvþAlÛfÁ¢… f²äÒCC!ššš(•+ìÞ½Çq8ûì³…¦ivíÜuÓÂ¥KÛëb±ÿ·jÕòŸ¼ñoA/»øeÿlKëg¶e‹¼å†×îîê&ÎðÀröÙgñ†7¼‘ÆÆyds9ŒR‰h,"¶lyé·>þñ¾úÕ¯~íï)ƒI# º./¤«šhޏN'ضºwÛ=,^º”¥K–L¦Ð¦&)„@Ó4„—ý\*‰r$¦eQ*•d €¡tšl&ƒeÙD£Qâ±(‡fýúõ¬Y³š` È3-[^ŠeÙø h,κuëQJpï½ »ß £³³R"YWÇÐÀ‘h˜P8ÌáC‡(‹$“IÇfþ‚…,_±‚¥Ë–ñܳÏòè£R¯cݺ5üAº{ºÈg³´47ÑÝÕÅÃ?ÌÁ¶VV®ZÅŠåËI%¤3úzzhjnƧT*eöìÛ®é,]ºMJ¥éL–³ÎÚÀþ}ÞvÛm?l;ØöÍCí_Ö_ýøÇ?^ÚvÿÝ?¿âŠ+Y¦ýC)åª|>?†‡Èfs yæ5Îãì³7±hÑbü>G}Œ×]{- xîÙgÉŠd29Òd<¿s7kV¯båªUˆ}­ yV­\‰ßoÐÙÕM,§T.ãXýýCÄã1V¬XFkÛAbц¡ÓÔÜD"‘¤£½=»w³xñêâ1Î}ÉKèèì`ïÞ}<ýÔSds–/_F2UϡÇ8xè RJº:;ɤ‡ ¾a -dþÂ…Ø–…ßg 'iin¦\.ñç??aìÛ·ööv‚Áuuq–¯XI]"õùü_¬TÊWüéOz8㢋.Zkh¾/”+•‹Ç™ç8Žpb1ⱇÛ;ø×½X4J}C=ÙL–ÞÞÞòéBZp~\7nIJ,¶?þ8_r Þ¾®î.Þõ®wñä“OÓØ8»îº“þþ~þúmocß¾456ñƒ|§&ÈÚpۧϽœð©~ôúòWþý¯4]ÿ¤TêLšt²Ù,mm­]' Ñ~¸3ÏÜÈ™Ï$p÷Ýw³{÷R*›Pzûú Côôö¯«#‰ÐÝÓ‹m™¬\¹’…ó[Øñün¤„R¹H©\"‹Ëå|.G.—% ³lùLÓ¦PÌÓÝÕE"•$—Ë„h?|˜†úš[šÉfs ˲Lâ±8\pBÀôc|>©TŠ•+WÐ2>ûöîö%K–,¦a^]´hÅ1]¿Ø¶¶ƒ´¶¶Ò××G0¢®®Žx¼Žæ–¬^³–r¹L&“&›Î°kÇsôõô  mÍßòã£îó<°x}uûå—¿üRË2¯+—J["‘hs$‰"D §·×²L»Uõþø¼à0ØŒ™¼ZU„SÇaÛ¶mœwÞ¹457ÓßßOÀ`pp€;vŒÇ¡FÔЉ>òtËb={ ¾vÓMK²½ýfr… Ír©`Zf·ª1=”5–,]LKË|É$ƒƒƒ¬\±‚ .8€?ÀöíS*Wèíí#—Ïsá–‹¸ìeWðØ£Òz õëדL&˜¿ …Îö.âñ8¡ ŸD2AË‚J%ÚÚ288È¢…óiij"™HrèÐa2™4CCi֭߀®ë¤‡èíícýºµ45Í£Ã×I¡'‘H‘Í¦Ñ ?}}ýÄbQæ54 ”¤R±Èf3<¿ãyÚÚÚƒ\|É¥,Yº„J¥ÂÁ¶vеk׉„Ù±c' õI¬J™îΞyúIâñ:êêêXºl9gž}…|Þ#©4í‡Ò×ÓC:=D¹TF74Ëïd¡È3 Aß;§»ñwÝs×½À½'àŸ0b˜_[X5ÌO$qI)‡IH(ãEµ4 ƒ¿xõ«I§Ó<¿ã9Êå2?¿ùfV®\Åk®ºŠýè‡ Œ:Œ'.¥åRñ¸….™ÃñÇõ×ßêO$:W÷ô ]Z./(—Ëë-Ó\ì('1Ø? Y–E}C=]t --ÍD#aúûûhm;Ì–—^HgWú* Û¶øÍo~G*•bÓæÍX–E_Ï<ý W¾üJêêâlÙr}}½hºF:“áù»hni¢\)SW—dp0CwWóæ5P_Ÿ ¹¹ ]×¹÷¾ûÑuƒ3ÏÜH( à÷ÓÙÙÉ¢E RQ1+ä‹E¶ƒe›´wt’ªOÑÜÔ„ßÐ9Ô~˜eK—¢é:º®ãóûH$’¬Y»–žžnzzúسgõ©---ý>î¸ãöìÞÃÀ@}û÷QWWÇŠ•+hlj&“vIªíÀúz{ȦӔJ% ð}>. íHÌkøM¥TúÖÃ?<—•w ¦Y¹K)õöÚˆJ1©q>ɰqãzúúú©KÔ‘Ï»£–eñ«_ÿjØs¾±©‘×^s-[·neÅÊ•½,¼àMù™Dâ*òVww÷7_€kÃ1ⵯ}í™Édýo …â’r¹$ÖoXÏÊ•+I$âÄãuD#a~õ«_ÓÑÑA8B‚Ö0|~Zæ·°ió&¶Þ}ýœwÞy$êY±bÏ<ó,ÅB¿ßO>ŸgÙÑ ƒ¡t†]»vR,–Y¹b9ÉD‚H8ÂÀÀ€g°Ï°rå tM#“Ͳkçó¬]·Ž… pþyçóÜŽç@@ٴػ?ó[šijj$à÷ÑÖÚÆòåËÐuƒ@0€# A²~–ípðÐaÖ®YM$¥Rq‰®§»‹R©ŒiVصóyÚÛ»~Tñ:©z‚Á0ÁPL:ÃÐÀ­{÷Ò××ë‘TÝð9>¿/Gö$“ónk^XÿÍ;î¸uÖ'¿8RO>ñçwwÁ…¯I&“ ÕB5…;ÄþýûÊ!‘Jrï½Û(—Ý€“÷ßÿ¨zÝ]Ýüþ÷·±`Áþ´u+ÝÝÝ£öO$qÙ¶M>ŸÿÖ‡?ü¡ñÖü9œttwt}L×ô¥ )%ÑH”uk×’É ‘Ëå¤bVxù+^ÁÊU®ßS&æùçw²xÉbêâ1.<ÿ|:»ºˆDÃØRñà=ÛˆÇâ†kn]ºd1Ï<·ƒµ«WÓÙÙÉêÕ«Ñtήž}öYæÏofñâÅŠÁš››°L“H4J8%_(248Äàà½Ý½,[¾ŒÍ›Îá@k+¥r™b©Dkk‰º:Áš¦±>¶žƒ‡ Gðû}èšÆž];yþ¹géêr£¡Äbqbñ8Í--¬Z½¿?H>—!“NÓ×ÛÃÞ;éëï%›ÉPv%)éóù ¡Pä@2•¼³Þ×øµ»ï¾»ó$?ÆÓÆg>óçç?ÿŸ+ÜH¦êazÔýûöËà³}ûöqõ>ÄÁƒmŽNŽòS,Tz(ý«×^}Õ ¹>‡™a` oa,X´) ~øaò…"guñº8ºnpÕUW³{Ï~?¦ ¥R‰-]„‚þAîÛ¶ºd‚ó›Ï%Šðò+¯à¹;0+&í,[¾œÍ›7Ó70Às;v†ØòÒó™7¯žJÅýH*àpG'´±pÁLˤ©±Ë49|è Ë—/ÃðsΦsˆÅbX¶Ðt–,YŠ´ CgË…ðÐC08ÐO>—§µõ{öìåöÛnC×ub±8ñº8©ú–-_¦ëä³Y2™4]ìzî9úúú\’*—0 Cù|þR(>XßP¿5H|ã¿ûÝÞ“ûÄN_ù—o~æúë¯oÜ´ù%ß©”‹óšçÏM4ÓCá>ŸÏQR" /Ô 5«¥/“u5Ž|m®E9lä÷&_{ó¦iRÌåt¥Éd2wð`Û½ƒƒƒß»îº×ýþdß9LŽ}­­ÿ¼jÙòó ŸqmËü…ŠEžß±ƒ¦¦&¢ñÃÀ0 Î:ë, …<Ò‘´µµaZ6œw.CCi^ºe Jn§ýp;kÖ®añ¢ÅJ::x³6n@96 õ ,Y¶”b©B:“ã@ë¡ æ·°rù2¡ét˲Ñ4ƒEK– ÷5ôÒ‚II4EJI&3T MkkÅbÝ0ˆÇâž-j±xšÙt†L&MÇáCìxfˆ¾>²Ù,•J]וÏï+‡Âáöd}Ã}ÿ›[·n=í¦ÕœÊ§‘kÇÑãÀeKVoë,ßöª“Ø›SBÿê•«î_´xñyMMd²9 Ã`ÓæÍ\vÙ¥„ÃaÇ!Ÿ/`Ùö° øä“OQ,äY³f ‘HÄõ>ïè ’HÔ±oß>*–źµkÑ479êC=Dss3›7ŸK¥R&ŸÏãHE]]¼¶?T*e ‰D¢X–E__ ­­•C‡cš&†a÷ܪnŽ´Ée\I*“N“d°€l6‹iVÐ5]>Ÿ …ºB‘ÐC‘hôÛüã8÷ÛP“eؘeøô}WîÂË×0—žlG¥”¹fÍšk;Û;ï7 ߊT}éLšçž}–`0Äú ñû LÓDÓ4 M#ò’MçpðÐa‘(R:ÜqÇ4Ìk䬑Òaõ«¸çžmô××ÓÔÒ‚RnéP(LŬ€D"L˶L"áápØÍGØ×Gkk+­­èèèDJ‰Ïï§®Î5š¯[¿ºº:LÓö‘:°o¯KRä²9,Ëë¯Ïg†BáÞú†úÇB¾Øî¼÷Ž?œŒû,„¸ø6°L±øŠRjÛ 8¯øðoJ©SvdsޏæpÄØµkW×Ë–ýeWGû†Ï¨¯««#“ÉðøöíÔ§’,_±i躎-%{÷`Û=ÛX»f ÍMDâ1®¹újîàA×FÀs;ž#‹‘¬o@Ó4‚Á 眳™Ç·?Ƽ†z–.]J¡P §»Ë#©6zzܼ”@€x+EuÎ&bñ(•ReØGª§«‹ôÐCƒýä²9lÛFӆ᳃¡`_*Uÿd(üÉÖ»ï¾ù$ßZ„ÍÀ¯7ÿø®b­RÊB¤€…J©g¼ú `ÐlÄ͵ J©½Bˆ°è•R»¼ãVi¥TŸ·ž6ŸnþìÕÛ<¯”’^[ 2`)¥&NRðcNU„9Uñ(±|éò7Ô'ÿ½hÉÒ ?$—ˉD9{ÓÙœuæÙø|ÑH]×Ù³wš¦‘H¦èêìdÿýlØ°ÆÆFlËB)ÅÎÏc[’Ë/¿„L&C[ÛA¶oœþþ>*×ó< óT½X¼Žh8B¹\"í‘T&“ñHj€\.‹t\¿CÃ0œ`(4EžõG‚7Ïkhø¯[n¹å¤eUš Bˆ·?æ)¥<)ÈñHë-¸„Ö XÀ5À¹¸ Wî.öðÿ³÷æñq]åýÿûœ»Ì>£Õ²eÙ–;Ž÷l„„ ÐB ””¥@¡…~áK/m¡|éö¥-miiiIaù±´´PÂÒ²…Ć8ûIœÄ›,ɲdíš}»Ûùýqg¤±,Û2‘-;ºï×k^÷Î]Μ{5úÌó<÷9ϸ¸¡ÖìG€eÀ•RïBÜ|G)õOµõÛ7âOèòà~ü‰ŠSÀënà[@¸M)õGgðVCà*,½ý½ß^¿zÝzÝ<ú7«V¯±XŒb±À³Ï¶mÛÆÖ-[ˆÇcÄbqªÕ2ƒôõõqð`CCƒ<òÈõ©¹¢$S)6^´É//ùCb2Y¦&&èëé!“Í™œœNz€¦k^(η¶·ï„B?Øké\ IDAT E;ny$tĘÀ/„¯Þ¼ _4ê¹`GUøÂ5¥”²… ž4Y`æÜ«½¯Ö¶5ãg–Ô3Ãsôã‚Úþ_žÅ·Ø¨·(.b@¸ž3=ý}ïÚ¸nýÃ4®[ѹÏóåP4F©X¤¿¿‘‘Ñ霽x²5‘*–jó\RSáp¸ÐÚÖ~(‹Ü.4í_Ÿ§ w· !¾oý|¡æ*~ø+!Ä%ÀMø®â2Ž­Ú8WüG_¨íÛ«”:$„^#„°ñE  n™þ>ðCàƒÀð`om_Uh&³D \ ‚ ¿nxhè]7¶u¬XA¡Pàp““㤒).Ú´™T*…¦iä²~ŽÔÑÁAöÕÖ3SS”Ë3ÞœÔ42CåÖ¶Ö¾H4¶+!’ŸùÁÝ?è]ÄK]ÛÿS|ëà?ðEÇrøsCnÂwý> üoü ýïà[p½ÀG])õh­íwŸ¾¬Åw'• 8Ap~XµjÕÍ©Ôîîîu]—¾à 4]›Î‘ªγé éìVefúC)¤ …C•P4r$ŠÝŠšŸÙµkמE¼”çBˆWâÒçW°ÿ%ΜŽ9rhݺuo?rdà‡¹\6Z,( ØÖLlY2«MÍ-GÃÑð±xü³wÜqÇ'i6à¹ó8þÁç p,(½½½wmèî~W!—ûº©kº•L5Åⱇ"!óËwþìg·/v—J©I|òyC \ ÎÁþþoàÇSÎ'8 àÜ@ñe!ÄšS9ïöâBˆ+çyì[…¿½PŸ½ÂpŽ#„x)p©Rêð ö‹ùl›Åð3ãçÃÝø)áyÆ „+ àÜç­ÀmBˆG…· !ö !îBüBüKmÿï!ú€ŒâóµmBBô×Ö¯Á­ßB¼Mq­â!ÄSBˆ7ÕιS±S‘Ç>4ÈÌð¡E'®€€sŸí@Om=‰Ÿ$z=ðà^à]µµ}7 ¼£fy}Ðö»ñs¼în¾ˆ?†ño/ágçÿsíœ~²êJ©áÚço?£WyÂpî³h,gþTÃû§ñ‡âDjï/ÃO½?ñTï^„/V¿W«ïeáÿ‘Àøâöeü õXÚ£J©z¶ü~…‰s‚à©b@À¹O/þØÂ:î¬u @¢ó&üÒ3¯Ç$ýà¯ðÅíBˆdí¼.üÁ×?ÇO—¸?K¿q¬cf`ÎÛbX\ç>?.®­? Œâ ÏÝøU$²ÀÝJ©*~À?¯ÏuÐ|ߢú_Àï(¥rø®ãzü²o àÏÛ•R•Úg6 ÕÅÀƒgîO`ÈC~ÎijEþ¾¡”º|‘>%~ ¯Kãóë4ù ,®€€s¥ÔAà{BˆŽEêÂVàc‹ôÙsĸΔRŸXÄϾãÔG]‹+ à¼#®€€€óŽ@¸Î;á 8ï„+ à¼#®€€€óŽ@¸Î;á 8ï„+ à¼#Èœ_Â|ó[ß6‰Ä{uM»F¦IYVS¨ˆ¬R*#„È(¥JµWÑó¼²ëº¥jµZΤ3…w¿ëçÓôöÏáZÂÄãñÏ€zc¥ZiBçÍžœxºú¯B¨ª@Xa #„p¡»|Årïû?ü±‡R„ ¸(P6Bx€cÛVXù#úsøåRÒ ÒRÊ )帔2ãy^Ùs½’ë¹eÇqŠ–e•‹…bq||<óþ'5áZÚì°-«I)P(˜^*÷™¥PаB…çØÇIÏ÷WP ,Û¢\®P©T°l å)4M›~™†¡ ÓP†a*]ו®ë^sk³×ÚÚ¢þë¶ï¸á +¤p„.[ l!…Xš”U©ÉœòÈ#ÉDÅ$‚q!ĘòTÑS^Ñó¼’çyEÇqJ¥b©422RüèÿQeQþ¿p-a”R-®ç–øÌÿ˜ã÷iR#‹‹FQ(Û¦T,S®”ÉçrØŽ#¤”BÓ4t]G×5tMG;Áº®ëh ëÂÿDüó54MGÓ$RÓȺ©ðM?Ï¿Êëî^­~¶ûnW)\òÂAyNµjÝ_*—ßÿ†×ß8|–ÿ4§ ®%²<×=‰åÔ Bó­ ÜÂ&¤$E…ëzX–…eÛ8Žç)lÇA)U{Rõöæzéèº?[¶çI¤ô@IM—¾ÐišÐu]èš.u]« ¡Ž^K­aݲìßxüç?ß l^œ¿OÀ‰„k)£°¦-. —Ëã(…çzxž‹ò< ¥D ”Ò·d¤†Ô$ N"L§#p3Û ÃÀ0t”Š P(Oáº.Jyxž‡]Û6-d¾Uûœ£a½q», Û9¡†N2™\û_ßúÎò7þæoŒÌq‰@¸–0 ,Ïõ¦Å#¾l¿ô:Oá*…ãå8Tª•j•ªeQ)—©V*”*\×Åõ<ÜšÐùKÿ½òü}Êóð\Oy¨Ú×­‰ã/=\¥<ðüs…çòjK<¡\$`H&¦ºn`˜&¡pˆ ™„CáiëìXÁ:‘¥Öøj°ê€H$lFcÑ.fê°œµ´±\Ïþ'U¶§àÕ‚@BjáÒ c*ˆÖÍQ ×S¸ª¾~ìÒQàz³–µsëK¯ñýôqÇos51Ûup,Ç®âX6žUÁ³ª(»Šª8àù®¥P.(MT( !ФÀ]èB K0¤ÄÐ!M`JIX—¼j}ÛV/¯YnBÔ&¡˜¯}í1ü (RµeÐÅ:å•×oÙºmǤª484)KÂu¼ŠÔEÞ4ͬ@d*­iåyS®çNLN¦³ïß{‚ú계k)£”=cqkÛ¾`)5³dÖ²¶½Ýöß7¼jÛ”jXÎ:×?nÖrz¿?™ß±ç6´+40$è!DX¡j"7-œ "wÜrÆ­½lhîÙOW2L2‘”xü±Ç^û…[o½”!b–05¼Ÿó*ŸÍò­o}‹X<Á¶­[éêìä›ßø&åJð¯C ÂÑùS ð|ˆööv•H&„ç)•ͤ{*ÕÊK>ùw;ºà߉ó„@¸–0RÓÊ®ëPU95á:•(#Ds ³–B6g»'±‰Ý±çÏ<"lÜ~|?‚¨)‰j …PþƒÆŠë1Xv¦ïË£Oíû´q. އÐg¢&¶Ð¦-.Ëq$ÆUÿl)%J)\ÇÅqœi‘’(¥ft,¯ 6³—gØÏøÈ®mÓÕ|W1®€¥Šã8Y!4«œ×u|zŠP"u’4ƒÓ ²Ï7µ¢!ùô„Û‰sn‹M×·QvfY1ÇÅ,¡;i¼éB4—…5Ýö),¶“êO†R~ö|[HܺÅå‹ÄIϳ-¦,¤&®iÁ:žaÛà gªyž¦c!ëçåN~UÏoáZÂ|ø>d}㿾5mq)¶eÍÏš·©+êTnßìT…SŸNl±¬Sèvƒp¹R§q 'Ì_œ¦EJˆi7P0í ¢kFM´<”šÔ ` ö«+·¿¿ýÍ6ba»Täßÿã›õö³ó¸¬ç-p-qÛÕj]ÓA€kWOà²ÍŠ“¹‚óuûNf±ž`ž¬ùà5˜e.3—ÂGiš6o‘šë˜úÍð]EÇqp·žqz\ò©@І÷‹|âÏàXU”eqñ%×öWÀÒ¥\©$úõ"„  1Ò±•ìx•eQfSóá$z5‡P<ËþÔzuÌq§p#OÑÆ/ƒ+k1. þ˜oúÉâ|DjA«£iJ)¤”hµ‹cÛ«‹˜ Yøc%+™©éóåÌC„Ì/uqÏáZâ°jO©p‡Þ|þñ t!hhtD4–EtÚ#’¶NKX’4µYq¤Yñ“äTzøÐÂê\!ñ¼Zp_Dêúy[]s€®i \OÖAÿ“‹Å™üt©ÿM`q,]„Ó »öôº£Ã%‡á’T9O‚„)H’¸!‰ë’˜.ˆ’ˆ&‰è‚p­@ßÉõ§o±Í?PÿËâ £žr0-.š¦ZœNbÕƒeº~‚§Š ”à‘(Í®™€þK˜@¸–8BP©×äÐêÉO¨á*E¦ªÈT½“'ÓBfJ¡ á[sš~u R ¤TÓÅô`Öp¢Ó ÔÿòÖ@5EÎ(ZCÎÕ©\Ź„LÕïoM¸<¥Ê›S­<ÏíÅÀ2é4J)tCG×ôiA"®€%ŒÔ´Šª¹E öÂP JŽGÉ9õ±³ø%güÉ:ü5uaó”_ÕÔSõ¼-1} ó{ŽxbÍ@ïʋ˱,Œdr:1÷tËÛxž‡ò<¤¦ùyýÇ —_ñ¿1ÜU*–Žë“¬»Š×bw`‰ò¯@Håw‚Só õÖú¾³jqýÎ;Þ^øä'ÿÁm.C¹K>—Ë‘:þN°, Ã0üa@óP={é8Žÿd²!¡uZ¸NÄU./\š¦Õjx‰%=,µÜ¼g/·kz£˜±¸âÀµµuìÆÎd‡„Î1Âå9KÞâªÇ¸¾piš6“=ÏiX]B jAöziU ®NoU­Æ1‰ÓL' jò¬æú‹Âuv‰ŸðÒ·jˆ±Î×_Ô^u<ீÿw¦:5[¸4×¹´…Ë^=ÆeUiLÒ=å@kŽ´ú6©ù…g,.ï8 \:ÞÚ‚†TÍ\Ò“ÁB \s Âô¿ì̤$tý×GЗ­Tå'P•½ÇPîñ3N‰È¡?'ý¥{ÉþÇЙ薮¿Æsg:'Ióìyl~þâJm&8_­¢†_¡á—*k㻈Rj æŠqÕP€P”æpaÆâ ›Š?¹çº³š6sŽ0ý¨;®ãÛðÔÞSwš˜kAkUÅK÷¸Ý^æømrÙ¤­‡Ò㻞S‘©“ˆZÇX†[mi‡S éÕªoq5ŽWœoY!%Z­¸—¦i†q¼pÍŠsUÊ4M#™L’L&…ÂØ¶3#\Ö•ø!„%K \g‹¶ÿBÃËüÜù• ‘ˆÐzpF ºÿŒuÍlø(¥ŽÕðÛ¶4i´¸ª•ª?ÎpŽ š”HM"‘ RQ*ä¹Þt5VY«¡”G$aÇŽ´µ¶Åè;|˜ï ¥¹™‘‘{ôq.¿üR,Ëbxx„wÞ Â/2(ô¥ý£pÍE¡î]Ð͵QÂ;®ÇGåwÏïC6ÿ¦¿žþâƒ÷Œè…Ô_Ì$nͳ êó%i‹Èrl’É­-­(¡|ªi‡ëº~ÅR×Ås=\¯¶lxo[~,SHë¸(¥ÈŠ:tˆƒR,(‹Ü}÷=T**å ’ï¾WTõ"3N²¹Ùï›#O,Æ}Yd^'„k²öî¸ÍeÀ(2îɦdc¡>eõ£Ê{Ž;A6ý:h €/Óþg¿KûŸ-p—f(ÞùÅûÂáÈÕ•J­&—µäc¿(mæ_õm„Ã4fDj.±š½¬gÙõ‡0~N—çy w]Ê`fÇŒb5­¦Ú‘Ä '(é ŠFœ¢¢ä(Jއp«\õûðJ‡#ßûÄKw}hnË¢ò'÷\·Øp-Æ€¿FFþT$®;f‡Ê~f —]€ˆ] 0üñïR“‘h„ºp)kîàðRB‰éضMµZŨÕ-›¯hÕ×ëøšc{´]y=eOPr®í£p«\Áª”qJEd¥L¼R û™; +›ª7³¤g±†@¸Î&üp)~(ö÷«±gy€BCÔ]D_´&ÎtÇ„ÔFÃáð̆ÊñÃM–J3¦×ÇÁ±mlÛž—…Õ(Z®çâ83—ã¹8®GßÁ” œÌnv 95‚95ÄÊ®i˜†a†GÂl¿îz&'§¿ p-v–k/€÷(gô˜DâecÀýÀÿw6:e†Ì£áhtfCyé•3—V ÝÊ#=peŽLï³Ûqplç´EËs=»VÌAøÁzÓ¬¾ÿß±ì*áp”H$LtEŒèÛ ‡#hRR©V©V+T«Uª•*½=‡Ÿþ‘[Òs*B \‹ÉFðÀi0¨ôdòU p÷²3?̃x<~$𱏼òóå]¡YE4»ˆP.R9H»Š´‹hV½œA/N¡çÇ1rcȸÈþŒ<¶s‹k±jÜçÔ†i Í&›R$â \Ï«YtCƒƒŒŽÎÙ‡YµØX¢´­8 f=eÓ@?»þøˆý"‹6Z\ç¼p)…fМéÙH§Š´Kh•z9^˜BÏadG‘î±c’ׯ_Ïøø8›¶l¡(«¤V­bÃ…×ñèÃó’—¾„|¡À׿öoÓLJÃaâX‹ë$Vã¾zJø9]õq‹ÕJ«ZëW-½¢8G)›Ü€ùåÓ< „kqØ â["²Ù 0|ülvÆó¼l,™Nƒô Y„碤vŠ3¡<´jͳÂCºÒ.!¬z%‡^šB/L`äF0òµJ­óãU¯z5×ýê¯ð•/}‰Ï}ñ‹Üò™Ïðº7¼î»3dÒ”jfÏ“O22:ÆÈÑ£Ó癦É+®%†iÎĸNaaÕŸÖQÊ_—RâºÎ ëpU+޶®'³î*ZÞËòLÿ‰.gIO p-pjÂ%Ldóêû>œÕè¸çyùH,æ€WΣ;l3¶ í ׯ¨æ1„‹ÄEW6Â)£U‹ÈJYÈ ÆÑ³#hù P _™"—ϱçÉ'8<À§þî“Üs÷nô12é4±D‚x"NKk+Á…mbóö†A("OP­TO,V³Dk6õmBÊé<.86aÞu]ŠžàŠ·¼—O¾¨“ßýa7å¯ÿå15À„k±;°D©Y\~ñó…êËÆ 7x/ÜðeàË_ÿÆ7ßìXÎû×yc;æ|ïóA5ĸ¦'m°¹Ç™nsõØ30öÌ ÛB,íÒ´5áZþ/p5zË:|û `Q§œ2 £Bã@ëÓDJI²©‰æ¦¦šûÖêµ¶¶ÚñDÂÖu½¢f12r ¦ñLkkëãÀ@!_êïï?ò±}X½óïü»•]«þxUW©T’xç×]EmÚUÃЪ–Š¥(QÆ$åX ¥X;¹Xùhy3ɺǿ…Ù»—+VP(䂚CÂPãµ7¼º¼àãù sËömÉ7¿ñ¦3>¸{6žGËI^7pïm·Ý¦µ´´49šÖ,”J!DU:NÈ^wÝu9Πû{ï~×À·~þ —ضóÑJ¥z}¹\nÖ4 ×ó¨V*Tª4©‰Düâ‚à[R€‰R¼ôW±bm؉eµ6ÅZ›’åa§G1'èÌ.N‘J¥8°w¯²,çÖ3umçpÃ_üùŸXœ…Šs!ןlkKë?íüÙî¿in]Ö©PRó„/SÒÿ*ïúÙî ð¸€Ç”PFLóΫ¯¾zAr6n¾åV¬r]wszjêÅ¥Rñ²jµz‘®ëÑB>ÏòËinö+švt´³²s%d²¶nßASSccc¨æŒ _€U.S9‚36‚˜&:5L<;F²š'¦{Jjš-…˜+çÂ÷£ÛôàB\Çó@¸Î:ÿøÇåÕW_ÝåIÙ-…èF‰µCƒƒ;€î“—ËåÚ”ç!jóž€&à ^”«öÄ®»îþ›ôÄØ-7ÝtÓ¼&R½ù–[u`}¥R¾8“μ¸\.í¨TªT*åv˲ôîînV­Z5=qó–͸®ÍSOîaõêÕŠ\×cß¾}H]çª+¯B7tzõL&©d'»õ#•‚J𛲠ê°PüÐrœOþ÷÷¿{Ê<­w¿ûÝ› #|US*±>¥óùü‘ññÑ»¾ò•¯œm÷}Q„+àŒñøãétþ…B/ñPE7ˆµW¿äšU€!aÚ©>e{Žã0|ô(+»ºN§mÅbáovíÞýÈM7Ýt㎛o¹5lÌf3/ÌçóWUÊ•­Vµº¦T*5‹±¬£ƒ­[·ÑÞÞF4æá‡¥·ï†¡2 ª–M4£§§ÏS´¶µ!¤$™L’/X¾|åj×sɧó\°~Ûw\Ìþ}ûèÍ„BáO¶¶¶þËûß÷žy—œý­·½íC­-mÚݽ®åÀþ}(ÏeûŽÄbqÚÚZ¹ë®»œl6ôðá¡ÿóÁ¾÷{§s£Î'Ä|“èž×ô^Û ôÕÞÝÁºÝ¿ºˆ½9ïÙ¹{÷åÂã/€k€y°ÇÇÇ<‚m;´47ѵj]]]Äâqfr*ã8$ Ž)zxÇajr’¾Þ>‡inmùC3¢Åbñ•Jå"«Z]ކãÍMͺŽ;.ÞJ"žäÑGá$ q͵/%™l¢jYŠR‰¡PÈ·°žÚC×Ê.ºººp\‡b±D¥Z!‰… … Ž=J{Û2tC'—ËS­VˆD£´47‘Ídyjϧ\.ÿôÐýŸÝ÷ì3'W sÍšuw¯ûT"ž©T’5kVÓÜÒB*™$‘Lˆ'ˆD#ºŽí8<ùĽ‡zë‡?üáç…‹ÙXº9.„kùÊWþí]«WQžSê‚R iò¤¹SŽãø“KTÊ”JeŠÅ"¥b‘L6K¥\¦Z­ây±hŒp$L!_ ¹¹™‹/¹˜p8ÄO<Á•W¾J¥B_߉DÓ ‘H&°m›Ã‡bM÷.ܰ×qé¥hni&qôèQꥥ¹™-Û¶ ŒŒŽbW-B‘0íímØ–ÅÄä$kV¯Fyбñ <Ï¥T*ѽv-ÅB‘}ûöªÉÉÉé©©¾÷®ŸÎ9Æò®UŸSëº$‰bš!ººVrá…¸ì²Ëèì\A"‘  aY6ùBž±Ñquûíw|øãÿ³|.s@¸fׂñž÷¼ç¯®¹öåÚ¾lÙ‚´§<ÇuqlÛ¶¨ZUJ¥¹lž\.K¡P \.S.— …C,ïXN*•bhp€gžÙËš5ݼüº—±¬½ƒr©Èž§Ÿ!dDcqV­^Íää$ÙlÓ4éX¶ MÓÐ4 ˶ñ\Ã4¤¯¯—D"ÅÆM‹F>:Œ¡k)iimåèÐQ<@(áê]…‚Á¡£(ŽãÐÙÙI>Ÿ'ŸË2C,ëh§Z©Ò?pǶYwÁZ Ãdÿ¾}ŒŽŽŽEcñ755}ñýï{ÏtÁúk¯½öF×q¿«”Ÿ¾‹Ç‰ÇãD"lÛaëÖ-ÜtÓظñBrù<ÙL†L&ËØØ?þñíý§úÔ'ä²H“eœ1”"rß½÷°²kÝÝkhimE×/„@jš?ZSøBå¹UË¢X*159ÉÄä$™tš\.‡òMÍ)âñCƒClÞ²™Mm$Žð£ßÎe—_ŽU­âº.‘X‚mÛ¶¡i÷Ý{?fØdõêÕD" R)•H%“¤§ÒÄã ÓäèÐQ Å<-ÍÍX–MתU¬éîfMw7Ï<ý4=òÉdŠ­[7‡9rdôä©dÓ0xä¡éë=Ä…n`ýú ´¶4“Ng¥­½ÃÐql‡Þ¾>‚U]]hšFµRftx„‹6n$dË~üãÛoéX¾üSÿøéÏ|Ê4ÍxÿûÞ“ß½{÷¿ìe¯¼Ìu¬óüqòù7m¤©eCCƒŒ£”blt”J¹L:¡¥¹•Ε¬èìımLÃ$³¬½•R¹ÂÃ=ЦAoo£££Äb1’©.ÜH*•Š˜¦ùg¥bñüIV¸ë®;l»æškº…’mU«×æ>N4alr‚D›% Óµj®çQ,äúúúÉd2Ó"•L¦X³v[·ï X(ËfÈf²ôõdb| M×»ÿø#xÌ}¾ûî»û·Öß_íõ—X¶}S¹\~i,[‹ÅãRÊðäÄ„eÛvë:ï9»ß„3K \ ÆÍ·Üjxž·abbü%¥béªJ¥bXV5£kzËÔÔ¤\{Á:Vv®dÅŠTÊ%ÉW_ý"tMòØc¿ R-s¸¿Ÿh,ÎUW¿˜k_þ ~è!ؾm+ímí¬êêbhhˆä†š¤RI:;;)–Ë 299Ū®.:—wÐÒÜÄàà™t†ÉôÖ¯GÓu ù<££clÞ¼™öÖ4¡Q®”‰Åbär94i066A*• ­µ DZ±,›L&ÍÓ{ž¦¯¯—d*Å5/{9ÝÝÝX–Å‘¡£xžÇÆ /$óäSOÓÞÒJ6“aèÈ Oïyšd2E*•dãE›ˆÆbòy²Ù Ùô=ö39>F.›Å²,4]·B¡Ðd4}Ì0ŒwŸêÞïܽóÀ/Οùœ ®€Óææ[nW*•mS““×–ËåË«ÕÊ&×qW AbxxX¸®K[[/~É‹éZ¹’h4ÆÔÔ==‡¸äâ‹fõšn<¥¸í¶Ûhm]Æ¥—]‚ë8¤ÓS<ñ‹'Y·®›X,Îå—]N:›AHt6ËÓ{žayÇ2,Ë¢­­b±ÄÈÈ0­--Äbq.½ìR„ÜsßýH!ؾm;Ñh„h$ÂØØ8+;±m‹r¹L:“&"¤`hhˆ–Öf:ÚÛŽÀ4/@j:RJ Ó¤©¹…;¶355Åððûö¹™åË—ã9;wîbï3Ï09•&dšôõõ‘L%¹`ýò5‘šgÿÞg™'ŸËRµ,t]·C¡P:>ÙÞ±ü›¦i|íö;~râ’ªpÌŸ×ÜðšËZÛÚ¿ŸÏç;«Õ*Û¶ogóæM4¥šH¦D#Q¾÷½ï144D,C Á¾}0Ã&kÖtsÉe—óàƒ2:6ÆW\ASSÖ_Àž§ž¢R.¡ëSSSlÞº!5²Ù,‡“ËåXÁ:É8ÑH„L&ËÄÄSSiÖoX¦i yž}öY6oÙLçŠ\~é%<»oR“T,›¢,ïè sE'áp„Á¡!º×¬Æóz{{ùq¹„®ëÓVÔ²ŽålÚ¼ÀGe2 >Ì“LMNÏ屬*ºax¦i£‘hϲeË~ GnþÉ?™×ì®§O \󦧿ïSÖ®»Â4Ì›–wvâyÏ>ó4Ë:–‘L$0CaLÝ`ûöKE„‚±±Q¤”\ñ‚Ë›˜äÊ«®B“‡30p„ 7n¤««)%º¦188ÈÖ-[°ªR©fÖ]°Žb±LÆÎqøp?¡p˜®•+I¥’D£Ò™ ¶ã éµ1ŒJ)µ¥‹F±m‡É|ž¾¾^úúúÀ², Ó$™L’J¥ØxÑE$S)\Ç!›ÉÍfè?ÔÃÔÔ““r9lÛF×ue˜f)öµw,ÛG>sÇÎ;úõ³Ä„+à´PJ½eøèÑ.ÝЯZÖ±œ\>Ï=»wS(ä¹þ¯À0CX–E …ç)®xá•ìúéÏÈf³\´q#‰d‚5«W£iB(’ýû÷aÛ.[¶n%_(â:G‡Žà8—\r)Êóزe+IÕ¶Á¶}«(•ò‡ö¸.áPˆp(DµZejbœ¾¾Ãô÷÷2T{âg†B¤’I’©[¶n#‘LbYUr™,Ùl†C##LMM159A!ŸÇq4MS¦iV"‘È@{Çò»#‘ð¿Þ±óγ6ËxÀÜÂpZôô÷9ë»×¾vxhè]76´¶·“ÉdØóäS„Ca¶lÝŠaè¸þ?=¦"‘H°}ÛV‡†‰'ýlîÿùþXÖ±ŒíÛ¶ƒ‚m[·±{÷=LŽÓ¾lžr‘RÇ …±l!%‘h ×±p‡D<†¦ë”Š%&ÇÇèëóó£ÆÇLJÃÓîÞ%—^F,§R.û9RÙ,ÃCCdÒSLMLR(äq])¥2LÓŠD"CíËï‹DŸ½s×·ã> !BÀÅÀ”Rêàbôá\&®€Ó¦§¿ob}÷Úß>:t§aí©TŠl6Ë£>JKs ë.Xçå‘–íÐs¨ŸíÚÅEmdyG;ñT’o|-<ðžç"¤ä‰'žÂ ‡H57£é:f(Ä ®¸œ‡zˆŽö6:;W’Ëe92p„¾¾~úûûÈçýú€‘H„d*EsK kº×Å(‹Ó‰œƒ‡}‘šœ¤X(àyžïš†DFÚ;:ŽD"_ÚùÓ]w.ò­@±¸XU{ÿ,pƒRjü,|öÛUJ©OœéÏz.ƒ¬!dýK²¾{í©¦æÿ\½fM$’ËåˆD#\|é¥\vÉ%þôõ±8žçrðÐ!4M£¹©™¡£Gé9ØÃ–­›Y±¢“jµŠ°oï~ªV•k¯½†ñ±qúúzyâ‰'ýª¡µïi4#•L‘L%I&S„#aŠ…ºHùÖTfjŠtzŠb¡€R !†aØáHd<>ŽD¿ºëg»¾»È·ï„!nÖ¯ÀŸÊò¿QJ}¿&j7Ï»”Rª&6/aà×€(¥žB¼ÿû}p¿Rê1!Äjà×”RŸ«­ÿ:þä ?À¯€û~`ðj |W)å!ÞŒáAú’Rê¬MNT‡˜M \¿4ë»×þaû²eßµzR£X,ÐÔÜÌ5/½†5k»§§š.Û²Ñ4ÉðÑ£ôõ÷ñêW½Š¡¡£ô÷÷sàÀðu,#™LÕbYILä0=$Æžg§Ò¾H‹Óçéºî„#‘©h4úD8ý†iÿ~>%t !†€/(¥>>kûZà`p0ª”zb/Pî>ÜT€K”Rk…âÏ(õàÀo6p›R*%„x%¾`m~d€÷¿lzñ­°W!V÷oWJµÉ;‚ê FOß?®ï^»Þ0Ì÷¬ìê"‰I§yàûÉòlß¾Í/p‰P,8<>F__?===ŒŽŽòøc#„ “L¥Ø´y É”ŸZ‘ÏçÈe²ŒåÀÞgÉfÒ¤ÓiÊ¥P›oP×Ýp8œYÖÑñt8ýN(d~þö;~2¯Íç0 ~!B5x~ÌëÍBˆu@OÍøðàñ'®â‹Ko(¥>"„ˆ7ÿÙ°Ï/F«Ô~!ÄSÀAüáC¿ |~K‘jhëôŠO“@¸ž3=ý}ï½ð‚õk Óø•+»p]—ññqõôËfèëëgrrð'Ž'$“I¶lÝJ2™BQKäÌptð{ŸÞC6“&“NS©T¦?G×u/çÚ—-Û‰DÿÛ …nùÉ?),ÖuŸA~Mñøÿ£û…ä„B)À·´¨-†õÙ®TkmÃwõ| ­á8…/du µx ø#˜.9ù\.n!„+`Að\÷Æá¡¡‡tÝØ±¼³“b±Äàà ™l–T2ŊΕ$’ PL»yý}d3Y2™4Ùt«aFgMÓT(ηµ·ŒD"õ„Î3œ>Gø0p'þ¼›~ã»ø|Ø47+¥&…'j§‘× !î.^ô–âGµ¶êLoî> ¼ xií³?[;Æf‘ „+`Aèé﫬ï^ûëG‡ï1MsõeW\”Ò¯­•ÍÐ×sМgÒä2l{æ»/¥T¡p¸ÔšLöF"Ñ¡pøæ¥œÐY ¨oƶ)¥«íB\¼8¨”ªm;pß{~à^ÕÖëü-ðð„Rj @QŸYéçÀÖÚq®SJýâsøq®‡•R¶â½À¢ÿ€Â°`ôô÷^ß½ö­ƒ?ÎdÒñb¡@½Jg)¥ …B•D2y8‰îŽDÂ7ß±óÎg±Ûç$J©ðã9¶—ݳ¶=Úðv÷ Ö=¥ÔÏf׃ï B-¦”J?l8fœ¡RJý|þWqæ„+`Aééï»w}÷Ú· ùoR×u+žH Åbñ{‘ÈgïÜu磧l$`AQJ]µØ}Xhá Xpzúûþ›à»p9é”ÀçBˆˆâŒý0œéö‚@¸Î#„ð0Ð~‚ý?BüŸçø1oÎé!?pœ_¼RJ !6 !.39q TÛB¼¨ž¤*„¸P±¬¶žBl¯7*„ØZ˸ ø]!DÇYº¦Óæœ6ŽãWñ‡å „ø~Bi3þÓÁ›ê !Öã'²>¼¤6^ñåÀ:üq‰^ „ø à~ü¤Õ”âõJ©}BˆŸãç{}õ,]×iX\ç;€Úúü ÷/³ÝÃà|á.¾¼JÑŽ?^ñóø¶ÃÀÿÅ¿××Î`&·ëœ#®€€ó‹P¬­ÿ:¾Uõ>à!!Dã”á/ï*±ˆJ©½øñ±[ðÝÉÿÁ¬·äžÅ¯.Ãw;ÏIá 8¿Ø t!ÂøÂãá°–@ãØŸÍ@§pmûçñ]ʯ*¥l|·3Y;îú†6Và¶>' b\ç÷W)¥þSñZ|WàåJ)KñU|ÁÙƒ?¨ ¿½œò<¿?ÎÌîl“VÖJ+±çýҼΙSžó̙پßïù>ßGq9°_)µoû½ ®b@ÀEDm€ôoãǺÎ%¿{ŽÛ?+‹+ à"C)uNkã_(©OE`q\tÂpÑW@@ÀEG \p\tÂpÑW@@ÀEG \Aêæ«wÿG$‘Hü–€hB[QòJ©<þ¤ «”WJ¤”%¥TÉó¼’mÛår¹\)–Š¥Ò¹}ú$¤ç„@¸–0‰xâSRÊ÷JéÅQ •¥˜Zd€É÷B9 l!ptMs#‘ˆF½ÖÖ)¥òþë›ßö8<ÀA O¡\Às]7$=ÏBhEP…Ê DVÀBŒêš6¤ƨ”²$¥,yžWr]·T­TK…B¡866–¹ã#·{×Gmõ\'®((åÏÛ®dm©˜Ü e¢”9±oÆ1S·ÁÔó]Ï¥T,Q©V©VªH%Ñ4 MÓÐu˲0MC™¦…išÒ0  [2mSíííêî{þÓCOÂCWøëà!„p5M+kBË A^)5ŽãB0¢ÃBˆ!)eQ)URR=Ï+ÙŽ]Ìår¥þâ¹}Ñgh˜p-a¤RiOJ”š¦†ålÂ4?šë| ‹FP <Ï¥T.S*—)òT«6š¦ Ã00 C7tÝ_ÇÐu ÃðßO¬ëzm¿á+! ~®®kèšš†® %•Âï©”JÉŽ+Ô† —¨‡~ÔO!<”t*UûéR©ôëï~ç;ŽÏu‡@¸–4Ê–žwjÑ©/@ئ œ!ˆF"DÂH+¤TT·jãx.RJ<Ï­µ£PÔ– ëõkªúµ HM )‰R0t C¯‰aC7„aèš>‡(†í8·<ûì®û€‹ð圂@¸–2 Û“rBhr¹< ©<¤ëá) ž¿_Ó„ïÖ醦£:†¦¡„8½å5Ï} 0u3ª® ’TH)‘R¢¤ÂÅE)‰ïµªÓ¼¨‰Ûäú¼ŽÅ_7 ƒT*±úßïùÏï½õ]‹õ5Ì$®%ŒßâPŠxû v\÷z\©ð”•à*…ãyT*6UÛ¦R­R©T¨VÊ+U×Åó$RzxR"=é/¥‡ô$Ry(O!¥‡’ž/@ÊCyÒàyþRÖ^ÊCyLlóµu¡<„”htÀ2ÀÔt,Ë$l…E£„C!Bá0!kv ­¾N]Èh5f Z86£Ñh'×D \K›)—r¤RH…ÿ¢þQÇ …V#¦ˆÔÍ“ OÕ×ý¥+ñEO'çXÖŽSÓŽoÌÆ6¼Ú¾‰s]‰ëTqlϱñU-#ËUÈ;(%R6 ®¡i˜ºÀµ—.iKXº†¥k¼u]3;ÖtÔ?¼B„æs3ßö¶wøþš–M@S4ë|ýëjËÎ;Ç•å“'ûÃåJEx®[•R•¥T%Ë2 š®KÏËJ¥²À¸ç¹£–eßú®wæÏÅàb%®%ŒRÊ‘žœpÛ<Ç© V]¼–øK¥–0)rµ—ªmSÛ&ÏX6^£áZjÎ6&ET*PB Ì0¨Gób IDAT!¨ `]8½ œeÙx\m9y3×µ>@WÒ"™HJ<õä“ï¾ësŸ»š!bš0ÕÞÏ9+N.›åž»ï!²}ûvÚZ[øÊ—¿B©TöÝp¡#(4„Ú´””ý{_ ©¥%ãc£½•ª¸ú/ÿâ#'æ—pñ×F×õ²ç¹€@¡pëÂÕ(J0mÛé¨Q°&Äfz»SDéÔmLyßÐÆ”¶'Úå\üý¨)ˆ™ jqµª+9Qv©ËØs{öax”¢}Ù2ÆÇÆÎvJ{<Ï%3>ÎC?Lû²eŒŽ2:6ŽlÏ&ÝU‰]±yæé§¹âÊ+è^»Žb©ØU­ÚŸÞz¶ýºX „k)£TÆïøOi#™fUÍS Ðô}§³ØÔœm(¦o;s‹M*°”•†„„ ¬ DmÓq]ÓIËa_¶ ø¦”þ=±í*†iÎv÷Îð^ûbÔw¢——Âu6\ºR¥ŒtåD|Pzžòü¥çÇe\Ñ”N‘ÉähÉ8‰i™ìd­çƒ@¸–0 FAù (\Ï›]Dfsûjˆ¾PÌe)Jàæ×ÆÄþy æÔ6”Rh@Xy5—q܆1¡H&Ë"†]@‹©OÜçI@á8.º®Ï¸wg~¯}ËÊuJ¥2JI,+„ªMw(|×P &*Ô¶¡™Ì(û÷½@6“Á4Mt¡•ϺS1p-m†•bºðo~n³‹HÝŠR³œ;Ób:Ugj±M} 0—À©Zž„qÏ!ç8X©ºÈÍU~JÊÃX€?“‰ö'cWf(4!NuÁ0)b û쪔¡ !´ÊÙwêâ%¨±„B ÁD:ƒcWOkÅÌŒ7MwÙ¦ ÅÜÛ)ãdÌ-@Û&Åôñ±)לüüž„Ãù*©(5Äš¡ÕÒ:<\×C7À⪷ï'ïÏ™O&ßËÉ8W.—ó-HMÇó<„ °¸–&š¦ !„’ÒJkÛ æ°bf‹#-l žyZl³ ¦šÖÏÛ´{Pt%yW¢5ìñШ§‰¸®C$=ë{=qe¡ÍÈü÷ͬ‰•)”fŠÇìúi®Úi²áç¨g~Û)õ§Boò ·–€Šå‹Äœç6îÓÓ¦-t]›xR¨”6Ù9.ѫ¼¨RdZ7à ËÐê‚WÀÒäwç7¯üûÝN=ÙR)pl{v9ÝS¾—íôzfoã´b÷Êõó°k°=9±îi:Ò“S,"!ĉÔ\KÝ0Oaqù ¦ð‡#í—)ÖlÝΧÞÛI2&“çK_¾»ÞÜ<>ÖO,p-qÇÕ*•2†a"“ã™nuÍmM¢9¥Û×h²Ý³ÔχFñÐñêW-h®ëú¼Åɶ7,™4¨ Ãw]×Ås½z@kfò)‚NFðv=Èíÿºi;$Ûw^^o/;ßïø'‘@¸–8ÕJ%~ôHB,Ë¢oÙVF†*,‹è´„44!¦Z/ VÌ™ê'ݽ¹Ü¾©Û|²áO¨%LX\¨‰Çõìù9Å©Q¤Ná*jºŽ’ªVgߘ"XuÉòÿ LåR »8ù±¡­Ì+úp?!µÔØõ ®ër$_xnÔß% %¤³,¢Ó1h ë´FtšCiËXOÆ‘æ‘ ?MˆÔœ¢´púW‚'ôZÁC_ ë׉Ô,dž>»«8#Îåo(Í­‰öÇ_Ù§ûÉ ®%Ž@صÇë€Àð&'ºQ F*#ÆíiçAÔÔHšqS#nøåb¢† jhD AXÓÕ~a3DŒÙ,¶S»}g¨¥Haà5T†Né*Îi}5,ë¦[ý©¢T MIÕj6í*N®ºØ E \K¡‰Ê¤pîVçužŠŽ¤èÈÓk邨îW55ÿeˆZER!Ð~9æšÛ$&þÐÅ,ÛÌ@ý\ñ·WJ%õñÖ§ ‚‰„æ'RÌØ¦j’TqMX\ j¥”òËÙH¿æ?B£P(`&š&Ð5Yÿ®D \K]×Ëu·Hº³ðElOa{¯LI|A›7¿6Âtˬ>ÁgagMâé!¤¬ rm@´1‡«8!óõƒü¦iNdÎ+)©]š%|‹Ì2à x®‹ç¹¸®" ÓðŸÌè|Ô‹–@¸–8Bˆ¬®ëØUßÒvéô‰O瑳uû^Ñ5…ïµñ‹ªž„:_‘šn}ÕfBš†n(%'ãfº¨6ùdQ)‰ëJ¤T“O ÏsBð™;?5¹c ×GIF ]§\ûŸ\” à-í™î•fâº.š¦MX\§rýÅ´å´c kÚLW‘†ì-UÄèo+—§Ž£®gÍkšváüϲHµÄ' ÓœtAáB ݯåׄ¥fqÍ™¿å¿9¥ QkCÓçxª8 åÒìÂ%á „ë¡¸88Ÿ6 ³×0Œ áRå‚_ao £„ApýžÌã:ÕÕ l~@ ]›.\S-­FJs—&ÄÒþ‚„ëB`ð0° ð€÷ÿy¾.Ǧ5!\²” ,.aPÁÀòü鮢¿˜ŸõUßW.¡‰ Ñ’RÕ™ÞP‚Jyj‘Ó†ÖKû "®ÅFþX¦*ûáÍ:ðQΣp†1jY“®¢ÌC-!u©¢W3ðìšÅ%çpý7ó4©¦®£ OzSY7^[M4E¹2õ oƒ«è.ܧ½8 „kq¹ x•rúÃw¡w}„µæ|v@J™ ‡ÂÒó< @²hÕÒŒœÏn\pØZƒ*=LØL¹z¬=O²&ƒý§d øÕhí©I¿ W \‹Ý%LðçjìnÐc ,€ÞóÙ ÏórV84!\J¹Xv‘ŠfÏn\p¸µI)ÇÁ´¬ºh§«éÛ¤ç?¥ˆ†I0掱7õ©£k:Žã „pf9eI×âñq IŸ@Ù=ˆÐ„¡õòùì„c;¹°’²!®±KTÌäùìÆ‡#Lêc8mÛ!‹Î=Ô‡ÓY[RJ4MQ >U¸]D˜˜‡)—=cç#®Åáà‘E%3߬i­ï;|>;’Éf2¡pX6û±”·äôžf6X\6I#9‘˜{¦emêB¥éÚÄÆ©C~fŒR¤Tšiqiuá‚@¸»Kø dæ› ý¨˜®ójqýêÿ@ñ/ÿò¯¼ÉÚP`â-ù”W›L±«ULÓô'dïë†c\×õŸLj~0þ´y\ Êå™Ó&êº^«á%–ôd°WÆÚFçøZÿ ¸LÙGQÅOn®-À‡jë ?¯ëqàœd…Ž79xSºKÞâr…9%Æ¥ºoíœR¤f´Z]×õÚ˜J¸Ò¦V‡¨cW«ÈYîÿD—¾ôf±¾ý¯ÀŠÚÛl \kÉpä†ÃÀ:`Gn¸Œµì9Wêþ$jìß™2$xR¸n›å¼Þwú€Âõã9€èp!5cB<ìjÃ0ð\ÿI9Á¤4iz½†YÃlÕ ûënc©<û”‰õ±Ššn-9áÞÔ¯ÏÂ5É×ßt໹áÃÀ€…›¿nÍ}ŸBX1•ÿÊ雲KŸF•÷Í8E‹¿N¢7½—ÊîOÐÿ›¬/ èúÛÝ)¥m<–xŽ£'&Ÿ*VmÃ0pç3К٬/ßEÔ4T}¢Œ¹]ÅÙÜD˜´¸,ûö¼¾i!>çE@ ¸™Úø_„k’;€wëñ-£//hë‘+ýt/‹Ì~gÆnUxl–“*²SzŒüí“ ÚŸR±*S‚ónô¥=N‰©1.Ã0f´æôÖ—Ð4´Z• ]×1MkfŒkZl¾Z©`™&mím$ ,Ë¢TªLW´«z,Ùz\Ÿ „«ÎÚGJ¹á­À¿¯Zðö[þ2óuPó+Ö'×#¬.¨ûÈ‚w©ŽiN}¯¹U³Ë]xú¤pU+U4MóÇ+Âq2tÝ'!š@Có‹kR*¤ôPµª†š(|ÁŠD#\uÕ«X¾lÑhŒƒ_âŠ+¯ ‰088Ä“O=ÅŽí;¨T*œ‰£[”­æÐ(-­-µÎ1ô,Ê9ÿ8À>àÁ]÷ÀÔ7Â5µxÀ?Õ^ E0(̦Ööë¢1(¯Ê{guµô{ê™ôFçg>²€}™Aþ¾»~G®­TüØŠ8UP/6”>ù§á:J( Ã@J_”f+éͺÏqê‰îéI¤'Þxƒ#8¡(v*I5” NRÒc”Ì8E¥Qr%%W!Ü ×þ燉„ÃD¿üùÞýÛ‹p[.á:?d€‚öiÞkÜ¡*3«Øˆèˆð¥óÞ 1‰F¨ ÕsfÜ]4(1ù§á8.ÕªiXó«Æ¥cOŽÐq¥‡ãx´î¸–²”…çJp®+±Ël§ŠWª WK$K9Vïý–P˜Ö„ÿ¾¤'ƒ…@¸Î'_¾Šï~ à/€7á N=J‹ 5ýLýÝÿιߦiÚÉpísUYÒUPúd„ÀqÛÆ 9SÅê4ÖWÝetÝI‹Ëw!‡_ÚÏâfÆP™Aë'6ÞGºœ÷K<ë:VÈÄ2-¢ÑÛÞø&†'~+p-v–`wm½@¹CSÐR? zà+Àç£S¦eö‡£“!.UÉŸË^0%Ñ+9t·„PB¹„ÇŽOìw×uq÷ŒEKJ‰ëÔsä@J‰ej¬}ò«8ŽC8%‰[ÓB|k7¡PÇu©V«T«ªùÒýŒŽṈ¤g±†@¸“KQ.¸c„µ ¿üÿQç|u$™L‹4Z\¥Ÿ áÒE¯æÐݲ/HÒEw+hv ½šÃ(e0 £˜…!ÌüÈÙ‡9¹Žƒçy8®3o±j\ºµÄU]<ÏþK$ˆÇ¾°¹.¥R™¡ÁA†‡†fíÃ4–ô,Ö×b±H(w˜É@½†H¿—ZBÏ'ÏWg"‘ÈH¤Áâ’¥ ÛÑÜ*ºGólšg£9et§ˆ^ÉaÇ1Š£˜¹!ŒÂÈÄyõJ¤7mbdx˜Í[¶Q(˜$“›Ù´ù<þøÜpãd3¾øÿ8q^$CÓ4Ç9cÑò+Ö&"Ñ´‰mvÕfÌ›’ó5[EˆÙQŵØX¢\ Ð߉×!¬•ÏŸ=Ÿ‘Rf£ÑèD¤,eÒEiç÷ç¡Ù%L;†ç[H^Õ¤j½œÁ(c†1rƒèå3×[Þú6Þtó›ùÂç?ÇŸ¿‹;ÿöïxç»ßÅc?| ˲ˆ%b/:Äðð¤Õ‡yýß„n®âéÄJJ99/#L¤Uhš†¬ ×Tü[_­TèkÝHaã5$ö?JÇØœãíÇæÚ±T„kqð…«ßÒShÉ[j3'ð!ü¹NÏRÊ\$“ÔrÖd¹ˆáVp¬ø´®Ð«,·ˆ!ºrФí»k•<¢œE/Œ¡ç†Ð³'ќه»œ-ÃCC<õã'9räÿ÷£åñÇçÉ'L!_ žHN§iik'šH²yùr¶ï¼Ó2 Y!¢ñ8vÕž·hM§¾Mh®;›pçy¥Æ5ÿíƒüùk—ó+ßî ð/2¥0ap-v–(”ë[\ZÓ;A àNàÙóÝÇq²ÑHÄ£!Ù6„Í)Ël*‰aˆxeL!1pн*º]B«ä¥ Z~rCˆÌI„w~‹vjšFSS©tšt:M2•RŽçòž÷ÿœ0M“K6oƲB˜¦‰ ‰Dü8Ö,î`µR—hÍ…œÈœ×ü‰f'Ĩfä*˜ÏXª‹Û B¦Ée]m<" BÌÒ®ZÚ³XC \‹EÍUB„7!¢;€Û£3•r%ñ]Å RÅ~âT1•ƒáVÑœ"Z9…1È@ö$2;òüÆöE)MSsšæt3Í--^º¥ÅF£Žne˲ ¦ie5Mô„B¡=ËW¬xJ)Õ›ÏçûÞÿ¾÷Œ~ãÛßù€TêƒÒ‘—{Ò5=Ï£Z­¾¢À»”ò´ó#*%kýx®œ>4€jµJÒ;ɷ޹z­äçö›M´€ý×¾°ƒç@¸ßSZúÖú¶ßa‘òs~îýïûØÇþ|Ê_IøáÏÏ8î|ø¯º®Ó”NÓT³”ZÛÚiii! iÚ¨eZY+Ê !Ž„ÃáÝËW¬xÚó¼¾B¾Ðû³ï»u^ßþÓ·LŒŒ¸ûk_¿UHñž'_å:nèLïóAÖc\ºŽr\-­º‚Ùv•˜”ì¾ûï¹æáµ¬>úĬmÍá:.9áZö¯Ó–}„p~rꢡiú9Ÿ9F7 Òé4MMiÒ--´´´xÍ--N$q Ã( M³LÃh²B­­­tww“H$èïï§§ç(=G^~ãÇ>ö±yöœïyçÏÜÜð¥¯|õÍv»*+W»Ž™Oà}>¨zŒ«öTs:®ëN´Ù9~˜Îñ¹«w‹`2X ®Åâ÷åý§€G_ZÜî€i%& µ1†ann&N“nnQ;(¹áHØ6jî›eYã G£Ñ]­mm»]×í-KÇö}·æ>ðüEg×Ê?XÙÕE4% ‡ë[ÓßëÏÿìû¾|àþñK7º®ý{¥Rùújµ›+ð>êçišÖPg~r¿mŸº„¼®ëèºî×bQÞ½Øh$uew÷o8vlÆ>˲&Ü·æææ Q …"¶i%Ã´Š–eŽišör<6ÝܼÏuݾFQZ¤ÔξRÇ<ùï¿üóãÏ0Î]ÿ…WWªÎV*å›Ê¥ò‹{ÝUB›aq)À±mLÓ$‰`š¦’8®KÞ¨RŽuë/exhˆñññ¥]á±F \¬ê^}û;Þõî«òùü¥®ãÄB¡pÕ4’i™EË´F5]?”H&ŸM¥Rû]×í+ Çîgß{¾Sìå÷zÛ¯þÊ“ÀÏ|îów]æ:Þ•«•7•Š¥ô|ί»Šº>é*š¦(*¶‹—HS4c „RTb-”âmä¢-ä¢íõ0kvý‘cûX¾¢ƒ\.»ägø@¸j¼åÍo,ñæ7^óÑ?ù˜¾fíºð/þÂÏ-\Éêy"%m§Ú/ë€~ç±ÇÒÛnö4-R)]©²”Ƹ™ÜÉ“C·Þzë9³J>ôÁÛö? ð¹ÏßµÁu½?®Tªo)W*mš8®‡])S©V0L‹pØB¯ŠZm©Çâ/¿'ÑB5ÞJ “’+)ºÊ/eSqpÇN8@:7D4;DSSšû^Pžçþ͹úlpLáŽÜî±uöÏ!xéö7·4ÿí=ò©Ä%~´G  ItÒ-í¥zd—€gP<] ß½åÚk¤Ìñ=÷Ü£ eÖU«Åmc™ñk+åòN×u/1t#ÍåèX±‚Ö¶6”JÒÒÒNWç Ž?N!Ÿç²í;iJ7388„–lÇ\¿ƒJ.Kµ¿wx16@ll€Tnˆ„[$b© ­"„@ɇ3óÏî½ï{3ýø%J \çGy$lÃjÝcµb J®éíëÛ¬<ÕyÙl&éyº~ªP—Š×(¸!ÛÍ<øð£·+¥OÜ|óÍó*ôÉO~2"B¡KËùÒ•¹löêr¹¼Í¶««K¥R³cÛÚÚuëY½z Êóp=Í›7á:{÷¾@{û2ŠÅJIö¿ø"º¦qÕU¯&sèÐ!Òé&ªù!úîü],¯"Sºæ ¡Aðo_ûÚ=ŸUóȳøÐ‡>ôj»bߨÔÜÜÝÜÚ<žÏæŠÅüßþô§gθòH \çŒGy$ìy\‹&^§Pë”k„Rk,×@(àÏ=?Ðßwºæð<þþ~V®<¥¾M§)—Ëßqïw¿õƒ›o¾ùG;>ûÙϦ«®»97ž¿:_(¼ºZ)or=we>—K E:»:¹lÛVÚÚ—‹FøñŸäðáØ††ò$ŽçÑÜÜÌÑ£Çq]‡tsšh4B8d’ËXÖÞŽã8¸®ÇÈÈ«W¯æ²íÛ9tð íÍEÓ2ÿ®cù²¿¼í¶Ûæ=húýïÿ…ÿÝÞÞöá•+»“¤R*qé%¯!“nNóøãOÈL&3|ìØñ?úµ_ûàBVñ½ óM¢ ˜/=ôÐÕRhEq->í Àèè(ý}½Ø¶Cº)EgW]]]Dc1Ž?Nf<ƒëy³ÞœŽjµÊØè==G8IKkëíJªx¹Rº¢Z®lp{y$ ·´¶`&ؾc‰x‚§žzŠÿøÇ¡sÝ ×ÓÒÒŒë¸ds9â±8‘H!»v=OÇŠttt"QT+J¥–"‰„8ÑÛO{k¦e’Ϩ”ËXa‹Ö报»víõÊ¥â“Çö|vÏóÏõžê3­X¹úšÕ«×üi"Éd‚îîÕ´´¶J&I&“Äãq¢Ñ†aây.ûöíëÝ÷â¡_ü£ÿÎCóúâ."á XPþñŸÿù¶U+»? hgÛÖ\ ›ulÛ¦R­P-—)–Ê”Š%ò…<ù\ŽJ¥B¥b£”$•L‹Ç( Dcq^uå„B!žyöY^}Õ•”JUNôž ‘ˆ Eˆ'âT*úú9vüÝ«V±aÃ\Ï¥·¯ÏuI¥|ëjxx˜C‡‘J¥Ø¶u ‘ÑQªå2¦eÑÞ¾ ”bhxˆ®ÎN”RŒŒŽáº.…R‘5ÝÝØŽÃýû=Z)”þöÁûï}Ôuœ|yçÊR°CÓ4"‘VÈbùò¬_¿ŽË/¿‚5kºI$’„Ã!lÛ&ŸÏ344¢î½÷»ÿçŽ;î8÷%ÀÏ#p,(¿rÛm½áú?²|ÅŠÓ<êÃmÏ/Ÿ\©T(—Êd²ò¹<…BžJ¥J¥RÆ …éêì$•LÒÛÛËÞ½{YÕÝÍ7Þ@{[;¹|žC‡^ÆÐ5bñ8«º»%—ÉbZ&+V¬@I…¦ëØŽ Bá0ý}ý:tˆD"Á†K7L&<9ˆB¡ öeí ôóÒ‹1-“k¯¾4E_¿_(Ðóèèè /P*ÑtÁŠe+¨V«;qÛvèêê"‘Hpð¥—Ȅñ¿ŽF:ÿæ÷~ïç'”\ýõ·HO}S)¥Äb1≑HÇqX·n-·Þú¶oßF>_ ›ÍÉdæÛßþîŸüä_ÿÉ‚|)p,(üà‡þÚ0Œßíèè {õ|WÌb®!vš¦ù¯ÚÐ=%ýYp*¶M1_`xd˜±±QÆÇ}¡B#ÝÒD*™¢÷Ä16\º‰M7…ùæ7¿Íõ×_G¹\åØÑ …"Ûwî@×4^xa?º®³fÍjb±(G{޲¢cº®süx/mmm$SI†O’Ëåiim¥\*²rU÷„å÷ÒK/1Ð?@2™dÛ¶­D#azŽž 6B°{÷öìÙC:æ’K7²ñÒ ´·µ1žÉËåhkmÅ­qÅuÚÚZ‰D¢8ŽC?Ý«VrèÐ!î»ï~–-[îlÞºås‰XäOn»í¶€›nºi“ëÊ/))/WJ‰p8L<‘ žHD(‹t¯ZÅ-·¼…+ÈesdsY~ðè«wÜñ"ó ü_ ° |âwµ{^×õÎætšæ–škC‰$‰DÏsñ¤¥(•*ŒŽ q¢·“ƒƒŒŽŒP,äÏd(KtuuqÅWÒÚÚ‚eY<üÈ#¼ýíoGJ=»wS­:ttv"„àÀý$’)6oÞˆ®iôž8®tww34<Äx6Ǻµk)—+ôõžÀ0MZ[ZH$ã>|”îU+é •JÑÔÔÄàÉ“äó9ºWuÇq]‡¡áŽ?N±P P,±yË&V­ê¦¯¯çž}†—½Œ‚¦¦&::;hjJÓÑÑIGg'Žc#€h$Œ'%…|gŸ{”¢§ç(xàIDATãããÄq’É©T’dª‰d2E©TÜûGðáËïóu×]צiúJÊ7뺱*›ñD‚D;r}¥R¾¼Zµ7HÏëÐ4-Ò×ׇ”’ÖÖ6®»þzVvuÆâàÁ—Ù¹s'££cTí*Á¾}{iineÇÎHÏ£X,ðì³»xýOÝD(bdd„r¹BKk+…|={öÐÞÞÎÚµ«‰„Ã=vÃÐkO«¤ÓÍ<¿ëy<éqÅÎÄâq‡Èåó,_¶ÌO½è`ÍêÕÄqòù<`ë–­$SqFFÆ8~¢— 6Ç&ªSx®G©Tàá‡âرã¬]·žk®¹šË—188ă>„®ëärY"Ñ8MMIÉ$¦iÍä&Djxhˆ±Ñòù®íbZ¦ …G±ÈÓáHüŸ|ð¾o,æ÷{1$ Ì›·¿åí×4µ5ÿW>Ÿk­VmvìÜÁöí;hjJûñ•¯}íkôõõ‘L&‘ÒcïÞ=„ÂQÖ­_ÏåW^ɞݻéëíãªW_EKs3ëÖ®aÏž½TªL]g €õ—¬G A._bppˆññ ë×­# sÕU¯"_(088Äèèë×¯Ç ™”K%¾tÍ›·°lY;[6oäÐá# éK9‚çy,_ÖΪ•+‰E# œÀ´LÇ&‰â¸.B3hmkóƒöÇŽ±eóF¢‘år™Á“ýôôô044Œëºô9ÌÈð0ÉTŠd2ÉÊU«BÏÆÉe²ô?ÎÈðc#£ä 9\ÇÅ0L'Eb‘ç;–¯üòýÜÿ/‹ý½^ŒÂ0oúOöþ’fj­B÷6–¢£c9ù|‘‘‘Q‡çµ×\ËæÍ›°«U*­­¼°oJIbÑ›6n¢³«“T2…ëyÜû½ï“jJaê¦i²eËvíÞæ!F†‡YÕ½Šuë×ÓÛßÏîçwÓÞÖÆ%—¬#ÚÑn yšC- …ZYB±ˆëJ²™ ƒ'ûYµj —mÛÊÀÉA×%—ÏsôØ Âá0¡pË2iiiáðá#ÄcQt]06:Êó»vñÔ“OÉd1M‹d2I*•bÝúKH$SHÏ%—Ë’Ëd9~´‡‘áaÆFG(æ 8žƒi˜®e¢áؾË;îþ¾}û‚ê @à*Ì›õÝk­myÛ]«„ëIÇfËÖmlݺ•––,SGJÅ pùÎ úúûY½z5º®ã8?zì1âñW¿ö5„ÂaªÕ ‡½ôo‹y?΂-ÀˆRê¥Ô£ø¢‚âžÚ1ÿܸ@3ð[À;(ð¯ÀûjËŸú€ÿ‰?‡çðÀzà >ãì µãžÀ®¯¯ÅË_þvu|øS`QÝé:pœ5/ëùà ëÖ¯·Ìл:º:‘R266ÊáC¦§§‡|ÞŸÉL×õ WoEG‰Do"‘3ñž#d32ãcd³Ûê"exáHd¼©©io(ùêððЂ: @“"®”*!–á‹Ó!àzà§•RßB¼?ö} O)õ-!ÄM€«”zHqh¤*ðÿ€vàw„ëkת›Áfmù|mù ðçøºðÇ@ð&|×Uw\(1°@¸„CG¿O×´š–ñšå+:1 ƒþþò…-­­~bi4ë9©‡ý|©Ì8ÙL×u'Ú3 C†Ãál*~!‹ýWµ\þÌOÙ³úÍSÞ2óO§^ÇÐd×åõOÏ 1 ’â"sÆ"@ :…R£D¯jP1* V™h::DMG§¨íäÄW¸=ŒÃi(r™H-­'D10BMÍ2Oº-Bb´'KÆ {eJnS•Óæt³íPºVW”ÞÒzp¼a¬šJÎZG\+»NédW¦V–‹Q“ k5©L¯K¨ïÅ(Ñ㌮TÏ(5€R=ªgÞN#îÿjY·“UãøÆüöù¦õݧ&Ç]N`9ÄÏ!ö”P¦Š¢žÇ_ßc¾õ¡ð¼†°Ú²Îä#þú„ª£gZµ}Þr¨g[2ßvQTκîú¸;’Ìd¸©f#N&‰’"é¸àTrPäÌ0T,E©³œÈL†N“(±½ƒhº8DMšSÔvr b²ÄwNÓÁ 9kŽ]zE:­U”2‘)e“Ë©¢TŒ¹@ˆJ „ªÜÅªÞØ’à=¶¾àEÃnÄrãì†Û?m(zÕ§S}ú;J}ÝédE†‹¹@@‰^¡Ô¤PÁ*ƒXe±Õö°ktýìT°ÉAâ„€‰–è½Õñß0EÓ$w¯mÚA§Ô%-'†…™ã4 œöÝzGõf“Ó¾Õ$¹²4¢§h´©ŠV› ­aÓjS%&ÙEˆ»ÛÖ9g5Ôït6.‚[—?2}&NÁ£÷DeŸÚÿ¡Èh |#æjtµ WÓéîÓÈØ‘Pˆ‚FAS#—yß°0RSï*Ù‰bg•XìÊ œ'L…åNV⪠€N׫ZШÐGT B¿ r“‰Ëy@ë´ï6¸œûŒ’ó¸NÔÄ+}:ÓR!Ý™ ˜™HÍLÌ.BÏ›løÜ3·Íõ¾U·Ã7éDåfüŒßk~eÖ¥õPøß€SÏ›v¨LïàAMp%^žû=Õ ÁÊd~y=ë鑯N«·L°š@§â¢ÙIíŠKtÈE¤ZÎA¶c›%§ô=È™¨ía'ºUZ}¿jw@€*„:ÍÖ˜½ýJ lk ùmÚA·ÄìîO¨ÓÜÒz´U¹TpTo48í[LNû.¥Fèý•˜ØË½¡»OÇ8$‡6?¯ñκò-ÕôñÛ×à²Xc27²\_LÕQg('¼sZ[4®(CµX£ Ñ`C;ƒžå2É“Uņ#U ô9%­&QôÃ*µ†á•Z]/@¥#§-Àà¨Þj¨®ø%JrÓiµí½¡³D_ÀtºNN·îÿäªÆ‘ö•ÕÀqÑ€qÍüM–šÝ BÈúyŽk;±8C`zû3Z*BGX¡¯½èaQ(ÀàP\ȵç›NÙ0f~ÉÊe ZØ eF© &qg=ôÆì•šÎ%—µ2Ò²Û¬ƒ®Ñ–™ ÆÓZZ¶†äÊÔTW®4;íÛL’ë„F«K— ÆˆŽ½Ä¥Ñ¤0ß)¾4ÖúE:J«&¯%&$¨EÕë‹°7EZ“Å—Ã É)Lh_sÒåG¢ «¨C_kúZAdÖ™Õåⱪ ¶ã…Kmå cZ}ÿ*ad¥Þ8¶Š‘? äÔ¢(å´ºâ7Kuå2›F“[Ô$Åh¸Ç ¿ˆt(4¦?7Ç»@ýu_8i"I$d×'COEt4F££1š0âT$¨ØkÝ™¿Ö1Õ©1Ï,Ö†U·¼ÅnÍ(kÌžaE¹£VFZr›uЭѻ‡J›­sã´ïÔW–/ŠrÚ÷ ¦¡²5z*Óú9(5x ý"ÈAhht'Ü<¾7Má©?%è30•<þâ4Ng³ÇoûJf*ÅÓ•Üý· „C{³Ù?Yà5-â£ïÑy÷ÆïÊš¥»¾Lôäë×Ó ü¾p3ûö£^i©@qÞÅCÈŒëÇÓÔN Ä`t?õ¨ª°#ãP6Ûµñ[üåj%/»(¼ê7ˆ³Í?õ®H(:mèh´‚€¡Ði'Õ'MûÊ÷‹J>„Î|a‰Ñ2­ŒR+9¬ ãrÔUUüls:öLæQJBòc²(Æ2Ïi õ©45é36}#éMiwƒ•«ö;Rò[:J1ÀÖX’U]®ÛVöab~É;LkšRª3_TF©…Û´ºÂt½ñô{Ur~$å¶IUÔT´ºÂ®-­ÇÙ‚êÊUæÊòŸ¢d¹P4[Ï“cns ‚ùÌ\ßyâÁð7Ê™GÜØG‡ê2ÕëÉØƒÑ¤C×n‡¹kD2ñÂÞlÙÏ5vþf«žté‘Dª*:¸õêÒ3™‚ÛAŸ1g$]»t·â?7œ¡]Ç82aÚÀÚ9± 6æ)‡‚ç?¸U6¾7€ã³XvF4ФöqèÞ¿#é9¨39q “åe²æ‹Á4}D0ܲB!ØK_jÄjõˆÓê0Ø–@²ìåØ\º,*+ëûh½éœr£eV©¨i犘rœV¢”Ѳâã$×Iƒ5ê"%.á&!´[èÏ4ÞxÛ¶ð(us½ýe…G §Ýó˜gݼëéiÇàõÛÿµRs½·æýþT(Dîݨv Ṳ́Jv’]eDïÍù%Jg½ºÐ`>¿<…œm“åh_{UòŠH m“ºÑ|¢#ÐŒþBCQ*iUů–ªòߢ¨`„Ù6U6šÇ8k^î lDê3. ‰œ„ùˆTt¦)".u(2n}p"]ùÛ^E‘¼WC E—¬ŒB6hdWŸEòO3ß|çÏF ;£€¥¤Å_9ÓæœC‡ïMÊŠ+ñäMoËwžðràc¬˜xÉZ”WÚø «ÉväùnÊrÔi§7¡½ÞDJ\±ØR¶Û²?ç‹NßË®·Ì*ÖéûÛ›¤PN«¡ªr¹¹¢ôëX³u²—x›«nK(Ñkõã:I’¡È·Ë€[î&Ø«\˜÷þ&¹éŸ:ªél_ ãjÿû3ýúIÔe‚÷~aN»£vÿs&’¨8+ùñ£_•êŠêd©ÓÜNz&AƒQÑñ`µ’¥߯V-µ£Ì5©<àF[’FˆL"ö”¡M:èz}~'îGÆ$R^úUTeůQZ]7%:ávIoè[Ý ©Ér^"5/½)úDSEÒ3Ž0{µ=û¥’é—¡‹¾Ø¶QXòý實Sf&Ÿ¿½Ì˪SJ0yæPRZT«ö±K®ë§þ±= Ì©rpg†ßU¡(¯ß¾¿Di «$5ÕxoŽwÂ!J£Ã¤Ø$œ‡e¹úm…/%W ñ.sôÝùZ]wGý8%$)G,-z'„ˆñ)OI¢f°ù¹PúÖ¸±íèÌY]Åž=cHÇŽ6ârÉ8|¸˜íßW¨|ôÁ9ãDI­`O»Ù¾ƒ¤¤šIÆñ–›SîW¸V'à¶{F ÅEÕgô¦µ»ÍÍ´ë&‘¤ñä§O—=ôó¯šHÒº·#Ëæ¯‚ÛANS¼c•Ø©=¢cÈé#¬¼ °Áeš f&¦Ã•¥Ú•ù¥jLSJMÖ«J@„–7ð­Bd­Ñ|¢]ey—“‘’Ùæ–»VÔ”¥ºF¾Ejs:êòshçpì´Æ§<çŒKþ›KgèWó"úP4‡…5_‘áŒãï¹Iëù7Tj£PD¥¬zÊô.—5ÄUU-‹1†w_ý]€îOuzM€ì¾zÔ±ê×]¬ªÒ)— ñ³CÆt§qI6²ì‡­ŠäRÎ'·T•7*@F[ªÑàù­,·1¥ÎÑØÙº5è©€áQq¸­}g 5;5%ùO¦”˘ƒ¶´­à[d¶Êò_l…yO¥­ãh|ògœó`=(pÏÒé(ž~z„æ£O&k/¼¨3µEé°fM¦²gOÒ¥k4¹ôòŸfig_ÖCPËùÕ}„O¾¸D3ù‚.gúW(ãË­kˆ6?ı@ŽúÄ 2Ã…ªz¹œ.¸œ’åYvضIMòù·^NþòuÒkÌ¢ÖáÚœt“×§¶')Ê[qÎí%W–¦¥ÇBkÙ ÆÓÃ9_õÑæ"è&Ëñ4EÕÈp‡1)+ù<º²r™Õ=[2Û.t›â½'°9 iB·LáÎi¬ß–Óp÷‰œÉïQË{°eý1¶iíQ6lLrÙ #éÿÞ]V½ºÊ‰Õ‹w)ÌJû îHöl=^}»àÒá–,ØÄλdˆªŠÙùHKOÂäÙ#ÈÒ IM½*Wèm¬Mƒ·wxç·¡ç.P9 —§Ž@†ÚbHºÑŒÅk­E§7š,1÷æëô}ø´—¿0e¥_FÙí;¬ ©/IT°0{¨©ïó­sµ'v ååN<üàj×ÊJë& wÝ3H¼óîÁ /uZ_~¾§žõøÃ«þßµh ÔôjºðÃ3žV|×v÷-S­¾êmИëVdó言óãâɉê á·ü¿¥XâžÊÑh;; ê¬B«-êHˆB£™æÒæ"è:}^¾5~s8öèòrîoçt1ǧ¾â4Û¦ÉuÃ=”ûòH¥ñLáÆB̧?RÝ|Ûƒlå¼ûÚï2c Wß6F°ØôÓÚ¿xÁfΟ=´ÖX£Œ5©79¼7“=°¢¿Î_¯@ÏÈW_ï{ñ*:t\ob6n›FÓÐsÖ¶‰ÒhqUr2% ¥/$•½ËPMZÚ†ð-ü­¤øÃX§ó€5.ù)Ws®NhûÇŒI¡'v .—‚Ëfÿä\±"Ã˹e†7_ß"=ÿÌ îy`¨`³iCìy*:xØ¢è8#tê6ª[L¼“6àñ@›Î Abj4!õ|çT ^ƒÄÔXh´ i·[:ƒQñQaK¥Etb,R§HÐÉ`ÆŒ„h¡¢àÙd§s¯®¥ÇFKo„Jzƒ131RíÛæ"èµ[Z¦+ŸM(()ú$¦ªrµÕs•d²Nv¡ßˆ‡Ûþ‘Š 4ô¼7e!>Ãåàžl¶rñ^eÂÔ>tÎíã„÷^]Ö—&wm>β2 Ù¹SûÓ·ž](;í.L¼x0ÕhE,ýnsà+ €+ö°wŸ_ \ÿÐ4j2aêcèÔ+Æ€1†ƒ;O°µ‹·±?[©HΖ{—¨¹Æz¤Ëi*½X¢HWƒ ‹ ¶Xr²·š¢âÉÕêzð¹é ¼¯ Z“ô7!ê‚Cé;žiž|r¸¾ÿî°|äˆÿó¾þrŸ|íõ}…Ž¢Èw_yq‹W^£5¸ýÞaâµ7 ¬•ñîäEßîõ‹&Þvï(aêŽh‡ŽÑDQïÏcÏ=þ‹th_S+;¥}4yðÉÉ´ï ö4*ÆÆÜËÌ~ùÑ:eáçýä¿ñùÍbjZ,îºìyЈ.dÖu£i—žÉD\9îE)7³(¨m½Q‹ëœFG×$¤ÄBÝZÛŸÉ~øï2eÙwëê•ñô¼Gh»ÎÉä±ÙOË%y%^ÇQÀe÷Φ£¦Ž$IiI ” ¢´ÛWneŸ>û‘RYVá•~òuÓÈÔ›f_?üŽíX±™]÷ì¤ÛÐÞÄ`6Â^Yµß-eóÿñ>“î@¶V¯Ã ¿Ï£æh+àò§î&3º©VÞ·/¼Év,YUûCmN²Î€Kãè‚ÜW“XÌÝyzÃú'۟ňšŠˆ}ü²ÍEÐ)uš½'¤ñ-¼MFQÁñÇ~K\»9LÖ)õÌk¨É‰4žC‘U¯ýôÉjzµ|áæ $Ïó¯gyÿßËYV0ûÚ4.Áv©K¾ß¬Ízœ3¥/Ü«·H.Ë~ÜZïcº…óV)×Fzýo_(kÛΊòË@AÈÍOÌ¢oÿü¤Ü!¾¶?XÝÛÞ‘:g=,¢ˆË’Éø(P”÷\²Ã±O×òv…oÁ6Æ$R÷r"(ÕÅ$>äª[«†plj[n|¼ž¤§GøôÓ½r°|ŠÂð¿OwË0rt;Z#c÷Ž\%?¯’Àé¬r¶k{Ûµý4Ûµý4+ȯô¨@ðä ç‰×Þ\ª¬þy3‹Šµ±ú¿·£ÖÆ1 шOƒ x«h‹µáå…ÿ ³îšI¨@±rÁ öë¼_Xþ©°Dzø‰Ñâ £…¾Û¯|þßí²ç—J=mXT´ÃFu —]8Ï•}ª„¹‚WÞš!N˜Òιy8ûâïµ¶ÂjÕ“'_š&ju"¾å+iíŠCŒÀ7ˆ‰5Êo|vxÁÌtɢʖuGü*zé cè·Î“þ\¹Ÿ¹ëÂàn¿à—‚{Ÿ»œ&µ‹%ëßÉþõÈgreyU­íE‚Žé)ò‡f®yì*Ú©WG²lþ öÉÓ)’ËJ€«C§Ý:ƒ\ñÐòþcoù 3s"ùõÃïÙüW>fLQ@  ©S;öââ÷èˆéÉ¯Ê N†ätáý{žc×½ôâ/¿¬ül!Û¸è75בpÞ󪛨Áœäxú¿ÓŸÄk´OkÚè2Œ‚`˜ƒÞ¦"袦¢-}‡õ×ÜdçÜÀÕ¤÷X8iš:mCÒË ‡NMŸº}¿±RqØ]¸ðÒÁ´]ÇØ°ìn^v Û¶á42\wïd÷ˡ߅¿lc û·c¯=4OþâÍŸè?²;‰Ž·…!¡±m -«9ul:z™-˜MŠò^Lâ‘ôÖ¹9ì{uÕömfkÜ->O+ƒÛü÷wêd#›[É$)ØwçÍÞ ¬]qˆyÚÔâ ¼þü¯2\vÃ(Õù>¿|³YÙ°ò@Xuð˜dà¨î¤¬¸¯=ü™\Yî=cC–Û*ˆÌà¶¡cdü̱$÷d.>zúcErÕVƾþ×—J~VÆÎœ@ŒV£_þÓG3ñÍkó¼žä?…íË60*P¤õéÖbH“ bj¬•V¾šÀ˜DZzì´Ä&P»ÿIk mÊA׈å¦0ß+áEÿ Á9wã™7TÂMßÐ<¾ykÉÔä4…uTÓÕ³ì‚Ü2¶à³?A ¸õ¡óÂ׋lR%3¹/)Ê/ÃÆÕá]ÔÔømþ:E‘Ýý”´†Ns Œo›Gâ|¶Ez›Í˜EŠóþ‘ätì×µ¼½á[ÍÆ”*ZTô~|Tü=2!þ/f†Ò烥ÑjݦÂåRB;.g¯Õ†¾ZlÆ€Më3üîN-d˜lõÚßw`;k–T<åÔÔiÏÖ“L–¤uŽW-wÝòýaº÷O#°ê§-JUEd;"º L!;×îb²Ë†¨,É8²ý#” ¹Sªßñýw3Yò ™}Ô½üvL²z[4i#ºê]beɧQ-=~Zb¨“Oqi‚XevgËQ§°àÝ8§—sJKÓØüÍ•Ö3}CûMcó‡^ÆÿÞ[-_|ÅPzî}hVF‘Ê…)°k–ìf%…0YôXòÝfE‘C»`ƒR*¸€¼¬š—²Ô"~Í÷Áèm6ƒdIÞ?’bþï´V×µÍ/™Ö(*ú Ö`™À4ºNJ8‘ñPÓž8ᎈ''›! ,è0aHmçž#.I 23ý?H¼l ´¤‡ä‘ζâÌ· —ÙIëì~øö׉@!Þ ©]!„€1ïèuNVøó¬ÛwN$pêx^=uk˜MIéìž3ñò dÂ¥ç äÌ„‚Úßgê•Ø!‘ÝyÈ« âœU¹ö3BÒõ Ò+’LŒ¶’O²×YìöaUz}ï65uŽPWÄN@›rÐIÀ¹{œ@TWo×WÛwãÚ¿á<3çÜÃq«¿-ƒ··ÿ1§0”2BOëŽ(3”¼Áô&3øãφBT”Vã‹þo{x²péõ#©zyêe;.\2ìi‰„¤£ûØý/^Awþy˜­]¼I>A¤¸åï³ÀýEÑüÓE>m¾£®Ö¦¡î µŒPhìKk¥·ÙŒ*YÆŸEïÆÅ'¿–}öÖô¯AeåZ“$ëlQHõÙ¸†ÎI¯™w®Õ HN6“ÓÙA0Ò¬²2˘"»‰W—ÁXx½Jot?5XôõVÅQ]3¥9Кâþe:.¶gjv‘AиŸ ¨E©ç ާ¾†3ôþMØÑÝGQwbð¼&1Æpú¸ÿ2·¾;Zã(ÁŒø(:?ïÍ]Ò¿³(5Fd]ð¿$‚ï8¶)Ý wÐCGAqñç±–˜«%JÍ mAsFÔëKÛGC£êE­N“ýí¼õÊìëF ±ñ¾«¹º@+;ø9ë1 #™zÅhZòä,Üu‚eËeå¥UˆOŽÂˆ‰ýhlbYÁk~,Gþ©ICe5÷øoªòšFî`«…l+ÏÕTT¬0›Í*êÏÁi*JJ¿‹ŽNzâÌ·%B±áGÒ³³+Xaa5‹5Y³»Ñÿ¼¹M–oÖe=)ìÙ'©õç9q$Ÿõì›B–ý¼WÙ¹å$«{¹±îƒouŽmh…Àméþ}êh@R;ÆÈ£öèœ:œÉìãÙøü•/”š—^ÝuªûVPl˜ Z-z\ÿÅ{ IDATz¼b‰ÙbQa¥Z1‘;gmjú™AMøÚV^ö›DK – ¢fëæ77ޤ®ýóúKP³¢I žÆ2P$ÉSŸzñš>FμÝOBJÔað, P¹“…£Ú…yo­ð ýº¿jeÔ´·¡˜oÞÿ]Ù°l7Óµ>¡/™uó$zýCÓé…W¥1 6l\±‹=rå?弿³‡\ßG†ÑÎêç7D!gÚ—Eäü†AƒêJ Á”X)/ù*†)Õ´¥mP[ÝìöÝAˆ…¨Iñr„ƒÙ·ësÌ£ÿÕÚ·Z…aî¿·ÊpÝõ½E‹E[+Ë·Œ£RéÐa)ÔåRðŸ×7É5² @*+ÜÑ혃J¯ <~|l“ï!rh_.€±çu¯×WQëðÄoÜ×Ý<¶?‹ÀèÉý‰( ð¾îÔ[¤×A5Žï;èN¢ÑiÂ’éM '$Þ8ªª XbÃÿRBÒsˆÅHì¿ÛZz<5÷©FnƒtN((J%-)]•ô˜äÁá´›Öe£º<íòÌ[Ç¢/6*‹¾øS t!ŸÐíqIåB÷_úAyÿ¥…^/iÀŠ·°?n‘5"E|J‰M´A¯×"/»ùÙEÌQížzØÁrÓÉ`@ª®Š—.°EEÏ)©?'Ò”—/·lSšÅè~óÍ!yΜ^Bž1dþ‚éÚ;o_êÊ8^âUö¤Éè+¯«€/>Û%gœ(õòBrN»ç£÷ê›Ñá>ïµÊ”é}è•7Œ¤[7œP6¬:ä×&f‹Éí£È‘}¿zë—íb‡÷œbé}Ú“ëžN?yu¡â»vº5ÚŒ²âòÉß·i?Ûµn7ë7º/¹é™éGO} øN§!„ ûž8¸yoƒëQCav.@Òúvoö y”Fƒ‘»}¯®­ÍEmÎA'ÜÑ ‰’’olZC_E§ïÒˆ‘Ÿ—î5 žV­ µ4¡?’U 3‡œ7X4=¸¬ÈOy Vv ßþÿ{ëã{L’dœ>YÀNŸÌ¯¼ªk}íJúÐ=€È¶]ãåÕñjÜt€H29ÆB>ÎþÝj±œW!Š õ|ŒŒId¹Œ:œGôVãC/êªõ`v&øÔß(úwüîú÷ëçŠ$Ð~š©Ý¶5GÙ³;Ÿ™Íôí—@úöK „ßÎß'¿ùﲯ̵«N°ª*FM£¿®¼A³gW“$¿.Ú§¬_sBñMﯯº®Ù™Åì­——zúáß_%®Z²_Ù»#“•W")ņŽ]ãɨs»Ó_lUæ>ó£ê¤q•¨ºO¹Þצ0¼ñÔWÊ+ŸÝ#\zËytÀˆîdóêÝ,7³ ÉÑè?ªqT9ðô¯û]ƒ…”=÷ðÔGÊ‹ß<'L¼lé>0n\²ù™yÌl3‘„‰8n1ZŒ¸eÐÕõÌÝ®üo_º³» çOBZïn$cÏA0IÂ_-bǶí®7¨åb˜UKW”ÿl3è{©½u{ÖI³9è§XpfN”ýl‰‰¹!rŸäÔKEÅJ³Á߬Ø“Í}îBaü”žtü”žµÇ$IÆŽM'Øú•Cz Ñ×î \Þ•Án;ÿéžç¯†ŒëMÒûv¨MR^Z‰ï>Xâ÷D1X|ÉÉÈÅý“¯yâ:vÆ9dÖݳ½²eËÂÒÏ5¡ÚÈ‚S§ñÖM³ $uî€aÓ'‚ýë6ãø¶Ýhj›“n4biᢔQZÎú—E#éc|]¥Ð‘˜Û€6`@Dä5VËÁq1›/ji=Z;•U[ E¥?ÆÆ¶û§‹x8†þNJÝ—==©9ÄÄë·÷_Ïyþå1Ÿ¼j²ýuT“S—§6ñÒÇc‰®Àz¨×ß+ñ}É£0÷M" ¤[ÝÿÛÊSŽŠlß:zå'ÞrËößïF=º<â}Ì»ÍUÏK sï[/Ïó­x¤S•G•á§3óm{%L¿ü>uÜsUÛŠªìó¯c > øóÉCêäø§…W>õzeØíø¡À%%¥ÌͧÙÈ<ýdŠ-ñEÔ$1ïñá?‚õËšþW#×û\ûϼV‹={ÆÐÊJ';z¸˜9’gšº1̘WßóÖÏS·ºý„¨ê@<õƒŸ<÷>“I‹´.±ÄjÓ£ §§3‹™{uµ~\7ˆŠþ÷Ý§Ñ è˜žLl1&å•"ëèi&IˆÇØ&ß{ŸâqÌ=. ©C"Ú'Šâ2f磬°Ä§˜W{=cóB(“ø¶#õøßçÅTyJ­Lµ¶Å™ãðHC|ö­,.gGéEVËä³þÅsYÖ—ddÎþ²þ”7JÀW0ÆvJѦ"èg?…œkcTUï2êŒÞ¹…Ó€Dõgãd†‘Ç/I¤N~˜rêM,"8G½7שn8º©D¤ˆßÊ  mðç5ª7md«>ÚétpJù‚,Š¢hÍ9N„‘åAÐ$©¾èâõGõX(ùk)/wbó¦Ó μªœ”Tg߃U•ìß•ÅjœÀ4öe’œŽì­YA¦v1âo³Y!Ùdzqúx–ÏMKsÒ5¥A$k)9R}RG€Ê&) I³M9èžQ>N`Σ:“yNXº©¿]Ci÷æÑдÉÓ˜|¡Èc*ûÚêûWo‡Ö¡¿@uFVíØ¯³ˆ£«ZZŸ¶€,—Q*ø~©Y­?„º/ØþHç 7gºpû¼¯ýkc¦~Z³žM§›M¡H9šÖ]ÿH¹:¶1hÎB)Á¥—u5õ§‹3»6¬?mß°>§U¿Í,Ë%‚ ƹ"o¼#eØ[ÚQoLjŽ‹ÉÙê°‡ã”Dºœ¶‰U¤$O.x›4.)WÄ•Æn¬“ìXCot[ÚÊßÚûjc¯=l¢—\ÐFìwÐHdœ£Ž­âÿ¿-Ψ|÷íÝ~ë-ÙlZúɧçÅ÷§«Ù7qR;Üyw_Ûïí)}õå­¥V¤I`å JÅèÚÿƒ¥ü±¦L‰ ŽZúÆFÕÃÑ1R²ÿ †²¹òpÊoŠ<­«rZ.i#Ö–Ç%剂&‘…ç4G"šÞÔNz8õH8°¾rZ¢ÿ+¿©ôÖ5^‚—\E[›^MwÐÄ™9èfÒyí ½ûÄjàÕ—¶«ÉüûSC£ùüó“Yw84;ªn7ƒÛiâ›$¤^U—!ˆ­±ú:P!;íDe_(ù"DØ2[Ýx%„àÌ’­N·HÂ?TÔ(ßxV«–@vf¹¤&o¤ö` øøƒÝži–,>^]P0\Ž‹3]Ó£ÄÖt‡ ”h!:¦ÈÅDã(LgõcýúÆ’^ ýàýòþ}Eµžm»vfrÓ= ¿ š½ûÎ.Õåܺt‰"W_ÝCÈÉ©d~ ž¦~Ý|Ó„Óöî´Ï½:Eì’K&zß•—SD@¤ vÃóZlÜpß$ ¨ ÿ} ¿¿™ÚsJåª>§¼A½ö77­E:zž3³žz|p˃l÷ò?šµìr™1*DÉ­±]ÎFƘKåH¨7²áÞð†;C‰†cƒC¥)ûŸïõ@Í^Õ·Oíÿúö‡Ë_{ ÊŒA€€¿z=š“¶æ ×®éÚ,gô»â+ÏbÕÒ=c´pôp‰+7§Ò+ SNf”Iqq¡];³¦5®Ë. 6E–Љ Æ{íoÌ|Ãÿ{jˆXUåÂ{ì•=Ó%&Èõ×õjöíØžÏ6l8í9GíÛ™éµ×õì/b~°«Þ9l‘œó`WÈëº7Õ öí;¾åM:̾ntXÓØìÈí9å_Rp£Úúp„t¨«kk¨¬ýò;œwÛµ˜ñ·ûɾÕë™,©9pMC™ˆz›Ÿã4 Z1ZrHùz„¸öx礇´Žw8'œý7ðÁÆt*Ï>Ù¸Îо5uŸI>ñúÕ°§Ä­ ɉ$1N>Ûm !‘{BÐÆôÈÌ*/w*11z!:Fç÷VòùS;ê)uŸŸ?7dÛÕÊ«(w)«o•o5k4ñ’äp><º)Ì+Ãö GT hht<ÔcÁö57­Ao\'V}ö ›þðdø¬‹°~þÂf+»Ðå„Å’¨ú¤y4bœTe?ˆð¢±¡íKK³”9z¤ùU,XÚàûC9нb!¦ ”'RÔ×~Á£è‚( ÷nÄiwâÐ5û)þºcp1”êëñ)8ž´)ݽŠKã;GÎéJ9&F/¤w‹ÒøÊ»ø’®¦šßëÖd:ÔÊKJ6‰PZâP"9_)R˜ô=«+ªwXŒÖI޲ÿ<@µc¾0\;']€ßõ™šRglss«˜Ã!£ÿ8:eJ]²äD@'½žy”~º…~;«.Ï3ÿ¡ýù¬NfýåûG‹"uñ–£67Óå’±wûIæÞ_®»°0œ?k(<ºq9%üßíŸÈEù­na¡&Âû´öΦïÁ´oÇè«f‘õó6‹Ñ(•$TÊ.$êÒUí'òhÅxYrýá÷C}ó¼ÕÏwÞ9sz7ÞØ[øûßÖIßÌ?À¼åvÒƒPÆQp»ž=÷Lë]¿–ÃF³/~ñÍ=•[Ç?Á/î»æþµ(—$èk«ôw" ŸƒÞ`˜{bx#É8^*õê«:ð1O›ÂÉž}ÿðÓâÈ1è W.pmݘYBûínH? 3覷ûøë7ÑqÓ†’î|WY¿d{3„p‹kL¿h:J$ ¢+µ ;Á:¶1=2w¢ÿûdOÅu6RJðéWÆýùþ ƒQCg_ÑÝT3½eͪSv—SösxÚw° &³†@Ö©r¹5Þë4É’(DÉUe˨Éz^Ðé&õé?nl ¥”`óæüzçÿôÓ1åÖ[ú°ž½bȬYé·ß ¨)ûýÎSS-¤¼Ì‰¬¬r¦( ÃG¤Ð±ã;`æìôÚ+J¹9^#[”ï|t‘fÀÀ$"Ë (dý%“ /î.¶ï%O˜Ü…vêCÞ}}).DmÞsÏëJº¤Ç’·þ¹V)+­®‰Ñ1¼öö qððöD‘Ž-dÉ)VÒ§2™6»/ýÏ««äÏ?üSñÔáÆ»Ï¡·?8^€òR;ŽÎcÖ(#†ŽîLÇLì†Å w)eÅ¡8èÁÛGíÿØx ž{÷zA£ñý¼5Ê’ï6«Z•Ácº“Çþ9Gˆ‰·¢¬¤¹§ Yû.‰¤ï°®äÜiCè w$ïÞx¨6½ ˜4s$)-ªÀÇȃ¯Ý@«*ªQœ_†®}ÒÐ}@g =·ŸpïEÏÈN»Ó«¬þ£{“‡_¿ƒZc,°WÙqúD.ë5´;pN_Œ>Jxí®7”#»Öê©ÑiðÜWÏÐÎ}:È=™‹âÜbÄ&ÇâÜ~ç’¸”8¶kÍÎZù—Ü9›\þÐU*K+yè$ÌÑô=€ š8 ë­båÅMûý°ÆŒ÷#›¶!}Ä`ô™pN7±ƒ®0† ¥•ÌdVÕmÔÙŠ@õŒ1“"øo̹øtÞ>yñâãÊÉŒR¿±ÞÄsÒ›,O09ÍÕo«*ìxdö ŠËá¬?q„õÒSe6U_J\.b’«-Ø’HÖ±Í9葸sܾ5ǹjùÉêñ;bb Â÷ ²y¯¬p)¯½øg©ZY—_Ý£v ̆uYªsÔ[‰Ñ³Š³ þg0vRjô9z¤eĈD »vÔ[QÆ^ûçVé“OÎÓÜ{ï@á‡EGe§³~õÛù”•+2”û joˆ¢lZüíÿƈÓgt£÷?4Bxâ‘e’§~wÝ7L00‰ìÞ™Ëî½ýg)?¯’Æ0êœôŸoM&M½åzrßãã…ÁÃÛ“½»N³oý^*Ès\vÒÝésÿš&Ü÷ÄaçÖSl÷6w¤ÉjÓãÖûÇ ŠÌpûUŸJ»¶œbŒ¹#Aƒã&÷ {C_ Q×h<ÿÞõB\¢ ÛÖfï¼øƒêÍSL¼O¿}£ ÑŠ˜û÷¯”Åó7(`î¤S/Iï}þJúÈ?¯nžô¬ìô¹8YlFÜùÌ•ô•û>TÖ-ކ؞ùä~Ú¹g{ráÕç’…/©UÐ`Ò㡹·SkŒß½÷3ûâß E–¡Ñˆ¸ýùëéÄKÇ‘ûçÞEï;ÿY‘Ü÷m/O:÷éDŽí9Î^¸þEV^ìžžCÀŸN½;ÖÚJ“͌˸’(²‚ç¯zR9´ujÚ[gÐaèyÉo£¡ã8ô|Ç·íbH÷ÑÃÈïoؤ†cKY9d-ÙLëZ«:[Ñj’$§óÑêÒë™'ÎÚN÷þ¬¬ –•UÎF‚?†oŠ9é¡7] |ÍÛ_eIÆÁíG=¦>6†HMl®²B㔃)mGgÛ°%<‚Þ ÎÌAÈ Î½·.)zâéQ¶+®émö|%>ëT¹ôÔc«‹Og•û­| Š3fw3@U•‹mß|ZuõÖ€YßËiÐutT+Úb¯ É®Ž\nQNœ(úrh˜?VŸb›6å(Æ%Ñk®é)~üñn•yëÞ¼÷ö6!ÝÇKKøÛc+¥!C“5Óft£Ï<¹‡[T\¼\zEoA–Ôå’ñÐí åü¼òÚcËP:tŠÁ]îyl‚pëåŸIÞ3‘PJ°eà ¶sóÉÚi€½Ú‰%?쪉¶7°x_¬jÚ†xàù™´×À4’“YÄž»ç²"+ªË:ÜòètÁhÖã×P~ýz=ƒÇê‹ç¯g]zµgÓæŒ%ç_6Šüø¿U^DŠÿÍýQY»xkm/Ê+Å—¯/bO¾4®¯—ƒ>ó– ¨-ÖŠë÷²Ï^ûF©É#¹$üçñ•®ý:Ó´îíɤËÏ%K¿XÆ ­G€%_üŽòâr/Ýó³òŸ•W[v‡îi ”`߆=ìÀ–ý Sgµë\ÝP«¡ñ[ñyÇ3©=ºEºp/ªekKÊXrü}%îþ×.ª­‡(ӈʲҥ]BW»ë;'Ý׎_4­?®ýæÛCòæÙµ6gÖìîtøˆd:ÿ«}òÑÃÅìšëû5Ó)fŸÏÛ¥lü3S©IŸ`"<:JèÚ-–ÀÍw f^Ö»öÑþÇïnRN-¬}ê¨Ñ¸üÚ´ÿ TÒµ{<©¬pàè¡|öõ¼-Ê¡}9LMY£I‹î#ôÔžDDZgG&[½d?[·ü{äùéToÉË/ËîïþA<öòLÁ^ídsŸZ¨ Ù…ŒŸÚ—ô˜F…áöéseHëšHÆLîCú íBR¢ jdeäcÿölÁGË™£ÚQ«‹Á¤Ç]Ï\F{ ìL`úµÈÈIHM;üôÙrvt÷q¦7êpdzshiaæ½üµ_°Ãh1â’[/¤]útFRÇDRUÀŽï?Ÿ>ü‘çû³Ë¼ŠÄ&Çá«Wæ1“ňs¯˜Lz íM´:-Nî?ƾñ9Ë;™ã[ŒßùÐÆvcìŒSQp´ÚŽQ­Õ߉$‘¬c›rÐ#ù¸$Éxþ©5¥oþkSyŸ~ šÄ$“q¢TÚ³3ÏåpȪ…$§˜„_~8\™§ÊeIŠàû$M@RÔ¬Òã¹/'˜¬“Q“¤áÏsŒŽÖŠ‹ìõDƒêxíÕ-ò· .¢·ßÑO˜?ÿ \YáÙTÏ¯Õ HL2’„4¢Û—ÌË«DJªi£È¡ƒî‹Fï> D«°~íI%;Ëÿqï÷ßìõpÐë×yÐðö„R‚KŽ(uk£×¥ÿî‹­Ê]újGD‘B–däd¹§R Úœ3±Y¿ú“½úC¤£An93®E§^6ŒÚ«xòÖO”²âJ¯ãžŒ˜ØÛí·Ñ÷eÀº¥;Ø´9cI—ÞíU×)[±èO¿}‡÷d0ˆO‰ñ’×gDÀâÏW¨VxÉW+Ø­Ï\Gú ïUë d&]1Úv€eÉT­³;m> ÇÐ^dЄ!صz+SäÖ=þ|©,*lPB‘›æ‹Ù«KJ™QßËnÔui#¯Ö…Í8¤:¿ôg›¢TÉ”TRøž“Ðæ™÷ëGf\Ò•nÞœ£Ô9èÀ Á tÆ%éôÀþöô³chz·h’q¢”EEéÉÄó:‘s'v¤=¸Lúõ§ƒ M"FŸÓXm:}ú'RÉ¥Ô†ó¿ýrgmÇìØ%†¼òæEB·ž ¤ªÊ…S'ŠXL¬½g÷£LïM_{v©¼ð«í^9)ÕF^ÿä*¡SzÆéB/œ5¯üíGeòÅý¨É¬Ã«O,DͦÁù3‘Š2;Ùµé8ûû¿/˜ÂP˜WA[ÛϽwmß9T–Û‘›UÄEAÿa]ɰq½Èù—ŽÀ#W½!çfºíŠV'bÈØÞÄhѺôjO:tM®Õsí¯îi­ˆ 3G“ÜSù˜÷ò×^mÞ¥OGòè;÷Ó„Ô88íNdËF—~IŸ‘½1þ’±äûß`»Öî`žçkȤa¤}÷4ìX¹7<{ÑôÈ;•ƒ¸v‰hߣ#tÞòü¥+Y«œ÷ÖÇÞÊ*˜ ª YÛ°'<‚Þ`"ýqY‰]YÿÇI‡ç¾@·O™'KåWž[WV_ºÖ‚N“ E™FU–~jˆIzT µí<½5³YC ¢Òåuܺ¼íÛóزeʤIiô–[ú ¯ÏÝ4‚ß±“Üsßá‚©](Ô[5µ¥ÖAoŸf#y²ôŒ>ÞÎpe…ÅEÕˆŽQ»8z—M¤ut!õØ‘Õte¥væW"6Þ„ÔÑää±–uª„-^´[¹`F_úϯK‹«°nåaeãGÙŸ«+e¥U~rKÿáÉ]OM§Œ1¼üðWò±Ù^óò=ÑFŘ`±!I2žø÷µ@%!î4 %-Þ·(T–U£¢Ì·@i¡;Òm¶™¼ö§tL$pò°ÚË´À©ÃY€äŽIµû~ÿj9›zí’Þ¿+æöw§ IDAT.ù7Í8p;VogÛWmc·îGÎP™‡5 W±s.Oùð祿¸ŒìX¹…íZ³ ;WocU¥å*¥¢e.2Žªj0Æ@ÎhDuyä?B\àtbOE%:%]ZÚV\h"2‹¡oUuÅ:³uŠZS{šÞù{è‘áÂòe'”k®üÁUQîÀpóm…‡)<ðÈaéâÊ$ÉÈ8^ÂÆ ÿÈUó’èý·ÿ|æ%Ñš©3@ðÚ¦ ]»Ç‘Ï>ؤ¼;wìpH `2²yå?—ˆ<3Yؼþ8Ę̈[@àþ'§ÐNéñäÏÕGØS÷.*Ê«02yz?òÄK Z]`·ÅhÒâÑ—g sÿo¡²øÛMŠ,É0Ÿq°†% 6³­ë(‡÷œªuM=nÿÛ á‚ËF’™N_ºïJ‹*pÅðGå'^¿‘Ž›6”Ì}üSeý’팜©_}SZQÀsï¢ ©qXñÝjöÁ“Ÿ(.§“P \ùàedæ—{çÞKî=÷nV]Qé'ïŽ=@~|÷[¶èío“ehµ"nyå>2rúx2óþ9ä­;ž÷ µF6—Ù•èØ mÅžD²ža}Ää¯Îß‚oal‰Q–9GHeÙRÁ«ü£æxY™“€Å¢ ˜Fýs«,Ë 7ÜØ[ˆ‹5ü°F\œ|úù4ñÂi]éš?N)/<»V~è¾ß¥ÛoúEºíÆŸ¥­›ÝN¨ Ôuu£Ñ=¿Ün—ü¯ÖlöjWÈ£ÌåväËJìLµN(-q¿Pc¨-䙇ÉÏ>òƒ¼yýqf²è1ufúìë3…Ÿþ|HsÓ½ã)<ÎCMÏßál‰©ÑxöíkQðù;˕տíbÁNdl¢û&F‘LVLŒf=ô&ôF-t-c8°óËÍ,òË_ótÈOt€7Ü-Qf@E‰ºÓY³ßm©YV\ŽûÏXùî…,ëX6ÒztÀÅ·]LžùêYúú²·h¯á½½ª÷î#o²wzíY·“-&œ3s¹kîÃäí óè%÷\¬;7˜H Õ[Ì „@‘Ø+ýo~‹]QðC~‹±L¨Ðjbä–¶=my‹µœSYUö{­Ñ 0LƒÚÐpee•³‡ï_.•—;kÓ|òÁ6ùdF)KIµÎ]£UÅ*ãò9i×îqdé/•×_^uÆ9w³eC{ã¥å²F#àæ{ÆÐ…ºöL ã'÷ åüýÞRE…½ÖîýþÓ.öý畚…üìÜSë¾ýd­òÓ×YVTž‘|õþrÅóKÉ@Uy5Þxr¾’u<ã.DŒf=Îxž8{,I휌Ì#Yxû±—Ó¤b ×ÿüšm_½ƒYc¬˜~ëÅDMö–¥²ïßüšÕ<)s9]øüù™"+è7vpȪ4‰a ‘l‡ µÊzm²ÔÒcª¹¶HÒÆ"è‘™âòøÓcl^œnܺé´ãþÛó÷N‚’jæÿ4;^ü¿5Å¿ý|ÄÞh…šJõ,-þ¶‚Œ¼wâ “õ<Ùÿå$ P»9”dDLŒŽL¤ÂáÃÅì‡EG”™³Òé]÷  «Wú~~ÞÍ•W÷¦‰I&òÙ¼Ýò?ž_'û®÷}ûÝCjæ’ןyªŒ@R²Ye81PJŸ¨vÌ?-dg–0HN±Ì“tæXÖ©ºHcÀ¯ßïR~ý~§b0h0j|W:yZ2fbwzóýç Ù™ÅømáŸz‡ßuz ^xï:!*Æ„õË÷±ÿÎ]â37Âÿ‘yöÉÆc ÷]:WfrÍ‚C Þ‚È|Ù./³)’ŸGJ‹Êü*Ÿ ÈÍÌóš*UQR¯þ5Ÿ}ý¯¯X\r,FNIιøÒ©w'<üîcôþ‰w+5+³0ưfÑ*¶vÑ*è Z6`Ü`2jÚ94i8™uÿÕ$?3k.±›?dŽv¿‹^UZ ¦Dvz‹CQðåé<&‹]í Q•µö¨ÜÙŽN“, D§8ª÷R¡—Ú ¡Ù,þå¨â;í’1`ßÞ|Ö!ÍFRR,äе—ýÕ¯«ã&v¡ðÃõ/?¯\rPyúÕ‹„n½’HMþ¾ÛB€UKö+•?ÛòÓ·ÛØU·ŒQÕ¿†_¾U_‘ªNW÷´”¸D+‰M°BÔ `¤0¯”¥vJ ©ÈáÝ}Fô$°äËå cþßÎøýË¥8nz ï­zm\»h•ŸÌ²ÂÎG\»DXb¬(/ªYu*Òã52ò6”V1“é¢ò¶eOø—‰ ü&“†DǨժ¥áÊèN;K`Ô¥Iî(8‘÷n@a¶Ný_VªÁ»sîÛ_¤ôê-tíb#þyÚ/9óˆ¨ú>zãmòEÓ:Ó˯è!9\·Þ §ž½Ü/*­Z‘á7¿Í`ÑõLÔ§.ÈÂpäp€ACS©N'§Ã{µ”a#ÛQ¤êçÏÉÅ€~ƒRk“{æKï‘@Œ&-ª«œ(È«P{FìÕ.¬X¼­X¼=ðä\~Ã:r|:ýmáÙW^¡Í<öÊ¥BzïT’q$—½øÀ2 aÖêJÉÍ*BR»XtïÓŽØyÂk:ŒgÂY"P{fgä²”NI¤Ç ®8²û˜_Ú8}"§öç(<]€Ÿ?þ‰-þôöŸUïÐØäXtÔl]¾ÉOAGµ›~[Ï6ý¶sþ~.¸ñb2`ü¬]¸ÅÕýõu> ­S4àæ»F ×Ü4ôL„Ñ=MŽw/Sd†vi5‘ùºßY'‹}Õœ>UÌj¦y©Ù@YV›]°ó¦tˆ%×Þ;…N˜>˜x>Uõ$¹],ŽìÎÚnªÊù–•–8y8DÅfe>ÅäŽÉªùó3sUëQ^TЏv‰0Y-zS¨)ë·‡ªª'¥æavüüœHIŸ®MMq©ÐÝ+3Òú4ýfÐ¥¹:&ÜQPVô•PQ¶Â/*¨­7nt™þýb©ï±`ù ;«‚}ùÅ~Y£¡¸ýŽþ‹¨<3·}ÌØöÄ7ÿmw ,V_žCòÙæ™J\œwÜ;ÌK®ÅªÃýŽñØW¿#üÇò#JIq5úL!CFvðœ7Ü1ŠÀ/ßï®$é ø·³›²÷G||§Ù ›N.¿q<²³Oûç•·Ž§/@*ʪñ·Ûæ)U•„ÊOŸ¯Uàžç.'mͽ¼ÿ÷| Ë[§ú~×é¾|ÁZÓ®ŸLkæ¶×³D›1ùŠs ü?{çGqöñßÌîõ¦^­â.wÜÁ‚›bz5„BMh4ÞxI€@B%@HÀå5`šmŒqïE²-Y²z=I×Ë]Ù½Û;lƒöûùÜçîvfž™¶Ï>óììW+Ö°>:ctÛ€(Špõ¾ÔÈçéY Ò¤ã€³Ó·)§Ì gÝp™xÒ4åËÉ”àŒ.Á7\[NfX˜Ö Çé7\ŠÓo¸ F›%,Ì”fÅ®À®€F/]Ö‘3¦8°>–…0”ø}×'ŠøOS3 r£½Ù?²„*>êçX~Œº’@šqº«³ýåˆù(¥Ç¤ˆŠ’ÇÃïçãÉï[…Ôéx˜-:˜Ì:MZŒZè ô£¿‰Ýð7qÇú OCÎ#VÝÆ®wkF;^{^äXdpvõ¸í™Óͽ7Ñçz|#?¿¸«Ú»Å¼¬;Âã—Oj†ôÔÊHTžÅ¬íOârø¿S·•Æ^%½ºåoY„˜,§ÅAÖ¬iƒAsæä*rÏŠŒó·¿î.½l<——oŠJN|óu¸ôü±ô²Ë'pŽn>û´R´Xt¸øÒ2îü‹Êh]m7Q4àzÒ÷ãO}#¼üÆEôÆ[fÑ)SsÉú¯kD«UGÎ:wéhs£±¾›åZIh9œ^xæá¾_/âžzñþ¹'Ö›×W³´ .½z]töêtøðgÖö¿˜jáâ ôúŸœL?x{»¸wGk;YF¶‰L>aYöãùÖ¬Ú6ÒÏ:=ë‚iä½76‰[¿­ŒÛw&M/&7ß»„À‘ªVvÑçÒ:á‹>öm=ÌVÿß6ï¼ò[°t&;¹ˆü}å/¸×ŸY)ÖT4Àï °ü¢L2vJ1θèDúŸçVŠ_¼×³c‹ÔØ+' [ÿñf¶ëA6aæXòØÛ¿¤ÿþ󻬾²žœPL®¹ïrjI3cÃ'›XùÖŠþ4w?}'¡„`ÝëÐPUÏ:Ûì(WLæœ9Åã‹ÑÙbÇÁÌ>s.¹è§—’/—ƪvd-µMHËJØéãÉy7_L`˪ aå›wÁÌ¿ðtòåÿÇö»J ”ÃßF zç~ÖÝÒÞ_^ƒÉˆË¾ÀÞ5˜§«ÿyqX²2péÃwØüþ'Ì龑wÒlÀž/Ö**K<|¢ˆ·ššX€í-Ìþ‘ 7{Íw€¼´%ŽCMÖ»]›¨Ñ4gP/lx¢2jk:ÙÈÑä7÷,*oa ó½®r$|U þH `N~Ø«EúÉÈ2ƒç¹þ¼¹~žsù‰4·0ƒ|ôŸõâS/öP+pé cŠ“šÏ"Ëú»¹¶•åŒÈ&ÙY¨Þw8*}VA µ®.0CÃÑÖ3>lµ³ ÛyÝ:>ëx¿ËH9©¬ëa¥ K-Ã%‡´r£„Ñã2ú߀ÓPß-¤ònëh`ÔJsni«nù{fÀié— „F?ÚGk››}öy­¸dq 1=›lÛÞr±ν'¬£Ã‹—_Ú-Ü~Ç I+ÒûïgÏÉ/¹l½óž9Ü÷ôXÄý~wÜúqpé…ãi¨‚ÞÇîÍìêK—ûÓbnî¼"2w^çó±aýñÁ»>Þûô‡<´µ¹Yü²o¾ºUd¢ˆ»±ûù¯…YåU´°_Üñž`ox°ÏåôaDI:¹ýþEQ+·O?ú‰øÍ—”p£©§žÛ[âí8ÒSÖ¢‘Y ½TM<¡˜L#ÞtÿRzΕóɃ¾.Jƒëlw ½¹3$_¹±Ñ÷[Ζánü“øÓ?Ü@ç-žM~ñ÷»ÂÊúÙ›_²—ÿçµ°¾ãqºqÊÒ“ÉŒÓ#£ñp#ž¾óIÑç{\^ä”äaÙ×E]×½n/Þxô%¶ýËpW½©çá_{s;Žõ8Í)-FÉ´I¨Ü¼ÍUÕƒV¥|^¬lmcT3Æ[˜}Cˆå\åx£8뚎ªæç²uº±ŒçÓXê­èrÈ¥ï9èµð ‘/uë ¯<ÐÆFŽÎ 3ç‘=~¹êñqŸsò¨/–æž:Fñ³A‘Œ™Øã‚øí—{£"huŒ,+””è}YžÞz µ¢GŠëùßXÝ„)'MDÙÌqdóçÑ«^e3Ë4U7&ÙPÇçxÝîèfN’ç/4Ϧ/9S-èIù€[| F ÑhÃ÷îÓh{´^CaKÓŽVRB`KÓ‘q²4·Ý3×ô,6Öu ßÅ­‡LºâÀ¸‚ûZêÚßJk¬»O›‘ý#Á`˜,kÝyõµraÉâzñÅc¸mÛ[ƒ¡•yð -[öqPjïø¾ª_Ø)lÞØÈ—+5ÑÿòÁ5Áw–—“³ò¨ÑÈcßÞ6¶{Gkks³ºÚ.¶üß{ăÚÅHÙö·± —ü+˜–®C^¾…TV´³`PÏSdç˜I{›^O Ìòñ›û> F ìíî¨ò.}›¸æóƒlÊô2f|6qv{ÙÁý-lçÖ#ÌïÂä¬ý¬B\4ý qÆÜRTšN3³Ìp¹|h¬ëdß~UÁÝáî„LUB\NÞý׆¨º–z`ÓÚì®eχù°“ð¾ú íƒíÍá>n§Ïüò-ñ—¾Àè #Hñ˜\PŽ’öF;«>Ѐò‡Äô>¯,{RÑo€%èñýŲÇE!ý–Tw· ßöqìÔ‘dä„bäŒÈ"M5ͬrwj*jY¤oßÓw?ËÞ|òM6aö’™—£Å{«õk±{ý.ÆD±ÿ|·~¾‰Ý4ýV6g"É/-€-˯˃¶ºfìX³5j›EBÆÍšD¼.¾ø×‡ŠnÒ@SȵÑÝÖò”%;ï ¡:Å~åRíKÕšG"eôÅinr2¤lb6Y·ú0 Ãðö¿¶‹‹–Œ£·Þ}2÷Õª¬¥©;jîzÜZðržúêVÔW·ö½ –É¥»6TȺß0ư{CyÌ·ÊÜUÅíªÀX¼U¯–ºV´Ôõ•+úCñ¸<ØþÕ¶#¬ÜRJ 0b\ ,6|ðì›ÌÙéP¤aŒ¡|ÃŽþr„Ê‚ATlØŽ>å!4ÌïñâÀ†m az“§^})êöÀ¶VÅÌ?Gíº+`L³â·þœÅØ^Q6kŸ(buG;èr!/ãâ®tÓlO¼4*Çy¶3<5²–†ß™sòï8Îó9”dÀ¨íœCàúƒ>ù¨Bœ9§€\xédºìºéýÇtå[¬£ÍÉà‘>lc7ß9Ÿ»ÿ7gHºó­úhoÿ›Š €o¾<À~zõkÂ/Ÿ¸€žzF9õŒ2âvùðõgå챇ÞVí|ˆwty ÷&îèyÖ¬ÜÉÞŸ³N\zõ|zí]Kèµw-ÐóΆGoYœ·h*)(ÉŽªšmëö±/|Ê–^»œsõ‚þ:øýOžÛ¤¶Èü™{ŸgÎN'μêtrÏ3w„Õ߯O6²çñ·ThsnýaRެØýN[Óé‹sïl×öøÛ¹%•zQ²åš"A$ãfàKpBJä Y–í¥…k.H$̓,°Î:qD˜“uv®‰³ÚôÔí°Æºîèµü‚Aí­n¡¥É)îÞÑèÿí}ž`ð;k<—¥Ë½S_×ñ®M£+Ív® 7”õw.†Ü\!hnr‡YuC­Å¤_‰d³ŽÔR«¤˜DËøß7+ÓˆÕ›nÔ€Û€£Ë˲sÍ„R¿/ˆ¿<¹^xíÅ-¢\9¤,»$ê<Â-è!rÈ@¼ð²È /o¸å;<ee”·–÷#ru»<ÒyJ­ÈË–=’˜ù²)+cïq&öëCDDËÖXý ’ç˜X]Ù²3Àñ<:ûåÐèrGÔ•ˆ€(`Ÿ³ :íL§í+ȸ¢‹çÌß¿‰g˜awm14u}aÍοWÐðÙLºï ¬¯@i‰•ä›He¥µµºz;ÃÈQi$7ÇH*u°ö67 ïK ¥¥i$7Ï„ªCí¬½Í>æƒÁÈ£°ÐJÒ3 `(ßÛÌ\oX²²M˜85Œ“E,VimêfµÕlÛ¦jð èË3rõ0+Û„ôL#9|°…‰‚ˆ¼|+Þ]w/_±§Ýxþ³B_”'ÌI‚Á öl©fˆg¡e.›ZD§Î £EGí­c;«YGKŠGå #ÇJŽl`íÝcžA§× § éYB Ps°ž9:ºÁñ&ÍKü^ì8ÄÂ篞ô%ã‹0jòH’_š‹¶úVTï«f•»F´[ÏgÔ”10˜täÐŽ è}X¼ÿí¥LÄÈÉc`0IÕŽýÌïõ¢Çõ¡pl ¬™6t6µ¡µº¡ý¢¨lLitÔ7 ­¶1,Ï’)ã¡7ÑZ]‹Î¦æÞ°9Ô¨±¿n·ww²Í¿Xœs{›†³ûù%(ì{ën~5~Ì‚À+_2Ö»¨+Á°Rг-ÛK 3V'¤ Kñ›ÇÏ´Ù$ãæõµ¾_µ"¡}ßDiu|mjs¬7q\¬i‹E“i®H‡h¥ªi%]"‰”Oeóæ©LAÿ¶³ƒís¡$ç¶vއ½rô(è{ênI‰‚>Ì\\ÂÀdq:}¢½Ã#vw{‡±Ÿ•4Õ³<ÛÎ\ëB§ÝµÕÐÒù‘ÙÞ¾\cµ--Ö¥ÆØ±êNI½2ȯ&ÅN/ ëÖV³uk«û·>ìñAV¶•“h_LßIM_/“86\ªó=zõØäscKW«öt#Í8Õ;*ïZ§^“<ÚåPzl†‰>ž;Ž´<Ÿ®3NFzú%¥‘JIµ¹\?lÜÁ† ÄQ’A3‰¬|g»ØÚܳ©Þ Á%?<‘^}ó)4ðúßV³äútø Ibe;– öZ‘ZÚý>|ÒÖ&Š|±¿4÷ÊNJ´I¶Ç÷‘ÔÕÃ0SÐSãôä#«O>²Ú‘*yßGáižãÉ4Ïñ8<ÚfÇZKm燳y¾hµ.µÚÉ^ÜãÜ¢0…q“i££•f(åÄ“­öÝÁs¬ë0ÈDT¹»±µËÎ:ƒ~dšç¹&üÀ¥º²|ÿ1ëJòïnmìúÌÜPÿKcfæU¢ÑxBRík.UÒÇ•Ž%ñLfnùÙBzËÏR—÷ۇ¬ !p9}øóo>ëìŠß!B(ßw¡¾÷m¬ÂåóÒ/ëTWæ¢Ie_v : —±zÜ`1Œ X c;¼f¾¹{©±áQ=¥&f4NeFÓ4fÐ áÛŠÓF¢Ã•XmŽFœþ¸$1ëL¤ìd-ñqÓ)ìûRå‰<Ÿ!³>¥ñ™Œu0Õy%‡#èÇw*=.Ô{º‰A“!¤›9'šg{ á{3Sç¹á!ig93LÓ¼5íïØœŽ¯¹Ì¬kD޳õö¹UA©ùTÑü+'GÁñþp+üðÁvÿÍo 3N,!9yVb²è±qí!VWÓŽOÿ»Cli쌘$dóS0"-êrvŤpÜ f57ž¬Ä©õ8ñy{ 3¦»ÇäŸíì™kÔy&šÔ½rX)è>n*ǃ&7XšyY˸¤Ëé«Òvyöë:Úþ­í£a3§ˆfãTÆóéŠg“DÛóXXÓ¡¿@ IDATÓç"ÓEÿ•’©Ž“ÄÊúC“ωjw©r;™3è…U_ê7fù&¤Oôiùôa÷¶>•p š¼`YÞOÚ[ë õ˜¦Ìf] j4yr–(žL©ø~k?/gk?ßÏÂý£ ò>VSiEg`¨rwa{wsŠ:¡0ëÇm~Ü1†3$…™a¥ ÷ZkÏSÌ=¹T[TšÆŒ°q9yfÊóœ¢1òïW¶º¶m¬~cË÷B,úÑ~‹~´Dú¹°ƒëôìÓu¹¶èÛÛ—k4šlf0”1¶iµùL§)`”ê Ü=˜8©!÷hû§VF<ÙJó:^ü‡ò)ËËôÁî÷ #àF“Ï‹:OxjdC™'+}’w¤~L€ö[Ê•ËUùþ“m™çÎ4Ïô´;·ZšŸ3QÎ «u!3›N¥U·TøžÆ'=•óm*ç=9YG{¬¥2?å²ÜB{vìqØaÐù3¬—¹rõ£ü©/“J,†—‚NRwwÃó—\3ÝxÍM³M¹Ö¨ý]•°zÕïv°a§ G¢ãÓ…\Ë|w®e¾[dâðÒv{i=ΚŽ@ vR ŸÆ´Ú|Q§)€V›¶€é´yŒÄrg<”Xa¢åD§‰ŸW´Å&9E=qËOrBÉå“|žÊò=D}Td^"Ý$h¥>棂è# Qø~è$:Ù‰÷©²8áCKâ+CAjó%ÿ(Õ1Jõ"Gl"¡Æq:‘#z¦¡:¦§z‘£zÆ=ã9£(­ˆ«¨¤-g Ó9 ±È)² qùŽh¾ÃÚŽŽ÷´¯Óä2®5ƒã,à98jdrÏ{ÝŸ¼»Ë?¦ŠŠŠŠŠŠŠŠÊñL*tÌ>†•‚ÞCª_ù{<ÉRQQQQQQQQ96¨ô¤IÅ­Mw§‡µ4vú –^·?u·Z*********Ç Õ=iR³ú_ÿ°Êõ×?¬r ¾<*********ßT zR¤êM¢**********¡¤RÇV zª‚®¢¢¢¢¢¢¢¢’jT=IHj]„TTTTTTTTTTRªc3* úÈq9Üä™Åšœ<5[õ„ã)þþØ*·ËéSMö********ß{T zR0’Úw-¤3éÇ«y²4Œ" ?×Èú‹lžÿ4$\¢^ÈÀQ¢^£Ã¨ôqöŸIËøúeÑð0B Ÿ€Ýß ÝÝuHßÈLB¶u¾Û¢Í=&nivÏMsQ¯E,æ“`Ó¤³¾qˆžïþzïóíCÃÚ&².¤ê'¼NCë(|ü‰òÅY"éo/É6 mOôƒ;èåk½»t[6Ši¦ÁI[\:w°“ó@ %Ä^ ’:¦tE#2nøÿèw|(Y £4žty € !‚0Ži8³¨ã3„LãDß±šÇ’E`Òá9¬i÷Ôh=Án. ¸ #®+àæj<›´{:¿`Î"¦Æù2cü:Îr\¸†¢ZГ&5§®ÓjIé¸\.Z^¤®7hINA€¶æ.ñX[ÀZ\´5Ý[YÖ“0&ã1t€‡£\‰Nìx¢òûâíz;zùej4X–—F÷»štß4½ªÍ±Ìuçš'éÒ~Pô‘}í_XÌæ94Ç45ÄBÛ‡’vUÒgŽFÿZÌËrlôãö¯Ìþ Þ|ëÏPäÓê®Ò6xŽórÄ8£§M€ÄêJI* OUšdå1ð„âÜìlºÆ¾ÅÔìàò­s‡¤îCiuWjk»Ãxde]É4œ9bEIi}'ú?U õ؉–oâµ0ó<Š ƒ\¹s·©¼e£1Ý8Õ“g™îâ)¦ÃS­©êÚl"Ô“i*r²2¾weLzÕ0™9Né<˜ÌÜ8Øër¬øÇzÎe „ÀÄkaâ5a°‚€gÐËvW«ÚvЉEi?pÙtÇÑnxª‚ž,„¤à ÛŠ]µÁÜÂtmÙ´b>D:×T×ròcQù]qói†¾ýÏwn8LEy’aÛf‘¹Ñù71žêúC àáOy+zÏq…–t+\Ží+O"G«Ž{óIÍ`21F=Ykßjªh= —µÔA{—VS‰È‚dOÛ*KvÆ…D§Ía”HØOéö•îq-µŠÛFe"ávŽ‘@fiºŽœ—e¥ßvU»‚¤ÀvRJÅvO¦É×nÌ˾–E»Ðß’a’oXHЊå.!/^»¤jì…É9-=nï®64u’o=$JºÀ¤¢c­I i\aÞmŒö?je$浟rKþrH»ÅDÈWÔ6¡ù(tO’yRK92ÍšŽiÖt²¥ë€±¼åntæ§–33ëg§·Ž?Ô¹ÑHø š}Óir¢,Ý¡$rŒ¨Äþk‚‚v:7VŸ‰$úÚKv4I\cC“)^‡©ÖlL³f‘v¿›ûƾÊRÇ bIú\mî1w§J¥Nw|®7ç¬ût·tz ¹ã‘‹Œ”*kÒqyÜ·,Ô€Ï`ë?ÛsLv&X€ìlùÐ¢Ñæ ³.`Õ‹b¨(@CNϰ™&¿¯å-kPô¥|Ü×þ¥9#ý¢Óæk“ÉwŽy6Ñ‹‡ôžƒÚTÉô NZã<`Ìμ„½{Ëï3¬Âê¾Æ”šº| ü¶æ¬:ó|.;ó"F†àÆx¸A̱¥“EéþPë k«û@ÊÆŒRþn[ó­‡ûÍ™Y×üœ›D­&W÷¾£di 8?·„ž–nâì[÷µ¼cq:¾7ƒuX)èR>}É|V¸Í¸¼Q€¹ &hÿòîÖÑò¹PŸ±Ðüxž`Ùm§ëÿúþ]V­Ž'ðþkë¼m–ª2)ý7ÙÑü¾Õf™G3­s#&¦Ä¬=)²¢Ç ÇЫ/áË›ÊóKíœ?Éd m„ß×ü«_pÐTõ‡ªÎFñjÐ3é2‡÷çÐãR. ¥/z_Réã]†HâçuV†™t;×Ý6n°í&`û×æì¬«Xßô,]GRc3¼Mä,‹<¿Ô='švI¤=NϰҦ®/LAÑC[÷}ŸvÏaÍÁ®]æü¼[™ÉPÆäëAΊ]§r+Òu¦ÔÚ*&GêçÊð¾_~Oü,­—ååPÑ³ÉØìØ®;Z×¾zÇ.ý¾Žu[Æ$?÷'¢N[À¤Æ‰ô¹Ä'JV¥Çl(ñ®©Ñé¥åôÅìõ5´L©žw•Áõ­\ç‘ù6^s õ]K«k¿öhëVaóxŠn..H…â$Š ¿»ã5çSËo·ZÒŒ¤lZ1ÿÂÊŸÛšëíbSm{ÿËõ?_b(,ͦ£'òF“®¿•ï¨ ¾òäJÏÑöñò Nº£e¥%?ó|˜ô#Y´Ÿ1’ø/w,J|a‘)½,ù}£Ô Ç¹å>lYn—ua·Q“6¨%âw•Ö &Û»š.ñq£ÔÚ}tXœ•AÝî-»çÐX<½Á.º¥é=1œÀåd_ËHo»†ÏsÒu-í_žš¾k\ž¦ö݆rò”•M òÚë¬\Y’—­¥.È-$ù\«vwÓ[Ö€àJÙêšr}+5 3@¤>7ˆÏÁ=uâ­ç<áxñ±=ÍaŠFs¤²Yøßûþãzà‡Ï;»í®”–CQYíߚ̦éÔ¨+ í=rñ‡Š!ÍGbÊ8ž®E)#S£Á‰V ­ìøÌŒ$ûCu÷N£Ív:TÏP´ÙPÉ;¦è´à…F7ØÍ!‰6©ìÚeJ³wv—8ÑT¶Ñ dHŒ¿£ÂL‹‘´97‘äxð»¸ýöÍæÜœ3Jâ»F+hƒTŸ{Jå/ó$°4;ƒÚëݾ ’l?©O·¿™ßÞò‰5-뇰ZNI™Ö$SQGûZ/ÿ!#Æuö˜ÏÁ¡ÌMË$§¤éù]ÍoÙÜΤæää>ª‹KRôÔ^j—g|[þüçÞåÏî-(É¢ã¦óéYb¶ˆÛée­¶oÛá`¨´{q«§Z㚢Ìy¡»Í„”%žEg0¾s±,wÑDÇKî.?ù¥8iËùq5ó Ç'ýˆ·UÓêÚ§Í1MHèac¿à!v§&?}h©sU¶’"U7ʎŲÖÅrm².!ÊYn +í«ã²Îs&’ΰs—Fy>™íåæ.¹ñ+~Ïÿøã[*”ìÐx‰¹Ó$;Î8B0ÞÀh›û 6Ó8&á‡ï÷u¬3egý5 ¯OE×[b«jRñåêQú¼¥,»ÊW7c>G&㎓º¹‘#çg§Ñ7›¿4Mʽ¢+;Uyƒº§mµ9'ï¦á3CNPÞ%IªmäÆJäïØí*Ÿ>þ\ÛÕE¾îãõùxñHÍuV*ÿÔ¯T—L8/›ÒZß·LË»¬[CõCퟓR]aX)èCí?ÕPÓ*6Ô´“Yäð´²k‡±$ÿ–8þʉ]H‹{’Z”¶¹üDq¼rf†•¼Ñ´É`ÒæMštÅþ臺¶3ÒÏíý'uÞñÚPÉo%ÿåŽ%ËÐ+àñ°ñáå ßÇ#!Xšm¥‡Ú?5+íÁE< œN›ò[)kË@Xd<)‹­Rë“tïwRÇ¥_W<é6L»*Ms²Í@Z› JÛÄì¢M£ÑæmÖ ãûûD,Ë…Ò°XĪëDÒ(É'Üš-+í©4íO[]åZ%mÒé·kôº’°qïâ•EI9‡ª?J壤M’m÷Pô”‚cLŒ[ﮀ P+ÕiG°Xõ:ØqÈÌ<<Ø~-õ‰”yl°}(2½™ã0ÎÀh«»R“èõÎî­ã‚ȧ¥#9¿)-ï`¯±â&[_©nÿT]_¥òu­•ê?J¯Ñ©(ß‚Œ âñÖ:ým\¢ýKù'u÷†ÃÌ µýiKgiN˜WÖ¿eâkúÐÛÑÒ%ÀéÎÕN™;vÐu÷ñ›ë|;ª‡äÕ´'R=§×扽Ëk$žß‘ ‹µ”?ê‰<{É6Þ2ª¢¥8Ù6Ž¥ ~rP²D˜zÃŽ•ç‘Í»©Ý[§ÉÐÈú=;¯ÉAÏrphyú—äCN?f»Fý–î?ýÿIxÜØ²£û—l½I6W,¥Pƒk§‘=6:é -SbúC»]”jò ‹r±ˆv¯èÿî?pW¤Èr+i%m#玒X» ®M”·ÇX%ÍžÃÚlãÈ@¬xÕÝûôVÛÒùu?cdHx_Uâú¡Ìu"â¿Â13(W—˜ÍK9WNbciºÅHV´î0äG*~†öÛ73s~ÂÆÁ@[F“8}J‹8Yþ^¹Q×ÑD–nD®©ò×Õh1ÓK ‰ˆ·Ýõ¹›Ô)ቸæ0,ÊH#Ÿt¬6ÍȻȑ²"D•'5 3]ù•gŽâϺ|^ÿ~[ï½ô¹ÏÞÒ ˜2w –,»6Tì8<$ úÁ®úÌôóY¬É[jBšX¢/ü=ÿcM(Òib¥'+~¼d KB|R¯¤Ÿ’f$ÿmÛhÈÔ_ ;éÔ:+µËQÇ£Û\ªÏHíjòþ_‘‘V6'õí¿@&/Po°›x‹ì¼=î-¡m"¥hÇn‡èxRÊtüøÒùÉ¥ %ñvIm›„ç?Þ¨'åíåºc©¬‚ýÄ)x¸<†vŽ /›žRظqìÔ¤ðelµŽr­F_&—…Ë“Vt¥ç®DÚ'±•ôXíÿš*Ÿ64]|E].ÏØqSÁѿކ“§Ó!ƒwÐw•&7ÎM|2¨û «Ä$ z‰ŸÓkóR?ºTŽK¬<3ç§î`·ì˜v¼N}á¨1ÑÈ“V÷˜7òÝA¯ÓŽPÛ$…è(wÌk[­ó Öd>ùhI%AæXõ´Á±]ѳ *Ç~}zúÆ 8¢ IDATê8RQÄ‚t9Üù­Adƒz· ©{HtXYÐñúà•/}Víè¿»j­kûÒ¾ûÂ*ßÚ7úΫ¦¢AHõ¾ìÐê>¢1'ÅZÒîÿÛŠ>€\œ]¹u|(­è߯48ì>¬1Y§F½6[`ABˆAÖ…BÞõ@ª¿È-ï'Ú”½-ñ»JžNWwkŒ‹Ð€Eú™ô#×Fr«òé#e$ºB&—çñ‰•ñ n¢ã ’•ßà>¢ÍÍ¿¢·Þåê8ÞÊ…’qEH~¼È¹9įÿ¡°¢§š‚‰e(;õd8ÛZ±uÅŠ°°\­¾@ /²á³à•]»tfëBF©!ähü}ì¥ÇBüëcôo)½B‰=©¶•[ÝJ­]¾LJòúîaáy”µ´Î±SWb=!êz9Tô¤QîâÒXÝ,6V7‡Ý^õuàúª&±¾ª)%·^Cq¡köÖklé'I^ô¥üTå”t¦pŠu,öd¢l¹8ZžŠ£ :²­í°¶Ä:%jÂéöµpZ]iÌôáJºÜE+žâkyYérst~‘~£ßxBx‰Üœã:(§É9¢Ü-(<\êK©k›ü±Xǣίv¡#hô·pzCq”³3ÐI5º2`d—¯?ù¾{.•±¯?JçN¹ySZ <^çÌ’éÓpö}÷ aßþ(&˜4¤Ís„Ï‹ã†Ðä®ÕæÞ$Ò}EÔ¿t $vJÆí3Ñ›àXÇ”*éqåãåÊÅ=~Æw*˜k³àõ¦r]©uZJôT2Ì\\z–¾ßWÀÅi5™½çÚ§ß(Q–äê,þ2 G‘ð|Í/^þrq¤òÛ0"Ìrذói#X_[÷k<ÉßÒñc½]J^8Ññ³òÒQPšCï«aîng\KÇqÈ*ÌBΈl08::ÑVÛŸÓ%›k¼Vƒ¼‘EHÏÍDwKÚŽAÀ?Fè8b÷µp9†¢(K®ÝßÉël%a+]¡õ8¾,‹ <©­é`öO”l tT¬6imêfÍ]aᓦJÚ˜ÇåEZ† Óf‘±“ H]U+öl­bÍõaò"ÉÎOÔ9cI^Q&ô-º;ºÑÞØŽ=ËYw{gXk†%Ùð{ü8R~?²c§Åˆ1Ed×êͬáP r‹óðxPW^•_Né˜Ó- Œ¡fçžÞf¨“´¼\¤ågÃÛÕ…¶ª*‰÷®ÑÀ´sRý»Íצ1›ÇJ®0†Ó7¯Å³b+CÇ”=âËüØÎ•ƒË#S£;ÐNcÍQõ®Át* ?g9%]:ÜvÂ…Lð‡ïByëó’‚q=+=.YÆ*†…_øË{1mÉXýâ?ñÑï—­;à íáüÄÆ™™:Eÿè©H ÑH6V«·I“—9QѤvÖ’Ñdñ’Q>YyHüì“C’2ï}èTš“kÆŸûZl¬¸éýÅ#‹¨-MgžX#6ÔvI%En¾w=x:u:}øý/>Ç–å’ë~rr˜Å;Òù¿¾¦ÿxò“¡xzñ˜3{ÉɸôÞëÉ¿ÿçoì³WÞí?n0¡3èàê솈¹3mJg4bKs•6• z*f :“õý¾àº©Á”‰ÐŸ%wu±MMñ… Èí*)ÿP‹V¯AÀ>hIÄw¨œÐòr<!(?×JÀƒ $´ån¯lb°O H¼Ž(Ç%‘o,Òx¾ CÂêDAd,?±÷ÒFX(¡ñ4Úži„Pvcj™Òh9h´<8JÃeÒŸþÜk?|i<ÒŠÕïË,i&L˜1–Œ;a4žxïú?×?.Vî UØ®ÿõÈ¢+{¶)ìhnGõžJtµÙ‘žŽãK1fú˜Ó,ˆTЗܺ Üó#B9®Î.Ù]ŽúFdæ¡hÒ8L^x >ÍÍ´‚nâ8D—¤P쯡ø«MÒ¯œGDôÇ —]?œ¶xÙ°º‚m\{€1QÀÌycÈ)gN&7ܽ˜¤gšñìoßa¡ùüèþ ÈŸDüÞ6}¹‹ÚSƒÆšf¤gš‘_’ƒù‹gQןoh~Z½¼ô3m,¾y-Ž”fF‹´ibU;+àuy 70væ$”»½?-;}Ê9ŒŸ?¡ :¡cOš8¸~£l= DÍ÷ž ‹R>3"ÞÀ8È/° 7×@º:½¨>l©ÛúÏÊ2¡°ÈB<î*+Z¶l(™[šž´4u³–ÆNèôLššO¦Ì,‚ÇéÞíGph_=cbôœÔ'GoÐbö)ãHAQÒ³,pv¹`oëFùŽjväPƒd „ t\!JÆ¢Ñù‚ÚÛ±ûÛ=¬­¡-,nzn:² ²àq¸Ðp¨„”Œ/EÙì °fXI;J¶íóoÃîçõ&#fž9y# ‰ÓÞÉ*·ìFÍîrÙÚ﫯¼±£0rú¤åeãÈŽ]¨Ù¶^§²-¨‹t<±{›¸HƒtùÛ9¶„ð½í{oì¸t²øœ1fÌÊç¾^]ôz{æïPëøÉ§$#G¥ãåç7‘Æú>A ?X4†ääšñê?6‘†Ú.‰y0[udÑ9I§Ýßÿâ#df›±èœI U÷ïªeÿxrକéðØ×%×¹4Òi#ë{p\ùëÛÉü‹ÏÂS×ßÇö®Q2¶CË‘<ŽƒŽ Ä'¸ˆž3¦ä„”¾kG ÃLA”6èå·Ÿ§[pÑIƒÞë</?ºÜ³éó)ÕÐ|‚‡X8KÄIÊM\ÒuqÍÍ'‘鳋ÉÚÏ*ØomeRqϾd:9mñ$²w[ {ý¯_…Y£nÿÕù¤ 8￾»6V²«~r:™®}èjBüñÆ?²â²,ùáb2~VrŠraoéÀ—ÿþ”­|ñ=°Þ„y矆…ËÎ&Ee#ÁDuå‡ñÁ3¯³ýßl“­[Êq8ãÆËQvòlR8~4t&š«jP¿ïV=û"Úkë$ÓÝðç@𿮎6,¸õf”Κ‰¼²ñpµ·£~×.|òØc°×ÖÊæ­#ÇÁ'8Â|%XÏ Ù–±‰¯¬+O›?|Yñ÷OØ«O¬`=o‚‘•—Æ~õÒ=´´¬7?r=½ï‚_ô« –t ú”óWy‘}úêG Ñób”ãgMDgKGX>cgMÁ…?¿‰BðÍÛÿ‡å¿ýó»Ý ½i9dÊé§ »µ=ásˆÄ@)‚WÂw`¿¡|»HsÚâI䥧¿_yæ ÖwŽÿ}}=.¿ñÜúàyäüeóÈû¯ÍꪚúÓ,<6€·_ø”½ñô‡½®Qb½ô»7˜ÙbÌ/·(Z_|?;²ÿpo›ˆŒöʨشÓÌÁÄù3Hù·ÛûO¤lÞL@Ã*Œ…ñóf“¯^þwøˆ‰ãaL³AQµikÜóæ À˜ö0¨Ý×Ìiõãzë=º—ýp*½þ¦éô‹U•ìÎ[?¥â,Z2Ž<ð›ÓiùÞf¶lé?ÃÆÔœNO]4޼ö÷õlͪýìé^MÍ]Xú k°ßÞù¦ètxCŽö¤_zÕIäÆŸ-!Ö4É·‘“¯>ÜÂþx×ËQ…Ê/ÉÆ]]G'Ï'™î³7¿dÏ?ôbºÓ.: WýüJ²wÃüù¶ÇÙïÿ†”N–¦zo%ž¸öÌÙÑ…Y‹OÆOüœèMýmNàó—߯;x–1 à Çó¸òÑqâ¥ç‡‚A¼~ûϤÊE±ž'[Ü |¶¡0ê:Ùâi䵯é VBÂÃrrMXöéô¥¶J¶±\ZùãÒñ¶n¨fgŸø¤0à^Ûóùß\M˦?<øž¸á«Šþs `‚1eÆ>.§Íœ¨¢þÙ«ïcÝŠO™GëáÑb¬A‹w­¦Ø2.E¦ûÔݸ +=ô´L )(ÍÒ‡hMf}Ê-úÑGxÎ ú\ãûÚõXÞÆ–å’ÙóG’êÊ6Ò›z•f’YóGŸÇ"¤'ýÄéÅdtY>¶®«`_*™6w4ñ¸}èêp"3׆ùgN%3NOž¼÷u¶éËÐ¥{†Ìž|çš–eÐã¦ÐXÓ“Å€ìÂLäŒÈ"^—å[*úÓ™¬FL?0râH<ðÒÄœf†³Ó Až“‹ïº’äå╇ŸcW=t#YtõÙ‡½–t+FN;þñ?äå{g[þouÔùfæá†?ÿ’”L-øÜ8ÚíÈ; ùcGaÊ¢S±â7cû‡G¥wʼÅE¸üÉ?"B‚>,99([´#O</_}5šÊãY¤bÃa9 ^B©±×B*Mìç "-íˆ “–!烞®}[+ñÊãï0bno²ãÉ»þ&>óñïéè)#1çŒYdóg›%e%¯Û‹ÏÞø$Jc ›v#Tå(®yôBAņíxíþ?0ÒoËî)“(صê+w²ô´GPfœ‰Õ‹rDÇŽ\ ‹n3©òÖÕtàÕg¿b‘¾·Ë_\Ö\2›”ŽÍòÛÎ ýìuF“i™=ãoã»™”\Qáè 6 ô÷ÛO¿Åjö–ìûÖï`ÓÌ!çÏÀ»ÿûRÿñ 'Ï"ðñ__c×üþ~2vîôÞU¦ž{²ñ'Ÿ¨Ù± >·;î:J}DËéû á¼”×¥3¹U#é—!",¾Á£lr.ºj&mkqà?/­›:Q69Ÿœ{éLrâÆ‘¿­¸…»á¼¿ÿ€‰zÖÉãÈÝ¿½ˆJ°gËaìÛQͪö×CoÐ '? sL"S¸²0L;¿}éª3hÑÕáÀÚ6°šŠZpAá¨|ÌY4ƒddIž“ÉjÂÃÿú-±eÚ°ò¥÷ÑRÛÄFŒ)" ®< ¥“FãÆÇî&Ÿ¿ú»í¹‡IÍÞCØýÕF8í]l¼éä„Eó°è†KÑp ë—U®›_ø3&ž6L±éÝqxËv˜Òm˜¶x®ùË“¨Xó5âaâ8é(·à¡CVT¾}È=óÅgUlᢑäÆ[fÒoî»»½Ò«YÑñ¤þ€€öVgÈÔ³“\0ØsCãèò ½ÕÑ\®Üñæ!ŽëY!éÕbA ¿G‰GO~z£>÷À ¤R ¼Î ƒßã•ås{áw{kÅ\I !Ðêõx¢ŸÍ‰‡ÞbÏé@ïî¶Hã)iôwÆ|ÖáX1¬ôÞ»=…ý"u›ÍËæÐÿäo*eFY±™Ýòày¤ljqÿ¼ávyÑmwšnÆu÷^@^øÝ[¬ö ÔjWQ¢×½¿V¶d{¿éqk)<F«žînÀ„y3ÀDûÖnÄÁÍ;1é'¢dÚDR½m'`ܼ^÷–o”-ë)A€ù©º~­Å'ú)GÍ‘QûËÏ"æ²d]üæÌE¶¬?Ìî»å?¢ÇåÃÊw¶±Oÿ»ƒ<õÚõ´tL.X6—¾ýÊ€ÿéçB ÊwÁ—?#ö)u}«BÿüӇ̖a ËG£åqû£×PA‹ý[á·='v¶v†¬$1üó÷o ¿$'ì<û(8U»+qÿÙw1§½«¯¯²Ê]¸ùñ»ÉôÓOĸY“ÉÚåŸâõ‡Ÿb¢Ãÿ\Á®ì^2ÿÒ³qÖ¯"ë—VIp™xÚ<ˆ‚ˆo¹{¿XÝ_žµ/½Š+žøfžnÜz4rü¢Wò:é<Ôȧ÷þ‹ÓÔÇŠv¸]~¶ôÂ2rÃÍ3¸§žX/öÅU6$;GÊÆ3g†ûáíg’SO#Ïþö¢ˆe?=‹Lš9’›Öìeÿzf%ûö®3LŽâh¿=o/çÓNÒ)G$!”ÈD06ÁØd Löƒ l°16`²1ÙD‰ „rÎ9_ÎióÎÔ÷coÃì„ÙÝ ÒÍûzôIx;;Ei?ýœ}ç/°úÝ÷±åóE8÷7ÿ‡Š)“‘SZŠ÷ォß|@ðLt¯«Ó”*[Ì8= hUÞ¿ÿºçß¿]y*˜"¤Ú"v1êùÕj¤ÅóîÉ!ÍaÃ}׿@ë¾ß>›©±® ÷_÷œðüg¿æŠÊòpég²ç~7ÌtÔ¤ ï=ûíÚ°_äýôº}ø~Áª‡UVu‡vÂÓwþ=ì…õûüxû‰×iÊéÓXÑ œ~Å||ô·iásï…yw4·áå_?I|õ"+(/Áð)ã±{õ¦0Ï1³OÀˆé“/Þú;Ú½bm¸5WÖà_W߆{>ù ‡ ¹wߊ—oR^¾}ካáik §]¹y3>ôQ\ú·¿aÔ)§ -;î6ù´‚!¸Ã9¨¢€àeœìKqJ^så2Ž×~äÞ P®ãb^wU‘ÿû+q܉ãP4°(œÎ‘=G°ë~ ?·À{Ï~&JdüôѸï¹Û9GfvoÜ‹5‹×Qqy›8k<zëöâï^¡Eo~)Êç1èlëÀÕ\Çæ^t*¶¯Ü‚C;bÜŒñ˜vö‰0´ŒýöÜ[(ºÎÊÏAù˜¡ÈÌ ¾ÐOÔì;‚´ Òs2ÑRÝ€¶†¦p:~OÄëéïîb'_u1¼.v|· ÍÍ6u2fýäG}Ò Äk©mDíþ`ã.T* ›vÁ nÚ]+B{_#Éð~?¾~á¿€±§œGv–¬l«Þxî¶vÉó=ßF<¿ÙÈÆÕƒ4/ï Ÿ½í¼Ç9T¼4rb,DÞÒP0ù±)1¯RSm dÀ°¦Ú&€¢‘û¼€Çox„Ö/Y (6ÝñSöû÷ž`Ϭ~ƒ]óÇ[YNQžˆYÑà2@{c üÞè-‡Ý·¼Édú¿àãK ç*uZN¾8¼‰j«@sC'Sêš;zb£÷=ôê7ôÐÍÏѾíÁ÷"†Ž)ÇÅ7žÃyãWÜk«þÆÍ¿òtÆ™8Q:!´·Hë8¢Vðˆ;VlŒu< þî?ß±,X–;—·1>qj0ý)a±Ûàs¹qhÃf•¼G`c@@ð‰ôî|œÉ”©¿q}$ó_¶o®¢{êÅm¥ë~á»k r 2P207Ì»æH°ŽŸÙLvÂìQŒ“9='V–1“ƒûÆW|±žj‰^hÖT¯ZZ±ËÞ˜§ž þH `ÇÊÍð¸¤[ ¢ŽÉË// Çe‡AÆV½÷±lºÎ–Vl[üµ¢\ÑàÆ;cæØñIæ’–suUÞyc ÙÓ̸é¶é ŠVwTÄ©8RåsÕÑ•]i‡pñu§°ÇþïuºóǹóUúé¬û„M+wSÅè2<ÿÙ}\Fv®?óAáÑÛ_ ;.|DxèçÏ\xí™ÌlŽd™3q¸éWrŽÌ4¼öØÛtϤçx]óýéú'ˆÂU÷^ÎòŠs%2dæfá¸Ù“p÷Ù·Ó7ü‰ž½çot÷¼[hÿ–½(5Óæ¨š>À³·=D»VÛô§Ïþ—ž¿íAzþ¶ßÓ¿oûÕ ŽÉÃO˜ˆ“¯ºí MxüÂkñüMwãÍ{ÿ„Gξ«ß_€üò2\ð›;eÓ˜òƒó°oåj<2÷Lüç·á?7ý>ü(ža2ÁÃ{dëX"Kᮈ~e [³Q€1„ü¦‘.~•¦ww=eöô1Ú}†yrmÀ¢\Öâý屯¸Ò=Áj5©Ò@N~ºrX^pëSÀÏ£½Í)¢Yöźå¼? WÏùð×»_¢ÕK6à‘•›~w»ìŽ)LØÕ¤Ý¾l#ÀØYÇÆÎžÂ`ç²àKé•;ö ³¥ÇO€ÅnèÙÓûÖ¬Ðö^=cÁ6­wžxpÌ‚X½«M¥º•sxˆãW–›liª´€º¶”–ç…·½ùü×äóW”…?¿z#{{ÙƒÜ_ÊæÎŸÌéö°…Í«MK IDATtH°¯ªÜ_oÒ&›§ú#òÇ¢@gײúÃ5²á>·'\¹§DNqÌ–àêPÃ!ùç YãKñJã¼ùBQz’wL…òþÜ3k§Ó‡]2Ž•Ζ„‡øIŸ‰ÃâÓiX¾øéß,Ü@ß±)¼2ÚÙîÆ²EAC·¤¼Oßÿ&u¶G^Æ\õÕfêhu";?Å Â:™qúdV>¼vÆûÏ/¥±þÛXúÑ2ØÒl8ïºs™œ|¯>ü5V5„{],~}!À°‰£˜ÜX«gÿâJŸÿóªÙùþÀóxïÁ?Ãëtbâ¼ÓQ2b¨$®³¥þáOàýòÇ®ó"0¥z¦÷Jå˜Ò¯ ôcã ô!ãÞ@CòV†šÈVR9öì¿/ ußn%Æ17s »åOW±—Wü•{ì½ûØE7Ÿ‡ì|yïtíMrÞ; àów…·*Æ Ñ˜­â_]zkMƒ¢rÛjëÚ·ž7h ,MgC£bÚ¡ÎÃdMþà cÅ>íø¼ðªGZš¼ìé™vddÉŸø‚y*aù ê4Ȇ7TÖãóW>Á“7ý‰nžv9ýûž'ÉÝéBZ†ßs]x<©Ý4 l;2ó”Ï O-¤Ãmp-³4 m­€ìlåmåCòâÚ¥¥ž¯ ¶½ºªfÌ– ¨¯jÂWﯠ?\ÿwºbúºo·\pí¼®Ãôcûòà>ô’¡åÈ/+ƨ“á÷ú°wmÐÀ "ì^±f«ÃN˜„Q³‚úîeÚ¶·y0‰æeöýw š›”O©ðûpv—î Š#ýÙÁ=u¸þ¼¿+–l£€ŸG^a&νìDvß߯aï­{”»ý—±¬\±³¢¨ë{ÍõÊýšü>y£@x, øüŠõ44Ñ`Qo8dG^Üìlj–Ä ¡£QÛ)ILæÛ ~ÁÇ8.~Ÿ¢†–f7^yaƒ`6s¸õΙG-´iuìʰuMðl÷–ÆÙW+ #"l[»‡ °4ÒoTŒ :0–¶F¶œ—º‚`ð¨A’0ÛWm•<¯;L;¿´P–ŽX¿p±$ÌÝщßû…£FHÂ÷¯^×õbª2R¹o<•8ê*erHlF4ç‚™æs®<ÝrΕ§[FMfJ„ÇÔS'†ywâØ„xÄ»ÌÌD b—eÕ½rÊ4±P_–Æ%ŠíXIö6ê4¸üñÆÒWþUøôõ¯éðžj0Æ0tÜ`\zëìoŸý‘M9eR\9µ#þvhð æGžŽ(2Xq¬w›•[ÆÙ(T/¬œ…Hp‰ÊXêù“ê!ºŽ(ÕžçÑPœˆ—åD©>B;îø!²EûpÚ)X$,ßd6aòœãUû#/+*•³»Ó…¥ÿû_¿hª“:£Z›‚õIy$+ÛŽß>ãâóÁ8ΖVTïž&¡X8 Å螤úŽ´õ¶¶èU ùz1hHnÜŠ1 ,ºï뽤,7\·ª7Ë&âêtcÅ’môÔýïÐ¥Óï^züc€‘Ç Æq3F†Ó¯Ü_KP:$ÚxÒ:èÜø"Ï«5z5q`©,  ¼0~úŽ1Åžß0½w9ýxþ™ÕcÀíÿ7Kµ\[[\¹yÑž{q:9]³¶V·$,¾î D„ÎOÔåFg‡>_@D ì’ã­¾6'†ÐõÑ,¥öÚž(÷q-=éH]Ÿ”Ë‹(¨µþ8$'cÀ§õxóž°Áô¡_â"žykÕ¿¹,l þí®z:šÛzàðÎCÂ+¿î½ñ‘ŸÙÓ2ÒØ¥·_h{îÞ—R~J ×UAä=äJÓÔÕgæE²úb†*a±Ïb1ú¸AŒÉ6aÂèã‚^U¯Ûžõ+¡îp=þ÷¯…ؽq=ðÒ/YFv:„ëäNáˆÈ“¼y½ñH5†7“Ç)RŸv|ø¾ñpïè&: ÅÊYIê)š"ú´7JËJJ0Nï¾¼Sg ǬSǰ_>ô|òæJrv¸1j|»æŽ³˜Ín×íƒ--Ö›"æÓÙîÂ_ß¿—{îÁÿÒÎõ{(;ÇYó§á'·ÿÀ—o-AcMä ÿgÏÄE·]̾y÷+lY¶êÕÀÕÞŠqC1fú8LŸ?°~Ñ2Q†>~êešvÞ©,»0|öûøÉÓÞÕÐRY‰Ü’" š0 ³~|!Þð1Ù²-®.â!¶<":Ÿ³Îbtò¿··ÐÏnšŠüÞúðrÓ[ÿÙ ÔTµa؈|vîÇ3·Ó‡]ÛjiÔ¸[vÑ|—/ÙI·Ý7Ÿ8«¿Ý 0eÖpœ}QÐ+÷ÅÿVÓ‘•‰ÜÂL<öú¬ú`=[¾hUªGsm ʇcô䡘qfÐëþÙ닃§Lžµ´¿íË7МKÏa¡‰íŽï׊ÂUv}Ý5øâôžkÓ“ƒG är‘î¹®íJºª:ìû Éa&Ž…·o…xX,fÎÓ÷IËvüälàà<ª:Ô$‘wÞ…“´·¹PWÕ"‘!–o Àã½—Ðe7ŸÉÒÒí()/Óïܸ³æÏ:ž½: šj¤éI!–W?©n„¼€Êí»1hÂLûá|ÙmIöÌLŒ=í©1‰ŽÍÌDD^UÙµŽï¼±™®¸v2fÏÂN˜V&»ŠŽjÅÈÑE(*ÎŦ+,N¤+J'^J}«¨þjĵóŸ¶U"õ/V~i~Äå"·—^Õ‚ï!–Å~a7ˆ‚Ò®mpGêãÊŸÔ&2ŸÛƒ¶úFä"«0íõÒm޹eÁ‰`¢c¯\K†ž‚£†æú4lÂPnȘA¬ýjC`ûꂞø±Xüî7s¯;[8¼Œ›sÁ,ók¼Á¹žÔ•& g%^pÁl’~‰ŽÅþTH¹¶2è›0¥œ¥9¬äv‰g ÓçŒ`ÃF—Ä•eÄø˜0u(¶¬Ù/zΙ8œåß.X«”‰žk"K§¼Ì—ëº k?YB'Ì?•˜6 ƒ'ŒÆá-;Dòq&N½þ ÀÎ¥+àj•uÖSˆÀ`Õs«Ù ^p10ݯ—ËÔiq-ÿz'}ñáœõƒÉ삟Ì`üdF8™#ûpû¥O ÿ^øœMaz÷^ñWá¯ÝÉÝÿümûnÉ{ßâ•?ýW’xNa~póðƒ›$›µ¥ï|ŽÏžWôÌÝéÄ#?¼‘®yü×lÔŒãqÉýwt[qñD`Ì$Ûïp ÕÑÍã àîÛþÛ|® 0¿¸kvxÅsÏ®ºåª7…›ÏB¸Td¹½þÜRÚ½­Wßz*;ý¼‰¢°w_ü†ž{ô#‘^=.Ú[:Q:¤Ýp–„§ÇíŇ¯~‰7žü_R}׎åž1gk;myÇìZ¾3/>°{ùj]ü=‚‹É"Ò½‰™@ðƒ1‹¼LÛê)ø¥É œsþh¶à£íáyíkšÿÓ¹¬¤¼·?z ÷øíÿ¢ÎÖNIüÂÒ4VË¿¿‘b0øü™—pócÊygaÕ»aïÊHÙ1ŽÃ¹¿º ¶t¥—ÿÅ 0Ùö#·7]:È)˜K,BêðxæÉåŸþ2»ýž“Ä{Á¢øíÙÙ@§5’uÞöá»›(Š*œÈ™çŽ nÛY×–Òá=Õ`p`Ç!ÀIó§³÷žù˜(Ƨ6ûÜì¢ë¸Ú‚u6¿TºŽlÛœâBL9÷töõKoŠÂÙY3g& f×Ýiû‰`bfJƶ‹KÅœ¥ ýË@‡¾=z3Î:!¬ŸEÿýÒŸô K ,~k‰ÿêû®°ÙÓílòÜãL+>]¥íH°›läç;™Ù䈻'On†Í,ýrýâW§±ìî~è\öøý ÈãòÂl6áäycÙ­÷Í\N/é6~‘ß^¿}ê îOwüGغf =3 ÿ÷矲Ò!…øxç_‹ÂF£ÉÄðä¿å½ó=}¿pMøC4PT–‡k~óc÷©ئ­³HEKÙúÍ ì]³ çNÄÏŸ{”½rçïiïªàò|Fn.⯀À Xðç¿§ ÅÄáØL6‘×I«Óu¯î¹|ø—ïÒšïvcúÜ‘(RÀª5Ñ–5ûðͧ›¨½¥o?¿„ìv *Ô+6¢¦ÚüêÇ 's=y(+)/ć±yÙVZ¶pEŒ‰°ä¯pxç?s<Ìr saO·£¹¦ õ‡ª°ô½ET¹cŸ¬÷©©ªýétÂü“1tÒXV:r(2r³ÑV߀ÚÝû°ñ³Å8²e{ÒõÇ#°pVEºüªEäÙºÕ•tÑ9¯ò³æ aÆ籶6m\s;¶ÖÀ øÇŸ—О_ŽÚÊV òŒåÄ‹O-¦Ï?XGã' dÃG@õáFl[ìªë'”תƒõ¸tê¯„Š‘%5qË/ÉFVN:ZÛÐPÙ€ K7£³µ±ubÝ×ëñàOO^Wäìb5ýµ7µâ‘Kn'‹ÅwGH~¹uÁ“Ïaõ ÁPµMþ$%ðü’k´>l&+øf5çÆxуÿlÁ¢ÏvÓYçŒdýùLî¸I%´us gàäÓ†±Ñc‹Ø·‹÷ÒÜÓ‡«_ëV¤)3‡°çÞ¹Ž}öÁF ~I´_5ƒ¥gÚQSÙ‚ÿ½¶œ¢yÜñû ™Í~ [úùf:¸»5‡‘“—1“cæ©ãìÜt»7ê’ðy}øçïÞ žû›4{žþìaöÍËppçaâ8%å…˜qÖ ¬¥¡_ó¨Ì*¼R‰M‹¾ÅÞÕë1|Úñ¸ùµâ»×Þ Itòü31lúTìZú=FÍ™——’wÓÂY .˜¸4HWè1-Þ–—…ï «¯ŸB'`ÝwFîß|u-]zÅdL;q»ê†éì?/¬¢è«/½j*›uÊÖÑæÁ›/®¤è¾J”ÇÔX:-åOŽU‹7ààÎ#2º—ÜrÞ~:r ñÌyÓpÒù³àqyðÉ‹¡QÉóI¦5©!lììØê¿$¯Sü’õ§Ï¼BNÅæÝ|5ö¬\‡ÊíÁÕs‹ÍŠK¾6‡|º}tËÑ Íœ¦ø ý0<è !8É W£¦Œ0€Çå¡-Ë6ózâ*aÍ—kø«ï z[Çœ0Ê´òÓ)5ÐÓÍ6Áh2§Y‹Hy«Kô–i‡ÖÚâÂëÏ-£ëï<…zÎ86÷¬1¬®º …%Y°XLXðÎZr;}¸øšDüÄü_ÿÇ"šyÚ8öØk7q- hou¢|h8¿/€§ï{“jšE jЈRüìÞKØÏî½í-h¨jBÁ€A€™³È¶Ÿ4Sš4s&k©Èp—…ÒöqÛ#þöÄ÷ôÌ 2ÎK¤éh÷à÷}.üáñs¹Ûî9…»ìªp`_‘ `ðÐVRš·ÛGï_H­-NY˜T¾PXlÝŽ@)kjmBÛ8Há_÷¿J÷ýûvÙ?dÓÏœ‚ƒÛ¡´¢£Ž>Àã¥_¡àjãóN¤­úx1.¼ëZL9{.&Ÿ9›5© ž¿õ~ªÚ±6lÅ—ÿþ/θþ§øå»/`Ϫõpµ¶bø´ã‘[Z‚ú‡ðÑ£O&r—nrÈÖ±DÊ—Dû•®×x+\Â@Í (œ¡©õGêŸ×«ÍŠCŠS¾VbÏç·»v˜³cºžÈ{ÏÕfåðúóË©®¦ —ß0‹ ’‡‚¢LìØT‰ïï¤ÿ½ºœÎÿñ lÓš8¸§^‘ŸÛåÃ7¼(\}×ÙlÜñlȈ8;ÜØ³í½ñôg´k£Ø˜Â?î}&LÑÇcùÅ96~0^@cu6.Û†…/NÕjDñÜ.ìXë]—usM#v®Ù†Êò Tí:‹Í‚Æ#5’Ì4®Â_.¹™æÝt9={*Œ¨@Á 24©BÕöÝøüïÏ£~ÿAÙÎéÀêu <Ò¥çŽlØüŠh‹ü[êZ±Ïã§|{i ¶<ÌŒˆ(Ün!cjû-¦^âóIÝ3!Kj½~ʲHÊœYàùNÎl -õ«{ù”iä hI[Kù÷ˆ…^Àn2Ã͇ڡ¼Œ N\wù»Âœ“+؈‘ù°§™ÙÆuU´i]%¹:=8t  ö6®N¯lüÖ¯<@Ì~‚&LÈÆO.‡ÛåÅöM•غöIÏr'ü⢿ …ÅY˜0µ‚•”å"¯ ·õUMغvUî« ]e€ƒ»*ñË+u}‹Ýo”0#–÷s`I6 ~^obdö‚aóþó­ô¯ïøO†ÕfÅú%ëþì±”½ÐùÏïžq”p{7íå{á½êtê°¨f™£¢ìNŠöÑž/1Få• Í⣳‰ÁÄ~?Eý‹˜÷3ÜÊ ]ŠüáúôÍå ·Û-ð{ýáã C/ɱ(Qüdd¥ÁãtxqÚ¡ÜŠ=ž$‘/˜i¸ˆ.,)ð$Ï8Ž1‹Í¿Û-͇¥/ÈÒ¤ ÿ©i¢é%g8ML|ºÉö¶}VwÚ³Ã>ˆ¤º@LÂÛ/#ŠzñX>RÝņË™“”ƒ€ãËñôG¿åà‚Q?ø@,ø56°6bžq1e/¦Âe+ÇKü_€¼])->&-e,ll§ŠÜÙ.›É&!<ì9Òׯ”!Ç‘ë£cËE®ÏŠ)WŠWD/+Cô='’9¦nÄ´§J›=¹¾Q9ã%óv2 YssçSX?,z< é,r)ƒØ1D>üïƧ¼6¼¼„ˆ¿€ØYdZ/!\:¶¦Y5ÇÑrYl„^tL߮΂e˜ìä 4ëiðE•¨ûð­5}ç…°qG·ª¿½&„ òœí.¼ÜÒ“V£VJ—¤9ì>wÊáI Aã¬dbœ¤^Ú2¯÷€BLµOQëAÒPúIñÖ)E‚yÕ§²™¬k«yÖ øBž©h©Iî™\aRZõ¶,¯ •~#®I—G­×‡,[!«ó|{¾à÷솲Þÿ+è_©¾*AkÄíkuÈ¡¿(¹niG‡½ʳ rí§ÔQ"¸\ëÃcEð!r’f<]Ç Wê%ã”:­ijí÷tÕ7¥z«O¢õ:1*èÖ>ú€Ûƒ¢4ù:–˜ –¢\£ŸmqÏ\㣽¹ìév–_’Ÿ2¥›Ì&däf0hojOá‹ ”9òuÎ[öÌð³Hí¦˜ß¡ðèo’lk`ˆmpòtêPïp´òcм´Ä;6±ßíFQZi@®NåÙrx_çAsºžÈë\ù>Ú£"‹t¤áâô¢ïÛ[œXüþŠ`ÝŠ³š§ÞVô×!ýÐWß‚'¸XÛx¦%]àý‘­!=)µ=­²„hùõÁí$a±i*óí/¤éj-—ÔC™wµ§¢Ì\!V¶4“^¼L©¾hi ñ ¯oý4jý&‹K¯Œ¤ñºÕ"RHS•?&gåòròÛ8 Ù9F@,æ<ˆÛNÄ«Š)_‡åúh½Jõ"¥A x,U¢ ÑŠ¡<¦ª—Gü6¨½^E§—¸‘Þ·ÆZù|Ôz½È²f ±/“wGZ‰ _èAhW^cu•c@Å.¯$—5×ÊTBÆLc }\¥©ººcVÌïkÜfÉËžõ4ž‡.Jƒv¼Yª½©ÐOw zнrìvù©"g€ì^:33 .™XZ=ˆrHÆ «¯jÂ_ï~9üò£vމȓ hO£ÎçC–5Ouo#ŸB Ö^ã•Mðùgïo È69¾ñÒ;zÑ l¶ËêÖa²¾™M¡ó¬µ¶µrJÄCFK_šJô­r'>âÈÄ8(É6ØQä¯r®³ädŸ¡Aøxå¡eÌÓ:.*ñG«•^ÏÚ“è 2(A*Û—#eßJe:‰¢Ÿmq!Æ!¸/I˵u٦𞤹Î5뉫tͽpNxR´å»M|*xÆ^“•¬ð’Ï/þtoìl=v†ÙóK¡c2|bgýÍõT_Ý w§ü‡$B<Ô<Z=8¾ˆØ IDATÚW ä=D,êwªÑ3^¨ :x<(Û’.(Õ‹,³YðùÂ:c¢²‡Ê},½/º¼÷FªRx.§zê^‰`—ËG¥Žªm<Çdüþz‰÷<ò_ZFjå£TŸÅ|ÄFe¬ž”ô¦îEWƒ8dË$žÇØOYIIçlY¼Û½‡ÉéKNò÷òž×·_]…‡õ!-ùlÉ…ÇÆ =—ïgÅPÓŸø¹|Ÿ¨­¯”ï'S=+Au>²­9Šý ô¼Ë¹ŽEä$™2Œ.g’”µ|ÛÓˆŸ©{ÕcÓ‘ç¥N+ícÅõM~V[åâKÃä •_y¬íÎñV.ÍxØëòP©£8b,eÙëgº>¬[¼&l Ÿã…–œ¢øŸxVÃРøÙ?˜k‚G7n[±¥Û¾¶s\Î0_Có½²ÊtÿÏ_¢«N{Døú“õ}y:}LaykȧúMãáü+úÖÊã1ŒF?(Ó’¡zv×ÐŒ’€Ó(“”b¯Ë¢´rÅc·ŠÓòyŸ[ß™êZ±vùZðÞFÚµµ¦[ø÷'vû(ß^¢z ±Ýd#+ ?ЬFfÀ€µ^/ÒÍ‚9æ@…¾„~e Gf—Ú®#; ë¿ZË€#ÓÁî}í{v^&ÓÃ#t• -åîyñ·vÎTùç//øÜÝ|´^ù¶ZŸ¿A¢ƒ ä=›ú½cñéÔfàZöOÆŸák…’gI/uô´µÕÉó¨óƒ¤òju"ך%¼‡!õÔ*yÔµè]ÏL}å%>}bqõ&Sއ=äÛKñÚi¶%CüGbä—‡\)yv¥ò«×y5È{û” ï}”DÊD+ýw@(u Pl ™fñþjE¿øN²zV~oÕAºJ!ÎKb+ñ~ÇÓQ¢u:YOz"q÷¸½ˆ×§1&fWøš›ß‰JBn¥"þêqt˜Z¸¼~äÓ—×EâíT úÆöø^t}²ÄóS=Nê­£_·´Ñ˜œq¾TÛ^Æ—„A€Î7rßúók~Ÿ'xR×À‘ƒ¸G?{2múÙ3ÍŒƒ¦ø«™Í»z¾åáOË.ÈaÐ\ÛDŸ<÷~@¯,z¯‰9Cýõ-_2¥ŽBý™Üså+ÎÀÑ%”®dÑ– ­ÉŽQ݆å­42{Œ_K`Ïäž±åýKá€t@”a¬TTÒMd¯¥æ2î¶òÜÚé£òŒA’SD䮋÷újºt¡vêB4HãÿäBQ™E¥{’O¢ûbµ„‰T™ø<¬dbLUçCÓ ë¢m…x— ”u-O§~¯`,%Øg&æìÐN#BÊÛQ¥Çƒ[!ÏÅ)G€XqZ`šáöì“©¯rº”­ç u_[ÙÉßÇ"ÞäJ>\¡^„ž)Ô¥“¸’i£ÉmuIÝ6æîu¹`1e y¶lJµÝ¥K2úÝK¢z•Wµç°ð÷[žðÞõܯmœÉ„œÂ\vû3wÛšªhÍ+ù[ö Uû*…ÎÖò8Ýpd¥#+7‹•Ä ›8‚›6o¦)#'3\ÉÜ.úóµðz:]©Û¨¤€[ŽÀµî#¯¯6kQø9‰´”CÉøVò´Ëw(Jy’a ÏãË¢†îŸÝ÷”%ß ÆG4:¿PÓ‡†g ,kYnʰéz¢ddÈ{Óãyòû3•ëRºñô—ˆ~éíIÇ'ð™lœôC9rš^XÛ±ÂdÏ¿0*M¹A_y@W—SÛJD÷×ÙäW7âÑîv¹©Ä1Dö$£h ÍØW¿Âœ95JJ^oåÉ´¾+—‰ÜïX)åãhégµéÚúJyO`2uDOܵn•;Á¯µ_Ÿš7ÒûmãÛö¥¿–Xä¾W7¤•ÇšH} EšhñY^Ééòõ#B«oܦß~ŠÏOü?ˆÇK Âw-­4»ä$_*é ½G±aÉþ©›óþüñÛ¬éÙÁ#óK Ù¼kÎÓ¥¿†ÊzzúÖǽGvJÍ7e5`ZÞhïÒ†wí¥7‰Å},A Â'-49ªêÞóhØM6²£“¼þf³v§xý+Úœœ9Js™dZÒ‰¶/¸™‰³w§hÇ4Àv—Ÿ¦•Ä}·ÇÄL(°X·÷ç° ìé h…“çá% ¥›Ó4[:Y– *²:ø¶Ž•\væŒî°º #XÛÞN¥éi&{Ÿ¯'ýj‹Kpе~ñJþ·óo÷¬ûr%O‚¾r øüøæí/÷ž{»gÿ¦]B¢2$reYÒh|V©¯ºá&菱ß]E{¯æ‰PSÚŸ%ïEKÌ‹®oo§º—(ÕÞ½TcIKÊÒ+ü¹Ö ]ujJî_Só‚°®¢½3òûÐãíÏ„‰¡´#ç’">ïxž %bܽ £ƒçQàø"{ž®2™”]êomýBFV9šR›ÓR>zV@äO’P+—ø{0•Ë$YlítRYúà@ìWF•®±Y¥þ޶ÅQrKû©‡OÜ¢iÕ<íJ÷êýltÚZ¼è±²ÊÅ“KW ÊýuwõwëÚ¨Èê×;ÖMέð·µ}Æx¾5¦ïŠçÄzëV®ÿC8,_ŽFy,¿J¨6†ÆÿÍ Vžñ½èÒ²ŒïE׿b¯_H¼^é¡o ø±¥Ó‰1ÙÃu×/­!eM£ºTŸ45U×Ó“?ÿ£·xð6ç¢ÓÍ£§3ULÁYíV ­»ÓM{7ìv¬Ü"|ûî—¶ÆÖ^›­ I/æk=;ùæŽõ¦üÌã»äPnÐúžË…©/%Æï€’‘EKøÑ=.7Zx?#o°î3\ÓL6*0ƒïtï3e¤ E<ƒO?â•<šT–_ÏÖ…¥-4.g’Ooº¹ÖLÁÔ¶‹|³˜Ò»žjÑc"Ðn@vO[K}™DØáôÓÌâšÛCº9²˜Kp¹÷qéi1²©ÛZ¨Žõ´‹dÛMâFzw¡5@•Âì\m[ö¢aã,4#”wuݳ¶²w“ŽËAÄ+¥gZh”Â’-ËddIŘž(M*â¤Çõu4­`ŠWílý¾„þe Qò[¿ëVã½'^ p&™¹Y,=;öô4æjw’³­míig=ï`azîÿuË9‡µ Ž®ýè±3xéþGéL=I¦L+÷\«GH^6µÆ¥¾/O) =“Ü(0™g݉Ö@ËZtrÉ,_¢õybö Àâ†E¦ ûÏ({`”™G{dã(êOëû bZyO^‰l–âË?zŸË²Ì r™œ]ê_Ûò…µ ÿ‡²å¡vª‹Ô³+_>±íU‹qÚ½½.Ã^"Kì}¼òØÜé¤òŒŠ€ÞöpBN…QÇv‡íVb]Ç­)·y]«õ“Ò•I9¾â  ÏåÊG­m'l¤ËÖ¹2ÔÓ–5µÑä¼) ÷kl¹4.ÃíßYÿ¢¥´øÆpBâòŠ–!Þ™áÒ±AËj³¼^”ú0ÅgqÆT¥z§ÜwH¡V7ã{¼7Ò•Ç[yY´Õ5ù´‚_uþ°®ŽÆeñç[³(‘þY+¥ns¿ÚâÒxm­T½¯’öoÞ#Ô¬¦Ž–vÒ» ¦»Á1'ŒñU6¼Å\>ãŒÞ£Í~?Þ¯o¦©Ç{“9¿ÕÌL•^hl]”Béú/üDøºÅIãrÆ(ž¿Ù– J£Fòx§R´c|{Ü<•;èþ®„…3c|f‰¿©åÓ^u ¶t:)Ï6€Ï0;’<‡g”ò¥fâšÞ5ÊÔàóÆz*q tÄ?¥/¡_è©>ïòh»2ÍitzÑoMÃ{¬Ã½/Æ"ç]Ðæˆ‹þ­¾‡Xž‡¾5•^ôøü¢=yJÿõòÕƒj¯5´Ò‰S½9–tJ¶> M/ä3ùj¡Ã¹%ª.(ﱕz¡"ˆ¿ïRž§”‡RæE—MW[YÊãÓÆV—3ÞgãÌI•ËŒÜ _[ËGàW”<±^.±—\‹×R¹ìÑ]üåvm:”Çdî• á‹Æ:>obÂçqòœÿy£Jr5µ’Žcõ-ß/’-¿åeè&/z˜‡R¹Å_ ÂÅóØÒéÅèì¡þTŒuSr‡úíCBsÛbÈé9^û`Pê›äËŒÉÐÈ­ž(§J}®ÜïDg±y’K§ç¼èÚx+Õ-=}õ·Í `\¦0&«"îw)Ru¥ ýÊ@‚úõå0ÙèŒâ‰ÞæÖEÔܹY¥~§bàÖËO‰.™ Ÿ:#=9$Çw¯ËÅÍNaNÑto¦%RUNÈ­ð»;—‘ÛW›„ÜJ††Öp¥°T—E÷”í·-í(v ñçZ3…dËÃÄNÌì«k|‹Ežë)ü—£Qz– ]%Ò‡èÃ7--4ÄÖÉÄö¢«¥¿ÏLÞ“žZC½ÙïÅ;µ‡¨<}ˆÿøÜÑ~=»ƒ"Uègº8ÆáäÂq¾Af§oõ‹¬Ù¹Ýد×ǰßíÆëµ äçŠýs‹¦&µç\ ÎŒS Fú›ÞE‡k·Q4@ Ââæ6jà3ùñ9#Þw®„!޾Üܨm|”Â—ŽŽ¬nk¡¿•›=ÞÊeNZþñÇItÕ…ØqQiL%=+•·´WÕãE7ÆÆ÷¤ëkc’J´NÑâ÷bEK=ùÉDg”LóY93º»^ɱÔyÐû•ÞK)Ç,‡i¹þ6¿“­i^`ñÇrÓÇ!7}$YMŽ(JmKxú‘ÐÀ®“¾ç m=q=‚€]Nvº<$ÀLÇåŒóçY3)ùôµƒcÀ̼ ÿžÎ:ÓÞڗ͹Y3‘íN¡®·ûdé­6©5Ý O°±£*=~šœ7ÞŸi½ Ð}(µç“Å·²þeKzÆ–“1™b„îE*ÒHœÝíØÜÞB#³†úKìyBwå¿Ð–)ÌÉå|Ëê_±dfÍaÙéÇ©$$;1RAw÷›ÉÆëþ:uØãÂò–fš˜;Ê_dËé¶rŒEšÉBgOðUº›¸õuÏ[,¶ äÎ#‹)3ŠJOù齦úú¯Äâ&¯gûŒö€«[j©5ÀÓÄœáb{®:9a 'ƈ¥rᆋƒÎ(ãó ~vÐy˜Û_¿ÑÄÃÂrÒG#ÝZ›9VS šÚ÷¸ÆóŠFžÉÑÅ/}5~Z‘ºzC:tð~4ú|Øår“ŸLè(LË/‚/¼¥6M=™QÄMÏçwvl0lÿŽËNŸÈ²û¼‰r'TÈy˜´í«•G¢õ%_ýô<Úü^líl§¶€@#2æfõìkqÐÙE#|ûûL»ëÖ›ÒDZ¬ôqdáìq÷$Ë9Ï«<]$ yèw_‰eõ <*=í´³³Åöþ¤ÂÉ®>2’cqÐÙE£}ëÛÖYª][¹’ü’™³"’çXGË®ô[ ±UË{<ñôšþR?´9¾x"|ßR·`N)šâ³p&MñRò´<¡<-Ï{ÀYoÚXóŒ9Í1yY'‘ÕœC)·*ÿ„†Ø\ÉçQn»úª¥è)oõ>4‘:“Zç:V¿ÛëQïõÐÄœáþéiù½l˜ÁRø’h¿2О}Áïh…³ÐèÌR~tf)ïü쫚kqîçZ^æâ}pà÷DË-«Å ÓÛYÈ J|”—zeä1P–M¹ÃVŠ †4“•ÒMvʰä Ó󋢌ò¾ 3aBVY`|á óˆ©¶e'WÇû˜3 ~Ô%V¯ê†¡tù_O<é½ò*þ¡#’ ÝÞÁ@212ÍapÆP>Ç’Ñ«e4,½–^Èræ*[v˜Ü<Ϙ1 1pá­rºUÓ¡r¸T§*º•å-·5$††bÞŒ,ÌL¶l:©`R€c=Û{›ÃÔœAþo;·ºîßf³µ”åfžHi]zS‚Ú$5ÙßjÏ“ÕNwi·#àöŽfTz\4.kˆ £°O¼T14½ˆ¯H/ä÷uÖ™v7¼jr óTæ¿ßè½=»:Ú`ãÌ42£„ i¼ñòš.?/¬„¾Y/€Šô¾"½ \þ‚@}SÞTA¾¬úFž;ò…ÁŽüpãˆÀÞÏ> †àKìRôN m™Âüâ1¾&_·­ícS]@à23&Ân)!»%âm€‰Ê™ª­Él{H6ýà¤Î+Ðá÷ ÅïÆ^W™`¦á™ãrz{ë ÀðŒb~xF1 Õîfî`ûg¦=Þv.ÍZ «¥&S6Ìæ,XLéd1eÂjJGÄA!ݷ΢&·,ê¹ø}¹ßñ Ý+LJ$ iÅ„AD'Ÿ/B¬ŒL†¯ôŒ¼$Ç+rÏ÷ÂÍûàâýäæ}¬Õï¡v¿…¶¡ÜQÌOÌ-Dúè¾SŸ‚H<ýÊ@g p)\~èïèiÖѾ֑$Ó1_öGOY}çÇZyô=ýZ3èäüŒ€_àqÄ}€kqíâZy/óðàuЦ¶¢ïeLåäµmñÒ_«L²ñdW¡"g¡Lseštbþ@ÁÎYºûöØke†8 hˆ£ -~'s:™‹ofN_NÞÏÚx?só>¦äœŠ¿M)(­'Î'1^Úò¯®Ë131i&+9LVr˜l”fJC±-›†¥Û)Û"úÂ,ë‹} ö®~ô+=ˆ¾Y¨ 0` ïÃÂqš^(0–5áèss-Ê…zGKQzÂèÓSx 0`À€G%Œ=è ƒÐçb0`À€ 8Öaœâ’"/N0`À€ 0:{Іøè0 0`À€RÃ@O†Ý€ 0`À@ªÁRøî¸ÚÁÌ 0`À€ èaô+:/؆݀ 0`À@ªAd ¤ŠW¿2ÐÛ;úúÇ 0`À€ }ð ŽÎTñêWz“ox»áA7`À€ 0jø„ÌŽTñêWº›Ïõñ°xLðÙ{[ 0`À€ÇÜBN{ªxõ+ü‚£ÝÌy Ý€ 0`À@Êàæó z¢ð ™íi\sQoËa gAܼNÞÇ\àÿNž‡G flz "´ý‹À:ËÕxaC/z¿6õ½2ë}ô/Ä×wß«#ZÑ7êRªõga&8ÌJ7Yá0Eþsìè-©þ g Ðð 'Šÿ€Ú\ËÁ½-‡îGgÀ‡®6nËÉZý.f1¥ÃbÊ"³9‡™Lyd²äÀÌ9€(ƒ4ˆ¨û8ýc$8Ú ¥.#7x¯­‹•<Ѽ)üLN ï'& “">|X¬LD$Ñ•¶t¤:e²”ri2 •ø‰üÄDª‡ïغ£&·/yÈ¥)O#aqt%æ'WOI*æ-—ªø·–w}´ä¯‹.FŒè)¤òDRYµú'_OÕ!nûbäÓ×äeФ+ :eÞñuªµÞ'+ƒ~Šébó£^/bÓÔ"§œ®äÛ"Á'è ¸Qëï@'€3@'€Ãd¦!Ž\ªpäRž5­oÌN ¨¢Ú3¹.U¼ú¾ÏyúöÁŽ'õ¶º-~ÛÕÙÊö¹; ËpL¤Ì¼ (´ ¤¨‚bMÅ…ES‰ûG±£Õ@Wê¨Õ t=tRcUÞP•DJô”È ¦œN¬ J_ì=‹*ù4õ>âºÍ‹$¼•xIãÉÑIyiË·rýS7ÐåøÉèò/© $O«¬P Ó7I7¾”dÐ:áH$?ú' ‘öËK ¥S4&IœoåÉŸ:ܽ–|kÑ©R?©Å@W6¢©#úyÇ£k øÙ^gû¾éœ|ƒ9°ô<*±eÆz„+PPSí9¾9Uüú¾³sþ‘“òŸè°p®ÌÞ–Å@êÐðayK=Wãã¹ì¬ÙTR<sE5­Þžž‹G£µï›rÚôòM4\+MwðO›’ã©E‡‰ðK]²qzÉȘlþR­ŸTò멲ëku¤¯ÉÓ÷m¶`JvNÈÎ…Gà±ßåäV7ÇL4#o P`uJìC¨ñ·-•üúNà¨Ñ7j{™}ýôÞ–Å@òð¬jmàöºœ\~ÎÙTQ45ªÃRó‚@5L~_J£ìAŠ<—ß~+ƒša Ókn‡&(’-'*ƒ£Ö-Ù´ÉßÛ› y´¤ŸŸ>=«Õ+ý`¢ÿúó­ï(Y}zz“- iÑÇQ:qJT×É@œŸTJ Î)Ü'–Nò\´@[ †­©;gÂØŒ,ŒÍÈÂw'[Ò°ß”gM£é¹eB¶Ùf(¯`GÇy[Sɯßè°Ïyòö2û:Ã@?ʱ¹½‰[ÓÖÄådÍ¡¡çc–®¹%U©‰ZX"Hõ€­%½DÃõzÃÍW*½Ç©ô’GÓ«MôòRã§—g2ÐZ{ËkßsF¢4;Œž”±/ê£/ÊÔwP‘–ŽŠ²tìèlg×î6 uä3sËã¥ÒÞƒ‹Ï­?äžÕJžýÒ@ßÖñ£ý'æ>ã´pîŒÞ–Å€~DXÜTËÕñélÈÀ_“‰K gŸ¨|˜ô{¬Õ9j‘Q.íˆ×R?þËOÒ4˜lÎâ{&µnRŸò¨íCÕm^b5]éMOž_¼ô•öÿ’D”îÛ6¤?ÏêF³úªDbN,VªVAR3ÑèÓèØÝþ¢¯| Ã3 ÀØŒ,ŒJÏÀÒ–&îãÚ=ììâ¡‚3õ¶hý5žÉ[RÍ“K5ã² ;;Ïù*Øðëhº\¼ïÔ2µ˜F°%7…s1¢ãhyžê8Py®7M5jáZÒWã/\«lÉÈ¡Éê*šFošZéºCZy'*COÅ ÅK=YÏ´ 'Ê»»Ñ—ôB_“§ïÀÄNÉ+ÀøÌtö^õ.S“ÏÍz{œîo—_°w.mþ¿e‹L3ú¥–6ß³fxú’SSqoËb@|nöI} —›{ådL‰ uÜR/‹ød’hˆJ9LÊKžJü+žW[îµ-òoyhÛÛÏã§Gn}2i¥Ó³×9Zßê9“ò¯zèKSŸÉœ‘"‰xi“HJ¾¾%’×Tx¡•û‚ÐSuÉRiªµédùÊãh÷3Ç—ß0Ò•0># ù Öïçfç• CY†²z;;Ï]Ü(ö¦šo¿ô Oamëµ {{æe\Ú®&Ÿ›}T_Ã]GÙ'(–kß@O÷‹!=)…i‰«F§…&QÄ–uOÉ 'Íx|RîÔwl:=§'ã…â&‹T”Cw•¥Övs´¤“(ú¢L½‡R›?.)Ū–jnŸ«Íð¤÷Àåâój—6ß½V{)iG¿5Ð`CûO÷6û†í Æa\}óò <>®¯áJ ¯¦4[¹rª ÄK-ŒÅ¡“‹§'m5úDxi‘Uo~´Ð&£¯î’])ˆ’‰§Ä'ÙøÉðã×—‘ê9ìCºJJ“â„)A-¬'Oþxr’ÒóDÒHzeH¦¼ôæCKzZ»Cw©Ô[C*u’*Ý]Oº}±žõ5yz™f3Î-,ÄÂúƒœ›÷£·Çñcõª÷ŽÚ²±ý'ûô”ôÛ=è!vÏhZÙzÓ›BŠ£ IDAT³rŸ¾ ~?aékXÜTÏÈ>žådN¥P'ý¥ËhD<-Ju¼ÏžKyÅ9µDåP+Šù¯=MíqB÷rRˆÃby«iBÏI3‘8ñtŸ‡²<‰òH|Ost<’¹SŽM­–ºRÙ'fjˆSÒÃ!ROc¥Uæ¢E‰Å“ç• L¹ÞƧJ-XWJÝuî‹ršG/´ÉoéÑ(µÙqbNÔ2]TRaÁ˜b8…UÖýëÝ™†aXÝzýέ?XÐÛ³1ã_[:ZY ŸÅŠò~§çUÀ•Â(°XµòÓµxÝ1ðÄ“5ý(Ñ$£-ñõÈVt¿Të4ÝkåŸh§ª^ô4’•»'ЗeëIôõrêŒÏÈD©ÍŠïZjYoçÇÒåÒÛ>¨ûç«.>ß§·LôÀ0лðeãVqÏXÖÛû™Œ+x+ÚZYIáåë?ÑãñKÑ2¥"½DxÅ£ÕVÞzeÐ#‡V$"{O#Q¹’‰—ª:›,ŸT!ÕeÛ]u¥'ë`oÖù¾ÞæÔp4ô} ssópÐÕÁÚü¾^×…K ‹ç‹†?¾ÒàÝ¡»0t¢ßoq‰ÆûuÏ.¼¼ô¢Üëî±½-KÇê¶f–•1 fS¤ž%ïH<ïc"a‰€ «·åé¯pñ<¶:]¬¢ì4+‘ÁFé¹øw,äŸ3q¤¦©r_ïK—ZÓ”¦¿æ­¼³XÏÉêѼµõïzt¥Ä_-†ºìj墅{w¤§”D\½é¨§¢^O•xªñÒO_ªíž÷ÊJÓKÔàWCªëèьôMŒIÏÀš¶6VëuÑ›ÜÇý ă_pt~Óüëÿnî¸ø`O¥ilq‰A€l»µ/.XÓö³·2§»ôÂõ}k+ËË>ƒ8Φ­ÐT¡äA¡ÂbeM6N,Þ°x<E<žzu 5¾hÕ©y O$M­èIj•G+^y´¶-¦ÚWibïMT¤¥á›æf°|ÃߨŸÞ±Ãyî’%M÷®î-Ã<Ã@׈Cî™M¯¹ßk„cñ7'å>yf¡u—qÒKŠá´(°@ª;KµŽº;!½¯`2¨}¸D‰_ü­jZT–P-–¶0õ³^”éR-u'UõKy;‰4…î3Rñfòçóè¯÷‰‰ÇI|Û†Ö‰N|E¢Û’´AI\ŠÞ1„S}þÔÑ+Ç!ÏbA׃[*Þñ:v »k·óÌo7=°Ü-äø{[À0ÐucëôÚ=®Ó_«Hû¾`\ÆcËlFg[Ž aŒ=IbŸË‰ ǘ¨'Z½®ñž'v´#]Å ïI¸'dÐê1N6­è‰}Ç©¦5 Éz’ûJ9ÒI}Y¶ä02Ý=®NVb³»™ÔŸÞÙàµó {ÖÎ í—ïvòÝúá!½0 ôqÀ=»ñ€{öÒÿgï¾ãã(Îÿf¶\—tÒ©Y’e¹w0`lšé-@b„H©$áKê/’oI¾$”  C(NÀôb°)îU.²Õeu]¿Ýùýqêw'K¶lµçýzùåÓÞîÜÜÞÞÞ³³ÏÌx×£ÖØó¬˜2Á±fFºZQ`ãÁ4•EKã8 ‚í!®´ùHöã•Ørsxä@;c^ têõŽôŒ8ðN¢/ûPﹿIÇ‘·þ÷¿ü£³M÷»‡WË#Ý¿Ãá¤3êÊá×­ÿ)/GÓpÙ·Ã¥d`&;]øWm –z3ÇÔG(Á­˜pù#"½¥.6s÷®ày;·.®’àÃöB…ôAà7ó#ï6ß¶åÝæÛ¶t,ÓyPñi»=™Ú>OºZå±+-Ê]ï[yäÞ‹s}§¶BkÿÂôx&Ë«Nö›Ñs½d9·Éò”»ÝNf]ë:ÿ;Y¾t¯\r–ì5S¼Ÿ$uMºOXÿëÐÕZœ<·»ã©Teõµú¨§ì~ì§^/Õ1IÞOÒòúÚ÷=ËOZ‡”ï;Õ>í}±ØëõXêϵ¯}šª½Ï©’¢úÚ e³Äõ:ƒC|®IS–l½ÔuHVç„õXê÷ÔßïJ÷Ýí;ÝûµS#z×/a=v¨ï~Ÿk²²{|§×Kþ¾û¨ëYV?Ë–}§,y†¾>Ù­ü$ç&{¼ïþìÓäuH|ß©Ö;ôãÞ‹nU8òæ—ä¿[ýÑçë¥,Ó­(ˆn|غüe9ÌÃ{íáÏ’š°r-F‘¿Ñ˜ÜÖd”„†ºNEúQ.«::¿¥::¿e¨ë2R0~ï…9®…užOS>©~ø/€z_©~Púø‘î#@O(¤ |°ä?<¬×6ÝþH¶^ï×<Œ=y@Ñõ_Ê`²¯ 3õ¾’)Ÿž»§ûv)öU²ý?ÒHµ¯ëšì5»"½Ë–èYVu7@gÉÖë;@Oý~’]Ìt–Í’Õ½ã8 ÇÈà詾ŸÝ×ë¹;¶ïc Ë¾Ž¹è,Ù¾êØNöئ×z)ß7O]‡>ôÞj“ï«Äº¦>Ÿ²Alÿ/zx窽ë#“,O¶^Âc–z›>Ü>n±-®=Ï© L­ï°>õ³ INK]Û¥Ú€ƒÛŒwšÎÚ,k8dÉ¡¼i2L”ÛsiiB!d´ó¨*JÓ†º¤oÔ‚N†‰-iLÉ=§ 9Rœ3qx·¼¦áZ/ݦ±Ë¿´ÔÆügŇѶæàð«$!£D† 8@ú0G:&ö¤q%‹ZÐÉ1qÕõsKÏ*±'>Ó;u Ñæõ5ƃ÷~è¾lé²I¶³.˜jŸ³`œîÍrrMSXÃÁ U[Õj•n;h>ýè'¡šÊÖÇ·7ËÅö‡‹Ò{—‹™²¾Æ/ª+Z¬÷_/–ïk<ìïÅÄi9ÊU7âœ4-WÍ)HWœ.ó·†DkSX–n«67­+‹}øÖ.£¹!8¤#Û®ýÖ¹xÿ•ÍF[s𨟠TMÃicB„üaº cF†¡2úát2L”§qÅGç rLL˜äU?©à°Ž„Îãt\QºòË{ÏϘ1'O뾎i ä乕œ<·2waþ™«ç9ßúï®ðÝ?^í¶o¯Û¶à¤ñz_¯õåï,qÿwå–ð½¿X0cëÏuËÎv]qý‰NÆ».7bQS¦e8yZ†E³”³.žc{äÞ7ƒOüõÝð€ _°PûÖo¯s¶4øå—ßÑ6Ôõ!äXq+ ÓyeV”æ+Ö(@'ÃD@gÜq˜½Ù ˜çŸÚúäêö1o»® ǧ)·|÷ä4øÇŸvm¯7zw­¯ X0qJ–úÿ¼"3#ÓÁൗw…ßz¥4Rºý Y[å· ‹ÓÕYóò´ëç^2ÃqöÅ3÷ýö@´[€ßaÅß> nú¸"š®ðÂb¯rÁgçØ‹&dª-ŸçˆF ùç_½ìïû;÷²¹¶Ï~q‘>zwoì©ׄK·Ö˜áPT¦e8XAq¦rÒéSôs.›G³•2Æèœƒ#”ä"N(@'„Œ9»¶7˜»¶7˜@ÏÑ$fÎÍél ßôiulÍÛû£ÉFqq85öëÿ»0##ÓÁþ¨øíOßh{}Õ®H÷õ*ö7[•û›¬W_Øyá©ÍáÛuvÊœÏÒíuÆoíí¸``ðôß? ý鉫½³+Ð.¿öxçó©ÜßÔ¯ÔŽà|óÇå±ݼ¢­{Þy[KXú[ªÌ›*ÍÇþüV(Ýë¤ cBf(@'„úìæ9Ç—xUøÛŸ> ¼¾jW¤¯õ·¬¯2n¸ä±Æ¼†e <úç÷ƒÿû÷+3¦ÎÊSû ë6•OÎVà½×vÆúê*¥DKSê™\á˜:+_8#_7Þ«ÔV4Y{·×X»·W›–yèk¯ÏÃ'ÏÌW&LÍUÒ3ݬ±®Mî/­17¯ÛkÊvVMÏt³‚’l꫚EcmsÒ¼.V2£@)™Q 8=V¾«Ê*Ý|Àª¯jJ¸‘?m~‰’_œÍ@Q9¦/˜¤´ï0«eS]sífž8U)žZ dåg23fÀßå¥UÖÎõ»­¦BÈ¡P€N!tÅ5sPSÕf=÷äæ~M€a™ñxo ÍÕûJë;#¿Â ^¥?Ûèº U ;arv¿¶If\q¦ò£{>çž:» áwb϶jó®ï>¨,kH¥+*ÇÕ·œå¸ò+§95± mÍAqËEhknhëWlñÔ|ågÞäöågð­ï3}ËÃtc¸äú¥¶/Üv‘]·i=v¯e ¬¼uäéûþV×Kýòño»U-^/O†‹Ý¹â6w÷íž¼ç…Ès¼€I³‹•¯ÿæ:GÑ”qI÷e4•×/üf@Z”ÐK< BÈg(¹ùV¿¸3lGoÀ‘œðj¤dz¡râ²¹Z$•/?öV¬ã9`çú}&¸Òœìöû¾âÌÊóòúªF±mÝ.kß¶r+ŠÈÌÜ >uþDeÞ©³TÆ’Í‹I!‡tB€ésºòÔ«*Zêp€ç_>·³#×þÝ ýΣø×ÃkC·üðl·'ÝÁùï×¼{¶×šoýg[týûŒ½;ëLaöëßôý³]—Îj*š¬Û¯¤­®ªEñ|ýç]ùÍ£7¦Ÿ©Ü|Çù®ÿ¹îá# |îËKíÓæ©±¨!û½§ƒkVoõžQ3;?ƒZB‡Œiϼt¡þÍ;—»TMÁʇތ<ú¿/‡ãõˆ[ráqÚIËæhÑpLþô‹ ìÚPÖùy¼þÌxëß¿üÇ×ÝK.:^eŚ؎Oö˜ðôŸÿ=íÒµx€Ê{_ŠtÔ­{]gž0EÉÊórË´ð‹þª-¯½×KÏt3A­ç„AF3‰BÈx3çÍ꣠sÎPXìU¾ùãeîK®šï€­ë+í«ú ¯|ì£ðÊÇ> uäŸOž™§Þô½³\}ç×}/ë§þœçô f%âqú¼Bõ„¥Stxäž7BÁy‡ºªñðÿ¾€y‹&jóOšÔyÁ’‘åfW}õt¼ôøÚÈšÕÛbH¢¾¦ED±>ô+¿¶Ìþ»¯rq…á¯?_êÎ;(*Ço¿ÔOüñ?‘]ö'|;>Ýk¾ö¯£ð™/9àkò‹ã)BUûj­®à¼§Ö&¿”)gV'„ÃC-è„26»ÒÙÈÚÖ:8# ÿè·füð7HPTÎx·±ËÖ´Y¿úÞKm™TJ‰ûî\|í…ÍÑs?3×~Â’IzaI–.-=w†mé¹3l,_ûÍíÏšêïcÚœxÎyksH¼ûÊÖ¤öšÕÛc-‘‘åæSæ(×î5`ÒŒ|Õfç?ýàÛ}vœMEQ8¾þËÏ:Ï[¾È Çäïn{<øÑ›[fÎïãYyÖügCÊYˆ7¼¿Ã<ïêSm…“òœ_WÑ `üÔåÒ/Ÿ£¿þ¯÷`Û¡[þ !äHQ€N!Ð=(Ï+ð({v<â!<Ú;u2B¢ñ`@ÔTµZï¿V}öŸ„˜%ÙÀú—J·Ö˜»·Ö½>7Ÿ>7_]tÚméy3lé^'_pÊDý¿»ÂýýëLS7>S€ºªf‘ê¢@J‰ÊýVF–›çev¾Eã#£´5‡„¿),É|÷wŸwMœ1Nim ˆŸõïÝ›$½KQP’£ñŽ _ûÅ•Žöš%¬çtÛädr®p«ÿ7=>yk‹Q¶½Â*™Y¤|áûWدüÖ¥öÍk¶›[>Üi~òÆFó`eå¶BŽ Ð !dj«Ú:#¼‚¢ôÃ%¥»;o_Õúö+»¢`š,S´Gãƒ×XÛܾYûðÍÒØ#x3ô‹¿^™6{áxí¸“Kô§LÔ6|°Ï€¬œxÇÔ–ÆPŸÁgs½_@v^zgʯýqS}ÛaW<¯(3ä·„d}usÊrr‹²âÃ$*ÇŸ>SKµ^EUàtÛY µßó=Á2-üô ÷¯úÖE¶%—,ÒÓ¼n¶ðŒ¹êÂ3æª7üp9voÜg=rçS‘=›Ë(P'„ * Ð !d¶l¨1,K@Q8¦ÏÎ=d`ئ)d4b¶£ò¨OÔÖ–úÙ¾ôU/Lšž§vèMíwš×Ñg%¯ÏÍ ñ`×P‰ uñÇYžÃ~¿ùÖãÁ[þße΢I9ÊoWÜêþáµ 4Ô¶$ÀÍõñ× "ò†S~Ò~ +žï9®Jü±5$x'"ŒÈGýLä±ß<vÜ$eÎÉÓÕÅž LÌãSæOT~þø÷]w\þ«`åžj Ò !ƒ†:‰BÈ„‚†ütmeκ`ª£¨8cPZÑµŠ²«£ocZ†£3h­.o¶ ¯ÀÛçïCþøxN{ME×D@û¶è.Þ‘Z2P«›ÅíWßç¯Ø{ÐÊïSî^ñ O^{kywJkOaÉÊË`±¨!õ¯Gío>a𨤨ùéë™ÿ{9úíóøáò»‚FÔ€fÓpþug%ípK!‡‹tB Çîÿ8ÄsÇoýŸ¥žî:SYzö[Zúá­GäéyjGpºoW]gÚNéÖøh1Y.¾èô©Iïœtæt½#¦tKegþ¾5fG |ÅKìɶí¦z¿øÁµñ—í¬¶rÆyùÝO~ÃSP’Óã÷ªz½ÕÖŸõü«x„–Ö¦€€4¯›ÎÅÄžMeÖ'on2 §Ð7l>WBÈè@:!„ Ðúu•±gþ±1K—M²ÿåÉå™yiI[Ò3}.þ«?_’þëû.ÍÐíêQä<éöðË_ñ^tÕ»nKþzé^'»í΋Ý`¶o¨èeû† sÇûbpãwÏvv¤²tÈÌöð/ÿ\'lûô€±áƒ½Û67ä³{/—ñTû‚Å““ø^Ÿ‡uŒö’JKc@þàºûý¥[ÊÍÌœ4~÷_÷L˜–ß¹Mã¿{1 ]·ÔvÊyóS¦åùøÌã'÷H鬝޷ü3Î0yNqÒÏnö¢©ªo\fÒßI‡ÛÁfŸ4]€ª½5”ÞBT”ƒN!‡áOw½çw¸4vѳœsŒÓŸXuoÏÎz£lw£Y]Ùjg¨Sfd«%S|ª®+LJàXMhS25[½íx¾ôÓ];7U›UåMVME³åòØØø‰>uñ²izG€|ÿoVêª[E÷hùÁ߮ݻâKZÉÔ\õ¾•_K_ýü†È=­âIÙÊ9—/°ûòÒx,jÈîúO¨÷k?õÀÛáÅçÌÒ‹§ä*¿zøFÏû¯níÜpÀl¨kÙùé|úüñêIgÎÔo>ïw-u•M}v& ´†ä¯¿?ð³‡nrÏ\X¢Þù[P«K¡ºÛ°Ä,;}õxÌú\²Û3Ý;“õ£ì¤DŠÀ>¶—Ý;öýšÝëÊ:%®ÇXÇÓí')õÚ&Ùz¬×¾Jùúµ¯ç}÷ú»¯mzìyÙk_%û”ãxgÌÄu$„`-©–`º cHüD Ã~uÇ[±?¨’7k‘£`|:Ÿ³`œ>gÁ¸ùȦ)ðê ;cÿ|àãhC}LcPHf ¥óûoY\2ÞÚÍz¼ZªNïµý9Öu,†Ã¯½°Õ8þÔ‰ª7ËÉ>9i~tÕ&ñôCFWýkƒ”nëHìÞv·\þ·Ðï¹Ü1iz.¿úk§9»o[¶«Nüú¶gÃvä ªÞµ'%¢‰o\~èÆïŸc»ô 'éKΛ£/9oN:´6e4,4) Rv-/%W¥TxÇ{,ö“/þ-ú“ûo`óO™¢üò‘¯z~vÓÑö @â7ÿ=vÑË/|ç|mÁÒÚ‚¥3ZÒ›êZåÞmURÑÀºŽ«?Ýñ¤qÓO®ÀìES”ióK”ióKâyõåìÓww°†ÚV&„À¼SgªóN™ð{Y]V'úù cÿÎjÎÐUç®ýËnÇ÷@¿_ÉÎIñe2á»ËìœÐ»¼”¯Ùëw8çÉ„õR¾o™²ÖǾêú'¾fêz1°Ôç9™úB:e‰,uçã¾o›õÕ)<õS©÷UÇ{K¾U×Jžt³ú¬Úè „Ûÿc0‡Ç:ŠØ`Í€ÆXæW€7ÓùƒRÞ( `€"Är’Ò½[Ú.Í}­Ù~féqòìñÃ’+É—õ>Q÷'PMЦúúñë5Í9ëYne3ÙkY÷²z×I–% ÞS,Oö\²Q/’ý°÷±K¾N|½þìÓþÕ!aŸöz.õqÐ÷q#át©˜9ÇÇ€ÒM¢­5 0ÉuŒ0LácóOÈcÙ9.¦ÛTU´ÉÊý-²t{ƒl¬%¼]W0{A€²ÒFÙÒî,/ÙûfH ºê º-ë¼pÃä9lòôlæËu#3ÛÅü­ay°ÚšŠ¹é£r)º‰½? UU0cþ86qZ7Þ‹ÚŠfìÝY+wmª–†Ñ{ø÷n íeäe°É3óQ25‡¹Ü64ÔµaßÎZ¹iÝ~)-ÑùZŠÊ0káxÆìÚT)£#a?¨š‚™ Š  Ű{K¥ì¾Ž'ÉI³Æ±¢I9,|&‚ma4×û±oG•,Ý\.¥”IŽÓxÙN·¾ü –žéÔ–7Ȇšf€ÝiÃŒXáÄdæ¤3ESP_Õ(+öÔbˇ¥RJ‘"î},¦ú\“}WÌ®J';ô¾í^VÇó©¾ûÉ¿Oíƒò÷x†õ:Sÿz¿ïäç²$ï»W#KG=’§ÉÊî½<õ¾býþ|z—ü}ó„}ÜÏó)ëÞГ𾓔Õó¹eõX?Ùç_¶/‹O FÄʆ„BF?‰xÞ  À~Ǿ7šÀ#oJ)7¦ZƒôÁ£xP^ Èè!—îú Ýž~è=êÕQèÉËJöÜáèI÷s?ôäõ”}Úmè‰õLRv·»/ɘÄ×K<ö.#õûIR‡ÎÿQפï)Åw€õ|%è}•Ýñ~’•×} Г¯Gz겡 ÐwXÕp¾?&mJ(dl 8€x°^$9ÙºCè”ârä3,D<}…–KXCpK!„Œ!~KÀ“ŽxäQÄxºÏ,~(Eb+Ä1Eúác¦8ñrD -i5önX&„BÈ j6T)e¡uè5Ç$€³`€²¡ª臧ÀÞ¡®ÈèQdIQO:!„rµ˜L B&€óÔx@ý±®"2p \ ΙWH B9Šü¦PH).ý“ àrÓõ S zÿi–(ꊌ^š„ŒŒfÍ&„Bކ P@-èý§8@€÷qL:‘R zy|œe³bflöÅA_ IDAT “„BÈQ°,XÒ)´!í9BÍð£A(:4/€+d uEF;).šá5tLB!GÁîP–¼$a`ÒoyˆÇ„G=H§`¨ov œ‹câò°YGÇ$!„rlõ3aÈ«ƒC]Îàå4q †RSïÁ›6Ô;2P`ZÆ>:. !„AdH‰Ãpjt¨ë2 ä Þ/ñ¨¡@(µ3äu%Æ)/ ‘÷é¸$„BQY(àôpâ\Öä0MpòÑ*œ¡äæ˜:Ô•“äòz“£NÒ„BȘðq›Qqc`¨ë1Ê`òÑ(˜ôDv'u%Æ®"KŠ%áXðEe¨kB!„Œ•‘bÙ&pad¨ë2 ‚£Nz¢AB‡””¿j‰øŸâRRGsB!äH½Òñצ¡®Ç(å0o° ¥½§L³†º$ß’òó¨ÿ)jE'„BŽÀ®`kN XBCžƒY è=ê=1<ȵƂ«¹´éó „Bƒo4EETü¥q¨ë2Êiäôh лä?Ô• ҤĭÁæ»ê0J!„ Ü›M!‡€éæP×e ˜‰xºË  ½Ë¤¡®éEÞêÆ”H¸å/”êB!„ À–@Hn øŒ˜xrÏ †øÐ‹ƒ‚ô.ƒ¶SÉà‘òoMFx¿ˆ†þCÇ*!„Ò5ÑÞh"*Vt9ÔõC(@dÙˆOÝJ†]JùâÁHëSÜŒm¡ã•BéCÀ2±².$¢bUg u}Ƙ| RgQ xâ¨õ|XËR®<lºK±ŒtÌB!IøMOT·Š°u_#°ÀêúŒA @É`DÁNèÃÞ|CŠ•¿Vb¡7è¸%„Bº©ŽFðXu‹h3ÿÒ qM$2t%¦ô™F À;Ô• ýqBLŠ55áÖKs,c¿âLÿ¢E£bBë¶øòí&!¢â:`>µœ­ÌÁ(„Z"çPW€ D¾%Åûµ± ó7üT•"8Ô"„B†„%%^oh•o7eQ±¹š‚óaÁ…Ah=¤}Ǭ$ÇŠ.¥|¼ÁŠ]ÒÖZw³ <«@Ò¯„BÆŽ¬8(¶/DÅG5€& ¡£(èñ+2Iy›_ŠO«#m‘HkÝ—µhè ÐhR„BF¯òp«l¯7έkbâá¦øD–d9âÆ_ÊA§}„ËR>Ð$­=m¡–Û½aÿJ›Ý}¾¥ÛK®dR´N!dÄ‹ ²pŸ´š¢ÙÈ3£bE#pJl¨ëER:âØ’t ÐG‰É¦”ÏÕKs½j}Ìnû¾“stÇIRw,ŠVLÁ:!„ÃoØ Ë!š g‡ qC8;2Ôu#‡Dú  iäG•ä‚f)ÿØl‰j8ð¬+ü?'P¡1î“ Ï’\É’\õBQ3Á˜§½'Gªø½k9K²K²^ò¿{.ïÝ{„AöÑ¥D¦Ü.uúó~’ý}è²’­×^K¾Nêò’Û}yêº&ÿœúJ}êZ?Å~f¦Ú}#½ëìxK½]?‘dYâë¤>ŽdÇr÷דƒP×TŸqòϵ?ÇRBÙ,õ±ÄŠì«ÛHêxL~é«®ÝËî{›Û±dßŸŽ²eBõ»êÕŸïz¯zʾŽÓÔÇLßÇHÇ ë¾^²ò?Ÿ¾«>÷U’ºê¼tÿ®uÇqŒ$+“ˆ €)d³Á¥ß”X¦Œˆ2]˜ò³!K^QkùÈrı%èd›nBþ¸UÊ·RÔ(U P©U X¥ÊX¥2òÖûîî=ôõ¸‘Xç;ünú©ÃŒ¡7°Ïn8ÕüðŒÜcµÿû~8½Çþ×ed[‡~_ÉÞ ¤W˜r‚ XñE&0΢¼ò±t2FptüNŒ/’ DCf˜TƒB!ÃâB!„BÈ0Bú ` PU@ÓFØ9B!„2ìP€~„Î<Ó´·¶ú‹›šüÅ>”é] !„BÈØEúJOïj5畆>"„B!G„ô#tð ës)¢¾~„B!äÈP€~„víâ†eÅÏš%ô¡­ !„Bé(@?B L¼û®€3Ï´ì M{D!„BŽƒ>~ð{óo„lóæY¶;ïŒfÜq‡­e¨ëD!„DN5ÈJ<»µ ½™§ë-<]oáiZ+O×[·êg LvÌ›+&%8¤Œ/CÇÿ`íËãët<ß>¯¨äèZÖ1/—Ç·ƒƒè\‡C">µ´”¬³Œîóø¶/ëZ§ãùÎ×èQ¶„l/¿çòÎõ$˜L¶¼kÝxÝD{Y=êÓ>y­è±¯Ú÷ìøŸ#*4±l"bÙdüŸ.#B—~Ã)"–Òƒ“ }lßÎk®±×?ôPÄwË-±ôyó,Û]wÙZÞ{O‰—‰p!„±ˆ3›õ…¾mó2?µMNÛ©s& ¤‚xÐÊÛãö`V*ÝÚö [ònA:ï ¸)%:¶ç:){ÇÝ‚ïÎ`Ö’ï4sÙ=@¯Ç!â¯%»êÕ,Çë&:¶k_¿# ’£ó"¢[ ß¾Rv®}Ht]TÈîÛñÎÀ¼ã‚Ct{íÛŠî#Ýz!»]„€£5æu‘L³.’i5D3¬ø¾  ‚§ž gû|’·´0‘-•Å‹-ûË/‡ò ²¶–[2˲úî@Z^ÎÍo´7«:B!£ƒÄiù¯:NÏÕ1;sƒÍ¥¹]-Ø’¦-6Ò´ ÷hA}’§ ¦Pdc4ݪû¬òP®!Æp°Nú X°À²ååÉ„ìsM+*jQÑ¡÷³×+£S;B!dlàÌÂ9/:—O|ÄSè:  ©Ä[¼) 8,ÛÞ¢øl­ÊTO…¶7P`ìæVbˆ5êQ€N!„Mã1vaÑ3ÎÏM|Ô“c¯U:RGÈÈeS 6#ý€>Ñ]­íæûƒy¦!Ô1“8Lú X¶ÌY£ªGv&ˆÅؘ9è!„Ár|öû¶ÿ™û£ÌLÛA¥#?›Œ:7ÙO¥>ÁU«íh+ŽU†²Í¡®Ó±@ú (/çÖP×BKfáæéÿ›ö¹‰Oc£†ÑJå›^¦ûl­ÊÖ–’˜)•QݰI:!„BF”qÎråÿ-üfÖ´ô­:º*BF¿\{“’æ Ù7·NŒ¶ÄÜâÐ[ŒLtˆB!#ÆãV9^zQÞôôÍ4{÷eW¢ìxo©½ÄU« u]ŽjA?JtlölKóù$w»Á««™¹v­êzB!#Õ™ã^vütÁ·²8Ôj>Æ1&1ÉS­q&°70nÔ„Gú ;÷\Óþõ¯ÇÒN:ɲÛl]G_yE -_î¨ï¾îí·ÇÒæÏ·t!€/Ùщô=V:!„2V”ó¶í' ¾•¥0‹Æ1'&¸j5C¨²<”3ª:R€>HœNÉî¿?’õ™Ï˜®þnsð ³.º(¾þyç™Áÿ[ ½B!#ÓÜÌõ;O¸Ù§ñ“£¼s ¸Éž*Ý*j™£&H§ôAÀ9ðøã‘ìîÁyY7^yE íÞÍSÞvyúi-ÔÖÆ\z©á<u%„BF’)éÛ´ß-º.Û®„(f!)MO+ײm­£fF#:ØÁM7ÅÜË–™¨ªbæõ×;Λçª^¾ÜQÿé§J4Õvá0ä+¯¨!X¶Ì¢BéFçQö›¾äóh­¯>1H6+}¿nWb£"ÿ‰øAðÍoé 1yÙe΃Ï?ßÿT•ÒÒx {zºä4»!„Òá‹ÓîñŒs t\Ò/œ 6ÕS9*F÷¡€ðMœ(Ô¢"¡À_þ¢µîÜ™:¥%™}ûXg¾Ta¡5·f!„#QäÞ§^3ù¯iC]2²øl­Šo¤ºP€~„&NWö<ða++yg€^P FüE!„ †ïϽë+Ñcš®Ð‹±Ê`ŒQ7jßQñFíï]ϕߖ¶Z‡UZÉTO¥ÆÙȞÈô#äruõÔÐÀ¬n_P ;üh”QÏtB!cÞ²‚;å¼m?Ö¯ûÔ¾}Ê·×­Ñ^«®8â3 àŸ{×hÏøxÔN¦ÓfÔ*ͱrEHsXèv%Æ'ŽðIŒ(@?B ]—hS§Š Ó§[ÛÔÕðË=B!d\7åO#>µEJ‰÷êv©Õï¡»ãC ÐY¯*#8¬¢ýíØÁ «½Ý|ÉË6m9.¼Ðt@K »v ,BmŠÜûÔéÞMú=%¤7ÎÉÃ.RÏè#ÔÔÄÄÆJtáBËv商硇´À§Ÿö/ý«_yfÏ:¼ù¦#÷BB==솮Xe(ÈŠ]Ògw˶£µ7Çb(p¦Ëã}ã-•w…Tõ?;lá64îW$$8ìŠ.§¥õøÅ {ýÕJe°ž‡Í(òœ9b²g‚åQÓR_+‚•Jk,ÀJ<-ÙdU¨J9¬àm±6~FþyшCe¨\ñê>‘ã(ͱ^æß£4Æò,=Ïš’6Ï´+ž¤)µ­F=¯îR[FnŠ(Òõ<‘ë˜fféÅNáríÍjmÄ;"ëNú øýïõÖ'Ÿ ç( ðÔSáœÛn³7¾ôRê¡uì?ˆ¦}ã±tøíom­Ç®Æ„BÈð´¬àßýž‘ûXùà`_¹¿T½aÊ<³:´—­®ÚÛ£eö•Ê]ÊwfŸa¤kN ››«øŠ}i2£ì]¯wý—çðŠÏû|ç){Úªøãû^³5EÛzäqÛ›¼¢øüØ\ï̳c¾Sû¾¶£u—zÃäëÃo×¾«ï”uÖ唜3¢¡ÊÊ;dläØÇY¯T=ggÄǽð?òò⯅rìã{®¯×<æØÚò~’! f¤Ÿ;5û‹!ÎFVZw¦ÞÆ5n1CŒ¼Ùg)@/¿¬†W®TW\aºss¥òÄáœõë•è‡*‘’’ø(/YY’ßt“á>î8K?í4ËÑ14#Ü{¯Þ²};¥·BÛ¦¥oÖ&xJ59L3p_®Ø­p0|{æIF±'S6E£xzßfµ´­ž¿P¾Y½nÒIœœ=QLO/ˆü¿ ÏÛ=šCÞ6ûÂ($‡ƒÊºB¯ýZþçÏÙmŠ&?3þôØÌŒI–ÎmØã¯äÿ.]rß¿mÞé¢ÐUpýùòÛŠC~¾äêHŽ=_4ÇZ˜MéêW»§m‡²£e“z~Áç"ã]S-CòÃú×m;Z?Õ^¯yÚ~uÉ÷ƒÝËS˜&O̺0:Á=ßpkÙ’¡.z@YW¿Ò¾£õm=M͵æg^šròÅáˆ1ÉrlÍJUØgzíá…ôArÓMŽF]³‹/6]°`e[° +'ý„,û 'X =ÒW¬Ð?ÿ9µžB!g¼èê:ô%hÄØÿžxN,]sH †L›G~}æ)Æm½d[SW¦\Û ;U]Ú»…1ä;2¤”ƒ”ñ6í•ûßÕ‰/N¹(65m‚%Á %ÃüÌéf†–&îÛù„ãÅŠ×ô[¦ßIVŸ›§|%¤r ^ÝÞÙRÜf´òë'=Tà˜h É0yqáuáêP™R*Sš¢uºžë]WM¹o 1Î¢ÈÆ[ÀYÀ=ânme…®²aCqrN¡•®Ûe·Œ¤iv™ïHÁÞ ³4ÍÕ¯c®&ÔÈëx±+WLMŸ']ì.ùŽlQ¬VLaBå=³ONð`tÏ{ï­ÀYd:'XÝ¿ƒŒq8'Y­­M¼Õhè ÷&!`J•Û‘m›`V†¶jA³™;U߈ê1çPbÃjÈþ¢}=ú¨|ì1-xÚi–íä“-ÛÔ©–æõBÑ4‰¦&&ÊʸùÞ{JäÍ7Õˆ5"»-BRÞ¶¥¥ýíioÚ'ONŸ¤}IȘÑÄ]®›3‹ŠŽÏ¿÷Þì«WkráBÛˆ:ùEœ{¤Ï÷ aá#NçÛvÎæ/ ëN‰c‰Ï^§h|d¦#$Ãg †HžY•ïÌP¨òø¡.r@€Éž†Òk(E!-4ņwêÑ¡ŒÄ4—!?(FƒŸü$ê-- ®\Îþìg §®S“ !£c«íÙÙ3Çýô§Ï¹Ö®UpÎ9”*1Ò(J¦ðù~Þ’›û§f»ýA7ç—e!:o1Ÿ}èÕÁĤi4£¬9L8¾ œ>1Þ•#êÂMüýºM)3,yôoÅ©L“PÞ“ˆon~Õ±ü#úû¡ssÄÕT}†’ª‚}¶éüûß#Ù{÷ ï»/’¹x±ES2ŠØíw{¦Oÿ‚ïwL\½*9AG4U-6}¾¿6yi_žð^-KIXfµ¼¥åæ,!nòKyÍQqŒÍò®×;ý̼öމR‘ Rtt”ìèDÙ¾¼}R€CJÑÙ¹’·wªdRi_‡AŠöçÛ;] ©Ä—KÞQ¶¬†YCÄ`9§ôÙe GP‰°|»GfØœ²½ (RJ <ØÆüFŒ•¸³…ë$”øq†6#Â#!±L芎bW®2^€Á ûµ¼&ÜÈ›¢mÌ¡:¤WË“<,§êj_CH 6ÜÀFˆå; …Û%:÷ƒ—A#Äê"µÜ£ye¦-[Ä÷M|¢" †¦ho5šy–mœåP2âÛËøs ‘~ ¸] [î³·rS-—še5F«”ÑÆ²ì%–Î=²cƒƒ‘}<&",Ç>ÓdÐÐQO)Ѿ¯;öUÇg&$G|ž¦®zÇ÷Wü3Œ‡¦=·éöXJ ó³—“>õ}óegÛøXu8ëX6’®°)õÓ7šÀ#oJ)7¦ZƒZÐI8 ¹b…Z±B åäHåóŸ7œË—î9s„Œ'žhÙO<Ѳßy'2_{M =ù¤|å5Lã¡2\ðz—åüñûÕ‹/V FßxÒ çVzúóõ­­Wû,kœ\˜töF2zs:ežC‘¢WÀ—mwJŸÝ-;‚ÉÞŠ\R‚IHžtr®4Í!=ªKvŸI´;Î&z D‰§P$ŽâÒS®#G䨻‚ÓÞœªKNpO¶â£Â$òêÙ"CÏñ‹”žÏeÙÆ‰L[a¬c&QÙž’©Z^uþݵ_&[ñrúž”Œ eP2ëÔý‹»j-rUÿùÏzku5ë¼r³ÛÁ.¾Øt­XÎÙ½;PøûßG¼óæY#z#BF£´´k}·ÞZ¦]r *Ç Æì2-íáFÎävP#!dHЯÎQ¶c7~ô#[ËŒîªK/uÖ=õ”ðû»Ò[|>©Üt“‘ö÷¿G²‡²ž„žìöߥvÚköÛo§[\c c™ÂåúKcWgÍô;I9æèÄsŒH ¼õ–¹ùf{ãäÉîÊ›o¶×¿÷žêzB1öª}òä»Ó{,qö>26(ÊLÃnÿ^+cË}]¤BŽ- ЇÀ‚–¾d‰eŸ3GÐ0Œ„ ;A–“sƒïå—C\§1 Æ4M»8¬ªócŒý*m¨ëB[(¿î™ûYÓ]\,ö{(D£º2¸Ý?ÉøÞ÷ü,;›:=@×ïh3Í¥¹À×@§ !ÇèGQf¦äW^i¸–/7] &NZ‹A¾þº^±B ¬Z¥Rº !C®Žge=îúêW-F7Iœ]jÚ7ü±ØÏÒ?7um!cèƒL×Á.¾Øt\u•á:ã ӡ뉃s~ú©}úi5ðôÓZ¨¹™ZÎ .22¾å½ûn?WUÐDD¤“¢\dì´\)(@1%¤BŽ: ÐÉ©§Z¶«¯6\_lºÒÓeBÓ[E7Ÿ}V >þ¸ؽ›f%døÙ¥¾éøÌg$:&á $ŽAÓ~Ò‹ý x²q¨kCý(@ï¾Ê›??1…ÅïgbÕ*5ôÄZàwš:šaÌëýIÆ=÷4s@9ôÊdÌáüœcw§IY£ùÔŠN9ª(@yy]>- xç5üÔSjà…´p8œt"/BÈ0c³½g_º”ZÎIjœ_¶¬•Žx‡Q2Zíhia¯VU(Õ¡ kŒFáPT¤é69Ñ“!geäˆù™ù‚uË^õQfIÀ£;éÞ4 ’mÛxì_ÿÒO>©…êêµ®2¢|¤¼ Î)÷¼¿¤”@b›QóË–õ /è£×};v¨oTWsHÓué³96Mìmk業Mx¥rŸòÐâ‹£¥k Ö_l|M¯Ùý‹—GtFc³’ÁAú ¸àgíž=”WNÈHåñüÃuãÍ|¸ÜòÑGMüÙg+T)Ÿýl~LÓR×Ï0îºë};œwÞ´Øüù= î¹gµCˆxP-% %çn‘™™.¦M›`ä¥<ƒ!öþûÚöï? ¶´´ð`0ÈuÝ&N—ÈÌÌ±ŠŠ¦˜&Ì4¼Þ¼¯[Q±]Û¸ñ5‡”¯ p¨ª.Å!NŸðzKLŸo¶¡iúqƒ±hâ@Òèîè(³ºªŠ¿Q]ÍÓu]~{ÖôF-+„Œ0.×*ç²eŒÃ+%­¾>Â>ù¤I˒дÔë !±ys'œPÄÑkúËmÛªUÙÇÀî«V}ˆ¹s§W\qNØápôXqëÖíÚóÏ¿ìŒÅb‘‰¢(ˆÅ¢,‹*--Mʾ};õ²²]Æå—Íß}Û`°…WUíì£æqœ«ÈÏ_5ëÚ€ÓY8luÆ–…¥|Ñ|!4Ôu!ƒëãúŸŸ8ÙšåÍìq/Ma ÇeåŠù™yB‚љ” 9ø$$"V”Ùg« )`“©ÜÑïšI)`I…òë™À¦qû°:_Ž6 ¡… -ýÛߎ¥À¯m£1r qÊÔ BÌáÀ˜˜˜hñâ©ÆqÇM4¥dÒ4†?ÿä“ÝÚuʦM»5!€ë®»´3ø¬¯oàÁyvv¶µtéé‘ ¦6›KÆbkjjaÔÍ›?¶ªeñŒ3nö+ŠCJɆÑÖÖ J}ýNÍï¯Uªª>²Õô¨ëQ IDATÖnÒ;îëmK†eÇzÆ. KyOè£Om8ÌÀ£õ/Mew[#d÷µ9ßî—^µ1ºÆåç]Òy )ðrÅzuCS™r0ÜÊ ×á ³¦ZËÆoô.û•ª÷ômÍ{”KÇŸ“Râ­ÚµZE°Š‡­ûÖÌ[BÙ¶\±§m²¡i“¶?P¦ø ?“pkYäœ`•Q4C÷%MØ«‹T(oÔ¬´ÕEÊ!%Ës[s3N‹fÛÇ[«ªtŽsLµNÏ»>an–šðNõ“¦çíÑr%bµ1»’&sìSÍ“³¿r*9”8È(@?BÑh×uti)¥¹2òlSçÍ‹™{ÖùùbΜñ¦\ Á!%ÃÒ¥ÇÅyäÇÆ»µ-[vk{ö”«'N0`ݺõ¶X,Æ\.§¼á†ë‡GÁ! iºÌÎÎYY㬹sGƒÁPŸû± `VLÓ<¯+-KA¼,‰ÒÒ×Û¶=ëŒÅ|ýú?¦ÙíY-^ïì„Àe¨16ÇöÓoç(”m·Ëª`ˆ½USÅ÷åˆC]pÚUæ;=â`$ ˜–@ž3MppH ëj Øö_[™ÿ O×]r¡o²%¤Ä®Ö*þRŇÚÎÖ þ•i—G»oÓó³šp=ßÞRª|X¿AËÔÓÅÔôI–ß²Žªkë×jeýÊxW±˜’6M@u¨šïhÝ¢îñïT¾8ù[¡l{~Ày_`‡ú|ùCS¬Ø5ÝÌ´­±z¾ºæQç쌥±Æh•âV½ MŸ4>gÿ¤ñy;gª,tÎ1]ªO´5JyðS­6¼#íÜq?õgéS†í¯‘ˆN2G¨²’wyy‚Æg#dÄ©V&Oî;°í.¼ð”èÆ»5¨¬¬U:ôªªZÆ/2].—ì«­Ãáî+‹¦×ç˜2åüpFÆ$ã­·~êÂd[·>ì^²äÃð®¤Mcç‚n,9»`œØØØÄ?i¨ç_ýàmqn¾X˜•'&xÒ¥'¶ª¹Òå­3N1¾÷Ñ˼Þ2ÙMÓN6t¦#ž·Þ•óFõVµÌOô䊯M¿ æTíRJ†¶X÷í|Á¶»­Ry¯n£zzÞñ |ïÖ}¤]6þ¼è¢ì…&À!$ Á¥”À’Ü%Æç&\Õ¹MJÄ/¶¸ü´ñ#mUåJûêêl×LüjgK¸”oÔÔÞ©-WúÁ )±ºj‹ W•,6œª­³æ—/‰ÀëÕiÉÊž’Vb”½ éÝùbw±¥s=a³™‹Œ{¾8Ø£š²kÓÝþ­jSô /p–Xs½§ô¸;UìšiNö,LzÇjmÃÓ@biΗBéZnKô÷IF‰û¤XCt¯ZÙN¾ƒˆô#$%ððú.»ÌpÍ›g ×BHNç^5/oÌÇç…ÂÌ4ã7=gçpnn¶õõ ʶmÛ´Ãj"€I“.è–ÿ¾q˜6zxÐ@¿Ÿ£Ð…EEÖ_OY»eÆló¤œ<‘ëpJKJlmnàìÜ Þµif `,Ö†¨Ÿ…­˶§É"WbNø”´BáÖ2hFX›H8MK›ØgÚˆ{ü{”ÖioÖ®Ö_«^e{½f•Í”$$Zb]ÖõÑ&¹“§Ž•¸ç$Y.Q)STn“>[±AÖûŸWo@k¬’²]í ‚ßýNo[´È²}¶é|â‰HÎ×¾foxï=š9”‘@U+Õ¼¼¡®ÅÐ[·n{gãBnnW ±xñ‰Ñ-[vèB<ó̳®´´×3gÎŽMš4ÝÈË+²TupGrp»Çu$~ÿþaúƒŸo ¼Ùܪ†Óó Äéù…BJ޽þ6öߊýüƒƒUʶ–zþbùNõŠâYýêsV×Þ!4ÓæNù=ÉÔÓdÀ³†H KÓ<=Öóêi)±Ò¶RåÅŠ—ì­±V :×%ƒ!âwƒf€eµ_æ¶Dã•-1Ç<¾<3a¹ßhↈ2xtßWÓûz¯~³Ž.Zèƒà”S,Ûý÷k~§SòÅ‹-ûªU¡¼5k”È(‘Š nVW3Ë:D׉`‰µk•ر©1!¤K=«º”@ss€¯]»K{õÕmàóyÅÌ™“:ƒ‚‚|ë²Ë. ­Zõª#‰²¶¶V¾víûÚµk슢 °p‚1iÒ,cΜ“£œÛŽ8X×4§Ôõ4‹µñXÌ?Lðs- FŽvXÉà›èI—·Ì8ÎŒ ÁÖÕWóªùųúµ-ëGN¶lϘJ¶®Ê“‡i­±Vö¯ýÏØ-a± /ŽÎLŸm:U”P¤” Oíı»m‡Ú}h*dzhL™ü°5Eâr«}]‡â‘ó½Gâ¥ñÎ/ Ö9¿A–:‰¦ ûàŸÅÀ¦ 7\TœØ:Ôµzä‘pv^žìÑÒ³x±e_¼Ø²÷·ŒÝ»¹±p¡«zðkGé‹”&SGØ™ðH<÷ÜǶÿûS;]t»]ïr9ä5×\Òu­GgÐùóçÆ&Nœd~òÉF}÷îÝjUU•*¥„eY8p`¯vàÀ^mýúlçŸM07wÂf¥iN‹µAˆáÚ7@0\ëFŽ–%yEÖºújÞé§ò\Gº€¦hbúJ‡æ˜Ÿ€ÏžÑï Ü=þ½jÔŠ²Å9§ÆùD; hŽ&öñÚâC!6Çê“^ø¶Ä[ÀÓõ\¡0MFE˜ÍÎ87Ê™†Ž×Ú;¬‚AHÖãõÉ‘&?K«r€ûÆqð¹$ê˹ÀMŸjó»–ýÀk€Õ/*„ÃÀÆÄøçâAy¼¡‹1ÀãqÊÌL˜6­Ø<ýô¢ºnOšfîñ¸Åi§-,YrºŒDLV]]«”•íQwìØª75Õ+-- Ê /üÝ}Ýuw´êzêÛù‡"¥D8¿¯i®ašB"A]¸FŸø<»©µÄâ©¹ŽžÇ·SÕ%d‘ Ëwôì´™esK§ªËúH;¨çEîž,KÛ*”€fnÕ!Ó4W¿OE-íi-z’ÑejÃÕ¼1š„wM¶†m-é§æ\å½&(ÚÖúABa 9ö«&\ªîö¿¯OK;ƒîô#Ã$@h>ðßÓ&€»ÞO|¾Un¸hô%>÷Öb`©|ðìQ¯f /¿¬½^yDgëêjN·†œ{­††rµ `¨k’HU»N+Ѩ`v{êªXÌê6ËgêÓÑe—=ë¬y1!¸pÞ19G|lôC×K×u9~ü³¨¨Ä<唳Ão¼±Ê¹~ý{(äçë׿m?餋{Ÿp¸^Âd`·g Ó½‘9tÎe~±~£:-=]ž=n¼H·õì[±×ßž)Û¡À\oÏIy²í.y ÐŒ UJNAºÙ}†PÎÎ-˜g>àcmEÙûÚ-Ó/Œ¹µølŸÍ±[¹ÿ= Î)X4 t)_ûwccÓuIîÒ˜Ââ±uÀô³—+ŸµË$§Š\{¡5%mޱ»m³öBÅCÎó ® ÛxšŒ‰{»öY{s´&é‰cqÎ5á•~îù þ ‡CñŠ"WÏQeb"Äöøß×K\Kb?ü‹sÒÓ0 Ð×O‰ÿ?u70ÏŸøü-‹»ç˜± ÈoÖÍ‚nàÃã—ß.ª;fUîæ¶ÛìÃp¬^BHXVUS³ Ã1@ÏËë êë#,==u‡Ìææpg€žºåYQ84­+(?Ò»œsœyæÅ¡;7é¡P€××WQÇÎÆÆ¿K™™3‡iŽwP€>Ê´Äbì_eeü™²2%Ën—y—Ô¹‚†H˜•ãi(ÓÒ³ÄgKföPÏÌŸl}ÒP©<»£úRÅV5CwJ…qüü¸Ë"ñçg™[›+”ÝmµüW›ž¶MN',!°»­š‡­(›‘^l-Ι; Ô°Yé3Í7mo‹Æh#¿{ë]®)ž©–eÁ}J–ž-&z¦šûü¥ 1Þ9ã–GF+ßëߪ޷ó<.5M†Ì6æTÓIJüë¯T?ìTXÏsì­“³?^×ð´ã¿Õ¿uûl%V†^`I)à7ëycô€bÉ+rœ`P€>x†I€Þ‘¶2ç@òçßšÿŸIàÑû€ë*Ú—¯Îû6Ó;—=sôëJM"‘³¶vxþ¦::+VSf“'g¤\·¾¾+/6/ÏsL[žãHOÏ¡P€G£¡Ã¾›hýöÎ;>Šãìã¿™½ÛëE½7B z3Ń1Û¸`\ãöÆÁ½Çqâ'±cÇ%î=ƽàÆ€é½D“PA½—ëw»óþ±ê: $$tBóå£Ï7»³Ïζß<ûÌ3’—ìßÿ?#‚ÈÂÃÇèëô2ˆæý4㾌ᾭetge)tØIfMe ÖhØ0k¨<3:Aš+B[eÂf5dz±,G¨p9ˆÃçm1 ©š ¸{ø<÷Ï{U»*s…}Õy© –Ç…ñž9Ú'·y ® –“Œ±’^¥ó{sRQ®O¾ÎùsárMV}–°¿v¿*L&§[2¼³¢æ{6”­Q{e/Ñ ­×7ª,ìʤ»ì{ª7‰EŽÁÇ$DéIɦ±Þ2W¾fuû7W#ƒæºãô#}[+¿ÐVºó…ìú"!z!XŽ3ŒñÆë'zµ‚5@ßxõO@ —Š€·!*®¦}ùV P­|v°YœÀŒJ`Ì^`ó8àhlïÛÊápN7ìödoAp¼ðÓ>!$DÃÌf5««ó’åË …iÓ¢:…+WæªÀ`YHˆ¾Çz'’÷\–eTU• ŠÍ‘Ý$*Ë>ìÙóžÑá( 1ñ\§Fc‘»rú"Ç Ÿ~DëõlAB‚tAü È  3 J(£¬í@Ì–$™‚åDSˆ FÛÍ$ (±ÜçÆŒö3ÖÇe%ëà¶33j’wFädoc]þ°ˆvYâå.€ÀÇ(TAcgFÎñœ êQ¶Õz=¨0:xºgTð™hœI”1ÂöT¯•zÃý^uAbŒtNÔv I–@‰í%às¤ÅÔ²ñuíË?Niþ>=³}ùˆÁ^Û±k‰Ãáp:€±X);Û¿—*X¼XÉŠ²}{¥°bE¡ßð‘Õ«óUÛ·+¡% ¦{Iv5Þ{o©aժߴv»Ýo­’$áçŸ?7¸Ý.11ƒ»$Ð=;ÉÏߨùé§»‚ýEAA©Þa¼õ½ @Ó³szJ„ž¼˜ ÿzœØ9é’Ä#»Úm¼Ô•+ì®^%ª©–¥˜'7´ŒЧqZt±EOÍægÎu©Í߯<Ò¾<²á&îÖÅ"Õç¯D'O–4çœãÓfdÈbp0ôzFÖ¯\÷ÜÓ:V=)IVét ’vè=éÔd§;$ù235 pöµ!~Y° Æ÷ãÅÂÑ£6úüóûÄ+Š„!C¬rX˜ž••¹È¡C5tÿþН³Už3'¹Gã¶ëëmtõêuêuë6iâââ¤àà)((T’$FjjjèáÙj»]ÉWžš:Ò3tèXOG^ïåË_4S*€1Àãq»½Rðz­žòqqÓ\·ÖSª>¡«§ž< Ê鯔8ó…¯óßÒ 6ðYÄ0Œ ÊSJ³êw©e&cJØ%.½`H‰­–ècê*²ä¶ñ‚j€ß‡)ß͵ÀT?ƒ1K<ð„!}*r32dõ‹/ºBÆŽ•4mËòòÚ ð%K<ænðš`ìXCá‘#\¤s8§žQÞ={(dYI;hPJðÒKãÜ|«þòË<Õ¾}U¾}U­Üe‚@1~²wáÂáJ zRØŽ‘æ•$•BNNŽ*''§ÝsC«Õ±ŒŒÉ® ÎquVWyùÑvNBŒÆ),,Ý“”4Ûi±¤øzbðjoÁØ20³Óýäp³:XŽÔÅK‡ëv«%¦H¨¢‰‘Î[àJ4Œôè¥7à®b€¥¨Vް±¹ìŸÃ_à }äAÿëç‡(Ÿ[koü©eÞ<Ÿî½÷œa͉¿óyûm±¾Q _z©OÿÌ3¢Ÿ‡Ó»ø|g¸6mZf8ãŒTè´Z7Ý”â=ÿüß‘#6RXh'åån¤cÑÑ&)1Ñ*‡„YgÂöÞ{ç8º6kÆŒ©®3Ϝ®¥¹¹…B]]=­­­'‚ †Éd•M¦ )>>Õ'Z¿ÛO÷Οg,ÆiÈC!ZY L« “U*ëJšÇ¾„±ïuÀ;•}m‡Ó‚5áò‰K Jìxs :`sB: Ð`Ú.໳¼D`ÆàO›“€Og6/sÁ>ÿë„)Ÿæ>·Éɲêí·]Mâ|ß>êyå±î÷ßÏÝw»-_ì3ø[ï÷ß©wçNÁ=fŒ¤™?ßkàÃé*+¯·ôÑjÝgœø }AX˜–…„èåF¡Û(j•ÿw¾nZZ”Ô¼^×·mµZ匌`YÑ) šÿï=½Þ"ëtÖVë5¬Ë$Iè‘T§ŽZÊX=xˆ ‡ÃéUD ÿ}ðÓ4À+k&+-IÈîôž¥5æPÏïu3;à©§ÜAz½2ïíþ#ÖþùÏššÆ‡•ÇC:}ôlÚ$¸ÆŒ‘4é鲨R>äÂáôs\Ë—‹ lÎé[dy™870+p8œÓŠÈâCíÀo¦Úöe!À[Ÿ+¡0myr àkèdL9Ú»6úG¯gdæLŸ6n\>Ú,ÎO„œ%îœR &F‡3ÐÐ0·;Õ{èP¿qårúIúN\ÒíYR9çD Ax]>0éEà­T`w,  µxj'ÒAV‚CQ@\ƒç|QŸôŒ YÝÚ²t©ÚÖÕWµÙÙ¤ÉgË„¼ÿœ{Ñ9ía슱r ³›r8œÓ‰è€âIÿç.»NlùM}>shx8kʦ°?íòÛçk1›Y€¼ÑàpŒÍwmÚ”àÝ·/SLOçÓ¯÷Q+ð¤ŸÉô8}Ä|2 ¢ÍÙ¸l> ”ZçªïÏÈÌ B4öµfB»,˜ \0¸aJ_[ÓU\®æ‰³‚ƒ».°‡•šÒŽUV>ðˆÃéC**Þ¬ºývS€çáœjdy£È˜À w_ÛÂQfè¼y]1½î·B¡å[ë"‡ ~9¤zh[.WçýœCõïè,:;¤Ö{(ÀɧŽè»ÌÀ·³€e3cÁ}mMW),¤M¢:-Mö3ÑRçŒ)‹ Ë@v6σÎáô-ãë²ÒhÕF_}ÕÒ8ÀôÃÕ¶Þµ”Ãᜃ|EE¨ÿãnì#GA–³UÏ?,ÀóÜ{ ¬+v’2§„s㌬­÷\§¢˜macBŒüÚ= ˆ×Ÿç€<ûWÚ¾¶¥/€Øƒ$gGR€œ¨¾¶¦;<õ”¦ö’K|Æ  FoºÉk7NÖ<ý´X³~½Ð.^1"‚ ^èÕ=òˆ'((H‰YÏΦ޷Þ¹@çp»ýéÚï¾Û%޹Qû‡?ðgý@„±zâpÜÂØ»•@$Ÿ |“§<*çÄÚyÉk=>Ì*£Qz $„6•­w•$Ýjfg„‡°_ŠÊ螪:R`w‘XƒžÍ‰‰–‡ZƒNøB·û<ø2÷ *Tc`sb‡úvVÒ=UÅÂ1{-I1‡ÉBÉ ÆªÝv²µ2G8XSD=²D›¢ä¹±ã¼ñ/¿l^ÙX¶WUè('UîZ¢ –“Œqò¤ðqÞ¶¯çsl¹Âþêýª!–¡¾xà i{å6u®-Wpú$Ùœænç5ªÍ 9Âáºß…cŽl•N0²!æ1ÞaÖI¾:X»Q}Ì‘©ªr Á(‹qò0ËÙn³:ª]»ï©þZk÷UÒ1A×8ë}å4×¶A,s|ÌE&…Ýe?\ûƒÆ¨Ž–‡˜/rùoÏbz¸þ3^% 6.jZÆ Š“,꡾ ÷VÑ%•R9 ÞŒ€@€Å?§†o$7çöµE]¡²’ÈW]¥+ûê+G„V 2j”¤ùì3g„$N§¶2q¢¤=tÈÅZµy}=‘¯ºJWîõ‚«'€¨©ùºâ©§FG¥¥Î8#^6rN! 6Û‚eù:`¬§¯­(Ø}ÆN˜SbØ]é!¡Z±u»hm^ _çVÑô =[Úô{‘ÝoóʨWbòƲjaMq% ÕŠÌ%É$«ÎFÖ•”Ó»G¤K“ÃÃO¨#æôùÈŠÂ,a)XöȲê‹ÜßUQË>/9Z_A->ŒG3ÎsëÕ:ü}ßšZ“˜ÔZVãq#uÅôp]½+ýâv¼ý5G…¥GWˆ6ŸƒèTZfQÙÁÚ,ÕÞêØS}@X<ør·VÐ5íu±£˜n©Ø¢ [WºN̵å&µY®óÖÒ<ûQá÷êêëSî´gÕR}•÷žN4ŒpI¹$»~ŸªÎ[C'„ž×Êì"Ë‹ÞÒµíV1\®õ–©ŽÙ'™µ«Äiá7:’MÓZ û£õëÅ*Ož©á]_þŠÁ'»ˆN,{d;1ª"¥<ûo/sxÙ- iwŒ³mßhÚ¾Õ³ÜØnŽ`M†·Ö{PUæÞ$Æéý üAb´ßi¬èfkÖ¿Nî½ p-þ”Ý×Vu…õë÷ܹú’·ßv…¤ ÀhT¼ä ÞòVOùìlê½ê*]yff×Ó3r8œÞF˪ªV–^wÝø¨•+m$.Ž'†(Øí[$iœ¸œOJt )r$H©¨½•nâ“Ò¬Ý ?[YTIMzöæÔQ¾H½ŽÉLÀÏ%ôµYÂûG²è„°0I8ñadÈ·×Ð:¯›<6j–'É&×yÝäûüLá—¢ƒª7¯°1!‰Ò‚øñ^Ua_u!]š³Aì©Þ$þZü…vkÅr1#è,V04µç–Šï4Gm»Õ¡šxi^̇Q*3P–Y󛸶ì=ýo¥¯µ©u&U{oöº² ì \#¬—ºh˜KvB j$Ïv¬ûF›]¿\L·\ÙJdË̇|û %*$æ·Û· 1Ý— Ò½C}2Ý%‰ýN ˆ[è«(à¢ýÀè=@½Xr3t+péÙÀÝc“Wuþ÷Cx_ïÅŽ‚güxCñwh+6m\iwBx<`Û· î»ïÖVN˜`(æâœÃ db¤²²oËæÌѳݻy”Ãé ›í«ÇSM'ýÌlÍéM<’†ÕxB:¼Ð²ê=cèžo‘à‘‘)R”^Ûôl>76Jl6±2§‹Øí]Ê ã“eܘ:Þ;Ȥx…Mj-»4q´OE(Š5Ä Òà²Ä‰^U:÷éÖ8y\H²™5ù­ô×wùëDŸ,áâij=#ƒ‡4µŠ¸(á\O´>RÞQ¹GUé®ng£Vв…‰‹Üz•"´#u1òÄÐi8R—©š:Ã3Ô2Ú*ªÆ˜à3=¡ÚhÉ#»I¡ãH“çÁî«%{«W‹”PÌ‹½ÃaR7{»Ó,gy†Yfº%æÃ¶ÊOýÆ„ÇêÇyG-r©r—‹T±'Õ4ϵ-×¢M°@ã7Ñ-×Ò(íÔNôTñؼGOÊCâ–ÕýN ˆýÁyJ zKr“”¿eÜ.`ÞÒžµ«ëx½`￯¶¿ÿ¾Ú.@B‚¬ eT­)/'Ò±cTr:y8 ‡Óï).ÞT²páìð'ž¨.½4@üœ…±:Z]}[$éb챺¾¶g R挖¬b•_1VãQt«Iݽp…t«‰…jÅv~' ,»®ž”9]$Á`:áºj‘ ³FÈ-+TS Æ9»¾œŽ Il×ÙH6GK(Ü©ªr×7 mà`mž ¦*Œj!Î! b,9Jh‰³”kZ‡‰¤˜S}"YK;â ƒ$e '†a–QíR8ÇêKå®B¡Î[ÕtC+uæ>æ% †á>³:LnÛPÃ,g{öլД:ûՎɦ~ÃÁLêh9R7Ê[âÜ¥.vîPGh'49&sl?h Éx¾_︆Zeðȵ'qã%Ì-qÎi$±# IDATGRßÑ£}m ‡Ã99’|{Šzh^ø®]ûÕO<Ñ×öpzŸ/KUYù§`Iz¬¸ØÙ×ö dʜѾTË>¿iõœ>Eci…î%?Ðùs0¨)d÷um*’PÞïïá:Ë®/G¨¶}6™p…€Cr7íD§žxd/TDÀóû?Ò¶vá0Ÿ“@…»²PµŠÖvÛÑ« L+h™[v³ÔNl[Å0Ü’£ÉŽjO©Ñ,¾U%Ø}UTf>Ð6]MêŽS§˜æ¹Jœ»ÔY¶4½Þ[ ”»÷ªª)\;Öëï਼ð>æèvÆ{·¤ëBèR  }ü ø$=£ózƇÃñ‡ÕÔ¬.ýßÿ–íÞ½ÔðòËââäÊé& 6Û§úººŒ²üaÏuÞ÷”:c;yfµ¢Mí¾î9CÒ³"ÿž|Ò )91§o½WŸjªbÁ¢¹…–& a ‚D ‹A,jK»mÒN¢•Iÿi1·ìl°ÃVCJP¢fós‘¨ZU«¦þ;,«ŸäÕ«Bå"ÇVÑ)UR­"eÛ–i†DÃyÆ–{¤Ú›ÌÝö€»eu¿Ìþ O—ëk z›  FÓÒduH£¿ÿN½¹¹|ÖP§ÿA`³½T½mÛùιsÿ<{v }ôQÀbáa/ý §s¥¶ªêe³$Mw2ö[)Ð>þ•sêÙ]9É}IÒ;&eAZå:«óHJìÆiB¸VIñ(ŠS¸#` Œ1Æ(Hƒ˜Bf½·ãÖϹÍ[å·'ã’ê‰Ä%g @úéËøñ’øÂ ®àŒ YÓò÷º:"/_®r,Y¢©²ÙÚ&åp8 c³]••‡Š>ÿü}ÃÊ•¶^u•‡üßÿ©™8 ç»ë_¸\»Õee/˜}¾Ÿ,[ÄóÑ¿Äê¢ùN‡Ï(ëTŽv½ÞaV‘ 9õ§×‹­ ² ÑĪ=õ䘽”Æêû&ßw°¨ä8/rQÉLA롎}*0«#ºeß`Ó\÷þÚ¥ºûr^!¹å:§Ÿá©¹Ý¸€Fê½Y*°ˆÃº}ÐK\AýòçnŸ.ž.«+*êã+*êã÷í³ÇoÆÐ›oöW¬pDµç`63ºp¡×¸v­#*-MIËÈápú’t­½¢"«èõ×福2…áÖ[%¬Zå n7ïsOŽª¢â cnîâÐââ·Ï«U²ü¿J.η¤cëKæø0Ô*B¯"8Xã9í¼ZçÄLôÀҜ墽!Þ¼-¥Îr*±Þ;e#uIR„6Qª÷VÒÝÕ+Zé·d#;ª¾ÖÀpë¹z¼;C'Ë1ºI‡¯”î­~CƒLþ‡6RãÉT@ˆ8¦[Ý)iäz¯ž{ÐOwfÌðiEQ .ûî;•]îä§¥Éê'Ÿt mÆ¢;`:]óh…ädYýÎ;ÎÐÉ“ Žc5‡Ãé]Dær=Rçr=\÷óÏÅ >4h4Ëõññ.,X cøp5ÂÂÔÌjåîõS,;‰ËU.¸ÝEBMÍV±¾~§F–c}²|‘ƒ±뀰~ù°H,/¸Ä>;ökCÛßL‹Ô±å²¯ÊM†iO>)l¸o_õQaÍQáÙßßÖNÍð…kCdŸÌP鮡ٶ|¡À^Dÿ<ò>‡V轌$ÓÂ/s~}ìyㆲOµå®t¾]Àë­£’ä$ŠÍzDù‹‘$ix½8}„Ü@`cÉlW­'X2«Û§Ö»8ÉÈ–8ÈOÇl§•@€S¸7”íQý\°Q½¦dk«7ëfµ‰M ëiï¦ ŒÑ§J—%Ÿ'éU*ŒQÈMƒ+iàJ™ ` ˜ÜPÞ0èRf‚ò;£u30åw¤Ec”1P4 àXCch² Œ6¬0Œ1Òª.¹ÑÖX§R&5L6ÎÊjch||s]¤_¿Žáp8§'ɪæ}ÿÈ~Çqqú&U”œj>ß«ŸìˆØ«1Iån«¯?§VlIôMVe' ïFuƒ¨e(ëµDÃXi©Ž2w®O¯×3¿=‡Ù³}ºÆï«V?mbJJó$Eùùô´8©8‡Ãé)Þþ ¨øðŸ7€±»¡ExKnðÏ+€¨G€sæ?‡õ­‡Ãáp8Nï ½%̶d>\ó=ZÞ\æÔ¿LæÞ ¾x`÷ªs8‡ÃápN'P 7’ê>X”ÿxí¿À¸]€ª…Wýè à¹E@ôÃÀœyÀŠÐ¾³•Ãáp8‡ÃéX ·äÖ`ÛRàÀSÀuË€°©`Åt`Î}@òÍÀc#”H9‡Ãáp8œþG?è$;€w×?¿¤l_ž=xâj â>àö \¨s8‡Ãápúý(~ÛG€çS&ûÓ¹eç‚æ: Î¢ü·*xí`õpà§O€DgŸ˜Ìáp8‡Ãát‘~ з[€gÆ¿Žj­­Ët`úvà¾ÍÀÙ•À+ƒ€ÎQr©ÀÁ!À•ó€_œr³9‡Ãáp8‘“ž15@º‡ÿüo"9`mBqâòK7ÞÓz¶Ñÿ; üßkÀ_Ó]Ô[€ÍcÂe@Œ»£õÚnp Àj-°E$¤@R¬Š LÜ'åÿúë_qU§vÖåÞi›þ:sôñíÌséÔ·w÷Û¡œm'„,z϶“?¿¡ÝüMrßZ=£•¢%‰Åû|l—aš˜ì Ô»§CNZ[˜@ßdþ>X=¾9\¥µ˜°¸cpyQçõüe?p øt®"î×…Wv°°½GLç6,Óò•Ø Ä!LÔ U…3* c”†2*{ ø{@²·Ã–7`ÖÁ2­—kû½ÍrÄß­–€„µù­e}mëiÞi·ÍÎÊ:ÚŸöû×QÝ,Gü/œL›úÛu5—ù{p7ÖÕ¾¬ù·Ú™4³Îmèb[uPWÇËÈ~loD>ºÛŠ Æbûã¿­Z·Wk»I‹åüïOÛÿwÔö~l=nÝ-~'mσVuwë<õ·Ý–ëøoû9þö‡µù CGûM[ì[ëm2tܦœ¬õ9GZÕ!ŸÐñj]¿Üb¹6Ë´2¢õþ‘¦ýêèºkkCëóÊßrþŽéñ÷ÇÿyªÿŽÛªÓkŸ(ûæe2ì>—`“ÁáÛ:Ÿe9^’«¼œíôÈ‹ìÀ  „ž §sNZ[ˆ@ÿw ðÆT%$¥íÍ%¬8 ð—@¼ëÄë¼ä°"РDßÉ‚¶.›Ë `ö« }0Ø%ª5ãd~2Sinñ¢Fg7s‡Ãápú5¡°ªEXÕb£à'¬¼²Œ\çzÃ~Û]¡Ë†³ùùj ^êk›9rºôWÏޤ4ÿŸJÀÈ}ÀM›Ûv¯ÎÐŽBZÚÂ=è§ù!º]«·^'‰Ú{½ÍRœ q‡ÃáôOÔ”"Õ`Fª”Á„Ìú-úuÕctù»—=]„´µÆékNÚù ½K pÎàÏ['¹s£j—_W¾Ï-îdAîAï×TR¿ZYfЙIZÃMÞ~—=”Ãáp8œ€H7ÉP£žì¨ýÁ¸­ö ƒ—ÝV'±‡ë=÷F>œ>1è#s€Åûbõƒ|Ê Ñãâ‚2ŠPè™írNEB¯×Γµ¦·½hÆÂáp8ÎéŠ@&Zd¤YGÖW¿k9`ûÈè–——C|Ç_›ÓËôˆã7@úç¿ö±ÅbûØNW ï(}:ÈòŸ ÄU9‡ÃápZ*`Vˆ‰¤êªïʦD¹åÊÎëÂx=N/pœD&'PÈîk8'Š ? ªW¬æðsqÎáp8œO¼N«£-Ô¬º&L O™ûÚžN7ÇN¶† t…œ¾6€s"¸!çD¨µùzSØ¿|„Z¸8çp8€U­Æu1Á4Zó’E$ CyÈgŸàÐQZï. !.þðP`«ØØÕÇ_~ppqgƒA;à@d7×眹:T4Ä:ËbìøÍ‡Ãáp8œFDJqE”…~SºNŸã¼3ÈÇ^¬îk›¹PÆ5ž4&Ð=¸cðÅt &»àá· ¸xéIlü(¸@Xù›E+Ey Ãáp8N„›ÈûE+½£ÝŒ]çèk›=ÞTˆËOa@üà‹€ª®‰óáz¨×Ãéi¾Ôú?“!øŸî˜Ãé_¤Žˆ&i£c‰Á¤íkSQ«FʨA$eÔ B(¿ÏqN\i¦zzO°Yìk{Nù=UY€xÐ}¸å2 ´Áƒ­u Ç”ï‡RÑLÙ”Yò < ` ~x&QŒ*8I#ìv{’õpz”=jBï 1„¾à#DÖÒ7ÄÆêɼ â(|¶4W®©ruz _Ÿ"hµví¨·o-oµìW%S“YE`÷®JyûæÒëšrfMLJŠìì‡osZ¥`MD¦Ïˆ¡Ã‡o<žæþõ…—&ÓÐ0©ªt±¯?;ÒiêÖ±#ɨ1á”1†O?Ì”öK_m$s/Liš~¼ñÃíòÁnó ¢ÌÁí/gÕ•Ûyõ÷÷ “ßø‘´cmÖ)¿€µz3.KãS"L´z5•6ÔTÔãО<¶oóVUVwªÍjGD\yò³)\;ê’ÛÁqpNƒ `a¤~R|a¸[ÞY Äp'dï²Jô!@úícñÊ÷ÔÃÀ7K4;ðØà‰T@ãV}Û¼üwÀó€ƒC€ìDàk[zb çNiô=P§ ôº0}Ðý¢¸2ïCä®{†©`ÕÊbOMUçâáÖ?¦ V«ˆ7^=€í[Ë[=n¸eˆc PZâ¤óf~ïu»ý?7ΞG/]”Lwl-k'Ї &wÜ;J€Ï>:ÜJ ×Özðøß§P[ãö­Z‘ïW¤‡„êÈ?_©¶X5øîó#²Ãî=NK4—hÁí÷L8îü yGkزÏÈ_ýo¿ì°ŸèÇœž`ö ôÚ{Σ–`ƒßòù×L%ðÜ’ä ?íp÷˜) ¦’¹×Ï#EÙìµ»_pû?ˆ5˜ì£+«‡¸å_ËúÚžÓ˜*™=Ya€ô_F)Ÿ:ðËÇ@üq\”¼L½Ø0¸û` Àèú“4Ä ¥tÖIÖÃé>Ò ê0¢ÒŒá½þÓ”ˆH¹òÚTáÝ7ôè1^óK¾üã·Gåó.Dyb²°s[©\SÝþ¶òèÓg«¥Åvö¯'·tÛó±ì‹CrM• 7¨c&Q±&ÄÄ™I +ùã“…K®N»k…ôû®’%„¾ÿd»¬ÑªPVXsJ·;zj*¹ã‰K)¡E¹ì祛ر¬Rf¯s"4ÊB"ãC1iÖp’:2žˆšyžbÌÁć,IüõäiËp“l®Ý«qËD`ÊIÏpÉñËFôð5 w¥ò0åó¬mþŹܗjÙ·@Ê  2xd*ðãO=`Ì#„ô@]œnã!„þ5Hg}š‹óÓ”Ú,V7Þš&|ñI–T_âÞëáïÝâ?9J®#>>Qõà’ßZ ðy%Ó3ÏŽ§ðÄÃë%›­ûÏ­¥ï핬dJ¼Kó=:8D‡[ïž œ¿0FÅšÈ ïœ¯ºùò/}9‡«Œzû+åÆ69•‘Õ·þùBJ(Áá½ùì¡«ÿ+y\žæí78Ë¿xýWŸÆzhk'@™ª¡_•Þâ’÷v7Û§c Ѓ±çÀ Ñ\`7*ß“Ë[— wM—ÎÿºA>`ôAåû¶ô2ˆX>`´O!ä%“Z;ŽQ!zÀ™ÆK³¥Ú,7ÜšvÜP‘®RWëÁ“l”`Îü$zöœ„¦û]X„žÜÿØD|ýéayÓÚÂ^QhU•N<óÈéîð€Ñ$â¡§fw_Mf F!iD£íž%:ÎBÆMM ‘1f¿ºØ¬ÇÈ q$}tô {f-§EñÓ“AC#ˆFwp»HP˜ £&&aQ–n×a0ëJà‡6ÊWÇ¿cY%(ÈîüÍ?¡ÑIá9e ê²=Z½É‰$cJ‰•º÷|c¢NÄàŒAH›±ƒGg·ëÖ &%#¦"©ãÒ5(‚ªÇ/]N/§Õ!T,U|Ù³'Ç`moTôü'K¤­uYBCÈŠ$Å"åÇÅ5²X  : @{BЕX`vÔÅé2µäe³Öü_žRñ4ÆfóâÍ×2¥{%\umªðñ{‡åŠrgvÈÖ®*¿û2K¾à’dúðß& ;¶–ȵÕ.üùé)‚É,¢¸ÈÆžzk¯Ÿg›×峕?dɳæ%Ó£#Éè Ñd×ÖÂvû:qZ<¹çñ3…Øk“¨–%†Ì½%왇VJyYíÖ™<#‰<õÊùªÚj'»hÊë¾…×¥‹nGC#ŒMu¬ÿåˆüÔ½?J»&‹÷Ô«uSeBÍü†šóɇYRi±ƒét*ÜvÇð^qÅýã‰m¾² Õ’‡þ:Iµ`a zV,e øÛƒ$»­gCk:âšÅïä³ÚÝ{oºs¢ðü;ªb¬¤¤¨ž­üþ°¼iM.s:¼>:мóÍ"ÕŒ¹©íÖ£„@- P‹¹òãéœ%Øm¬]~DÎÜ]Ì`êìú×—/Pé "^ùô*Õ´sRiVf[ûó!¹ ·š©T·=8“^¼xœßg¢[Πƒ‡D¬%lÝòƒì‡¥;åmk³™ËéÁ˜)IäÍn§EøõÔ‹¢ jQ@»ÔMv«pæyÃéC/^&ˆÙºúÛ¹>‹9lnDÅ“|ò!vPX—¢cŠò*š„ó¬‹Çw+²&"./|wŸ0ëÒIDdnËf«¿ÞÂò1ƒYEKæÑ'?ZB5ºöì%¸ã¹ÅôþWo¡Q á¨*©Á¶_÷²U_l`™ÛŽ0*PL»`"¡ôÄÁ‘ áxâóG„ÔуIξ\öäâçZ‰ó+îYH~ï~—KJ•aó[ØÆe›XÙ±r$¤%àñϧ“çMnjI’µ' •…•·Ó¬=Y­þlÕÊ£Y£Óàî7¤Ó.9‹‚€âìBl^¶žmøú7–¹q/“%i“†ó¼ý€PQD¤ÆMå<çiÏð”ð–^!<è ·pÌÚºlT=@d%¥âÇéþg Ý>HùÔ9ü{ØOŠm¬Rz¸^N'úµQÔ]ÎÅùÀí–ðêKû¤¿>3Auñåƒèûo Çòl=zìmõüíáÒËïÌR3/‰ž5K‰;ÿò“Cò– E§,øøX^ ó¸%ˆé#[‹Ùä¡¡äºÛÇSB€eŸí—Ÿ{lµ$ùdÖ`-žym¾*cl4¹÷o3…mëód[}ûl0f«7Þy†ðÄ=?J¿|›Ù´_ó/AïzŽ0aZyáƒËU ·]ò¡ïÈþR0P!÷?}=÷âátÑÍé²OvÉ^oë— ¼¼N^þåïryIHƒCÚ¬ÃÃÏ_,Œ™’Dn}èzßâºxçSÒ^Z%úßßd&)¦GÄXð÷n"bƒÈUw̤¿ké ×ívz±eU&›4+ ›HÞXù °öû]lí÷»Ø±¬ŽÓz¶äÚûΧQñ¡¨«²áÉ[Þ”ìÉeû}Ö‚ äö§Ñ´qƒÉ7Ì$Ÿ¿òS«:ç,šFÎ\0‘H’ŒÏÿó½üÕnè00½Sæ#Œß””QƒÉý¯ÿ‰š‚ŒØ²|{åÞ7e¯§ùQ7êÌ rÁÍóˆä“ðöcïÉë¾Z‡ÆzA`—ß»œwÃ\rÍ£×=kw3G½N›O_ý¤<÷†óÈ¢®$%9Åxæê'Z\ ÍcFŸ=ŽÄ¦ÆC–düù‚ûå¢#Í¡¶2Dš¥OÉz?!ÃÄh±û#ƒ[žËóxž» ŒYì5Àƒq =œ63yŠ2vHù¾|²’^±%Ï ¶7d€ o¿Þc¬B/ö8m©#À!µ ¦óQ[„o¿Ì‘s²ë˜JEñÇ»3zÅ‹¾á·BùÛÏ•|è¢F@q¡ýûÙm§4„Š1 èXaÛ’Ûï;C ” '«Š=ûÈ*Iò5Ÿþ5UN—ËK×Ü6Þo|úÎù—o´ºv¾ÿl¯\[Í mdù×£+¤Ãû›Eª,1¼ñÏ5²$É0apZx;±õÑ+äò’ö¹ÂkªxbÉ’Ï+aôäDÔ-¡¶é׃ì“WÖȲÔlzia >}ý7c¦&w¹ÞWÿZ:–­ìgT|¹üöYô•ï>ÚüWÕC/_KϾx<1Yý§_LGÎ8Wo>ñ•|xO^+%½æ›­lÙ{«\ô‡YÔÜüò×b•w_@à§×°/_ý©]ŒÛáÆªÏ׳–ûë çŒ!þà>j 2âÇ÷~a/.yMöº›ßø¨Ô*,~øJ _üçkyÍ—ëXKÑ/I>yn);ú{LÁ&œ{ÝÜ.·c|Zà÷µ»Yá‘cíʽn/v¯ÚÎ*ý„A:=€<ýä8`Soo$@ú´†^Èþ4à`›;æ-ë”O»¸ìv`ĵÀœyÀЀ®'àâ-½dœ`”IŒ8½Î7:•f‚Äg 8HÃKÿÚ+À¹óèÐaÝyÇ#2ºùÖ’—S˺’ó¼§(/U†ÙŒÍa”Œ›KàÓwvËþœª5UN,ÿZÞã§&tØ>ß~²×¯âkäŵl뺜v[¨*·£²ÌˆŠ³¶-î”úZò³•Øø˜Äà.­ÛÈ÷û}?´§€€ÉªGW¤V•ÕáO¼ }øÂÏrþ‘æÔ–æ`&Ÿ“A–<{}kõ#œË'·kÏ‘g TWcÃÏþó£/{w•Ìd­^ƒ¡c5Õ1l|2Ñ›tðz|øâÕŸºíh˜{í,r×K·Qµ¨ÂO/•?|zi»xõÄa $21Œ1¬üdµ_;cØüÃ&I#’ºlG}µ2lèÄa$,6¼Ëës ¥R ØÊgí: J:Å_OÅÆ Ä®;¼%Žü©±À‡-FÄþ)xo/°+pk}À}mÖtøçÎ^4°ñ ”˜‰€i·ÓB¿4ªõgroÌcåòyßÞ*6<#˜,¹o¤pÛõkzÔ»}é•CèÄ)ÑT–¨@0ij q2ýþ«¬Sú¦&,Bñ´Ú[¤t ‹0µ¨8Å(ïðÜ?´¿LÁq¿Ýãö¡¬¸ÎïúUåŠø.Ì­îжºj 2‘ГßA•¦"ç/Ccƒn$Fsû0Öèø ²£2ó³ýïwuy}ÓïA¡&Rr¬ké)}^ Ÿ½ö«üÙk¿",ÚŠôqI$uD3}(I ƒÎ ÁíO,¤³NþêÍUMuGƇÈ;\Ò¡—»¦¢Ueµ‰´"2>¬é÷è$åEoYA%l5ön¹®yè2:ëòéÄãòâåûÞ”·.ßÁüÕÕ°-{Ó/R&]"h»(CLrŒ²|bd—Ìnûy3[pÇB¢ÑkñÜÊ—è¡m™lëq`Ë~VšÃ_.÷G†eZáùŸÁÇ&ðœè'Ž Àr…§jƒ"4§TyQ²°ü<˜w~ ,Î>?»uÊE*ç­þ·²‡²·,(³EÍÐý`œŽa[4*ñn7¸ = héÕU«ÿâMÕâBl›ø÷s{¤·>š¡š2=ŠŽ›N¶o)ë‘k::ÆHîzp¬ Þ}ýw),\G.¸4…ÞûèÕæu…ÞžÎÓ„ÑqJÊÃÆI :®ùVRVÜqü}q"¾ & L-êk[‡:^0æÿ‘<¯v›§Ãú]÷´Í`NB€'ÿ»P˜<3…€½Þ²âZ–s¨ n·r»šMŒf-túî9åluþÃae©ÙÜVécºAyQ5~û®šýöÝN†§¾aãg #w=·ˆ-z,ºc]ýÍv©º\ ㉈UrT–tžœ¡¬°Š…DZIDlH“ðLP´Vu?×ýÙ—M#°cÕn¶uùŽë‰Jˆ$`´pÍC‹Ž{aZûž"²¢ ÏßøŒ|Ý“7Ó¨AÑ:1 ˜¤$§¾ZÍV¼û=óy¸Öë/¤èuØPýÞÇþÝqÓ’2(ÙZlÇ[°' öÐÖ)k‚Má@r0«LÉ…~J©ð€Q =ŸxÀRM 53Þ¤FYi³ˆ ×’û;öÂi4Œ&u»õŽÇ–M¥ò¦õ%òä©‘ôÎûGª®¾ä—“ŽA!xü¹)‚Þ FÖájöúK»%­VÀ¤©1êðH=yø‰3„»oýõ”ÜCbâ-DÔ(žòÌ=͇½YØèôê=œz£HEl»§.7N ôå¬*å¯OñBÉðò;€±†à³5œ4ÇBCyxK’Ÿog²Ì@)AT´®S!®m*ÏÏëšÃáßÿØ#MšI3F…’™çÄžô䲫‡Òñ“"©$1ú‰ß¿û *Báƒz~ÌÚã…2îpwÃ÷>÷ N€ >‰><§BøÍ"ñxd”+Þ𙳢:½oÌ:'¦©<7§¾KÇ3s_[ñS¾ wÜ!P¡ûº/&ÎD–< „¶¼÷Æ>éÀ¾Ê&[Ö¯)—}©ÄŸßÿØDUPHï¦;)†Ì¹@Éa¾w)Û±¹°•½²\ñºN›•ÔaÛž9'™@þÑSûV:~°"4ËKüËãâ‰ÎÐ?Ç›…D4ÇóçU4ý^”£¼áHɈ'Aaf¿ëŽœ2”èŒÚÆå›~Ï?\ 3cøÄÔnŸÀÛ~ÙÅþyÛ˲ÇåÅ™M!wüëæ¦‰¥š¶uHD«Ö¨‘1­ë¹È]v%ÔÊÒõˆÍÊÂr|ù¯YÁ¡<@|Zb—ëàôAàw,¶Æ E”Å Û§šŽ ôžÁ` €w d|ù@}_Ô?)¨ÐÁ|Tœ>çãJ0mz0)Ìï½#,\K®¿I¢«-’‹ ]îp½üü^I’§XÈŒYÝó¢üí¹)*N…ì#5ìõw·s9ÿã‰-¾òR ÖâÇ'÷ÊÛD³U‹{›&¼ôþù*B‡Ý‹§YÝΖOßSì;a:Œ1µZ3æ¦ÐôQJÉÒ·wœÒW­¥Š0Ÿ1X»c!jT¸õ¡Ù÷‰Œ &×Þ3—†Dø× —ß>›Šwøhfs§ií÷;YMe=Ô¢ —Ý~NûýÖªqåó Þ“ËìhöÄØžÍöm>ÌàÚ‡.iñÝa÷Ú}ìïø·ìr¸1yÞrçn§-fòDYA9Ö}£ÌöyÃã‹©Éjì°.A`nÝ•ÅÊDEÁ‘Á°„ùÏÞÓYܺ¨a°(Û¬.íëÛœ®`\ ÕPDù7Þ…’$ rÄ÷×€Fp¬áo€`qL ô ð¶o)VQèÊûïfK—]‘($$É;LUôA¶´eS¹|,߯´dXº•üá¶!*‹E„Û-áÙ'Ú‹â!/§ž}ýùQùÒ+Ó àîÅñ.º6M3!‚´ mi‹­Þƒ'Ù(½øÖ,Õìó’èŠïsèªå9Ý u¹ôªtZYé Ó«cBT¬‰$ ":r©—•ØÙ_îZ!eªl—‘ãÓ÷vË—^AãŒä¯/W½ôô:i÷ÖB¦Ó«1õì$róÝ“غ>mY—wJß2íXŸÃRÓ#ÉÄéƒÉÍ÷Ϥë~>ÀŠU±¡Ñä¦{gþ?{çGqöñßìîuéÔ»d5«¸K²»+ƘbŠéÅ”À „HHx! HHèÕ¦WÛ¸€16ÅM’‹,KVï:I§;]ÝÝyÿ8tU–l5Ûóý|ôÑÝî”gçfw;ûÌ3\˜ÒÖbFdLpq8Ü(Õ \qûBîÒ[æs?l>Hï« u•-´¥¾úÒ²ãÉù×Ì&©Ù ®ð–Ïm–m½îX6‹ïþs“|ûÿ^Îí\¢Ók¸ oí MÕ-4-7‘\õ‹ HÖW¸ËמøØï÷xùÑ÷ä§?{OŸBþúÅøuÏ~&—¬¢†NÄ&G“qÓÆbéÕó¹û.xDêõçÌ¡]%ôñ5•xé^nÚ’|òëîæžùùs²Ãæò“ë‰uò”ù“ù¨„(<µáqþÃ|D•ÑÖ¢ôˆNŠ&ãÎÉÅüKç‘ÏÿóÝüææ{(§¶.QëÔøÅó¿âöo/¦M•(ÊŠK©¡®×?| ‰IŽ!ß~°ß_F[kA8™yYdæÊ9ˆˆ‚,IØ¿-ødVÆè#L!XëÎî`Ðåñß €ÿ£„a‰kæ_̺ò§6nºòOжî¿@(ÀÞ`xAðãJNÈà v3é FáÅÁï»çý gå;âïgéŽØ(4YàtžÈãJøÚ ?Ÿ]J‚Øàg³O:¿6éIGÙI=¾=nÛ½óx–E)pÇ­Û„?<2=|öœxõkÆò7®ëw¯¬èt>úÇ] õï¹—ºlà8Äà•J¹‹çl&ï¶w¥yñù½ü…«ÒbU*—‹ P'ÏÙ žé”JQ×J¿ày{›ÀÙ)¤f„ ÷ü¦ Ö¾v «ôp­Ið°ƒxøâþðm)6}žvÞ…Yšßýi·_E»±Ý&ÿí{¿+Q V]í?ºì¦©¾SÚøñaÛ»¯ZLF+¸ž²zª‘÷Þ¸Žÿã3+Cs&Æ+þøô2¿vݾé¨ý©ßo4»Óm‹B!*è ,p6£ÿAÖÐÎf t<<‡0‚Bl±¹ÃÌ·ŸÛJ¦ÎI Íž˜(\uÛLîªÛz/ã]&ýý-¯wÞzÿyÚȘA¡m×I¯6õ¼'øžŸ½å¼¶ùô‘ é<ʦ$عï{­õ,›x[ ²}Ëò¨“ô'ožàÇãßß „ N;÷7üˆ3 Àp†Q ·iÖ˜§;YZƒ¿Ë0u_ˆ œ PŨ›¾jüßA5û*Ôïâ8×4X€x†cð»9nêQè‰L|ÒÑ º‘zæñÝ^]mrܶæë–%K“ÕÓΉUI U$$èøöv›\]ir-i?z¿ÌâtÊ4XÙëÞ*1éõJ®pO“݈ѿžæÆ.ñ‘ßílÏÊ W@}­Y¡^K‘—68_ýO± ì'u·kfv8¿öõŸÌ¢(㕚{Êó†ÝËS~ÛÙP×)26;ŠÛ·«6ÀÅÜ¿_ÕÕÅ7þ³Ç{,¥°ÛDÚevÐÖf³Tr°Ij¬íð"¥Þ‰=¶×T¶Éw¬~«må“5¹“âÙ1‚Ã&Òò£ÍbÑ®jç·›Ú}f æ¸A|û…ï-6«#È,@н;Ê6‹ƒVk–üÒt‹Ë ïÚ¢cC¹C…ÕŽ^eAáp8é=«ÿc¼øÚêüÙ™Š˜x=W_eŽª—¾ßzØQs¼EÚòq¡íÀ®ãüÁ½•ÎÞãrýûàåo­J•@ê«ZEϺ;ZÍò»/|mÑ)yœó½Ø­¼ûÂW6 SG—ì¸ÓSŒxöùæZƒ´fÎŒ™ã“øüùã„”Ìx>2.ŒÓG„Îv3mmh—*šå­|ïèh1zžx¿Åg¯lµïÿîˆ8ky–›ÌG%Dp MrÕÑ:yûGß9Úš;<ÕëÜ=V\.ÝñÃ] /›«HÍMᓳ’8¥J¶¦vZq¨Rú~ý.Q’z×d3¶uÒÿý™ÃÕ"uÿ.îò*—»á1ëŒógȸé¹|ñv×[ªæš&ú—5Û¦.™Æçdó ‰\Tb4éê0ÓŽ–ZñS™¼wÓnÉÐÐâwlw«X¸e—”;m™M´a:BAMI¥ PúÊoÿéÌž>^Ê1‘‹MM a1®…ÄÚê[hMÉqùÛu›¥NCG ~×Ïë¦çïéßü¯[ÁDëÉà{ÊPßJƒâ›ät›%ÍÀ!ÁåwÍ…:@Å}D"o¾sE ÄÅË€ÏJe™VìY7tå3†Â-½L93Y©]ØÇzï¶À£-ú‰G’ bt'3âã7*ÜkPŸ£Ù^Çø˜,Ð}§'Y½:}ó>n¿²iÞ^ø·U`¼GÔzÓø•ÈN¿·,Á×Qàm‰tŒiúj« uºòJH ÷§?Ÿ(z…`@1DØäxz¾“@éúí­¾¯sÃßNâ1ÒèÍA¿kð~Ú· A¯èÑìû¸½Ê&ÛÊ{[ð¶ °½ûµl@ú¸FÐà¿+õ¾¦žø\ño«¾Óõnç<»’ßñ¹Ë 4‚ÞgÙ}Œ ûw÷|½O{½wL"€ýApÞ`O§_V~¼¶ŒàfxõkJiÐUê‡qýÓÍ6_} ƒÁ`0 Æéóf0 ƒÁ`0FL 3 ƒÁ`0£&Ð ƒÁ`0ŒQè ƒÁ`0 Æ(‚ tƒÁ`0 cÁV³"ï$!)GB4±UZuT­VѤS†µh¡¯´*FÚ¾ÑHÅGåIpdé5‘êÞ¨SÁCú‡G j­ç;%}„qó%X8FÏ$}…Žó›(/ÔcW °`$h˜E ¡æ]§ßñÐ@±|OÆ‚ô’-PYê :Ðßî@6a¢ƒ†8H˜Eÿ:½>Ó 6ôä>‰”^Éú†0àñøFÚ*h:ß„À…•ø^¼Üéh· .<éMç}Ë?±°#€Ïr*~WËàñki Z½Ë¸Ý'¹ç Åóè}³_IÔ·=o4}tÅA„׺MAî,EgßmåeŒŸAŽ›í#Ì¢B{`"±¿Ý÷3¡þi‚Ç„TvÑA=³@õûæóï§ýèóõCLúœ×ýèÁ¼íc!± ¢Þ{Wc\ûß/¡êµ;øñ¶Ù_ ÷þ; Aë±í ,¨‡X>jã›cô6ÕÐÖ2:‘›¹jb•áÀІíW×8Í‘£nñH&ÐOAkR/ügvÄ„éÚ„²dŽý–èf0 ƒÁ`Œx¥U–µ'',kONÚ%ÏH–†±µí‡æUT}~w©h °šôðÃúIBx'ɸìÿr¬=GИt#mƒÁ`0 c`p¼È‡$—¤†$—¤&Ì_7½aûÕ»xÉH»À0~ŒYñ¯Œä¥¯ÌP†µDŒ´- £—Å‹&qI‰dÿ*yÿÊa½¸&$F’Å‹óˆÃáÄûï~ëçÀ`"69³–Î"»_¾³~ØÁüUËHhD~Ú¹‡Ö”V wõ ƨBИt)Ëÿ»0nÖÇyµ[nÞU½áÎã#fËHU|:–½;,÷–û–hbªãFÚãtB£Vâ/^!¼ûÑ.ù»J Ø1)Ñä׿XÁÀ‘£õô…·JÁÊüß?^!„…iñÃ¥ô½÷¿—à‚ó ¸3²¸×^߆ý*ƒæ ÆŒ‰Å­·ŸÏ›ÍV&Ðý&15‘¬þùµ\Wg¾|gý°öYXvÝ*’˜‘S¡ô9 IDAT[jJ+Fõ¤9c¸P†µDd\þäò„k›J^þëVcé9Æá¶…Yì'‰ ßL™ü«ë/gâœÁ8V› qádÆôLnñ¹‚^wfž3–;gºëoÕÅÓyBÏŠ #K—LæÎ™>–³ZíLT0 cÐÑÄTÇMþÕõ—'.|3e¸ëæôK–_Îq}þþ¯@AçðÖrdßøà”„ùkgÒW88ƒÑ…û«hê˜hR0%5èyT—FÀé” Õ`lf9VÖè'À§æ§÷”QXØ;ê÷ÊkÛ¤O?ß-WUµ0ÑÎ`ôƒ·Ÿ|jtZT—cç ƒ^iSe]÷‡•!c_úúû‡«ÞaA·)»Úõ'Žz±Ë«»ø¼®X”¸à9Lœ=„CÐP|ŒÓŸ}E2$㓸¸0¿ó‰‚¼Éi¥À e(ÈKxÊ›âÚÞÔl¤uõm=æà¡jºýÛÃr%è F¿8²{?-Üö54´Œ´)Œa„гË !”$.xgNÞW,âÕ]íù pÉð²å!)GRGÚ–³eíÍæ³2&ëp¢PðÈÍŠç²3bIb\©¬1ÐcåMriy v¹ÎL%:­’Ô7tÐVCU©—ÀMŸDôz yí¢¥ËÞg½…Ž“6§æ§“õ_{Õ–žCÂõ¨¬j¡Û¶–W]4+ÈO#ï~ðƒ_Yy®ô¢boŸÙÔ11$4Tæ#š›^ûÆK&Ç¡ªª™šÍV„„¨1yR7n| i3tÒ’’ZZr¤î„7¬ÄÄ(2iR*IJŽ"ÇJëèO*ec‡ùDÙzHN‰!YYI$=#ž;ºhyY=--©¡6«û%&G“ðpÚÛLh¨7øÙ–•“B V‹•ÇëýöI‹'º5ZšÚ`hñnˆH=¦LÍâ⢈6DƒÎ3 -Fz°èmmiï÷ñ@DT⢈ÕjCUy%„`LZ™ŸEÂ#õ¨,«¥‡ R“Ñ´ BÆŽK%icSHt\$Іš&ZYV‹êcÁ—˜„hDF‡S‡ 5”çy¤ç¦’ñy9D£UáØ¡ã´èû^ùCÃB0ýÜ©$.)–´·´ÓÃE%¨.«9áo¯Ñi–JÆdAxT©-¯¡%´¡ºá„m›‡ì)9$>%žT9NK÷¥¦ŽS‰›}¤ž ´¥® ‚Rôq™ÈÊGD§Çö¡ÕGÊ!K§E¤d§C©V¡¥¦¦ö^7[Žã>1š£Çá´;‰ìüñ$ilêÊ*QVtˆ[ '´Q¡RaLN’s2Ixl4š*kPSRNÊFlþÝY™HT"°¹2$<{wnþƒ—«÷=òÅF*óCú€Ãz&üüöYLœ/ªH"u8ÛFÚŒ3š¬ŒXòÄC—(2R£ýF°÷UÊÿåS±¹ÅäwÑy辕¤ñIÜß^Ø"<\OÿñäÕ ­FÙ³ÿ“/ %KWß¾à†63­¬j¡i©1¤`J·þËb¯›ÃÔ|רxaq%ýé`µ,Š2¦LNã!^oVbbô$))’@aa…W¿¸{ß=ITzñeï ¦{ö…V«Â}÷½*êÃ4xè÷«ž÷ ß¸aŸüÌ3Ÿˆ»ÿš„Ürë2þúùŽêóÏýã3©¦ºïQ{F…{?oÅt¿·ím&üßckÅ=?–x•±`árË+ùÅåôWwýÓ+6o¨^‹ç_¾O ©Ó‚ËW<褲· ÿ÷ÜÝBDd(}àEéû—H%„àŽ_^Ư¸d.§P¾¼øå×~Õï›÷¼%S¹ŸÝ»š+=\Iÿóg¥Gþv7?~òX¯>ÖnèÄ3¿,íßsįž?‹¬ùÅj>,"4`ù;6í¢/?»V2¶ù Ú‹¯]έX½„Û¹y}óŸïJ¾ð—ëU÷Á½Gè_î{F²YíXºj!wó}×q eς΄Êïþ÷Cù×? úÎZ:“Üöà-œ.Ô+ª.€Mïm¢oÿýmÙiwøå#„àŠ;¯â.¹y•§M„RŠ7Ÿ~Mnª9±¸ï‹‹o[Íͽp!Ùöá&ºõÝ ôÿü‰ Ð{™PZtÏÝ÷„ljïðËû÷s‰)xí‘¿ÓŸlî9~Mˆ¿{ý~Ñmò¼KÏ#ËoºÜ«]eI"ëþòoúÍ{_m·ñ³§’›ý5 ‹‰òÝEöløš®{ìïÔbêÿ.cpè„HUªaŸ”|&’r8mÂÿ3óàsÿõ=D˜@@ÆåÉÎÛ’7Òvœm¨ÂyI™@* &¥pÿzê…RÁ£¾ÑH7l=(×Õ·ÓܬxrÁ²Iüôü4ní‹?S\wûKΆ¦Î€7ÜI㓹Ûo\Àµ¶™éç»Êä†&#ÍL!N±×ùÂâ*—@Ïó÷CwûŸWÊ6›%¥utâø’™KÊË›¼FßÝŸ÷ðÆÒ¥S¸eËò¸ýû+墢ãÔb±#??ƒÌ™3Ž;ÅT®±©íÿè1÷Þw ñÅ39øvûAyßÞcT©0kÖ8îç÷\Äïøö`PA«Pø×‹÷©iqD%|¹~·\r¸šFÇ„aþÂ)\FfyüéÛ„gŸ|_Úðù=åî=Fo0~BQ«•°[{`þ´lŽp®¦Õk‘•BJKª{Ú#-3‘DD†B–e(:ÖSæêë—r_q.'I2öüpˆ;REk«šiˆ^K’¢1çÜù?ü­—< š;»¼ò%¥Å­N[÷ÐêòZÚÜ` *µ’¤g¥`ÑʹܼófØÄhþ[ ÚÑ"c"ðè ððÉ›ëeCS;2rSqîs¹‰ÓÆ‘›î½–;\TBop w¤ø(=°ë µYí(˜3…Lš>\uÇå\EI¥\ø]‘_Ÿºá—×q+¯]A`ߎBzxßjj7"5;,¹t19oõy$5+•{ä¶ÿõë7?x+·ø²¥võ#=´û ”òçM%7üf ·gÛ®A…KLOÆÿùgí²àë6ÒæšFŒÉIÇüK–ìüñøã[åþ¸únÙÚü-F0.½çF2uÉ\²oëNZ²«‚B‰s§aÂì©äšßÿœèÂõXÿß·ýŽcÙWKuBptw1=üý^t4·"!3ó._I¦¯XD’s3Éc—ÿL–ÄQ·ãI’É3~’DlÊO¿ôiCÅG¿.ª:˜@÷!nöG )Ëÿ{îHÛq6¢ŠäEÙid¾þCĽw,æ• GËšè÷¯u¶wX(Åg_îÇŸí“þõÔ5Š˜èPr×­‹„‡û$àÝrñüqÜÖíGäÿ}âS§½g”9Øóþì-ª/½x—˜AâbÃH“‡J^÷äÑ¢ý.W˜ââ*yâø¾ ?+/o깑äwû¥76vÐÆÆŽ‹›åË ¸çŸß ­{wGO™ï½·wß}!ÅsøÕ«çòëÞÙ.Ùl½M–G.ºhÏ>ó‰ôÉÇ?ÈîeÁßw‡|Ç+ø«®97蜞U—ÏáRÓâˆÝîÄïïY,Úw¬ÇèÝ·¿‘zä:~Îü‰Ü­ÿs¿}Û~¹ËìQÇŽÖP³ÉŠP &çe’=?ôŽ>LÏ!Py¼¦e$üéÙ^ݽß]†›EË\#ø_~öüÜSë¤Þ%ÔA €—þñ‘ª× ´Y)é ¤®º‰ÞuÍ#¢¡¹÷a{ý_Ëÿ^÷'A¢Á…W.æÖ¾ø™—ݱy7ýäíÍ¢¹Óì¹;€O×n¦O¿ú>gR&™³d:ùn란¿ùøülr`÷!úäýÿlWŸ¬¯jĵw]Á-\9›³l&>~ý yíóïQÚýVfý;éÏÞÇÌÉ#ß°’ø ô±2ÉלO¨Lñôož‘÷nß×½6;Öï [?Ü‚?¿þg>7?—ÌY>‡|÷åΞüÉ™)dѪ%^yâ%yë›{&3mZ»^ý‹ë¸ ®¿hP®y9Èñƒ¥ô¯wÿ‰v]o¾ûØñéúÛÿü™‹NŒÅy×_B>yáŸ3S—Ì%o=ö<ýæ½õÔí¶¼õíOpÙ/×`ùšÕäü[®$;?ÚH­½¿yLJ".þùM„‚7y–~÷ÑWÆîvÿfí'ôþ7ÿÁ%d¤bá5«ÈÖ7ÞcþÐÈÙ)AÉŠ3OWRÎÿ÷BsM®±eÏʦ¡(Ÿ…Yô@›P¦Í¾î¡å„“X»ŒªAmL ófŽå&ä&rðÔs›Åv£Åëfx¼ª•¾øæN Î[4 ä];ÿëzq>0 ‹+e··ÊÔü´ž:2ÒbID¸ŽTרÁàr±)ìö/wû›»Éïi/,:¹˜ÍVSOqîæ­·¶É Ó©‘”åUç 7,â!(9RC]âÜ›—þû¥ÔÚø­ƒBÁãÚëó°qýn¹pŸw´ ‡Ã‰gŸz_r8DèÃt¸lõüžë,Sì/*sM˜žãu]*˜–Màµÿn j¶×þüîôÅ{K½êKHvý¶»vñ§”¢ÓØhW¿øï³ïÉ­ÍÞ>ìm­FlÛèj·Ìœ1~}«ª¼ŽúŽª»i¨i¢Ÿ¯Û"ÀôyyA¯Í’(á…'^“ÝâÜͦ¾’©LÁ <Ú[;èº>”©‡Ë¥Ön¢0vBñ íyÓ}7B6¾û%u‹s/ûªñÑKɰòú ½2_zËe„påËèÖ6{奔âÝçÞ‘ÛèïßïüõÚe4ym«-«Âæ·?£°ìº‹‰Z«p¹?Å7ïù/¢ôÉsoÐŽ”j–ÝèíþrÙ½·A©Àž/¿¡;Ýâ܃Žf>þÛK–Þ´š]÷‡‡$ƒWsì¡èàx‘Ϲé·ç«cª~Rõ§ü¡(ôt%ëÚ‡§óꮓ:bœ2ŠPN–VÂ"¹ >ã³TT·ÒŸj ³Ï¿Ü/9"r»ÓûòÝ®2¹Ód ´«_´wt¡¢ª¹[x§õ\ ºÅº{ôöÿT-Ë2EÞäTÎ-šb¢õ$¹[<WœÔ§ow ˜¯½ÝLMÝ#Íqq^Ç?n¼KTnܰ/`^Q”°eSaÀŽŸIBõZÀçŸø‹{Àåƒþý—`ÎÎMñª»hŸK`»9ÄÅG’„¤hRy¼þ¸ó ÜÕeÄÉDèö)çy“¦dºÚiïQ¯:»'›^sóùüØbùT°Û(üñPÀv¨­jÄÄGöY†R©@|R 7%‹LššK&MÍ%¤{\=>9&h¾šãu´©®Ù¯nK—m®ÉEßï§’äÿV¿±ÖåB¥T)Ö³]¡T k¢Ë—þ‡­?½0Øu˜–è%ð3'¸ònÿl[À¼’(á»ßʯ±ªÇö—,kÇg[)htZ$¤ <œóÎO·±_Äî ®cK›˜ãÕ—²§Mv¹mù6h¹G~ØK@ mhÈ€íbœ]’¥†‰óÁ@ИµY×>wBùç«¿j«šè®ïÒÂ]GèO…¥Tìç|‚@4Ô¶Pä»Óh¦¢×ì[³U×ÇåLÌ úЛt_S}ð0&£™FD‡“¦ºÀiºL½~١ᡤ£ÕÕÏRâ{÷¥·¬"’S X?/¸ž5•*%"c#ah2€ã8DÅ»&F6ÕÞ\ÛLÀC,oÞT’1¡w¢­ë…Íb£ÞðŸÌÚ\×´o[Ûá°Ù¡T«G*,æyKY›»÷Å$'ôlÓ…ë¡ sMø]xåE˜uáÏ%ïü²,»Î±´dTýtt f1N’2‹á“ÔŸŒÀHäÄíõ™…:ËwmŸè5c·¼ÈSZ†5óÊÇf"³×l#Lì Îܸ½(L6–M^Dâ¢õÚÚ»ú¼1·Ì45%ŠÄÅ„<::-§,Ð ‹+é«ÎARb‰‹Õ“æ&#Íëë…>a‹Tɹ9‰.?ôãRAw¤—ú†vÚÔd<)[öàã´ex¸Ž‚+ìm{{ðhmAöÅt‹{K— }¹Z]ÇÝý[¹©®j¢­­FFò§fsÛ¶ÊÓ³»z© …{Ké̹‘?-›;PxLrûŸ9XA§—úûfË^¹Óh¦7ÜzŸ3!$§Æ‘äÔ8rÙ5‹Ñe¶â÷·Ê¼½E?q8úp}êã×ZuíyÜM÷\Á@CM3=T\JMF3,f ¨L‘”š@^0›ðBððÃNG_¿««r§3˜}½ÆyŽ€Ç&Åö|ΟׯûCHX14¨>BžwÙklóžâ&ؾ¼ySÉâËÏó«³³ÍH6¼ñ‰ßõ±Ó¼÷þè¤8DÄúES9!†àö&ƒËEGB(¥ˆNŠïÙŸsNÿÚM¦'è³—0‹#R—13ôäýØ^NâÆ^õèŒÂÇ>Ù2˜å²I¢pM M=˜>Òv0€˜­µòãCH¿‚ ôA¤¥Û¯;"\ÛçÍ2*Â5ºÙbº°gûºýÐ ¦æ¥“’£uˆŒÐ‘ºúvÚÜÒéµ"XQq½êŠY(ÈK#ïøò»ýÑ ‹Nnôüd0»¨$ÉàyáẠé‚í3´¸|Ó5Z Î Â72ÒÏ`ð÷e/Þ{Œ.Y>LÏ&ß|U„¼‚,N%(*£P´ç(€¼iÙäõÿ®Gþ4—ÿyÑÞÒ€.5…»KhÑî12Z)ùÙÜìS¸sæN$º n¸ýB..!âía‰‘›…ëﺌ€ç]Þúé·Ýõö6Ãâ æ’…Ì–ÅA)!<¨@ ñ®ÑÞÚúà#f§ŠÑhAyE›GòóÒ8V)ÞþçnŠ÷WÉ”S&§q1Ñz’’|jþç'ƒ$Éhn6Ò„„’T¨$$DlÛººV®‘Ù„ÄHR]åï' I).×–ú:ÿ…_ ÷•Ò%˧!z6É›HÂ#BpèÀqjµÚATU4Ò6C'rÆ¥’Èè0äŒïŽˆ³'°O²›¶V#¶mÞC·mÞ#i´jÜóÛ«ùs—M#ç]4‡{ù¹e³ièß‚gO'<Ï¡©¾•nùôÛ€¯1ÇÄÈÛÍÚŠ:J)!)™Éä§]{Ú3˜@w–$ †&bb›‹CAêˆIŒ xlÅ;öÑâûh ²R„†ë¡êžÚRÛ8às;:9Øx…s÷hykm¯LGs+¬æ.hBtH›FjJÊ艺+*{‰=ÔTZ-ÏQÙkêA…H»ä™™-{W|6XeŽ @ÿUpÎ)¹¥ ¡iB5±• 'NÉ.b¦ó]m…Å!ÊD6Š>H9æº)M%¹Yñ¤ä˜ÿMú‚¥“xµÊµxˑ҆!À…Å•òØŒ8¾`J*Ñi”®ŠŠ*ýêì4Yq¼¢‰ffÄ‘ËVÍè™Tº÷$ýÏO–ÒÒZšAÎ[>•ûüóÝ~vr‡ÅKóÞòê Ý+—j°|Å9Üÿý…_¿ ×aîüÉ;Zëwln?ô¸øH²ò’Ù®öò/ÞwL^´l*wÝšå¼ ðè겡ôȉWÇtcµØðþ››ås—Mã .!Š˜M§îÒt"¢bÝó\BØ×ÏH¡0ùŒ h`·ÚQS^CÇŒC–\ºØK ÷‡Š#ÇiLB ™·rÙöÉ×~y9ŽÃìåsE*%f¤ }üXRy¸Ìoßì•‹8lv4TÔ¸ìY+“m hÿ9+¨:R浿â@ ?{*™wé ²{ýW”Mþ”8»äðY¶2Ô M(KÖ&”i- cedã¬â?ï½´‘¶áMüM—¥õGN–ظÁbû¥²[”ÿúÎ%BˆÎ{!šäÄò³çó°õ›ÃryEß«bž*ûŠ\£åÉI‘dÖŒ¬îD‡M,Úïî—]:ƒ€Ú:mi Òp¨xëm2¥“&§‘¥Ëòý®›×ݰˆ‹<±Öé”°ö­¯%¸ð’Y\î8ïÈ)‚Àãž_]Æ«Ô ˜:-øpÝv?ßÚÒAk«]#ïË/t-–TØíÖâÆíær^÷þ…ÇdYö~–ÐjÕ8göDâ^àÈ—Yó§ÀõÖ ¶:ø¤ÃÁ¤Á5Iq‰1dÒ´\/ÃGpÓÝ«¹˜øûM¯<õ:¥”bæâdù•þ>án£Â1ñœI^û?yåcJ)Enþ8Hˆ_|ó¥$:!xdšrÕ½7ù…QŒMNÀòë/!°uÝôd*Êž:3Î?×Ïþå7¯&Q ±Nl~í}¯þòÞÓ/PÑ)"kê$\tׄãË ­>æžÃÆs‡NQDd§Q“5ƒè½ÀpCE‚µiƒUÞYû=ó=e(Ãx)~ŽÒÜyt“6,õB6Š>P <ûÂWâóO^­(˜<†{ó_k”ë7ÿ$ÕԵѬŒXîÒ•ù\x˜–˜»ìøç‹_ùâ…Å•²L)8B Õ*ÑÐØAƒLú,Þ_E/_5:­ë¡¢¨Øßf¨)-­£[¶ËË–ås¿èJ¾  “ìí^Itöìñdþ‚‰Ü?”Й³r ßÛ!¯X9ƒKJŽ&{þ.á‹OKWÓˆˆP,X<…?Áå’òê‹%S·’¢}Çhò˜X"<¬;Žôn‡¢½.îžÐZ¼¯Ô¯´:5yôé;„–¦vºs[1­¯m¢Í íHL‰!9ãÓȬù®Ðx[¾øA¶Ûü—­ ~ÚWB›ê[i\b4¹ÿ±;ø]Û‹äÂÒ¨˜p2kÑ42nÊX²{{=gAà7CÍáÂ#tÓû[èòÕËÈšßÜÈÍZ:“î(¤MµMPkÕˆIˆAZN*ÉŸ›Ovµ‹Ü} §Ý+JŽÓï¿ÜIçœ?Üõ绹 Ó'ÒC»¢ ¥ó§’é‹fâ…4onÁ)Û±ý%4%+<ôÚ“dÇg_ÑæšŒÉÉÀ’+WЈ0 íØøú‡'uîøv7½õ‰ßÜs¦ dW¥“æMÇ´eó\Âÿíi[£·—iCy¾xáMzÉÝkÈŠÛ®%“æÍ …[vЖê:ð¨¤x$eg`ò‚Y¤ò§Ú98+ª2‚³­«MNº$´SÖÖCDÄøéFYgµ@WGÕ©4ñI#mßԋtÆ]ì IX óJ=»˜ {Š«ä›îyÝñøï.V¤&G’;o^àuþï?X+?ôøÇbC£‘öo]Г§ÓdEùñ&š•éò-.Þ_ô7.òYßWx|ØüÏ=yâ±÷¤.³ «.Å­¸`:·â‚é=û^yi“t´¤–Μ•ðšêpˆ¸ãÖ¿‰¿úõeüÂ%ùÜ¥WÌóN4»ð×'ޕܱÐQ¸·”^¸jà@q9•$ï¤ÍMí´®¶…&%ÇtûŸõkS§S¤m†NÄÄEUW-ôû‘Në?Ú!¿ðÌ»ÃÖÆ6‹Oýþù7ÝÁÅ%F“%Íã–\4€+ß?ÿôŠd6váœùÃ>IÔÍ+O½&—•›~}#—›—CrórüÚÎÔaBÙÁ2¿6ÿ÷Ÿ—-f ·ôŠóÈÂK‘…—,êÉûÞók媣 ^[V…·ŸzIþí rWÿêf¯òªJÊñ_>&›;LÁ²÷É{}‰¶5¶àÜÕy—ž×³RŠOž{n|i]ÀówãKkiÕ¡R\ûÐ=$%w,RrÇú§ÕÜ…²Âì?Ä4;hàrþüH½eÑ%KVè ggÔ)Ïz&ÁbÖ¸ y;ðu×Gªå›æ»>ïz|¤}ÐÓ/}:;uå?—Œ¤ ŒàÔnî mþ>U‘y­ôN’r¯Lp’õŸÃçNçÊG=¤§Çd%¿ÓÀcýóeõ~÷ÎãÞæ}ZQ_¹KÚڽغo­ÞeÜî“ÜsB™çÑ€J%`Ò¸$.+#–$ÆéIUM--o¢‡JêeÚãáÝŽ¹Yñ$$DEjjÛhS³‰Ÿ ×k¢o[ùú ¦§ÆÈÈB(E]}[÷º_[ò&¥Ap5Û‘’:ÙbµwÿÖ½õ@ff< Ó††vÚÐÐîfÊäT );Ö ûŽR»m?n Q«¤ª²™¶µ™¨GÙ=Eee%’ñÆÄÄHR^VO¬G¼ IDAT¦õu­T¯×bìØ"Š~:à~¨ ~ý4#3de%‘´ŒxÒÙÑEËËè‘C•´«{‘$ßcrV«•7Î5ÒÞÔÔŽ†:—’g_JÏL$aa:P ݱ®ýû|JZ<?!DÅ„#,<„t»ÐÜh …»ŽÐvC'‚õ¿ž¶òiûè˜p$‰#–. ÊJª©g›ºÓ…G„bLzq:œ(ù©ÜÃvW:¥J‰Ù HrZ<‘D e%•ôØá jlë„^ŠÔ±IDE”ì/óê )±$*6Æ6#j+ê¼ w[9v|:QkT¨«j í­ Ô³kPð<·è.;\FV{Ï>ϲÔ5ÒsÓIrFRâ‰ÝfGGk;­=^‹’¢ê:|&>vMËI#c'f!69ŽT—VÒcŽÒæºf„„… 5+•ˆ¢ˆÒâêN÷=IôgÜÃͽp!Ùöá&úÚcÿ–u¡:’9)›dNÊètâøÁR”í?B6‡ßñ鲡ҨHce 5öD­¡ÐéCðíïqðû‹n“›ªêŸ–Œ±SrIBæ4¯AYÑ!Ú\UÛ“§·lŸy*RÇe!!#•Ä¥%Cr:aliCsU J÷SWHOßI¢>ÇÝÓýûsÐëiO;ž8WÙÔ7ç÷À÷‘@¿WÙ4X: âui÷H×ç„Ú>އz§€wÚëåðBZ£ò4žÆPñѯ7U}qwyß©nW¿¦”KqV ô¼û¯\žû㸑´*»hKŠH¿]R†$R&ÐG {fõ?ï›o=®þ¢Óö~ ôžtÔÿÆã—Æ×ЀÝ÷{°ß'è”í#Ìò¿™û—ÝG[õÙG·=¡þi‚‹•@eÔó7 T¿o>ÿ~Úá(_°ßßó¸½ûã‰"¨øoyÞ>é‚¶—GÒþÙ@ƒœ›¾eº–¨l¾Ý%ö*:ým$Ð¥;ññø¤ëw&ÐK WX-øZÝî˜øÇØà+N1¶ƒóxæÍàKèè@?«'‰*BÛØÚ£Â9k´­†Ò—Ùi9åWÀ ƒÁ`œMEÍ-rú­­#mËÙ‚Bo¾`Æf>£ ˜·Ëõ;<3ú@Ðu J#2†Žˆqj{ê¼±õè‹¥#âzÌ`0 Æi‡C–ñ~Gƒœº&¢U—¨`+A  ]Ç  þó$ÑGºþF‚ÖÄúi@ÒÉ\mR´W¼§ŽÌ¸’Eua0 £(€OMrø­1:Ÿù'‚¶sP´åYÅEИx^iU8%c4}Sh[ÑGãM ;ù„yL¤3 €ío¡%û҆ʺA-×nµáÕ‡Ÿ¥ @§¡ýÄ£ŠoLjÏ$¶ì õ#mËÙ¯6kï$TRœÒ$Ïaè—,¾œ3ð|„‚¨m@Œ˜T ܵ˜ßv²–h“JÙèùiá€I¿Ô7?¾5^ͼ>å|&Ò ÆYOiñZZ<(a—½"¾ûl ó,<­)Å–ÎV¹.Üî{ ó;¡D›P®íªÍ=¥–ÃìƒnSvõÀÿlÀ ´ÆGr÷–çÞ̸¨;©QpUDƒf°Ž1´(tœ\ðpD§Ýã4”¼$Piħ10 ƒ1*°JÖ¶ÕËmãdË„c9-H4R¨£êÕ'NÕ7§qJ€Ý@Á]Àñ‹m±aÓ^Iè¤_†µD4™›~zV!ÚÚYtƒÁ`œÕ´:xËP+‡\¤iϼ9Â@NcuwF@¤SÖ&Ãìâòøà–¢ç“P TëÚ`GÐéÚ×\p5pä•Áµ•1šI¿,Ä2Æê8ööߣ’/”tqùòP¯~É`0 ÆhB¦Å]t—­CûóÈ–ðl•ýŧÃ,Ð :]§Šc ðð$ௗN%P’ü+¸óø©—Í8]ˆ™®±êR„Æòµ#÷oU†Y%i³٫ƒÁ`œñí2Ñí]­4d¼Ê:ùªØve8ÏæfAœ¦Q\”2ðÄ~À ^\åÚöæT&ÐÏ>´ñ qÒ½ŠæÎãveÙ;ë"M ‘BxÊ*I’È|ï ƒqÆQk³b[W‹L’8{ÎÝÑmÚD…8Ò61ŸÓT »ùïÀGóC4P’1ÒÖ0F}†ÊQðª±í@—ºüý—#%‹‚ÓDL¡ÚÈ)’*$‰uƒÁ`œ¶ÔÛ¬(u˜h™ÍL1¼sÌš°6}¦ŠEJ8ƒ9Í:$7¸º1 À11v9Yc‹œ¬©·D¾eO±¶eO¡®å˜¬ÐFäÊJ].UTzÊ+C)!ÌgÁ`0£‰RtI",¢“èD…d–«¬èGØbMׄ‚X«2Œ¹²œ œ}L °®¨. J ‰M`@%H)ËCL)Ëa-2g(.W·.ÓXÛ©àh”x§Yä¨ Â+5”ã•'ýP×?‰?,ÏŒ$è—᳡ï:éHOãuÛC¬=†Æ¾þµ½WÝ'õsúo|êÇ?0VßPßʰ t˜ú|¯a„îùî]V¿'¨ 'ÝGú‘1`’>Mîÿï38mJ—Eí’ »(„ªBxYÆKÊ(Î:Ee-˜oã”,dâÙÆ ÐÇ d~ZNŽ›­³Ä͆ÅwŸÓ$q¢•q@*6RÏ`0Œà(B8YвøÏŒ^Î^ãúÏI@Œsdmaœn(ByYÊÖÊc0 ƒ1z8ÍCÙ &Éõ9¶ydma0 ƒÁ`0NÓ\ _±0é]ŸÇ³‹ ƒÁ`0ŒÓžÓT êe+-s»7PàÞ]#jƒÁ`0 ƒ1 ³úkc€žO&€A´êÖ0 4 øÞýów+›ÏNƒÁ`0N¥a¥¼Z¤œ (d…D‰R"D)Qtÿ$B2%‚LˆBdA™¸>»·I„ðÄ=ù\Fwtî¹èî¸1Ô#Œ,¥®ä½á]ˆkâ ñŽ)Ò›‡¸·Q¯2{Êö¨«ç³wZWԣ⻄B=ëî©“¸·õÚF‰Ä3õJëÊßñ„öõ<®Þ:¨·]žû{ÛÄ#+= \wO ¡Ô¿NÏc„W^ê.»;Ò”ŸÝÄÇH„Bæ(•…ÄQêþìú/»þs2¡Tæ(¤îÿÞßeÈ„R‘—a$™²€1£‚aèë&›æn™¹G/>Ü2 ÆPBEJ(›š;8p'°;êHÁQ Ü&ó‘*DZ©a•ùp+Âm”³AÐ9)ç-ZÑ­¿ÜÂP™¸7ú7/±è-h{$H€<ÔOð\üAöØå'B‰÷÷?OÑê’="•x?)ÐîÀªž¢_ö´Ý¶x>4ô!лÓv·—@÷xq‰b¯mî¶íÙFÜ,=— {SoÔûÇ]g·^(=6{ˆéž‡¡žc„§Ýî´uPÏãô,Û•¦;=íµ› žP@r=íø÷JÝmâõ @<€R׃„«í‡ Q‡ Év…Hí Q¶ µ+DjDÙ)HìZ3LœÆQ\tf`ÕvàÕ€À: ƒ1ʰ·K|[±U# ©S’ YeÂApÝ:Üx© xß=¿øGVö!à×~§ë>$}§ó¶Ë×\¿­Á.T4ÀŽ€¶*€*K®Û1¯æ\ñ“ÃxI-ˆQy«&V`Ë€Ÿ"Z'åbº$!ÒJ…H‹ÌGØd!ÂJùpBí2ÏËnDºu·Çˆ3‹²Ê8­ PH"Kv½‰ëXr4½â‹ôÚäq=b®4îcé\½ézòu—Oåe»íö}r÷{[‡Ôlx³AæL­2o6Ø•æ™kÜdvˆñkÔT%dŒò´‰é/‹”4}ÕÚYØš™zÙE„Î}’—4:xÝÑËB GC’y§.Iጜ¨FʲP³ÓFÑZlÓÔ}e=úz›2:Oc³"¬“W³¡.7J‰’IMÍ”F‡vL‡¨Ò8)×+Æî{Ö^ F¨ NíTpQfN^¦]*»dÔY$“Ú.1ÿöþs»¸0N–¶VµyWg˜Ô.*œ zѯ€i3 å¸a)÷Š^ŸQMôŒxÂG »¸Zoî%Œ{ÊñØø çÞ´£‹½éà]žg_¼·÷ŒVÒ¡èî² u]¸@•ªÐ bxŠÿè±Í,“ê=vuå—ÆðÒQÐUÙ“–ê;G­‹š¿3‡´o7†]¶š£7<É‹ U÷7S3£^Ehì9ZKôt­…Ê@ý7¦ÂÇãã焘“‡˜ÉYê¿ÎQ ·Õ¡.h°kÇ·8´ É¥Âeæ“ÂèNQ†Õ.¹¼¶û ûu¤ >èîÁ$Ô+O@txú—{æéÍç탮T T¡à†õ|WH ·h„p‹F9™vj­R‡Î*v©ìlÒ `ý,ÂTéP¶~Þ9q¬$<ðO +‡ÐÞ׿èÛ¿ƒ1ä(uÍ\ ±f,ÐXD¨+´«¼Ô­£´§^nT„Žže  ÅVMë—í æwo¤Ð0÷Û‹‘¶ŒÑ„’…šãç†tU¯ïÔï}¤1>å<}gü]×Ù¤K3۪˛#b»$…g”Óæv+WZÓ)Ô¶tõ„¤vGÄQU+yî-Ð='ÙPÏ$ðóö{mƒ×6Ú[†ß>oˆÇ=½;Ý.§Söš$ QÑ)5-ܧ‡ò4dŽDšuB¤Y't©R]d»Ó®GÍ}m´ÁúY€­Eš?o‹ŒœÊ7^ 7ôø¶2F'$OSÛ§ªmÇ·[Õ?=Ó•¯íJY®7qÊ‘ñ¤"%µë QÉj»ê™µœœÌÉî· ŒÓ^Ihúª0cÒâSŇÆð–½íø;¢ZyõðŽ® 7*‰’Ë›"¦ÖÛBF‹ÛŠD» £QöŽ@âYÄoÈC¿ð…~u ½({lðËÓÅÅ3ŸW'T¯ Sq”;C|ñ;»œäPe»PÕdæ%$&B-e%ë¥Y“b„s7 ¡¾¡ƒGqñN×(.œçÄ#èðé®bOÅ@c£™¯¬èPìÝ]§!ˆ×‰™Ù‘ްpÕ‰g]Ég5Äq-z“ØÞyÚ¸t'L Ÿá4nèCYWè3OP2w¡”ÊÄÔéEÚ|mÌlµ­d½EWôXc|æÕmá¹jûpÛá0J|ÕË-±W_!“[îä%OW#Æé‰RÏË9k"Ûš~èÒ?Ù7þ¢[G­KÕ °øx—~Z½-d$ºm£Y"EMNr´M„(÷ &BUÂT<å9~½û³G¨F×wô|÷:ž÷Š@Ñ|º_ uÿtu÷Ž §DÑi“ˆÙ.Âs Rà06F#K ‘"´§G„µÚV ¿ó§fÏägE9§‹vr|ï:޾â÷Œ‚qñ:1.>D¡e ¡Ñ$íiÐX­N21/ΞœªMÛª°+D¹Cg9íƒ$ 6Ã,ÐßOöÅ MÙ¹íÀMÕCSöé‡ä ¤æõ–˜s§8•O½ð ;}:à „æ^bN™£±~ÿL[¤e¦Îœ¸(Ô<\õ›*ʺwZbþü¡srL—ŸaÄÍÒY´ñ ñàs-1™«#Ú#'ªm#mÓ`£’(™[m ®ú,NŠojìäˆA$BxäÇ)éÒ Kˆƒî?‚îNç¹ÿ#è>·‚Œ ÷Ä÷uµp¯ |Ý­ì=â ƒeàX“…ûêH›Ðnq…À!;N+MK “x~t´®êökbÃÕòÊÙÉvZÑÝÆôÌä'€p I¡bB’^t8DòSQ³jÿ¾Fufv¤#{|”pƒÿÆõ &Ðýfþrþà/TäfZè.l­¢Pýrsìý¿ù›Ö¸"¤œµW›3 M$/-øctë—Ö:•Y×E¶îÄùN…ö"‹¶cs{äïpRj&{s¦š®tL¹/¶ùÐó­Ñ¶QH\2l€ÃA¨]æÕ"å†:0y³EÆÇ¥6Îä Xœª¦KÓÕ2ÏõŽ~Ÿé§ÀŒKÐɹ : ìÅÁ:3ÿÒ·uʽ‚.-ê5#;²ÞÔnãÖï®UMJ¯Y”nã¸^·F/J%O f$Ø€[yi›ò‹KC§ÏI²Ä%… ê[6•Èñ]ìô„¹¸œatµ«šßoyýeJΙ&¦Î@x¡3~Ùvä#Sè§›âÆßӢРÍRS¥CÙùU{äÇy)Töæ GÆKSîmÞÿts¬*‚—¢ò4Ö‘¶ét¡¤M$_wP%¡«²5r¬–õY>[Q  RõRAj˜TÓnã>+jVÀ¢ ÑbÂú8‚RàëâF¥¡ÓA®Y”fS+v‹ìÈ̉tŒÉ w~ÿMµ¦¼´M9s~Š…Œ²7"gÃ,Ðs›€ºÃƒSVK$Ð?8eXêBǧ-1_oILa÷ÎtrW…šBâ¡çZb¦ü:¶i°/–£Ä7¼ÓóÖ»DÖ‡±þt¶À „N¸3ºeÿSÍqêAÔ%)Ø®>°Š¯üdå"Ô½3O+«$P„=€¤•|ÝìD‡Ù.‘M[BÀ¯Ì‹sà ôZŒvîók•3r£ÄEy "ÎÎ袧„ ptþ’´®ÚêNņêÏ™Ÿb‰‰×±sVFšaèÛ `ï©•Q¨~¹8œë½?«÷D‹ÌÕ½Ñ÷ñ» É)nÿBÆ™Nâ µ«YJ_o‹Ì¹9ªm°Ê•”T½Ôûô3„¦¦3·–³ ¥ž—soj=üBktÞoãš!£'Äçh¢¸Y$Ÿ•ÛÉM4rJ(z†¹°Ôv؉Ñ&‘ñ ºAýýCT<½tZ¼³²ÕʽøMµjÅ”Xgj´vÈúX}›•Û\Ø ¼êÜ4»VÍ÷ûz&J2œ¢L žrCüaµ‰ä›]µê‚‰±ŽØh­{4©@@§NŽw@s«…/>ؤš7+ŪVó#ÖÕ’RõθÄñ›Mºœ‰Ñö¤´0ö?œF~?eZ`Å `ÆýÀŽ€Üm»Ú ¬Þ|úÉÈÚ7rP¨~¹9ö™¿Hüø #m c¸»2ÔĤvSç M€«~£5úö›)7kf:[ MU:S/ 3|®%†²Õº½pHÿÞoãŽD<4S' åO¹ÌýMvò³õ-ÂQƒcÔ ¿y¤Cø×ÎÅP•Ÿ­‘o;wŒ}_¥Qø¬°I!ÁH@]«…ÛRØ ¼vašM«˜¨}{s¹æwÿÙºáûÕ æƒÕ&’ï÷Ö«[Û­=ºìPI«âðQƒÒý½Õ`åØS¯¶ÛÅï#‚‚£‹.È4—2¨j*ŒCÖGÎfNÞ¤V/&þظ»;‚ œÿ pèIàݯ8Ljš9‚Ô¿oˆºq•¨¸på5xÓo ï0[µm¬êS-ËPdÕLHt(¯¿™°QÓ³œØéZ‹>]i¯ûÊ󪈎“)£e§)ä‚ó)‰#tܳ£^Mh‚SÕz£>ã²pãHÛ3R¸Ä¹»u’ZN™±-‰¯wð™y“]†FÁa~šVþÙô(Q-ôÞ¬N¯¶ ßV˜9“Ýe=5BI^šèŒÒ ´Éä$ÿø®Y(i¶q¢LÁ ;F#ß:+Ö™©±3?3N'ƒÀùú·µªæ¥Øù“|=A¬ßÛ ¼fÁûÉN@Ý}¤E‘“&ŽK ã£4òîC-ʉé^£Ûo®/ÕF…©åë.Ȳxnÿ×»CfN޳/bã8Bßß\¦±ÙDî®ë&›TJ׃ÝWßר·ï©SßvåDS¨N c§{îÕbýUç˜ÇgEõÛ§ûÍu‡B«ªÿÏÞYÇÙQÿÿ9çŒ^÷½ë–dãî@Ò$„‡ÒßRêBùÕ©R¾-õ~K–*EŠJ A‚„$„¸ll“lÖíú;ç÷Ç]»»w=«™÷ë•ìîÈ™3÷ΜùÌsá­&é:E“JÝê•WLq\ÊE±¦&Fx`ŸsíºÒØÖ7N˪f »M ËVä%6?{Ìö?·Ímöù­íyÈ ¢þ´Û]6×\³¾$ÖWX½¡$ºù‰r‡?Ûªs‚ià9Œ1~×t€ß~ .+}ùŒC?xàŠšÑéר¤~kÄvõ Ì63l˜¤È[m‹½óÝÚ¬d£N$/7 ÂTc(üvØyû+æàj’NîÅöè{?¨ æ]bD'9ï ŠPpÏŽ$¾m–DsGIœü~G3yþhŒÜ¶Ð¥ÏË–Yy£Š~·½‰kŒÜ÷×dé©gÁ/Öð5}l¡×˜hBcðÞÙ86Z éIÁŒ LoZèÓma‡ë“ø™ƒÍäG/Ÿ~se±"ð£gó)ͲR =øV¥pÓªüA¹®¾º·VXRæÕDž *5le}œœ­“5ÈI,œêS7¿S)Eâ²[„#øðÆ)ñ{Ükÿ÷KÇå6N‰?â^Û^)]º²0‘ŸcÒý´h~vòÚ+¦E‘0M§èè±&þɧض½{ÖX¹"/íÅá×OYV¬ÈÏšPÚ,õ¯m©`»wÕH—¬+mâ‡6ɤ޿.ö»`Ưʋo{õŒeåºÂ>E½Iߌ—ß”|à»7§‹ó’þÀþ¿™â<Ce(òvØõ­¯›ù¼L:@ ôCŽÐɇ\Ý·î•°ãn°Ù†¡c&ã„ 7:B'Ÿ 9G»/£Á#GTtY ÏFËrИ0Ðæ£1rÕ »qy™æ9xvq±•~r‘Gßy6Ô%Àk'£¸¼AÁw¬òë—L²¹ž•zEvíl·°¥ ºvý\1Ù'ÑlÏVOr·- è1í©Šº.(ͲÒŸLwïîVÒõ!ׇ4=ß1há»í`=/‹„Í*uk §ú5Ævª” ‡Û!Ò«.-ï;Ò(¼òN¥ôèærˤB—¶jq®2Ø>¶1uŠG[ƒ_9³iS}jI±[;q²»Oxq‰K]´8')Š„qf‡ÙŒÙä¡ ’®Óv-±ïýZ);×®ù–}†þl«.ˆ„U2]]ΣlA0àû8:9}yÎY€/=ðåòÑéר§þ¥ç>M‘Ýn¦T4I'0ONV¼qDO«¼­@è÷Tiäý¨í~gfm1ÉŒ¡%qêÙ°ÓP"ÂùsœŽP¨KP¸aêÀ,§çšMÒ)ƒåùéé—Xè/Þ8Þ¤âÉ(oPC$lAnïi φTôÊÑ©j(®QÄXê¿:¬a•ÕÓ|ú·TˆSsm†u•GŸÝYÍ_»ª`ÐÂW7(ì:ÒÈÏ™äÕÚ|×VžM.pè;6/œ¨ž1É£-ŸŸ­¼òÎÉnè5ë'ÅÏ…u­¶.F¶¾])W×ĸX\E”¢”ÍÆwûKK=Ýžód'ßÛ^%>Ø ÌœãWä왿nÓäÈ`ú³pUn -®Æ IDATü…'Ž:²óí:WÔç)£$Ð7û¾±`ÏLH«èà«øä‹wí0§Ù{‚ ûã¶Ï÷8ÀÙ_ܽ×ç½Ó|0)]º†!ÞœD2é×Q×è|óWGöÆ,×\e›ô޾ox?aí~Œÿ9®¡Åg’G“EÂÞ=Pßž—\kŽ(i:ª¦!Þ-c Àîà üŽ}uâÚ•‰h\ÃO¿rBJ?NžjáImZ?)¶`^P),pê~ŸÅH$ôi»y ‚ɪ³¾¦:ÊÚ_/N›éOrÜàuXét¯züPã°çŽŸèŒ°@ÿÎJ€w°ÖãZb7=pú§÷îÆÎ«ûF9³}äZSL™ôŽk²l>¤ô+/z¢èxbuêßPY´àÕó¢šhò”"._1¼ÇØþ6C{ßgxò4ÄV¬6Ë|Gü åxíö¸Õ3Sêu°M6ê$'Èl1S+šô° JèHRtM•†à6–9ÜdÀTÏÈkÖ§ËcøíÊô[VæÜ2×¥_=ÃaüsOˆ`° GfGôÈþ0YSj£Åî”üÂ< ]V`¥¿~»ž¯ kú´€Ä" …•q|í‘ëäÙšÉãW[kùîjäJ="Ûròôµ]óî„åæM€ÌJ´&}ã#'÷$ä‰.П?¥£›¦\`¨SÂlv@`á$EádjÈm›'µµæ•þÈl‡á’{áX”l9C~+Çnœí4®™•.L¿¹: ýçP˜l­ˆâÿ a‰Çlz@bv)õ±²ÈF# Õß8Áoœˆ )~‰þ`}¾ö›7kù€­Ãtðt’:dOŒ!³¼Ì£¿SÞÄ­›ìѧùPe˜\·²pHפÓ*Э,Hú]RF[EI®ÝX·4/)´º½ú¹ëfFßÙ[+$T­šT–ÍÎVßx¿JÌ Út€ê†)+vió¦ùU¾“ÛȆ ‹V ϪëcÄç‘ YâØ…Kó’~OGpïìé~µs¤™ß+«–å%¤N/·Þ4;|ðp£p¼¢…/,pj×OŠ…Ã*®®¶ t‡C Ë—ç%±ÇgyAS˜3¯ÿ©{#§Ð¡ØUw^è²ábŒåA7é‹èÞ˜õ¦›Ù˜œŠ4[ `ÍáÕH…*Ø‹„l¬Yå'•šâܤX‚¼^»-fí{Ëñ‹J"*·8rå~ýb­OgYJ £´›sãÝ8ÅFÛ¶cu»yøàt§ñÁéN ó¶F°qšÓØ8Í¥w^þíµ¹iãÄe3<¤ê#*¹™>¿»®W· JRE‰ßÛå3륵‹ó”Φ¹~«qõ%% Önã@°zQ®ÂZ;’—e3r³l­ßC„ ¸hY~»–%Ž­YYfmž73 v>–ßg1.^UH]©ÖD‘°ys²”¹s²”¶ö}^‹Q\âÒÚŽèpˆô ã½}2;·Wɇh”M÷ž³o4ö&dÆ#,з êíái{pÇã Z§ ³f3”ª¬jbÒ;¶\^‹žÑøÞ:\VöHöÊd<#zˆ¡†èÐSPŒaŽ·P˜áЧ8îðÙÚU‘ÇÖì†)Å}õâÙÊ0)?Ò(^~eY˜Üë Ê€0úa^’Hý3,JÈ 99£Ý “ñ‚è!z¢Aïõ>WCÉÎ1_øLúgÁÔP脞ūŠQ”k3ÅÅXÂçXCDÅ›ÐÍ?š€™r{p”i4Í€KÖ•D˦yÕsù $B•„ŽÄä±7éÀtqg ƒ!Qù\ÕU• m›¡Ý;¶;f/ÀÆÊ‹0E}™êªzÅ'Q\SÉ͉Xn!f«ÖÝ›Õ! àµguŽ2€k9 €ƒ» ²ÿ=ƒœ=I±/Óu×ñª;бO}5ů>©óµ•Û݈MžMô…—ðÝ‚{*¤ú$%@^"(™,-¡zŠËwê<€)‹yÕêJ÷»gàèM¨=©“ºÓ”PÀæÁ´p.¯ÍÔÎùªi|ÓYƒ¸ó‰ž5™×Zª rfŸ&ÔÖxɉ¬2^+X(eìǹFt£å˜Òkº+ª0l³›éÏMúšà–±š8ƒU½Å$…Ï.²ÆˆŠ Ãl_8®!»<øì-ç3W\=5Âc0 aHv—DCÍ ÈœÞ÷Ö&]1ú8ƒ##Ÿ^ñÕ)þÜÍ:§¦9Idá2L~?§¹¼Ý­¯Œüë/÷»{4!™aÎä×wð›Ç¤Ä´y)!¬$ºçŽTJÀ¿¿JŒßÞ¥ˆÛ_5Үϧÿ® wþ^ŽO_HŒÕ„¿ü0)é·½¸|ƒ®~â–8ê$˜­NÄüIÂ’ˆ2TuœâMŸ’ÒzdèþzÌ~ú ÎMšÏkó.Ó‚dê* òØ¢¶ÊCz7È7([)*W}Ïj[¶gsBÞ÷BRž»IŽ7Ÿ5Èó? »h›OÞA¹äk®fÁ:¼9ÿE7g¨-F¯¯Q§ü&MLú ÈEkb‚Ö =I0îðÙZ^ͨY(e¦} ‚1°žŠ6™ô)ÐÇZ„b{d¹{CO?¦sKWazý-Øæ"¶{'ÿ¾Ç ;ß¡øÖ«4á±-Ý­Ò?¹SãŸøGJ`¯¹Œ+.%za b Ž`xóã:§©™UáïHJ• ß|‡ O#´¥¡GïS…ª Šù•„åÖoJ‰?Ý•”]ÌiK.å5›±=oëÜóªâÛÏiÂŒ¥ª¾rS‡Ïµ?Ó[¾m‰ýþk1Ûæ¿&åÉó9½l1×niêÞ¸åôA³»1ýÈ÷­Q„;f(ê* rïÇC.]eÈáÃtÙÕRŽPZ ’›;²cбr†6|Ó_ü‘Ó¥XSg"cÉ*l\s‰*;ÌÐ3ä²kHûƒúàŠŸügJœ󧼺é:.U<šP¶ô"d|ø3¼¦õPï­²‚áßýW޹ý˜QH‰åy+‰öñ51{SC?¿#aÙt‹ Üôe1IY*ƒÁ앜N)À ©â ÿTÅÎ`Á^»èZQyõQEüëwcÖoþÓ¶{ÝóšÆ¿þ¨"#pãw­Q‡ÓÎùµžúE̦« åMå´›~âÉÄ(CÀ°¼Y¼6çRRëÁY¥ñ´ÎM_#%ÖÞáQ”úðü“y5g¶ <ñÿš|õÇ4þÄ›I©x¥4lqX@Œj=Ϻ0 €±™bÑd€ `L7‹¥™Œ½b0k8Œ=’qËÞLÓµïݪu» ÷¾¦ øbÄqß#]5Eh&„ù‰éÞ e¥Z4 UY¯"Q3XŸÛô‡–¤þ¼«…TEV¥Q5ümg#w¼Qés¿Gw7r»+ccJ4G5äî)ƒ À˜11¯ sûŽ6òûŽ6ò‡N4óÕõ1¢¨Æy9ŽŒ‘¯dÜbZÐÇ£`çœ2 ±Âb”ÑlÍlPWº¬I¼n÷¥[äÛ(^&&Ë_MXZ*Q½‡Xr–›©8dï꜒èþáÀáí©x‚ÙÒnÓ¯Í5Ý™îÔÀ*š}~ E(݃»¿ÇA †ê…BGÿ4ê¾z }~Kß¹ÔFוd¶¢|ÿ­0Ù_§¡Ç®ôëCQ¾¡$…Ç„ÉÜ ÄrìýÏŽ¡èžÜßL \+õн~wÏh&—–¹`n®mÌX?" öÚ…ûƒ1e£ï‹¾ùÍSÒ™š(!cFëÀï– —ä*ó¦ù{pîìûÿ¹Ï^ZìÒV¯Ì?™ðLÿó!a ô!Bs@3ÖSþÐM“n´]P°Ášm+äÃJ=Ö|Pù÷‘¿„+Bå=8zt‡·aZß8²£OvnÏV²œ¼Ô:U¨«a(+ˆX,šÊÜPP‚5¸r2W.[k’¹}™û$v²•& Ù3Œ‚¾\L?r§%ö§oÄlñC6¢·ü ÝZŠÔÌ€/wpBÄîÏü¢àÈJµ©Ä(V¢óV2<BÖwø§AÏKãŽÉ *Ãx‚ˆd[TÇ(*tô/ˆ{^Ïrlž;¡ u%݃:B …mgô¡)Ê Ñ.´qìÇ—ôOÏU!'" e¹¤Ϲ4h3ž“™EŽQqÌ XÏß8;Kh¨®)AÞÜU->þü1Ë‘ÍÜõ—•u3õE8ªàDr`³&£I}uŒsyåQÿ.Æ3¦@"w®øµað‹j(ìægVWÆ´H·äëKé[žwi»+ƒCp‘<{ ¿8{µå§ï~¥þ½š­ý*­+ºˆQS=²ÏDk/–NëbDB.6ûàÆÔÇ ÑDR_i´pHSõ‰hê\Dëà^4x©gÃK~-Á?LŽ.jØ œ-ó OXB4bÈîž>Œ×}QL®¿YT,öîßhEìößÚ"ŒŠº¿|͹XPƒ¥Ä` /"Ó”‰ž™!ÛŠÙþ¦þ¿¹"ØP,Ò?ïKàê(EÙ¶ô/Lbl(•@\cp¤QCMI ~™@©—g—~¸Ú˜ðZ8v6¢£ãMš™C$Ì#Æwëc:ªhÑPBgãài±GìÑ?K3”7*8ªP65 1›Ô?K|8i Š¦$Jê y%êËàrRRÑé¦$6cn+ÏŠýÊ“¡ëÊú°Šg8{ J^PêÖÞzF ½3V™gE¹¼^”ëП}£B~ó½jqfy£6sŠGPT­há*k¢\"©#—S¤Ó§x5Ÿ§CÜîÚW+¨*Euõq²}Wµ€ 'ÛfädÛuE1Ðñ“-|UM”$:v¹$cj™WóŒ²8>¼§^œ¿"güXûÇ ¦@q0÷PØYýF<“8¿ ½¥³8·á¯.ý™ÿÖç.=UÃ}Š@Ä!–LŽì tMuÏ>¸ÕgSÆY„r RB(+1IH&*+*ž2R=íÇöèÜïKZ\L[ê(~à{1Û'~i‹tÝÖ——oMU½§)ì‰hCÏÅ\"õ©61‡˜ÕK†­”v²É ¢³÷ÁAŒšj·;'ŽE´ Œ RSÌ]]Œ˜¶˜Ó(h 2îŒ+€©Ã)kÕ¡\‹-F1'÷þÒ7Þ™äÂð¯£\9©ÿþMJDö—ý xîDÝ:ÛšvÕl>‘De^ž•¸8öÆÝõf ‰CД à‘1|}¹[ŸŸÝás÷Ö&â³&[NƱÌ!øêJ¯žcçá“ÿ­æÿwM@ŸŸ#S€ß¾ÛÈm>Á‰02S)”ùEúÍÕAÝcIY¨iðéŸ(Ä”ÔKÈíÕŽ»³<µ·‘û×®zž1žC è6Íòé7, ´Ïÿííjþ¥ƒMœÈcpÊkŽi(Ï#Ñ»>4©_F¨ÞhŽ©Èié9×¹Àa8 ±¤Ž,ÒØÌ‰~Éò‚ä»{k…=‡ë…6þÆö³âîƒõBvÀj ì@y£ôÚ;gä+×OŽÍœšª^q:Äë…–°BŽlá$‰°œl;¼ùN¥´ÿPƒ Xu„i”·¾uÆòÁMS¢S§zFÅ‘R‰¸Ž­½¸$™ô)ЇÀTïQæ,`kåó±LÛ\=õ¶v»ä¶ª-±'ß®ŽÑ¯›úIç¦ÉqÈœ_>é£ö‡þ6”iÿ®èldWÇŽ0¤©\†çÔáý©ÁÝ@L’ åNò‹=zá=;(^µvìø©ÆB Ýÿ͸ÍÐV]%&/¾^Tî¹9â8ô®&¼ò@R^óQ9ímß_ê{m…Á)1†ËÀlãic2ú˜4Où8ÛüØÈä^s®Pšt"¸H¯©ð°ƒÓkª5~òÔáéƒÉÄBi2ï;÷õp@€GBPŸ`à—ûg X0, òìù“ þØlk»ù½¼IGÇ›utÇb;ȳøýz¯1ÉÅ3§Ãüb{ˆül[ ùûåëdm~çL/Ë—éï.ËÒKÝU U‘î‡Ëó-ôó]†[&Ìë’è®Wjù¼ßDn_áO»ÿÝÓÌ}í¢ ¶0ßJu ìç¯×ð¿z½FøÃ5ÅŠ]Ê\œiëñypgÿ¶¦Ìmð“¥ó‚Ê%+ €JùÑÿý±ƒ¶^«Ûú•§Ä*ÎìpN)u«ë/)Ž `r¨]²([¹øÂ¢D[–5J<ô¯ö—·œ´Œ–@?y¸Y(šì“ßÁxbLEi7¼rV»eõläd7ä³I¡s²P=¥ÝóÎí Gšöªa¥™þiÏ=ÍÇ›*lè·ƒƒ`ôaX3g§ÓÒ ðÔ£™-ÁÞßšç|Cº5mÃU©‡÷ÿÐù¦†±“ã/߉[›ë(ÎLŒk¾$ÇĸÀæ?%,'÷êi/¬ÓVªÅ¨cèõ‡–/ÚHÉáדR×åŒx.n(Z" Ù²ÔJ³ADWïb ¹yíÐA³R‘Iÿˆ×h¼ìŸ˜ùÏ;³¾ˆg›+–[{C‰ÄjcvÕtì·ùD‰Áš¢”ÿt±‹c“Ü.%Ž}t–ͨ‹¨"”îcLà KÜF©;ezp™—#S·œ×ÌÌ’è¥“íÆ®³ñnc÷’+]œo¥Fð‰¥~]3¼r4ÔãLácï7pÅ^‰^1ǧ;$Â$Ãú=Ï-ÒW7sM!‰áÖJ³F0%h²u[y·l²»Ïk®8Ëj¯ŽÍ»†[»U ¡ˆŠ;ý͈Å5T]#•UQ®8ß¡‡£*ŽDÕ>5šÍ*P„âq ÕÔFÉÙªWïÐÃaÇbÚÈk<P¾¿A*æ1ú1-èCÀ!ºÛ/þÚXe·ÁciεêžWO=¥,}Ðxëì‹ñR÷tÑoÉæPëq_p.N?yR|¾!w¿_ð<ÀÝßÔ9·‡c} ­ê'À·¾¤ñå%€Û¾”n¡ýðÇ9ýéG rò(߸B‘¾û+A™>¿CÄGà =ó¨Á-\¢²‘™*þï--ˆÝöCk”RVëEÔ#Û5åÝçTñ߉ڿüg‹Ôê·lq ¶î6KüÉŸÇlo<˜°P`ÍÇ,1Ô鮩حñ5Ç nÑUr7_;ÌÛò›ˆC°bZ¸HTRþæ/ü4ä ×/#6ïjkÆ™—sE¢^çœeR¯VÏ\Kü釟øìpöÄd¢Ð¸'!g¯²EG»ÃM©Ã‡6<­Ê˜CDð܉$^ä 2x¹BÁˆÌʧžeðøá8~¥"‰êã%´Žq¿&j IžŽÁ¯À\îóÉPÓÑ#{Cd_m5% ¤S”Œj0èì>ÅŸhé–9°óìlHͨþUƒAMDE%^‰Ý÷Vuë\jÊ5Œ1€špj¿|·H˲,ôg/ž¦e[é¼|»~Á·aé§{O胤F‘Uâú5Ѹn~P}f{•xŲ¼1™Ÿ*U±ËÞà[S#O½x\®¬IUIµÈ\û,y(¢"›]èµ½ÚºyæùãÖªÖ*«²Ì·Wï ‡l±Ž¬›ÉûÛªä²9¾$ᆷJöù€)Ї@›{ c #Ùíbœ—µ¼=¯H¦@ÐÚØY€ÇòÊÒ¨ísÚ˜”XbÿyF‘-ËôeWbšH|ú£:ïÏB,˜ ¬üÊ ßû9¯ùˆuöó%Àþ ¨w~VŽfø¶)’Ã…XNbáuU ë:À¯^ëqÇöÜ“¿OÈ7|UŽeâ4Ÿïk¾b‰U0¸ÚSyèQÛÇ~b·­[üA)ÙPIÉ›%ä­',o=š°xrˆAxÄBuNF.]$(™úŒKäd¸ÖÀÿùn‹ÇêÁ†ÕMhã)3t†8±Õ_p¶ˆv<¬õ5ZÊ©èCÎ^ݧD1*[‹„¬öaìŒÉø‡DO©‚ëÖîu&" ³¼zFGå÷ϧ™Çk‹Dúßc Ži ¶Wk(¬PX_Ú!ŠïÝÁ›'ð-³ì´ÀIh®gM …¯¼Ü˜ ŒèDЖÙå¤3ªÁà›/Õr—OµÓ<—À¼޾~2FÙÓL Ê€ïd¹î‰C¬ó‹BgEŒ8$޹ä6ÉèKŠTà:\ÿ¾³©XyïT„¼^ÞBÛU'<ú^lšã×®˜èw¶²®¼¼¿ž_Úëy~ Ý[b'kc¤8Ë:¦\±’ŠšBI<¥¸ã|üÏ+Áˆ}êÆÙaŸO¦OØÑ“-üO´1Ö÷sþ_O¶sf·Þ<'äõZ(Ïcvìx3ÿ¯ÇÚG:~¦¥1Aš“dÞr38ô\` ô!Д¬7BàܸEil„eΊæ–ÊaµÅ8Ö|°Ûtí$k-¼C?ºw®œøÏ›ÙÝß^—„ÂbÄ/GlòTÄþç3Ä((2È[¯S|p/ò 0w1¦ŸûÑç,Ì,0KÊûûsbò¯÷êü¶×(9YNÑá½ "@Á$LW^Jô’©ÙQ0AlæÂ”+†ÐƒÁ X€é´ÄÈ-ÉluÇ lÑ Z31 ðêcŠX2“Ó‹g}ÙFAíÚaABìæ»­‘Ç·&" •ïÐøÉ SE‹ØðYKlêJ^yýŸ K]…A*SžV'¦Ó.’ó6f~Ñ <°}ÏÙòúŸ£¶Ê½šPRãÑ삺ä&{Ä7™×†sM4ègÁ”H}[2ä)–ø+/Ål—_9¸Œ5&ç¡£Šh+•Çy<²±˜gß}'‰æ8pŠý;é¥{üH^9¥à7+U”m#l^VG¶“7N+xm‰LoœimŠk¬é»xPOœlVQUXCw®èË ,”¥ÜKXBË|+W‡ÓÝvtÊ &¢á¹¹ÖŒ"Ø)f :xvÝ|¿ÀP[puúÌ/q` – IDAT€……ca¡ÃHê”=ônðŸÝuü¥3¼ºM¸%½6¤à†ˆ‚ÖÍ Hh`A¶ú·—OJ7¯)6p†’Ñâù­§$M§hÁŒ€ Oè¨9”Ä—­)‰çmF›?[éænD0]OÏ,‹k(Vð†µ¥±ì ­ÝðTUymÇÞÙrÚzÑe%Ý.˜ S ºV 8À ÿñ­ÊÛß/(Ø`HjDßW·=£€Ë±¶‡^6%ëú5 3…£²RÇ9yƒï{_|äVL?ü1LYk–‹/‡èw0É$/J/êÍüË ·ÝÁkÿhˆ…²8e m͎Ѷ»$üâ)ÑÚËÔîeáÕ 7 jOÇeľý7k”²ŽìÜv·%Æ =cæL"Æçï3 q‹fóZáù€dæE\ë9´¶ " ¶úSöºÂæ¥À {iêú Ù;·{¦L8æZbO<³^~åðöÉd|Óð~\öÍïß55QøôlýnO}c±Ü¯;¶ÔEØÇ?’@§Ãºe–5MUm„ŵËhLcððØ 2Eø­W;F¡³a ½r¬»Èxõx_=ÛlbÊ:¿åX˜$4 K {ÎⲪÔI_-oáÖNuë¹®ôBLÍq¹-<‹*’xÌHkúG‘ÃPì“顚 9¦¡ tOî¨æ?zAþ€}™ F°v~P}ôÍÓÒu&ÑktZ«˜ETל$oîªOœ s gÔ²·ÀÀ"s,à•Š3!nñœ Âà`y#¿mWu·Ø%»]¤•U.×Õ’zá³ZxæõÈFÅé?onjÿCG„ïu߸yë•SÖ©sIQšK“I¦@¡£šj(L "ººìãÎ]5o&zœÙ^[tU»³À¶ªW2>Ðrl€j(¬?iÛ¦Z£O=ræ3C?‡"J麿 `s 6̳‚Üúr2Àý8¡Õh„†¯†ÝIyêmÞÆþlk+´#¦â „îuVLL ù"_áêWÆ©‰BЊ¡ÌCàÕ3º¨ —RóØX"±_îŒb„ ͽ`c©Ì~µ#Œ¿º¥™dY Ûr*—äJ=ä{êLØÂ< ½oG·§&I£…ÝU ñL¹õg¿Ùá–/ÉM¬\–Ÿ\º('ùÂË'-¿üÍvÇaP-]œ›x}ëé'6,o½rÊêZõâ)nµ?±t&ýÃèC ¢¶ÐWOÿ7º®øj{©{ºøÛuOçœh9¬Îò/–ÚüÓÃJ³±½êÕŒþXeÞ9@uôô€üóÜó¬ñ‡8?ó™žól›œßèqŠ u`åØí lÑ?ü6jûÂ'ºÉ¹£ö˜ÅQ*$'rÑž¸r’ÀîÚ–@96S<}û…_Z$Òæd*¿yÀ’ä¹i²LK={ùdiÀ‹Æ’|‰æÙ9(vuX×—Z¨Mì>Ä»$Ìnœí4²í~ñ߸À¯¿[ÇÛÎÄq¾“g×ÎriF¬È-¶Q‘Ãpݱ¬Ðj„“Úv:†|fE–vñdgÚ8ñÁY£Ä×!ª%Ãÿ^V¤¼v,DŽÖ'ð©&Ù%gzõEEv`v®^5êÇ’øtSÉa×.ÊÒ.(óèµ`¬Œ†ˆŠ.™éRˆ²<‡Á0‚‡_¯®¿°hD,麨$‘PuÄ!œ6:í"ÅA&ò³êÌ2¯zàh#Ï „æ:tŽÃ›m×>Kû÷2¥Ô­ÝñéE-ÍqœT(r¹RÁŸóçf)3¦ùÕCå6ÛBYfÛé>žM÷ ¬Í"€›ç¤„n[Ã&[i&Aç”|tŽÓèÜ6O¬,´Ò•…ÖvtJ½¢ÞÖ¦@|xž§Í-“ÍʱP€ÔËVד¹b¶Gïº!€Õ“ÆêÉN#“ºCæØ¥Ó=ú¥­ýj›heTÅû+#äýйqåÀ][21%×n @ððkÒµ$9nxíZyYVƒ¡Žóè{ÒT›7# vþ¾K íß]‚@XvÐft΃ Š„Í™P;o[TäÒ†³æ/£ ¶¾rÊȶ™â|˜0-°C¤>^mÜþòÕÕoV> +Íío«5±JíGï|±në™ÍÝ[Ö—\gkû}wíÛŽxölð4}þ3 o,â+äô‚¹‚êÉ\ÑjØÀu»“–Ü‹»WGí Ì!æ¾ÐÙò“õ¿Ä¹ÉùAåË›o¾ãíä¼s pç ý×iê^,Èdèì;&»+B䯕y™s>’ɹvcå ¿ö·—Nȧjïïo’¢±>Nž}¢ÜQXêRËLq>l˜ôs@T ÓŸlûr€W¸¥™¬æy©âÉØ­â}Oݶ_àöbA=óš ¾úš"]¸zPÝ6&]e‰/¼Êo „iŽþ;ì,Xo¡Ššô†™-¶ågagåi†ròÏ?W“îè ŠkÞŽÙæ+X3Ú}m ð%zÏöÞT*ÀœÀØ,)?Ùy²…®Ž‘Wæ K›‚€Õ¸åҒij;ªÄ}'[Èç¨m­&ýƒ1€oŸ•Ãa…¬ýà¤/ôíîe2xL úÁAÖê5‰:£/q°»öíä»U[ïVmI¨†2¨‹Ü·ÑÓtÇW h26‰Uë\äŒÆ–X—iøÖ{š¾õuó¢2IQñdÈ™{‘=BÎCßóLðà›K,tw÷íIಚô“¸j ¿¿Y)4E5üáå¹çÔrÞŽ`øà²qÄî˲o(‰ ¦8vL>6–Þ`{ꪽ…O^µ»ðÚiŸpŒôñå§k9ñ¯GFúÈ&c•#ÿjq]álJî™Rò´"*¿¿—™ãÃyNí¶˜%^£ñÙ«'~åÐ@À-3$vqÀî~'†7é¦)vì=!}£RX7˯¯å×FêC, ÚŒ›.-IÔµ$ñß^8&½{¸7†;ïí8„1€òC³ÿ.·Ÿ®há/Ý4)Z<É=,3&Ý1ÀCÀÂÛÛ?¿]µoHUÌ®ø7º›¿õl?6G7Kœz)jC¦îiÒ}ó®÷6<ø86^}Ééç+áŠpæùˆcú§} #Gz8h‘°ñ9 ™ì&ì[ˬô­³Ü·'“¦9½?4F5ô7+…ʦþÌšB%èGüƒã9 «f´›×MJZ%Ž=ôò ùÅgÅæˆzÞy±¨†ß{·J~öÉr;5Úð¡É‘Å+ò¢8tu]ÖÖײ;ûÞÇdâÑp@Ͼ·ÌùJ î\´‡‚¢[uwÞY“ý÷†J&›î çJ“AŽü¥É;óóþzNÎ\¹w¼¡cĶ[ÃWŽzÎe»<¸u–ÌNG öÐÁV)°M“$6ÙmléÊÑÚ8~³¼™³I»l^–æ±ò£î¡‰`f‘KŸQìÒ«xûáz¾9¢"LfYIyNÃe&Ä=б˜†OU„øª³aNײØxZ:Å£.X’`ƒÌÐßj]!Ó*ŸS Sácíª¼Ä5?Ö|pT.2[ ©«ÝMW]ÛìÙü rÞ¿ÿŸ_ÄjuîÀ?[\³¿¨Ãü¹Ò¼Ó¼úën»¥.ëñÿ‚árŸ«–MÆ2F’¡ý¿©÷OºÁÝ,gN&¢áâÍK”0@ëŽÅœ‚qngóí>?ßJÃcÿ=¦àG'ð²\‘.̘M˜Sƒ$œÐÑžÊÙWÅ¥ ýð²Uäð€Ó/Ž9^ ÍñZ 4 PQ!ïì¯ãC1·g–²Ä1›Ì1IäÚO#•ö’@{¾óni»ÎÒ*vKwÙþ;jû¥ŽZÑz¬öcfl›±ÎýJ*:ŠÇ5œHèí Æ’eŽ¹Ô /.Šs¿0xüâÉ'”wú\âY`×ÔjÂí_ŠÚîý¿‘¬™f2šè †vÞÛär³·Qpõ¿(Q±æñš{ƒ§áªËš|ø36J§š–ô‰L²Açü®Á—w‰=ìž!ŠÛÞpÂÀkE–Èž ¿ò`Ô=­A•ûÞk`Øž.Sƒ¼S¥¡ì‹áˆÆÀ)b˜—%°ÙIüĢà ¬Žá#µqœÐ(8dŽÍȱÑO_T vˆÊ±GLÊs“òƒVÁ›2'³˜¢£hBCñ¤ŽXë)eèí´‹á b¾u=ë¼>Ó>íÛeئí÷´Á¹³@oÜ‚È1«•§’…c¨õÍ‚!ƃÖíFb€g¬Ù7j\!ÍÀ¦óO˜}ˆÜ÷þ›¾½â·YKs×X7”^Ÿ|îø#£LÜàjyãÿ…Ûñç?„MKú„&ÑdwÕäÍû€#ä(†möÆ5SNòî@í­·Ô¾ý=Æ.^71\LÒi9¢ˆGÿÙä™r³§Ñ9IœÐSÎÍ1þ<ßÙàIdA•b[£X1ÊŸËc°"W`+òÆAX¡°§NGÝÅq­Ãꉀ.3£ " ubéê¯Ûß횯‹y¶Ë¶éÇAÝ—ut my‡; B  "Iémúªµi‹@`Z¶•^³0K“yÌ:[Ê'гH³H\§Ï£»@ÏT¨¨«@ïôÙ0ÝÄx§}Ú-謳=Ý¢ÞE°gèmŽü7Á !¨F‹5a„,qC'¦0ï S ‘÷j¶&½óÛ Ÿšw§ç“sïôÎ ,“9ô‡Ð‰–C£â”ž“¿þíg›]6ÄlÿzÀaú¤OHšŽªÂ®?µ¸Koô4¹¦ˆÃ^(šËk%_ Vÿð§uƒ)þì—L‘>‘¨z=j­ÙµÏþR Nôœû™˜±J“LŒ—J-á—J-áì¨ÁÏ­Vä¢f]Ì "OÏ­©×!bX•/²•ùb«>k¯ÆÉÆ ”dH§]Dt» Km—ú½m}›´‹€ïTI´}Yú: Úì"ÄÚÚd¨ó~¬s;Aà8–2¡V‘šÖ–)ÀÎs ÄX\TiLRŒ%n¨œa^ÀèC@æ¬èû«þ«-4`±à¥¹k¬Ks×XUCaMÉz=¤4}EÁmÞ¯þi÷šÏU¿‚Ý-M»Dõ¢5Íž=Ì dò¹jÙd,pê¸åè 1ëŒ/øë$/7bbŠ·bZúù`ÍæG=;®MÊßù_L KM——ñŒÒd·¸¨ÆÐܯfÕâó8×yµhÕ“-fù!](nÖÅÂ]ÈQѪÒa›“ä1ŸwàÖÓL H § mH謫½½MŒŒS„Éx@%:‹KªUš4³³ S shªwŽ”i@D´æñAk^ŸÓ¦ƒ-TÔžù–¸àçµ^]øî ]}Íøðû3é-ÁÐÞ‡CŽp%³¿’UKÄ‘Sä_ïmj9¢ˆŸ¸­É3o¶Áý¿o`Ã8…ÝxDS\ñtÈÑrD‘Š69B¾ù–Äh÷i,AÀ)§žrqí®>’Î'A9oœrî#îåÜIF\IÊ9ÀñFWÏc“‰…)S9ƒ*¼ÎTÎ` §S•Ó™ÊLåtJÍÇÀ9Åè[>¯}.»ú§6»õIùž1X¶Ò|†Œ7¨P¾9j;¾%aÉ^m‹Ì¸Öí8+W™¨¸Ê²«+vÄ-7\Ñâ^»Ø'¾@ «}tûeÒ;TeèÌ‹{Ý»1kî{¤ô:wÍL¦1&IrˆUÙ‰Ve'Ým*Þ8ãÜ JÜ Æ¹“Œ8“Œs&q$®Îâ&&c Š(¼NNg oP…Ó™ÊëTá ¦ð:3:GS ˜¡Ÿ|~ÃÙ¡¶“ÔÃvÑó6Ls¯÷6&juîö»š÷[Áš±VnÝ·È÷.°Äw¼±¿¸>âflØ„ØêuØðøÇV_ÏW´(Åuï%äú] K²^çK­±ß ֜˔œ&QѨ€ÔӮ̞0$ ñ ¡õßú7Oq­?£íwÔ¾Œ£€x#õ“3‘Nþñ¬Íó¥‹yç@À¶L#mkYÊm¦ý•uZí®é¬mR ›9ꔊ/µœµvµmÜeŸ¶æºùÆ·¹×´uuN!˜òeèD›:·®çÞ‘±‡`Ëέ@G®ïÖÏ¥ó6ÒŽ™!À³£ûbºejÉšv:=ŠBÚ1SWÚ¶¥ˆÅÀtL"Æ ÌE ÌÅ­?…´åˆÅŒ­ûPÌR?Í!aLa ô!@…êèéq‘'XÎâô‚[uáŠpãmÍÞ\¯º’¡µë1óúG»w&i:©ñÛéÌΤd+•™_ Ô 2f}ùȺØɺ؉×hÜã;ã–¿?·Ê„âÕ—`Z:˜/ 3o1O1îœæÊ0iƒ‰fƒÄ› œl4H¬Vçê÷%eª2äž)'Нp¶Ø „Ñ©¨f:¦cÄ íúŠQ½[æë!´ebb2Ö0úy†£DT_V'juîOoÄ,÷þ)n• %ë×[» ¿1›m´{yÁâ-7œÐù“Û±ö°*HY¼æ™+Çg~ÙoU-A^·¬w†a½3¬E(~koB~ãE§aƒèaSB”u/¦ÅÒ~tYšé—ŒxÛôãf°ªõÞÇnÖ¯Lô$›X+{<§·E)›&8ˆ!8‰!¸ˆ!zˆ>í¾Ñ}þde1111™(˜ý™Õ=, ¿1 ý‰ 8Gñ Š™€t·¿Þ¶íy»îiBzìWë_D@ŒwClo°9Í3SNÉLûab2\˜}ˆXy;¾û‚ûùŽR •œéLø¸z6R¡MõΑܒŸdÚï…E¯™z› #«òÖ[Ln2ÞQš Ò´+fÑÎ*’2ˆ£#@‚àö!Êsö4O?èXÈ «²êR)1C@ik^´îË{ÖdÝŽÛ)[—¾ôÖc7G}m›¾¬kLëœc­c?]SèQ¢„ BÀÛ°!8‰á˜,&ýóåo»Yc¡rEŒìY¢G“rv‚K/B4‹™7€¨'@ ‡‡Ó` m݇¶~?”¡ö¿Sq&©ï›²ÔwH[cM:oCS9ÛÚb”µ.ë¼_z[Œ±Žc¥-km»cŸÖí:µÍJµÛùx÷kí#ír^­}I;·ÖmZã`:mÀÚû×zn”!–þwÇ>m*CJ“N”&ƒ(Í>¡Š§ž ;;¦Þ9rÜ¿À’\f0²‰É¹ÄèC䦙·;ÛÄùéð1õïßÝ´¿~§ð“‹ÌêI 7$jƒ ï'gúJ ‚+å¿ìýiK¦íFf0h>¤HÉ•gaƒÓ#QC:§G)Æ=XêjmJýÚÝšiû´Õ€³e ÀêÔÊ[žú‚`@p¤,R‚‹è¢›îi’"ùÆKQô´Ê‡öÄ­±Ã ‹ÛÁІ ˆ.¸1_S‡‹¤j+¶ ƒÎýÓ!fÒ„ ´î×I,¥o—.°:oÓ!^ÒÄc—uÝ:ÄTOmµ.Ko§­ív!—¡íÖv:ν]ˆ±®b®­Ýtñ˜&ÜZ·ë´–h¡$Ñ`ÆÃŠxàw ~Æ<3å„·dóãæzêŠ1pÍs!WìXBž1Óë®Âtþ AåÅtáÛ&@ûuï™ " f òº%Èë ´-OÔé\Ã{qùà} >fä­µ‡ý ͪ´&&çS ™³¢K‹¯´4'ëo¾þ?µa¥¹ß–«³‘“ÚLÿBÉoɵïÁHRÔ¸;!ÇöǬÉ]˜¹Óg!æ`ê ê ªÍÝnaa¬>é–&èbê,z CÐt³4eh‹µ[~:„Qkû\š0H³buX•Ò…Qçã÷&Œ:ýÝ.ŒÚε›PJµÁÚÄT_P¡V‹Qšu¢4Ü‘¿5yô8ÅîR¿À’° cof…Ô¿µ6¿v–”\y9b+î!ºÕÖYD"f ¦ˆ®TzE{© n°GÔCuïÅå㵸µ°Aò×;BþãG8*Cµ/„œ±1ëÇ?Ïk6 2…øCpzþzG$½#¢† rò©³ò刣øJW‹kЍôÝ‚‰‰IO˜}ÌËZ.q˜GÿØ÷«æˆs€êØ @â,ØÊÛqL‹ŒØ´t󡤨ørÈÍi^ºšÐ•_ÇzÉT)‘qºt¤:už9Ää§ËN `£#l$)jÜ“Ï<¶Ç«uÁ=CJ^æs–Ñω޸'!7¾Ðì^µ ÁçþK¨ÝŒ1Ô>mo26àm˜f_`‹/°Å”fƒœ|¢ÅUùbÄQ|…³Å5U³Â‰Q€Ú×#ö–·ÃŽë>Jè•?TÂu¼ø™Œ]'1Ênö4Å«5îÄã-î3›J®vµXsù1›ÁÌÄd,c ô!ÐÙò}¨q÷€z ñêvŸ=¯œEFB Ç*5¾ú?MžÅwÿŒÓ³ 9uš~7}ˆ„Y`‰5Xb3P³5j}ÿžÚ¬¬¥ÖhÞZ{s#˜>®uO7y§–2ü‹‡°‘•ƒ˜)㣣S?îmŒUjü‰Ç[\§7‡¡ôw‹5ol '¥Ù ÷×Ö¬øØ³‚*[Úý¿MÆ–l^Ÿùy}è¨"ùk£×¿ÈË_爌v¿LLƦ@Û’[”Æ‹kŸ%ØîŸ®QuXCJ³AªŸnvK1UøÆœV6GÐÛüfMÆ.d_`‹e-³ÆÏ<¶ïº«&˜·Î.·Æ†T%|Ôo ;衈ãÞ_ccÊ ¢·¹™Œ/¬y¼6ëv}ËE<ôçFoþZ{8k™5>ÚýŸP„êGýßû Öç.Æ´-hÑdüâœ,*ó¾žU{äMîÃnô”ÝìiF£`\01¯œ§IºÎ !¥¹Ý^à˜4à—<{ ßö{K²aØ"à¶Ç,§ï« ÞöQƒüîQA:gô]%Læ+Üä ÏùjVmä¤*ìþi]@‹Ða½™ÁàÌ?|Ɉýѧ‰^6Ã|¸N\e¢2ïkÚºwãÖµ¸FÛ¡»áݘ5ôŸÿŸ"ú¼ÅؼÆ&ˆClêǼM¶|AÛóóº€1LÍabÒOÌ›eœ •·OÏ,îwt™³ yY+d€ÊÈ -¡ÇÏýƒ‰T>Õì†\÷ÿ[PW¬1Ó`wx¦“?ânÎ[kìùimVôŒÆ÷½×ÀÑ">öëÚì+.T…_þž¼0G1-ˆ„Ù¬/úëØ÷õ~#IG%G}õÓÍ.gEÈõ§x-˜k¾NTòÖÚ#¡=?¯Ä*‡gÌ21™h˜}”7íS[”&લ[9sJÅLÜ4óv—Wp;«·žóif#Éбß׿f%å_þW-6óá7‘ðÍ•Ó>åk8|£¯~g\>—m'ëuîįk²ðŠ>ñYs¶e‚J¯uµ[b»R—¥Ç‡wF¦+õ¯Gì8a¹÷¯œ&Ó+Ød,â™)%§Ýæm<øÇŸ6-é&&}aÞ$C€ƒ§Êÿ9+þß þš5Í;¯W[£…Q5¨ IDAT·áÏ-ø¾gCé øÿìw|ÅùÿŸ™ÙzýNwê’{·Á06L3`Ó!@h„H€ôBHB~i„B¨ I€/Ð{3¡˜âî].ª×ËÖ™ùýq–t’N²šmÙÞ÷ëeKÚÛÝÛùÌ3Ï<TãO¯xPÐqJÖÿ±¾â²‹¾á'‚Õ] s‡w¥hþ½ÒÆ]og¼ÛžOú£Lªs´í¡¦Ò{ïG|î Ž»Á¡@Ùlwnø"rÕ½-a¾†c‰µºÌ¾Hù~õ'rÀÆhwè;î*Ñy^ ¾êÞ–·ÌÂ=á, Ïmx,sTå|×ø’©J•w¸ø›ã­X]¦¯nYªûä(uU g޹Ì;*8QšZ6[ È¡6KûÃ+ÿLÿsfr´åþ¦ÒŸþšÐ©Gbæ˜?nD7f‡ÝTÚ´êîæHý⌻âXO¶ß…q€m7•Þü€Ã¦!'²Ï!DÉTUËõÿˆ…Æ]ŠíÍsiM¶ÐòßhøïÿÉ»N9‹A-JSõÜ.K\÷H,4þê’èþ®ƒÃPű­›YüÖ÷¯m^]¡`„aRx†rþø¯*=ÃD€£ªæ»¯:ü{¡ãjÏð´ŠsžZû@â¥Mg³>[nŽ\~âÓŽr\˜ðõpË®w3ÞäzCîo9õÏÆƒ'i g_à¸CŠÔžæK3“£¯§½{ëTç¨îá¦Ò?ÜMXIÄyÎUjNõ¥ßþJj¯=k:Ž@2fŠýàK[ug¼Õ'½'¶%7˜¿zÿ[MÿâŽä`Öcçó‰À¬I”œõ%g1è¡‘Ÿtm¸yýc±µ{½¢•è’¬;œÍ¹r«3°;”e(ÖüiÎûBïÓ¢÷ÞRÿ|C ÇÅå  áÅDàªoæöìïš8 ªæ{3‰µ†bÄè]]Œ%t«æºðËNćvjOó¥ÞÏx¨>8‘6ÒÛL1ÀMiö<Ç…Ê¡##Ï Ä7?•ìïz88 5>$¢ “FœãVu¿…‹Ò£6aÛ5uáùØñ;w„†-ò'7ÿgÏ^Ó ±àO†¸ŠÓ¡"#^y¬']÷Òà„ïlz.Vr‹³¾Á¡žZÉ’„F—kN4|‡œnyÈDFßšqkø ×|÷Èß•Ì(Ÿ£`´ooiãsñÐw~H(r"Ê:ž¦jFÌ&=eÍî´DŸeÊ'œìx¶8t¥òo&ú¹®4={n—%”?Ù‰ÚâPœágù“;ÞØ{уD>ÈDEskx~6ç¾²‡N{³êêÿî»×Ó§·™¢—ZÒìãË”CWFžHl~*Þ­=ùaÊ{Óû²F”Ïvg>ȹRN|iÖ}ÖÙΠС{ÔRÁ¶s –K•ƒÃÁ€#ЀAuþIý»9›YmOH‹Æ\ê¿ó¤§+ï<ééŠsÆ}ÅPÂ}{×âï¦ü7|Ïqmq(Žo¤l”k(Æ,»ÑP9vÿY5øC9ëðœûå'í>fŸù›!_ytÚ÷—÷}Z|×&J¾;?üÞ‰‰P2gÞ÷ÕDèÿ-ŒF6|dö˜5ø` r„+]žëAn­æ:áTLjàÐ3Á‰ŠÖ2ÀgÍÁá`ÂèÀ¤ÿåûßl¾ü…ãvÜ¿ü7Ñu±•/0 ÷•®˜rcè‘Ó߬¾uîý¥Ç×.tID жòá3ŽÏ¡{ÂÓ]¹–Ïr®ÎÛ³;-qäHâ~”˜ZŽ£l†#ÛêûÁ2´ G†}~ŸгéÙþYëÌGFŽ#v¤*—ƒ„2‹c+ËúÕWqJ|*ÃàÀëBm€Æ57pdè}?>ÚÀë§9#ÙÂp.Õý÷Mp”lîß=² ŽrÉþ»·á,ÿöåªs”k°3I §½·#3\ZË2­K[åàp¨â„YÒf’½°ñŸ™6þ3Sá©æ;Ó=·f»ÂS+`D`jÙlujÙlõëöOØG;ßʽµíÙÌʦ%}_Hl0ä)Ó0GœØxÝž®æVÝÓ©=Ý—*ÜžZ‘u]r6’O¤"^;ØÀú.ï=BSÔ\ËÒœZ1דíë±Ñϲ®3NØM~õ*üû~[ؼŽáB]Z‰ø´Ùľñ7r‡¼7^ ¹ÒIŽnþ½¢«`Þn*ë–S’Np$Jc'ö×oUµHuWãÆ?~«©Ÿ` ó/”Ù %óŸ·k®uŸÙB¬1/ ‡OìK~ìÊ–Ì'ƒ[ò‚!¿ö°®Fw1àö#vìEª6ïbµK˜Ý«má?¿NûT/b_¹'ÿäM]ùš®4m¶EFüå„N8^ÑŽ¹ÌÝçû °î•œkõóoéDÙ8ò¢!V?,é¯ÿTwUíÊŽ;¯½M Gïÿ¸¡œÀŒ•6¦6[Ò¶—S¾ô6Sf&G’ÐàdE{i(F䮳nV†áº—R¾†÷3+ÍÚ=ðÔHFåqžLåqž³f{GHfn—%r–w¯rp8Ôq^ƒA¦>Sg?¶ê®ä×^9m×÷Þþrý«›ÿ/2mÝŠ*¸ññÃz~yìåœözÕSn TyGôy ”Y™uŸ¾ÈYtu0B Žš>ιš>ιúb*†ä# 8@ç…~¹5šû¤SÍlŽ¥5˜}÷a_êæ‡}Igqõž‰áÒZ–öϲi¬×\'ŸÖÿçì±{mñ—ß6¥«V]ˆO=ŠÐcN$tôDÌQŽ^}*/n ©ßÆð®­ o\Eñ.ÕÜŸd“p9bÃÇcÊ9ÀªO¨ðƒ ²žDKW‹x¼‰ãÆ:FZv1üÇk3ž%¯˜’¤¯OlAغÊþ|mÚ›laø…û4׿•óè9ކMl)fÙ$Ç/ß—s¿ùHW×+Kç(¶“’x=#ï=–s½vOÆ›K27¹Ãï<’óÌ»Ò9æWŽsàŒ¬]lÊ/þ¿”Ë'¦¼âÅœëðE®\îÛ@Yÿ¯DIéLWvÂÕ%ÑÖL²é­¦ôùÍ¥Ù–´é©D`ÂÕ%ÑÖý›–ä\é­¦ŒâGü¬¼Þ]Ý1›u®Þ²»º&UˆZ*Úz³-ÀXyгm;8h8}@¹ î|Sûp盚Kôày5§¹Nq®gtpR¿,¹[¨¬.ˇ¤ Ô¡O„§»´-ÿIZº³IumW×–ŸÜD…÷stÍu˜^pYq±wÍ…–´³Ž£ïÞ*XsæwÜçã÷~ðN[\ù ÃVA!>íhLoø¹dÂÅ…ns=G÷Þj(«—1’háHV;Œ°kn‘µêQ= OΞº×P>~Ãë·0Â9@¨ ³…_•µ¹gÉ]²6l¥ä¾›3à?~Ÿ(&NßyLS?yÑP¢;)Á t8±§ÌWôc¾¤H4½|["«³…Y—yÓÃg+E³ ¿xSs©mptôõXht{vÅíhêª'R~_­hμ¡$ºæ‰¤¿a©®fwY"w™`ÕœäMWÏ/îJÞjJ[ŸKúã«t•YíÖc"#æ##ΠĽä¢ƒ~Á…Õ"JßZaà€Pÿü¨¶ndضdàK_¬ÎH]Ä\ Xœ°"'(¯Åì¦?(š ´^9³³®’ŒnÓÕeïÚâå?€¢à×þÎ-)Ǭõá+NèqÈú³÷h®M+lñ¨3$ý„K}÷€N½FÍ}ò’!çRo^j‰æJ]…&?WÒç\âÊV{Ü\ÙhÚìÊ~ø¯¬çã'sîÉ T ‰û~`¨Fkò×Ã-…/w¸d޽,ýâî–Ò¦²žagø“®rÁÈî0E€’©j®³8pUˆ¶«BìÕœ€$´³…ÞÁáPÅqqÙ‡`„abxº492S®ñì÷ò<­ÁÇŽwææz‡»R´ô‚io#N…ªª®ûÕïز‘£x¬{{êŽmmÝÄQ6ÝqŸw_c事Mù³Æ`Ò4ÌŽ_@èÄ©˜YÀÛ/R!ÚT|‘]Ó.޾{‰¦~º˜ •µ˜M;†Ø‚€`åJ¾÷%ͽü}Ú­!1÷ýDs=û7C15@£#¶7ˆX¬‘áGoÓÜ/?j(±M€æíŒ4ï`]„gý4ã}å¯9wt%ájBÇ)F–£×ïÏz^ÿkv@!ÓÍ”$ë©`ô°@5Ýh é[°ŽûXY†³¶ µØÂ²ûãÁϧ}Üæ9LÉ©aÁÎ6ØâÚÄCk‰wY’™ÞjJËno,.×\œrä®Íð45ç)XD<ö…®fwXݶI’ŸP=Öý÷P ns$ÀY!Ê‹SCx÷å¾à” DK(rÔØ©yÿñXÃÝÍ M|‹ Ÿ¾GIÃv†â-ùCˆ—Õ`>sž`–ÐÅò°k+ÃÏ=lȪñKnV´M_Pòá˶´ñs›h@#&û¨ÓEsü=[Lê7SòÁ³¦Ü´’æí”`?‚Ù虢9ídÙð†;N‹o^j‰Ÿ<§+±]”dã ‹ âª󊱂5v®¬WMn·ü%vQ²äñœ[TŸû5o*ºÕÖ¾¡©õk-)cÄ_-XÓÎwg"ãóÖUfq´ú¥œkÛûº+— ÄUBì²É²1ñ<_ªØÖÖ8jX¦)Ëu%Ó`‹F‚"c®†‰í­è5§zÓ tµ|Q“£ ÿˆF_LP£úÅOj‹!ewZRd†+«„š\ßþñ Ú %SU-2ÃU´sï–üRж.׈SR;bpˆÞõ+Kd à´s ½å¢‰âœçgx,àoRìÆzþäý–T3 ³¿½ªf#•ˆqŽ “áè×ßÔ•Qáî[tåÞW<R¤µZú®-ú‚ˆýà~WfÂLÑæÀ¶ÿãvÍõÎLùåGuåØs%C)âjPŒÏ^5ä•o™2Îùž'=õYÈ[`?xJW_¹7ëÙnA}!±Õ’RÛmqê5Áhõ±îãÀG°å¥´w݉àÎw2ÞðT5:Lm³oýoÒÏL޼#$cÚ÷˱Ô~?8ˆ¡)’ŸtëŠ"5b”¸+»ZH»ÃHP.í¿8RŽøôÙ˜.ý€‘[o0åGî´ÅãO'öôc›4Û{ZHX5¢¸kX0’ßn[Ù$Gn×:–Õ¿ž@»hWßÇåÏv/ÐËG EÛGÕ™;ˆi&ÊH²ž’ò‰`lýØϿƻg 8@ÉhÉRƒÉËSÛÍwŒ\‚¥GmAon<”Îrå¶½ XF>¼ygµ¬¢E¦«¹àdEw•÷ÎrÞŠ$¶cAwpÈãô½DØUNæ;Ó}lÍéîbÖrÝαëßͽµí¿Ùe ô)hOÙByÅÀê÷³¯ëÊêey "Bªñæz†7®bðþ+¶ðösDøÑ½jNêd‹52üöÓ–ä !~øÁúà šÛÔ9ryÏ¥9Ú¹‰’^4ås®SµS.“»\£/?¤«¯?ª©¶•¢„8c 5×Q²ñ3KÌÄ>í:wÛ4ýKwf=Kži·ØÈnĵ‡ØŠv®¶Ä–:JÎÿÔµ cxõëšKv#6öxE{ö–xÈÌrÜ*IÓM”ìZiÊ󿈕M’ÍW-i^¿Ûzˆr-”´¬3åæu¦4ï'á–Î×ðÅc‰@Ý;YOëßDALQœÝe‰-+uµþƒ¬ç°ïDš”HÇN˜Û5¼ŸõTÎódVßúöšIJr –û\oóÚnZÒ~€¼%³ÏÎ)D@Ú.jAï/¶°msÞÚ{ùu‚E„Ž.¢0o¡œ£¢®?½GÖ˪kýÜåAüGw+ú•ÇåÜ-õ¿ö¤)-¸Xê2hdà꟩ڤY‚Ýz,&_ºQÑ–¼jI¹G–Ù”9bÑgç²Þx$ï:[ѦŸ*…õ=ê\U«ßHÉòWôý«™Œ8Å“®9ÖÝad?|7ÜjJ åÜ›ŸIB‡© ­ŸeëóÏwÍɾT¡8È¿ÿ¡)jmè»p2â” «ìË]¹í/’yÇÏ-ñÍç¨P·‰áGïdÒ£wZà >ï4b_q“dz‹l€|›Vl{áÚ†îžGÅU|ðZ8(]ÅËoݧ»œ¤".»ïîÜîÌ2QFRMí÷ûÅÛŠÇì?îÆ@lÄÜž¿»¾¢”nEµÌ·iz´½ÝRKû°ï”6nx,Êî´¤ÄZ]M¬Í¿#j™`UÏ÷¦ªæ{3½‰˜$‡j¦îààôAEÜèØÚÓ\ÇÕžá™X2]ABD0Î`U˧ÚÛÛžÏþoÇ+9ÝÖúe]âi[(«˜obE-æ3æó˜S»¢SQв€Þ üÞW|H…§0å/}³øb\š£?Ü ¹'AìË~¨j‘*Ì’1Žž½_—ßxÜTžºSS+Ga:ivGaôÖ¿tååò!ŽZ$ëó¾¤è‘B)Þ²ƒ’e¯›raÇ·u…%¶Šó“¾æÎŒ=F2‚•e‰2¼þCSJ5OEn=wKxÓ&©GÏÿË?|“ }I͹}Cwog,cw<önžJ3—ì*žvÿQG+c¼m"¬õ]ñDHŸ¬çÝŽ °s¼Û÷ÅÖòï’àê8Ã'3YQŸÝa‰-Ë55ºBSS› Ek´Å ÿŠ—¤6›ò„kÚ–v‘§æÁŸ_ÀÁ¡78}€`D`fÅ<õøa ÝG”Û¡¥í©Íæ»Û_ȾµõÙl‹Ö8àÌŸVŠ’HùÀÜ~øGYouAh]d¥º?áLÁ*«Bìû—h®ÿ>hJç\-B?RÛ5Ó›îteç/ÙDü’ï©Z6Åñ‡/YÒ³÷éJ¡@O43üÊCyqsú5jîÄ+T­5*æ‘ZBOüŠ+W¸èªî‹¼¥¦v²h;PÍ1^Ð9…0›zš¢µ^Cg˜ ¨j¦dœò"BÃ{þMøßh.Ë4Q!ÛL…Ón7…F‹ç8Œ9ÕmÙ`Š[kî-oeÝú„ ý)¶{š™ñü”3®èžêÒ¦\_]©¹2u¦è©-¾è ÀøyE}aLaÑ-1€ü‚¼Öm‚³Â¿û‹”wQ”r’’ð@KìÈéçúÄCTxè϶ðüã”Ì_Hì#ÅlêÑ„J{Xm1b\÷×7ll^4í,.²Ë†u¬¿3 ©hï:üènŸtÙ…x°¢ë€ t¤`#œ÷U/$cx×z[€ÝïRë¡ Ÿ9¸)$fJÐbõó­Ö™¢\£%ø¼y7®²Ùîlz›)7–sÇoÐÕ’ij®ä0U MVôÞ<_¢—°ô6³Ok–8T̼?øˆŸ|¶`Ÿxغ&Á{¯Ùäö›L¥¥‘£ÿ>j‰—|«ëìÊP…3€d#%þ ¡k_À2-ùçÐ_ÑnÉ>õ‡þD«‹KkÛoÓZ5·¨æÛKëþyÏõb¥7Û‚od×ÅÕF,ß«‘â.:îjÑrW‹Ö°3|)=j“­Ï&ý ÿËz?ÊzjÏð¥úâ"åàp¨ãô  .ô·¯Vùä`—)¹„£ïïx5ûæÖg²ã«µó°ÒŒCý ]Ö&Î 4F<ÑÂQㆫº‰¢qú’I×iâÓ®Tô_²¤mk¨°s#%•£ó¾š½`JzŽ#³.éêþRŒÖIˆt”aΠωefœïîbiô–ê+ìä.[¨œ*ëá1¢ÕùÊWô­‹5w®¹oÓ­rˆPßɈ¯ÑÕÌvKêN WèMKø±·ü„š©üLCî㞸áÑ’O>l Í=þ€->þ€¢}¡_ÿhTÔ¿Þpy÷a$KÊòÇ4׳¢5V»q3hw5`=l'VŸ¿?¾p÷‚cw³L'ÿâíŸ[⳿JºVàúʺlJ {_q,".º1³2 ëͶàZÕ'zÓÌæP÷b*`çò1­?ÈzAÜ?FÖGžëOøFemð¾=5ïoæÎž€ù‹ûÝ—¨ý¿×¨°mc÷ߡʪ· yöÅB—ˆ@›–²¥s„0@xDqÜÞÝQU2MT(f¾13 §¶ï9Üaã'9Wé‘î.uÓ[l!½Õ”ÜÝ´k…(%}Q0Ñ´$çaGÙ–èt‡Þãô `Šs“üÓúŹ·êžË~Z¿Xc|ïhhÆú›´#¹ G/?e [Ö1ÜÒÀQ: ù•} eó?·w/Ð'Q¼©‰©Ûx6ÉQÓvÖ&жäÅîèi‚%HÝû`2êÑBˆ×SrÏñФdcâx“ºîNцéú,eÓ=Xüv{®z|{Ö[õîŽÒC„€v„B|ÌÆÎÉÏJÌèô#Òžš [/>XÉœOµŸ}iÛŽjøÒ5'{ÓñÕºû\W[–i.=j ‰µººô׺:éÚpSwëv¯_èS³#ú«ßÙ—#:R¿ƒ#åUE2WZVåIÕü„[?£»&§ÊövÌÈrôþ?òQ‚ÆÏS4OI{ˆÇÞžŸÝÑâ”l}_SkiŸùã `ù#I¿­íy0Ó²LsÇ×èiÿx¥mæ‡3€MO$œ¸«D3|x{Ù©†ä®‘¬b‡ôSl é鮨³8§&G}u¿rp8XqúáœÃšè2ýݺ²ïn)—³2{½³@xàзž³…?ßbÊÙL»ØP݈K»ƒÒÙfþédq±"ÉÀ}AÔm@‹PfÙ$%Í;Ú- [ób·´¶÷Öÿʱ‚½à[žôë÷g=ÑTXü÷œ°øï9·;„ÙèY’1m¡š+S\d‹*bD,^G´ûÉ—½Å­b¤UÔß`Jß †ÃâDÎ jä;A+Û½ï³.2½½Ù>›`@›-?©ã>^oþg:Uü[¥9ÔÝ[ IDAT ±¾gñ*+óN!tîÉȾù—",ù%·}Ç’b-=ü'Küù½]Ý=švv_fóμ-­ÜHÅhÊ‘Ž1L-\¤uÔR™E\ª&ŠÖY?“­ a9ÏkôV¨VD5ïWl劋l=Á0µz¾Çzœ‘îfAŒ%ÌÎ_,‚"BST=4EÕG_Œ'×òú¿ÇBÙ]–´å™d ;n&)‘ü}›µS‚„6|Ú—#:²z9÷|Ó”§éŒ9„VÖbîò_ÿß¼GIãNŽðãÎ蛥y#¹w1{øºDpæ9j®l´`EwPaù‹šÛNIEüèK»Îüí w ¡ÃQs[ÞÓ\Ý“ƶXb`¸h梔lÿPwem!0B4[º§ à)ë+þØ\Vy¼'å+V𑯲îäzCA`ä¹¹¶½”ò'ÖêJxš+ç©MµT´©ÎPj³)·,˹¼Ã%Ã]µgnÄl"õ0Cäàp(áô³²ìšWNÝ٘ݹO;ÉCh*ÉQI¤–†Mkþý Å4μL´N9_0«Ga&mâ¾yFÎ]·±{É÷0ÝÝê£[õ -ºA¥Ö‘g+ú¤y’¹j±)mXbJ[—[R6ÆðŠWtuåkº:ï*wzæy]§d{c¼ìë@ÇÌ0üÉnq^:UÉ _àKûGI&ó3œ#þù=-á¦Os=ÆËî«5r XiJDŸÂ¸G°ê9t¾úêay¿mKñÛ²t ï€hO`pä~‡ ¦†XD6¼ÿš- ·÷\ºZ)F¨ !n›­|˧žÜu0ñù[]ãª÷_¶ëW”¬/î ¼kÅžÓœ3ÊÑ®%9µò¨®bºþü T̔𞅫¬lÔ,ð¥Ö> k¶] ÈGdékÈ<)HèÎnbß÷$|ö#Ÿ}Ð5f}y5b×ß*ÃÇbÖ›Ù¸¡‚ "~éï}‰¿ß˜ ,~¤c”&_)¡gÿŸTÚŸkšq¹/™i¦¤y­)¯}.ãmÝ.¹1›ûÃ’æ /g={è“® E×ý3ÜñzÚ¿ãõtÛv¢ 6áêpKÉÔŽkr$¡Ôà¸ñì§ñîåÆ+Úį•D{ÓØ1JäÂ}:8J8}Pnþç‚ÐÆz[,‰ôïøw_¦‚iÌ9Y°¯ý©d´¦š.¤y–RËHF9ò•𱦼¸/­nV¥µ„Ö­¡BÓö®ížð„0›y¦¢ÍX¤h¦hÍÿ i鳺kÇjK\üPÖ;vŽbxËö~ÃÞü¹.ë JÜe‚5ã;áŽ.jÐcû'ÁHO J”`þþ`¿@wììúõNœœÿ.ßx™áuúŒ§’·ßRüºÒIŽvî4fbqWƒUKw»"t—:çè±;MéÊ›;f]|ë¿¶¸y5#D8óʽï¤zŸu†¬ð´®¾õwÍ5f–hªþöH©f†ß"çꩌ=Q2B´tØ´XW¸Ä“=íQ8r1J–ÿ+íïM9ëŸIûÓC,˜Ò£”Ô½–öÔœäM!Üþ\ÆWëJpbñ´ö©ùAìîD”™ Bp‚Ò§Å®X@Ü@À#ÅôÕ/Ôܪe oXÍps# =Ç!RxíhLgÎ%ê²@ü'÷*šiŒ_|ÖÅåEü–\YnoÇöëœoÈú Hz¤››pf×ßíI3ž€küN¾Ê•;ú,EItÔV?fWý%ß¹Ö¶n z–ãŠñ¢Us¸dŠ®6OÃ>£1;ùÖpKÝǺÛj‰Ôä'›%$SP1{JUÏueÕ’îgðAü°"Í©­¦”Ü`Èz‹-x‡IF`‚bÈÁ®mì¸+B±‘çúñµ†¢5Ú‚™ ‹ÀåÁŒ•îÖáÈSÁ± ;8ärBÂaÏ`¯`77X"Ö¿ã·¬Ë ¦á㊠¦M«Ööà‡ ðÅ[˜}Z×i˺u”äRùã £lTŒÈ7¼>³DSçHèç"IQF|ò|Å>U²î¹$ZBm@»ÖXâ¸} ÐSuùEVîÊÝÑ<:]•e8»ËÞãB¬ž(ôå´Ò F3I‰´»s•„Ömïª-N9³£ß²‘£óNµ…«¯#¬¼±M8zòïŒøüáRÄ[:YE[š.:ÉÇOÁlîI˜VG<Æ|ÓZ†V|ÂÈ'ÿc!€ÓÎ/nÑ|¡OÞoIÛ73<û$Á–UÄ—¾O…WžÈßëS.ÍòšâQU›¯P´åoJs%÷~=8ò,E‹ #´~#>zZW%1w³l¢w¡;3é5·ôÿ²=Åð37EÃOwgfñ:[X÷ZÎí«,=Ù³›‹+Ll+Çð{?m*6ßqWŠf²Î’ê^Ïx$%r€ÐÚÞTá1+~ßT¦D;Ì&z*¥{Dñù7ùãoþ1L7RaÉC©6‹yÙÉ8á'¡èÿ]ÕX=Ø%fÓ¯-‰¾[séšÇ“"ÇxkEóðoGšw˜½„êͶ°ãµ®z,!^}’/Y{º/Õù³V¬TÿºèÃvýNŽ+ª…{Fk²ß(iPÃ’:8¨8½Üsòs"ÀÚèrãÿ0 à=øŽÿoÀ)_ Ëì r€ÐˆŽ0}¡/}æS¦!FD€Ûï,-0r\»àªªEüíÕŠ¾në–qÜÔÀQ*™·¶WCì˜ù„Š t™¶ÿæ-’‘JªY¨ ñ™Ç ÖŸR²q#¥5˜MšNhíXL[cårÜY¢5áB½¡îf‹®‘õyçÈF¨“»B¤³ëîò¤9^l$\Cèõú›–ZâÖ/lAR¯™$XUD›Hˆý#OÊЕêßÀ©fšlž÷çpsݧ†«³EÅiÙ$É,-™÷ƒP £€‚#º÷ùöTö ¿/¯­7¤è:Sâà%›Á‰²„®.Çü¹jGv—%&ײ³‰•aDôª„‰žêÒÄnKä׌P“#ÔèÊXWîõW2ÞË®vºÃžIm6ä±—cû»CG ÷‚ O(à¼@oÊÖ·uš(w× È¡s™½A-ì‹ûßßÍ[@ìy§ ö»/ÙÂ3[â3["By¡âò þ‹í¯¿4ät‚wë+®¸Ü|—+sû×rž_^žñ¶Ï,yé\Ù1ÓÚÓ¯·2÷\Ùü¿wjîo™òŠ·Ì.‹âæ\P ì9@Ý–X÷Eñø½’ ñ³æKHjïÂ6oµh=Ë—\÷tÊßø™æjüLsµJ]„&\j‰­5”Æ:.þê+£. Æ?¿³9¢5ÚbÝËíVÖÚÓ|‰‘ç’})Kk²ÉßžôÄ'X[6ÙÂð‘]…“×°è<ÌvG i»¯Œ6#ïjÒy*c€ñ“17vŽÚ]ºqE3 3í>ü¥•ˆ·P°çì¶ãºùNK«0‹Tåv7U_5ŠÐÊ‘¨-W+²šwSh´R IE|ülÉ{tÞš—O“¯kͤVTôºzƒ+ˆÙ¸“Tq¤µ^ëµ”O–ÍbîÄ#‡)FÉEo­[wI»Ü•¢ÕŸ8Ô‰µºâÑ¿dK%3ܹžÊø.»º?G;J¤·š’«B´q‰:88ý€Ä]%Zk7sd[¸ŸÃƒ[î’õÙ'á½W©P_ÇPYâc#ôÈù‚]=³“Ï­h·GŒë~Z{ÔdBöwwfÉ붸q¥MrŽFNìY§Šæð‰íN[s¶lŒ›%Z¿lÊõ›)i®£Dv!î/ÃlÜ‘’9aN»ßã7¸3æJæÖ¶ÝIq:Ê&Àýe„VŽìÃOWsUyK1›y;C¤î­v£ç©¹ÈÉ,›XÜÇÒ!tü™ÞT±P{ãÎõ¥‚ãdcÛ[w¶‘Š¢SßÉ MQôÀXEÇ*frˆPÿ˜ŽÉ˜°ˆxíi¾$€ ö»Ù;\2úmå®èrMÑc¶`e¨¯ õZ–æÔ’)g3䉮ì ϧ×Ý0(!õbš?Ë©áj¿ü‹å¡uIÄÓ)wÏûÌTŽì¯üÙïÏLÄÞ†(˜ÏúQi#Ù¿oÜ;Ó²4§†§õï9sp8qz/8çéiuŶ§Í$[ôÔämûº>žQŠþ釖8ëØþ7¦',ìã v»•´}1Ñ‚‹D3ïZг•°j¦g”)€ÜÿyO–?€’JÌNùŠªµº/°Nq£[W}ˆOœ'çÉk+A…µKÔF_)¡³¯ðdº[$0îDUcµY.;ãŽzØÅ¾TwŸ‡'ÉFh¢¬Ö·õºKgº´ÈÌ®!ð°ˆøÈó½ö^Æ"âÅÊé+Ñ•š:ášpªÐ4Wî…ÇRþënhé;© †2æâ`¼¿Ç»&¨¹·_Ó\ Ï=ð &²ñÚ)¢ÕÓ,Çþ!€à8Ù`°»-Þõ‹­ÒÕš¾ôž÷tp848àR$;äqOqg_~áÀKqí°o±s 3‹ãÎ üD/ai³D¿e—á@z›)ªt;LugŸ}¦ÿñÐ~ô¨M3AÝ?Ö{‡¡ˆcA?@ LPôOŸehw ‡¢´,ÓÔе¨^àʽðlÆsÉCoÊÝ¡+•³TÍ[-ZDÙw–è¦%9wdúÀÂ繫EkS¦›×s4|Œó¬9teÛs)Õ|c=wp(À±À€Y•Ç«¿;á_å8áñòEc.ݧ–Èå’¹¦HX;‡VZ–æ\‘ÅVÉoúÞ{™NP³ÅYx¼dú‡í›ˆEfŠâø*]1p¿àȡح·83~]Éî²D­ÉÃÓîÎçàp0á4˜`„¬8.t˜<&4Yޖܰ×3vÆ?Ç—ºëwûV —×bvÁ·dýÌ«äätØdwX¢•eØ]]\ЉnÌ\‡{Òý‹ã~àЕ­Ï$5§z“ƒUÃ7R2› ÁúôCÇ àБÍO&#Î 8ÎvpË1SmSÍI#¶Ï}çce£>%Ø+?Ýw^¸³s®‘Ó.Ýû©×Ææ§‘çõÜñ•Î÷¥þù@ªOvr –®3¥²£ÝƒU£lQ(öëŸ;Ý¡ÄZ]F¸´ìô'pËÐÝÑ[ºÆ7jÀñÐûCù¢`ì··RÇÝ¡ñ5½ëø°ˆ¸ÿXò÷·3ÇŠîÐÆæÿKFœÝûˆC½A-lV¦hÿ}Òi¯ò °6?•Ž<pŸ3‡ƒG €M3ÉÆ—Lí’pg_àªmÓ/ëo½DïÒ¡-O'½íøJg{2o¼ƒù¦ Ž«‹@|•®PƒãÐdeÐÝØÊ÷ܰaó¬êløG,T2Õ•s•wŸ1×ÁáPÆuÀfÿϺ‡ F^èž¾_DzùÂ`âîß3b:“„ÐøAÖå®­^w| êËᦫ¯àØ »xh£5Ù¦'âÁ W—D÷¼wß\˜Õ\iúö×)IÄ‘~¨²ó´ÇÖv†/µ¿ëâà0TqúùϺÓ¯oy:-`ýhö‘Ec¾ì±´O;ÉO¨÷(_òÇ×ÛNØÌCœìKÜþZÚß—„H® ÑöŸj¹ò†í}#Äa¨Au†Vÿ¥%<îÊ’¨è>ƒð@q•‹vÉé%Ñ뮤„:¶ÓCŽø*]i\’s¿ª$¶¿ëâà0”q݈‚.šø ÊŒ³ºÔF³Ö7Zºúð„ο.¹ÑlÑhBo¡|ÙÔwe¶Y¯oy:;º”ÎófêþmÊÜiJW}Kpº½C+Íðš¿EþVÒ"ºûžð#0EÕ=雿õþéžC;ëã¡ç«þ לêKyGìýàÉŠÞØèMýô»ïmtÚ«C…\ƒ%l|"<üÆÒ&,81ñzÂè@&2:wÜUþÎÛrˆJg©½-geÓÇÚ@:@Í…%Ñ—îj,5–’y§ì= ˜ÃЃS«îmŽŒ8ÛŸpWö?NvÙ‰þÔ·¤»ÿdÊ×~Ûé‡ ÿz‡Kfé‘KJÔÊæûÒ럲ś¾¦+¿ºS°%i_ÙaX«Ëþ¿jïÎÐ88,8..Œüj¤ùŽß2¼qÎìPbÝ£±PhŠš+™Z¶Y}^(Öô¦/?Ë›Ÿôƒ•]og<›ŸJûNi“w¸¢×Á¡78ô ÛÿËÒ[¼˜ªYk´)^Á…Ù°+#M7£¹ìû?çö¬yŽ¥â`†™­y Z"ù­=Í7(©²‘€øð«J›W¼˜ð_rnÖó×G õú£d‡¡„§dÕ½-á²£ÝÙª<™ýUÒã¼éd¹h~å‚hø×"tâ4ì´Y œlx,´²ŒLý^Y#–·‡Þâô`1“¿¼ùÉýÖ±u‡«B´G__Vÿ»Û›ËÎZGÑ%_%ŽçAˆ§dÕ=Í‘²ÙîLÕ ÞA+N$£K%ó¼3â¡¿>ŒYíH§s=XHm6¥uDKF_Œ'(Æþ®¼bÈ×”5þðææÈé ©pÙ×[Ú/1±‹ôVSÚøïx04YÑÆ^râC98ôÇ â Eò6îÛe ¯¼/Ø?ûŽ-YΤâAEj£!­ücSéÈóñ½!Î[)™îÒ"F𮼔ã¿ÜÁ°1葱ö%vŽáMO&þ M¾.Ò<Äy+JX°ÇÞXÞðÞ6WöÂSLééQÂ[ú‡Þl «ïk oz"uA >l¡ß ¥èàÐ~ƒć_nÙ¥ºÓ_;Ï”×®tüÒt˜ÅѶ瓾 ÿЇ¦ÜiŒßûËS+Yc¾[±ëµMîÌÂláÉÇfÎòÑ fqT÷RÊ·ì7eJX°§ÿ¸¼A-zÑS°ˆxåjôõO¿'/0¥Å¯:Iج Ãþ®º¯%\6Û™úý²&ߨž3;88tãâ²ÀãCGÁ”ŸèKgÆ©úm¿ˆ…ªÃ–ôÍ VE­¡ã€‚Ô¿—qïx#í+;Êö£²Æ}¦ ˆWœâOÙs½™¿’ð?ú€æºþfÌ;Sì$n²0‹£†÷³îo¤½‘™®ìô[ÊÈà,¸0«977â¾Ô'wÿNW¦E؉§!6åB‘óÌ Œ8%-ŸåÔ–šËÎ1\=ß›sq0Îr_‡ãô2.t˜tDűêÄðt¹Ò3Lt‰^¬*æœCÎʰŒ•¢[ëÌ/Z>5>ÜùF®9W¿_&m=5¢5úº²ÆÄj]þîµñдé®ú¶`yƒC¿³>Ô‰®Ð”­Ï&þ1²>íe‚«ï1Î Á…YÕ9¡¸§©{O~ûK]? ñÓ">ûBU÷þª™C+Všá¦Ï4¥i™æÒ[l!4YѦ~¿¬Qôî¿ç¦¿ÈABk¿\å”CÝC¹ëÁ¬;ûSC5ñ9Çc©@±ƒ§ƒ p\§Ë-Ë4Wb½!»+EkÒ7ÃÍJ‰0dBe?¡®JѪ9¥´i vÞŒºÐsÍÔ†ú4‘'`0l‘gzùõ×\ß´6º|¿ùçEŽtç"Gºsz³-¼¶,çz橜K` uaã&#,Å,TŠ˜7ˆ:÷9ƒ392â”q›è-Tˆ­Ò•ìSòŽŒðt—6òü@b¨gÜó”MßHÙƵ&[øx»)ZË)á[¤)›X)*XÖáeéNt8têZ‡Þàô>P殾:õ¡Ö¿3f’ýã‹?Ç_ßúLÖfV‚_ásÆ}Åwæ˜Ký‰‚nœy{ø¦·.j`|h¼D7feǸ³îlçÏ8à¶“éoP@ya¾¿«áàààààà04qz¸pÂ5>Upc€]™mÖ-‹¯nìí¢Ï¤c¯ü}biÃÿ´Ÿsw™L4*8QžS}Škñö—r{·æatDpØ¿P“£l)qJŒ m 4e VŠj ðzáCÛ=»’ žËáEÝw÷\^øÝ}®+Ý}Žz܉(ˆ‰~Bå±%?¡RP9H¨·V²Ð!`A¶2 Û9'lìþ€Èˆ‹>Bׇ½‹#Ð{‰*¸Ð1Õ§ºLjð_pCs"²¬húÈøÛòßD¯›ñ‹0À‚Qz‡¢@g&GFÛª}›0û@uºZ`¸¨$Tz0¤¾6“”Ä–åTmuÎÍ3¶0n f•*¦rˆ”!)G¼¤SEÅœí¾ZÆó.H ðÝ.AœÏoßíBÌØnAÍxçýòB|w9…eÛíÚTXo+g÷6^¸­­,ÞzŽÖóµ–•ÿµÊæÛ:—½»œökß½ç×Å ®ŸÞ£öýÛÊ2såZ(Ñb»ÿm3åØrJ6ì´D%Dì’©.-<]ÕÔE|Ôà(¾ZW´MºÂÒ¶`¥)1R S“#Œ¹<ˆ»<[]n¨ã¸ªˆëYO.oÅüãzZˆÙe±N‘Áàž\ì ?ïæ³ǽ®ƒQλì_ô³ûôtLþ<î3¶²ùÁ&ˆ‹^LE/a’ŸPß(ÉMVuÑs`>wC G ÷’#*橪à‹·¿˜©Kmìwt„×¶ü'{öØ+ýUÞáâÄðtÅ+pÚLì·Mo±…ØÒœ+»&ç²3  HV€#ˆGJ/ #@»Ón· Ö†½ð'ß-¶Z7ññÔú#/FPÁ1eð‚"[÷kû½íüP œ:ž³}ßöŸ¼HÝ Å^[½Z;¼÷ë\wÎ;^O[½ ŽØ-º8H¬33I‰™b˜Y½˜†¦(Zd†K;•Qƒ£¦wRÞì9·Wåø¤ˆÍÿ:fUÃDk·˜m­ÿÉw‹K(ÓÐk IDATñn‡Ö¿ öï°_ûö.b¸ýƒ#Ðï$Ð;—Íû'Ð ˆG¼] £Ýå Žò¿ï¾vÄË"æ’0ÿH°o/‡@f§%4~¢©ŸßÑAxd†+Wyœ';¤-ë µÅ”R«45·ASYŽ’ÃgbvÔi˜…+1 –¶¯qInoC:N94½›ö¦–¤}—\ù?ÇÌëGV¡ÐvØ¿¸+E{Ä")5l¡?e$)ÙõNÆýÙ­ åU'ùRsÜÙ¡4fenx=éK,ËyFŒClÑ ˜y¡¥•‚Ý&>w€[Ÿ1‡¡WJª” 2Ã¥ägÕ¢+u¥~qƳþÑX‰¬¬;ß:Ð1JëtÙJQÂ2”ð%v† fšbªsŒ m ïðcðü=îÞ›²x—_úX/üuÏH·eu(§×·«çBq~ è%Tôb*y }˜*aÁŒWŒ!9[1ÞKFÆKy÷– ñUMk¢KÛR´ MÞg=¾ZWš^ŒG8ë ÌfýV4e¥ÐåÀa ”´ú$oºú$ošêEWjêöWR>=JɈ³ýÉàDeŸàºÀš—d\±·SÁEg!~Õë‚-+mÖK‡!Šä'tø™þTÕ‰Þ̶ç’þÏ~™ö[èO†§©Úþ¬—™ ¤áµ¤?»Vs]p9¡gýAÒ%©ƒµÙáFòZ1×­˜ëÎrÐøAÖýÅÝÍw¥h[èO¹«Œøü­®V¹µ95µÁP½‡ÏÄ´|4‚`8Ÿ +P‚h "Ú.0 “ fVŠÍÀìi[á§Ð¨UXfëì]Ñs’ÚÊïfö¨óŒ t1NµÛшÖÁhÅ;ðŠÎÔt6ìuœiítŽŽ†´.F/N9˜)FÌ%F’3I±•¤¤ykεéÉDPô`œ¨h%STÝ;\:`¢¤9½—„Ôˆ_:QW6Å×´‰ü¥t¯ém¦Øð\éÏî²DÏ0ɬ>É›ò–‡ÜóWˆ#Ð{‰*x@ÆLJLDÍÎr›Ù\ÀrKÞ½ÀLQ¼óÿb%.Ã’ný±ÇO­Ö¹ÃÐÇU.Ø“¯‹´¤6ÒÆÇCr€ÐÑc’쓯0µÑšþÓùÓ]˜MžŠ)Äëæ‹R"Ð W—Dc+5eÅï›J'\UuWïkfÓ{iObq*ðõoûÄE’#Ì)@ä—9Â¥Åþ?{çßFyÿñgÜÒ¦%yÛ±åìÍ ’” ì= eÓ2 ¥PF[è PJe¶”]fH2RFHBÈÞd9qâ%Éš§[Ïóüþ/ɱg˜Üûõ2Cãî$=÷<Ÿç;W¤¥õ/Fòò†KZåÙÞ(<êñÁúÏã®Èü¤kʽðil–rV;Kµ=Tû)R³J¦¹’%Ó\Iƈ®Ñ¤­³c^F,?ÕkíP|°a ô"ó™òŠª™ì³{4m¥¨Kð`¹Eü÷5‰ƒßñJ(ÿÖ_!r쉼ÁlaÞoqqw44}«:V<ÒX0ô*h_[Ó›¾J:­%1Ï«o!ZPm+ш¼ÑM p¡5φ§»cñò> y±Tж½öWøMþ‰9¼!;A‡r›C¼ÑÍ7BªßôfÔ»ô÷õ…ï „¤à‰O7S5|wG¿K9Oþ1"}È›nd­á6?, À7BÒ|#$-±ÕjÞ¹·ÌŠzËOrÇ‚åôÁþb ôÂ#íC™ÂZ"¾¹–c÷%áoSJìó¨÷‰8«¬ÒW?‚“ä´RÆ›kžÊOvÇó—û¾D' ö­H^—v<þ>&‚Ôçg°9‹ykìù«Ÿ Rµ&?àtO¼¯ÏÛ ‹;Þ®»Óçs–m…´ib]è‹FV¦¥åi̯8Ý-8RÙ¯%‡C‹RJÇQß¹—"rîÂ.:v%eÚ¸*cäÍÁZgr5sâžÚÏîaWûÃj³Ø™ƒÀ±dÓ§0v¼ÛìåWż/ÍæÍ²Êþ™½l“¹·Æþ2¿±þë¤sĘ̈§¯“ïDòެÐϾˆ©äèÛcÛ\p2¢£oËoLÕ™|íg g_»anÜŸ >ó gq>:8Ú%Ûtärhã~UPßðMʹþ…HÞþÇÄ`pë‹¡€c}Ìûï9œyùõØrÈûþ¼6'ro »Ú®<Ë]ù·¦`ÂÔA1lþcë‹¡À¸`Z~æ5ÎtfË–°ùAÀ9uk~#1Zûϰ¿¯ŽšŸTŠiÚqÿCÐŽ†:D€€aWù#M‹U%²JëIãÿ.ikÂýÚû¼Qn lvƒàÆtômù€°áåˆo_ž+±ÕÖ=\WtæT“òÎôøìñi“Á;TÔÇÞUÐдHUÖþ3ì':; /vˆK/œ =|ü«}q¬¾N­û(æ]f¿ú-gÙ5ƒø@À  }Íë_ˆäÕ|w8ͽW! ñº`.Žùžšƒ„öø9”€d#o 4-¤©@ ø›äB~]¼M R Y÷<9“3yÁÎ{±é9C¯È‹¬~*Ø<3ê©:Ûëëã×Ï»R‹ž¿=ÃYƒ‡C;òÓ¦ ¼‚èÈŸ›j?K8—>T_8ìš@h%ÒwÆè½Ä-x±ÛïÅú::YšvˆµIçýo±S6û!—ú"ËmÊWŠy30~Ïê[ëÍ7Í ßš©ä°C0Ex¦ÕWå…Ö<Œ½#¿‘“{_‚1¼4íH΋ú^y—³²½É³é%€á×úC+ÿÖÜþQÜUv²;ÑW‡®ÿ4îVj®çÿÂÛ6ÝS:Ý•ô õÕO…‚#o 4ÊE{n´ØSì—©Z“oþ$’÷ÄóÁö–ëbFÜm}7æMÕšüžcû‹ù²‚"ÛÝ{(ã,ÌòSÜñuÿ çõö½Ñušú ìÿ×›Ør{í=žÍž1#o †Â+ÒrÝI¥/ŽÙôUÒ‰7&\O¼Ä™v^MOq Ìê«ü¡UO„‚ZÈÚïêÊ–s=ä饅1Dû$©)]¿Ç;3¢Q¸ý¥¦üg^ÀÄíµËBªðND‡]ã­}6÷«üz,¡„ðbÕ1i4EcÆ#Ûåkò'Éjݼ¤3¶QzÚȃ Ö½ ¼ðg  = Ù숇lÔÏó¿û]}‘·ZÒù{î§:ôocÞ—fq¦(Û=hÓ+ÜU‚1ä_dåãMÁ1¿Èoï’ýÛ®Ûæà¥øxW2²F“ôfÒ£<‰¦/®ÓOg ˜¿¯¯Ì¦?á ˜¢³BËÒ» Hn3xº5­\zµ]JѦo Œu¤±YãBµ×eïŒ(Á ï„Ï¿-ožºg³wøÇ:Ò¥Ó]‰õ{þ·§Ø½ÓðA³ïw#Šì_Ѧ¨˜áŽm™õìîµDg0þMÜ}ËmöâeÓ•ªs<Ñš÷bÝ×ÙgÔ¾<øgDñA—:oóC`ðÅy‘­ïƼDë]ɻڷÂywüÑ`¡=¿Ùô …“•5 /Oï—ö}¶´ë§¤v˜¼’ÖÅãO°­ç6 Œ—ÓZ“Å¥vtŸ0ÚôuÂyÅ•Ê}’†e“†ïMþ»·SÊÆ/û¦¾øþDòsÄU)èÝYÑæ%œãF24n’]7ßfß æaR8YIlžõöô=Í+ÓR3…gØ^›¾eð%y‘ÍoE}ÔÜ÷5ÒmÞOiú ’wßïlqn“ªs½Ñ-»Yд5ªrÖ9¶°Ú—Ô.7ø¯ŸOºV}”Þïé,•"3E#{n@,8RI5}—;¼ üUÂsç½ö²Ù·”äNDV¥=EÔb°nvÄÿУû/™ÏæÐÁ‘ÏYÁ‰²ºõݘ{_ŸËèýK¥ˆ×-nÄÛug“w•h Š,•f½Ç­4E"%8¿OZnÙŒ,¼»®øë›kËÂËwGž Ï@ÑHn3„l‰Ÿ©Z“úø¡–æ44¿1éúÛM W2z`; ö†…ÿˆy>½?ìß±Dôµôà.¥CKv?–ë>Œy~|de?Ìqisà)?Í/OËj¹O ­Ø½^–vüèD{òÙñF‚½©ÊKÞÝsÒŸÉ)i¡¥Ù?{dYZ:éäý}E6ý€«BÐc뻊½È¤ó¼ >ÝšJ0øÅ&ÿå&¿7e(`Ý·¿þ[‹·Œþ#ÐCß›Bý*CRC=Kï/©¤¦º È£ƒñ¥Içõ·ØÞe›}â «8ÓÝún¬ÇaW{tž}yp›}ƒ¾&¥œrº-ÐwGd;ÁóžKºæ¿”tèk9'Èj®øa}MÊ9ãôý}E6ý‘Àx9Ýô]×ø*U>匃/Æ7´“ÁÇ©9þv—&3[¦ý`ð  µÁâ‰Fsn–BKUÇÔ~0¾ƒƒ“- uñýû£¾¯þ‘pèk9PÆÉéøC$ûpón×Aïg0‹AÒlò‡`b׶饔7µÅ1‹AȵmèÀh2…a# Ý“1”N0÷CŠà„Ó!K¾¥hÙBŠj·1T:Ò3.æ:”6«ÝÊÐÇïX¸®–!²Q“9ì8Ümó‘Æ -þÒB›ÖPì΃¬z,&Õ0}ý‘ÉSÀØ)œåpf߬Ö×P´qá¶o$XK1XX…IÕhÎ0|Ï›ž@´žàUŸb´¢t‚"QTñ!Z:J0‹GìÆn©%(Ú¾ÌêÖ±ð•ófÕ)Í+]ëЧšnX­ ¼‚iáxIËv¼xÉGkLNÌãHÞ0Q€d­ÉÇ·˜|k¼nt.Yi¶Ë#x0É™ýxÙÈ)i[gu´Å7êÂÀÁ¸Ü?ÜÚÒ’ÙS‹¼Ú²þö—ÏyêÃ&` þàzòøÇ8ÔÆEª\4Å™µ/IjiÒuöïû‡ñªv#Eµ› öå#2ptÿŠ—Õ¼u±.æÇø~ãYêk  o„”}§: ŽTÔ}q[ ÷3"k4ñè)ýc:Ð 欳~ãi†øC€wˆ¨GÖh¢´c— 3¢—”îùÚ0x× ™ 1G+Ð;®1…o¿îëŽ_}–p¿"èCÇ úïg,î©?™m¿= øqæÏ~—•ùæS‹{ì—º”Nu´N”FäþçäÔ“÷¤eËào*Éò¡¸ƒ”XÌ~Zs|ø¢.ÑNË„s¶¨y³#Í ½¿>ûGJ™ÿfZé|Ü *8â%yôJVñ©±¸÷îmÎS›i ¿bVÒ5õ._ÄWÉwèÚÙ´Ö>Ís—rf.¾s¡êØø^Â%¥ó†› iIÚ±é6A]ûY¢C2“w¨¨õF #2ˆc„Ö{)¶\U®;Ûž‡lö/ù‡ÉêÖwcžlÝR)²Â?b ¶úƒñjÞ{†ðþó†8ioþü/Žƒ²¢M÷¥$·}÷ØÝ€ö}ZžzÙ¾ŠþÃèÀ#Dƒ²ŒÕøPÄ;LÒ¢k5©½@×#®(î›ãßr…Å×ncð–û8sàPDê|î¯_WËàý?3„+~Æ[OüÞ¦Ÿ­£§sÄ¡öÍ\‚ß}Õâßÿ·ÅO<–#GNëhI_±à?ܬ9`ìQØ:ñ|Þ–"¶q%Eo?cˆüDÍ!3üõç)çªo,^R ;ñQ«ÍYœØæ•„›óOÍñÅÛº¤« ^|¯’ìÍg]õ_]üêµ´ ÏÔÒἨâHª™ÁH­…7Ì7D”cFM4<ûîf¿§[/r&œAL¢;·ô­¤3ÙDð×OÄ<§=hêÍõä"’Cuðæº#~K¥¨ì$wÌU!ìÿ‚»÷a)‚=J°äçˆÉWê^ ¿öÁ«–14íTLŽ=‘9o¼`Å+SätC6t$¤?½ƒ7ýÁ®Çùè-‹[ú ÅAÖÔ3ù¬ן2„ÚÍM;‹7GžC3ÿaë–’]¿ÂßïÖ¶twgqoŽÂ™=ù̦Îà˦eÆ8ï²*¹Ú®sõW&¿ô3],ÌYÇ^$¥—Í5Ä5_›ü¶5Ï‹€•VsæQç:Òù]-£áíñ’ªðÈN¾Å•¨ß`q+>Ñ;Ö˜|ª™¢@%gv®’*Ù°Y:ƒKÞQåMßèR*L«“²ñ¢>î\g2[ê²×®x=áM—S#Å]¿{ª‰à坯=C6ñ_sd£Á×ÌSåÈFC4S ¹+x£øHY-˜$gÌbpç—)¥qQJÖ› g©q ¢‚ï`Q+8ZIÉ…m¿ÕÎyI%ºV“|#ZÑ1Ù7­[ß¹Õ:“/œìLõdÓ¨”𦶲Þe¡oSò©§Û…löžA¢‘n²xK¥ˆ“û>ïÁèý ·paqö…Ñ2˜÷)E«—1‹10j¢cG´¨ ²µË)Ú¶™ÁÒ*ȪGeHé€K¼yE;k( AZ>Ñ#¦ñÊRQ;bpù‹DÈ&MãMCcpõb‚×}Gp,BaÉ@L'LåM_A÷‚ÀÐ\÷­ÅíøžàÆí»ƒ˜UakødÞàÄ®Ÿ5¥hýB“G#Ž5Sc°f¥ÉïXC¸Ðv‹>UJW&©fж,6Ä6dJîÉ¿~­Á7m²øæíg¨ J^DƒC£d¢¨µ oð±ç,â-ß ÁÈv¬¦•š¨5SäªLg ¿KX*EKÒ(1ùUßX¼Ã Ù}¯8ãÁRL)€1ÀŽá¬áGræ.K¸}dˆ“ϵò^„»¬_`0l² ü3g’1(€U6`,ÆœæHSš=¬ VG¸Ê#Dí¤_y›Ì|%ã€^8ZÔgý¬)?¼ÙêVèbÁ¨ì…Þ ñ–\(˜^mÎÏ AŒ—Ó{sLÁƒ‰ÞÜ&Ð(á ‹»?‹çS4÷CŠK@º`áß|!#œ9€Ûܰ /?£øÉ7E½lì0îW/¡øÓÙçtslê™ ëo´ä+­^Lðб˜Œ:<ã”XöµÅ­ZÔ&пšc íßS:‘±Sz¶ì €Eÿ1D8ó&Gº½@¯Ûdqß}bˆ©8ƒ”0çIUI†LK1X¿‰p+?7¤óïwÅÞq®H5S´ê¿º$9!9]ÒÞ¸;æ5Ò "œILM† ¼m©!θ×Ó\8L0ßùU³¯i³Å˜ñ©Í7¬3…ú5¦pòo|áÎ×½c©.…7™BÁpAo/ÐõEÛç§eÄAV1UI}ý§PÐÒäˆZiŠÔ&‹«_œ–žnƟ퉵?&³\ò§Æüø&}W=Þ‰ˆ"\ºÁâct @*Ïòîz_|“.6.RœŒX.Þ¼ZsÄ6ê’{ hôD #2f¬.QuEÊyæ¶WÇfÿçP¿UÅÇf»ÚlÞÏ0c„ËÏRÖ,ÜÈà­WYü²oÛB Þ~‘Žà¡§xcÁ<†f½bqg]ÆYÕ£Pq¹ô‚¾Óv°.6™Š!&½ýQ)]1¸£°¯Ù@ðß~©9|H«ÇsÖï~ª*5ëÛW0Á¬§ué§¿s¤ÆNá³Z­6-³¸£*¡´Kän~9&Þ¯$J†vQ¡íÏü}Ò-*UŽãn{7·YUüeœUu˜`4m¶¸ÿ÷ˆ ¤Ùº¥hÞ Ï–ٚȤ€»ˆ³Îø{°¡õ‘ÍÿU埩ÎÊ©rrB¾é½„;¼V—Ÿí‰¶èZ„àµÏ…àíH/{¸¡ µ£M<Ô} ç@tØOý!ÿXG1Uó~Ì]ó~ÜK­, )sâ ø8g|ÈeyÍŸ}ËgûǬ˜Å•gd_çzÅOoÅ–Ç;äâ•”C6r¢+S”_Ù%×uµXN;[Ï´¸m›:–ܰ’âuËF€Ëntˆ:î"Üyz©`¼üˆžµñÏÌ'3¿å™×Šéü2Ô%ƾ¨ “£fˆú3uiч†ØžŽgî w>Îi)0»·a¦\çŽAÔñy_gñFÓ÷¦Ýnq}!Ð÷‚½™pŒEþ`÷9­¼ñ<á,“_?ÂÇžˆ¨Ó éÊ% Ýw“!Öï`ðÙGLþwO }ƒ{Ï“ŽôæuÝs‰ªÀs_:ãe6MŒÀ‰}ka­YeñëšÂqIê” iÙ‹Xh;Áïü9¥l^j ¯ßŸpßúº/"{ºELÁ7îŽy)hG_ª¤öˆÌ†‰FODô®?òÆ-—â¼ÿ¬&”•½÷èH2d¥ml+nÈÚ tÊúÀmÔŽt’Á£Ï–Ò§Þ «´%)Ó_ŠÉOþâŽ=~EÔ×TC¸y/§å“oêNE,.è3îrÇ[¯ÏW‚ÉÉw¸cÏ_Dw.VO¸ þæ*9‹2À`Äɲºs¥!lüRs¬Ÿ«Ê½è¨Ñ IDAT 9“cî 41enD‡_ä‰Yiж}žrnœ÷L”w‰·XF€€âãœñÞ˜é8[~Š;«ÐÚˆ^lia ËEm÷8cp{x÷,]@Ñ»ÿ&œ(B°}+CÏü9“_“Ùl3°nE_ü‡à‡_µê±]×–æƒÝ­K‹>Ïxn @«ÛF¹Åó7ï‹ÿÅcuÀÐÌýoh|þNÛÚÒ´“¢ÿÍ¢Öš3¯ÓdeC1yþUùfŽ!Ë©ÝÑ ðñ¿4Çg/kÉ ÙôË: Ë¥Ÿb"LQõ‘‚ñöÃIç¶U/ `Dë)Þ±>É×o²¸Sos&>ÿ—ª|õjfSÛúšÐV‹Ûò!^ðˆ/â+í»ÍW*DðæÏUEöc+—@m3ùí_¤œ¾!‚ÞY 7,Te-B8_µ¤¯ýWÄ•¨1DÄCé&‹¬ÔäðŠ´<òÆ`ïê8çnÿ8áÚôFƳ8³a‡éF &¶bó:ÍçØÑ›Ï#ú9KÏRåª/°z?w-öÞ{“àÍß3ètðïy£°Q ¨ ÀäéˆüùÀ¿õ"Éú[§Uþþ`¦ÉU¿à ®ŒÖEtðHÄŽügýú'šcÙ7„{ù¯†xýb—Ýe*Á` _•“‚ c#áÄé¼yëÉ w2Êà³MaÚmÓàÍGÓ ¥œx…”>í:Gºõ}¥Ã u¬`üó΄kí|S˜ó¤*Ÿ¯«Ëgê ê) ¯}Îqæ!º+ä€eîXðRÒ•ŠP¬ø9ý!ØU„ €QA`0ëH›Æ¾ñ–êÍ„›p_a#?3Ùº*ïH¯|¼)?²Jsly'æw·´ËrY‘v€w¨˜®þ‰?ÒþX¾a@/>Ö™êVutzŽ% WPØ7Ÿ¥°$»Ð—™³úós=ßö¸–fo eª«É”PËõ>æØ\Ô×PÔj¾ÿ‚DÎ.o­¯ ïlÄsžRåõßZ|ç× œÀ'^+§`ò…Žôúù†Ø°Ùâæ¿žV濞V|Ř”áÁG‰ZåaÙ½* øs/pœ#³ˆXúÁ[k[ôbû^È$çz~©ã@tü¨KTÒáÇb"ˆ:õ;(¬’er;ÈA€ã.’ºÌ‰0ùÞ˜z*&?¤ëW1ô§; qËŠžú½!<þfÇßš1¸Ns¬_Nqi¢×Ü#j#&a lÛDá3¿Õ¥• ÷—ÛÓò_f+I„3^Àç¾vÅß~R?zÕÇË™×=èPÛW r83žž£gÆúÅ7ÿCüç=ªóׯºb­¡Vßgq=—)£{þ]rÒ_‚².{³M9.È.Ø0Š3uÁ/_ÓóßLËß} 9$7¤ó_K+Gž'§ÆÍ4g[;×™üûJxbõýRÒyÚÝCž6¼Úœ9ÀFÝlÈ!iŒ^ž–Öÿ+ˆmÐ¥õ/EòFÞµ¾^ [xó[q^2Í/>Ö™”KxBÌE‘Õi)¶AïuNÉϽyßWì:èýj0(gÙ§½òÌà8ÿJl•v/W߯YbÖ€f¿lò¡zV#záu|0Wÿ*c¡ùü½Ü]³.»CÔ$¥ã¹%²‰ÇgÂ?dƒ‡#ºjIW—þ÷«2;‘]Õ:Q1Q^@OidÐ×iq†€¡ã²ÇòJ2 M¤¾ã¹·¯ÏX~†Là¬Îq¹»Þ[Š‰Ã Y:É`d'Áþ,n¶AÝX-sÚbq”d®»zZïâÍú‚àGÖ* ® Áüœ¥…-.UkòîªL‚—s€ 7.R•Æ…ªÂ+ˆ©¤ÚWåØب¥R$™ïï`‹Å2c8ÜÐ5¢•¦ÙCSŠ*ÚÆÿOîs¨¾ÂŽ^ZBZ’U[€3~.«?ºÊ¡‚ïe0€ t—0q†¤?MÒLÀõßÂêÿêâæo ±~ƒÅð§¸ç¢¿ú:x9ö”ÖJÖh4ÕÜßÓ>€µþ³7cÈÈ-œø–åÓìQ]•¾ãÞ “®Ú]Å퉗ŠÚ9?ïùœàí&Þ[€IkNB¸–à’j®ÃXÈ0³zûpKÈZ«ïLë¶»jF9ÈÁÜ1[j“Å©Mm¼øg*¶ÅvÎKº[ 1±Õ7¿ŽÎôv¤KOpÅ»KpïS Œv4Ièa‹+/ß»ÃV Etú L:ÿƒG ,„¬©žÁºí –T¶=÷ο2¡*çü”7ܾ®¿%ÇpÚ¥‚þÌo4Dz¯ wÊ% ×9&‚Ùµ’S¿¿,éZñ¥)|öŠ.­þÆäc!ŠJcëì[²¯'­6CJ;\]¯­j"oîÜ`ñpÌ%]+÷TM ˜ù®A¬`9×§±è{‹„”v– fçßË7BÒ\=Qcˆu_¦œƒ/š¶äï[*E–Î Ÿ¥¨ÂÑ:è‹´®Ø½Á² €¦zµ–å$[òh+…%­ZÒõñÚ­™~Ö ÿîËV–Z-Ì9XWC‘/¿ãäîöAÆ ÙŽÔbñ×µŽWÞ+<ÿ}C\0Çhu+u9iÉØÕÔY cw0»[¯;ÂÛ2 3€ æ3ÕGö'R0wb¢˜‡--lqév‹cétW2ôªÄ7bí§ wí§ ·äç,ÏQ Œw¨qr:WBL+í?#t í Ù÷”TdqývŠšêôvÓ+dw늕dÈ4•Á+7©°k2t.¼ùˆºƒ`W%šVNYnK>'@6ìXQ¯ž"jß½—v|òIwÝ:“OÇ(’ö ”agä–°-–ÛJšª3sÞ³°EÄõE§;+E§dÄ"Ä€™]–Åþ…/ˆh: :.€ìêÝ¢MIî%™Q¶ë`8Ë/˜ÃÒñ5ûb3Í2¢+'-ÏAÔî䀡—å5—žàJ4}«ÊÍku)¾Q—Ò _ûi‚¯û"é~m k‚{·—²ÈY `¡ãÆÙˆ\6dïäÑÀ¡¹==ÞTÇ€¥ì¬É Ddk–Ü:´ädî¿ÏSÃöÞå¼´§¸ “ ït¤^ü­ê|ço™xgI†ìªß;“œ»ý9 sxiœ¾Ìýì "8º®ƒ :Õ%)ò÷`DÌLU#ÖúZþ ëëx Ï`1gÕ÷ QOÔ¢Önõ  ,BJ4†ßWWœ(«ù‡+)Wef#²7p2¢f’ ÞÕ· µ¶@ïG 2½Ó´ÚÝzÙ]ïÍ2m («BÔŸ;Ü£ €]ò¤õqIé:’Ñ4¬KF3±ÆÁRD¼ù™‰¢í仸kÒ²tZäÄŒ+°·'ד™Ï,Ȧf.'厵Å-±ØDk³Œ"²q÷ÖוTš«rt½îЧ}c9¾I9•bÞ~] ¤”f¯”CÍNœœU_wP ÃÆcRYè–u½ú¸.þìc>Ó)ßÑÈ'xØ ¼ùÅlCxçIM3…3ù,e:÷ÅÕmß=é#k°³(³‘3UŠš7¼w ÐáÈñ“¯Í^Í€ŒëUo&\²Æ Ž{ÕHCo&Xôfz1#õu}m+êˆ fnk½›aÚÜ´ç;Ÿ’S ´mÂv ‰^vߌ6æ]ÑŠZ–WrpuŠT-ì*ËžHŽdDœßÕ òÖ€žø€ n©5.RåÚOnµÞä׿ñõXImëÈh-ûJ»ÙÌ™ñÞ‡èQ‚}#VBjRÄçܪö Ÿ?÷pâùëk»o¤qCFËøü¿{s߇­„êé^Ý3G͌şšæêo2›ò³nu¨Á,•ª:ãÌËî…Á-¿äÎ"Š["ÉV=, ³nû#5] }#ìLŒ½¸o‰¥@nC—”—©0Õ>gBð`R}•?´á¥ˆ_o&\«±‹“õ —ÒÅÇ:“¾=oàÖˆ!£9Êî ¶@ïGð ¢ñXÇ»± 2AÀ0h¨c0WÒ^}–ò‰PZhãN‚'Ÿ€É•·ñ:mY´Z-»B Ýb¶·”cÒPCñð#xóœÛdµ}ÈAk5„Öªí-š}¯e±Œ÷2ÁªÕ¢Äº±š™êî­$ZÈÂrqöű5Ù¤óä!EÇ8SEÇ8SÌb0¼"-5,P•ÈÊ´œÚi «Ÿ {¨hg¶+#E¼Ò6 3'oíܹ»«<0@Àå· ú×j޹³-Þ04pÒ…¼á/BlËZŠ^ý«.A‰}ÏV ýÂÛ¤ôò¯L®q;Å_“r^|—¬–ë(޶®¶¸¯fbõáœ1¶›ØÍμqÂ]>Š3FÿHÒÄv–V5JѼçRNð`â ì~Ñì /¢!‚Ú`ˆßý#ê;ú.Hôd,4‘õº°ä©f?Ä1š}ñTJx#¾ÅÃËÓ²«J0<ƒD‹A–z·qÑ£+-eCyg×y¨¯),Ý›žõ~JÆlØ‘ý^mÿŠ3˜­òT_o¢¨±†à`yW¾aa&üAñ!**ûßS× +uÉUÆwI\Mî49=’™•¢î:q2¢ÅÇ9“Ž g-´±Àˆ¬6Xœ\˜™»Zç°–òœ]0Si‘Þ'¹êÂI’®!†Yçƒ}‰Õ® ãÙWó†âjë@›Á©uÓ×]^MOر‘àï—´ Î5óMþÈWYVщh6ÁÏK½4†õ䛢Ýx¯ZßßéÁ‰rÚ7\ÚÙô]Ú^‘vD×jK¥¨i±ª4-V•\åŠw½ î΋½'ؽŸa‘Ž#!Ê* Û´ÁE_14fb—Šv cpãšì»÷Š!.™ðÚå{î~ÛŠ*Yñ%[×ìY‚½ÁWÖb™L3ÚlrþÊÜ%ùÚãh 9È‘±Íj§[ !›@'…馌õ¡si{ Y`¼œŒ—Ó RòÚgÃAµÞäÕ“kßÉ/¢“mµ}Zm®O™tgÝòGI{ò~MúòC‹ÿòÃ6‹L°Ñß¼àHÝtRÊ“ed7?ª¤þy¯*oYM¸/M¸dbDÔƒÑ&†Ì–j)#q¯lÝõ›,nͺøŸ'S.§QO&ÉE±†ÌxÀ<®ÕàÔö±'iŸ½+å4t«'q榄[ö¹)Ì{K§œ“;Ôc3ã÷yÖR uú.ZcQ[‹/ì7YТ»÷¬ha+çkô/P¶pRNF´è%UtŒ’b„ðò´cç¼”3²2-ïü_Òœ(«¾áR¯6>fŠ"ÞÕ÷^2»ŠKƒëærÞåwΫÿ 8’ÅíûÒ“„SSÙw›Ófd’4¿ûŠàEÿÛu˜'þH0`ËJ‹[2ר'õÆs¨à¬@eÆ:´è•¤«§ïsdnÀÈSÈV¯fnR±Ò»OÚ«›½àŽÏ“Nj0È;ñ 뙫­5‘²uØ#ƒ¸S˜‡˜‡Im¯*½vD˜ÐR:/W—òˆ= ѲÊì–^`Ô$DFM„Ë"ò¦ý˜3›)§.¹EПƙÓÏá”Ò|CNákB™¸óÎï8 [¿}Ó?ãZ)=p4g@ÍZÂ…wR,» 9™7.ºGNÚ»ãi?‘ScNÓyŘh w¬5ùxÁî "£NÒW<å —êfä bZ4Œ7ü¹7\žbLò«ÃUÔõ5ù#DcêoyƒbRõ/y1©<Ñ™8üŽ@ÈYÌ[¾Á‚Þ¾)V+Ž|Î:üÅ;ËOqÇ‚å”gˆ¨¹ŠšRÒûÄj#F81¯m’¼ˆ4Ôï»Ê“¦`RT©®p÷•š´bÁ±ƒk—Rü‡[té³Ù&ßZ¦3À ‘™k}õ1]zë ]\ð‰É/üÔäëjúÖáô!ºôS]zýÁ”³~3Á¦Îà–å&ÿÌqosÅd“/Üÿ‰èÝ–Fá¼ûšòCkt‘ Æ·™üÒ'#y;dª„tî$ºø Ëk 6.Teu§É1‹A­Éâ¾IÉ›^oö€«BÐÛ tÿXGZ p5\þhS~óZM4âÅ6èâê'BƪsOD # ‹µRòc«¦fÿÎTj€¥ó{oh[Jͦ⻿^ùCZ®¯¡8XŠÈ5V’ç´$†Îþ?Un-¸Ð_pdôŠž 8—H×äέi¥y]î°¢èúÌsŽüî½@C/§GÞµŽÅØ÷»WêmKêïKúÕk€àF¤¡ž¢²Š¶ÅéüË1yéÂíØÆàŧ˜Â­÷ræ°±ˆ†œ3“àw^&\idµ[»Þ #Æ#úãK9ó-þÁ[ éÊÛ˜qÚE¼ ÛIõX„ÁOgY|í&Šnz°kô=¡¼“é‰éO^Ö¯<¨*á:ŠŽ;_J·?o*Æàât±vÅŸŸ+Ñç s#{½;6ó—‘@Í·ºô韢ÞÉ׻カ-±/²Õä6¥Ic/j;oñ8QãeDEKžz'\çB.Ó aÛÿRòš×b>ÄCF»± @r›)¬ýGØ?ôмj±&4.LÉ[gÇ|Pv¢;Þ>aí³a¿R›…“•”ài[ÐÌ$E›Þlö§ ªdiz³…Û¿€L|ÞÎÚÞo­øƒ=?›7º³ ]~#g^vg¶†Fuƀ쯯Kéö!U)„hé@AoâÄßþ7c9É+@g©å @fpú5¢6ã§’F©8’ƒÙ«¸ô„‘SE}Äñ¢Ö~Ò 9deB±ZªÅt`è±’6dФÑnjó>Ë™u–3™ë5yƒsêƒÁ&Æ2µÒ¡ië÷Q~¼’*=NIæ <˜T“)ß¹7X)ЏvTÞËYuµ_\ºoDæ¸ù~Aÿíͺ´a%Å·_¤í \‘€=ð´Cý÷† g7*üä.QûãÍi¹f=Å5ëõ]¯¹àçbú´Ÿô]X@éPl™êÐßüCÊõÝ2íî[¿g¢—<äŠIYx DsO ôŃ¡àW†òÛ_3D »Ä Œ–´ö×Ì(€‘•šY©eÒþÛ½AÎyS°©ãyt/²æÙP0¹Í—ÿ¹qW÷$@6òæ@cÍûqO¬%ê¬4E8K7X1#;ìÿR€ç]üU—Þ}ÑŽ;7+‡åŽçÖTÅvýE™×n]›I.ÍÅ—ï‚ ãøé$C6ù,Q_·Øâ—Î5„çtÞñ¢;Ö¾[éÁŒäæ!#&ƒ5_ªŽÊ©J‡ü˜ÆšîF|·Ò¼NsÄ6ê‚{ØÁàZ¢:R;L"ŠkëŸB4s…ö±v›Wz—ào&H‡¹±/±z?ƒssVc½!–U´MÀ?ßæŸ_iòV3tçµm]Ê àî‡ycå†j·Zœì캠^óKÁH&üä‹ûûo ñ™?bQ9¢<XSCñhfâ«×·.œÓ¯s¤Sq†æ¿gˆ³ÿ/-¿ÿTZöc‹€E)JÅ2Öè¢=k)ÞІóæ”ëܱùÿJ¸7}­96Ï×JÑ…h*L°£HöcÒ^ ó2bÃÏtÆ—ÿ;îÝöµªìXœ–e?¶Ô0áˆÎàÀS]ñèfCè.q†]í­ýG(Ø´XUäbÞ0š g´$KùÇ8ÔÒ:nFÔ:“oXrnžõñ.L$?¶ÌÅZÈâk)y•?”ÍG¢7,x:~XB,–„,•@Vöæ[Üw0–½b•eðïÇ3ßïÄã»·Ž´GvÁ]‰€}…äÌTOØoâ f’ˆ{]¶h/Ñš,ŽïÔÆç Æª&?áˆÜšè„Ó1­‚Ø„#s/^ç\Á™±fË«º&N1“§Þ•Ò3_°øš a€uô‰œU1ÓÆœ9r&ƒGu=þ1˜üý#%±ô+Â…ëLÄdÀ!ãz^  ;éJ)ÍX×PªöL:UÔ}…ˆ®™o ÛV›'BV:Œ3'Í4O~×MžL¾PNqÝä ›*¦ ‡qFQuöÄowGÆž£$9¡«X4UN&z^§¤â×PΛÇý&ظm~ÚÙ`fŠ"w¥`áP½C%½ó˜w[°!¼Z“¢ßë¢&œ™ KˆJ~lårhG*©lÄÀxGz½…uµŸ$\©&q¦úFþ$Yu– ¦&œgˆ¨»+»¶±ÏF|“!ÈÅ]¿É­õû6q9ÇÏà¬ÿ¾gY‹>'Üíç«ÊÙW úÓ9«hdj €Æ­_FñÜwLþÈyë¬k„]žÑ¡ã0A5Éà㷩ʰÃxË€”1FOæLN„lÇF‚_{8Sµå¬›jù0LZïÿ‹ï‘SÛÖY8TKñëP•Ë~ëÌÚ ë`a*u¤6~¦:—¿÷"‚àHQ7’î\¢Iëf&<?¶Òáî=‚“åi*t¡7ëH3 `ÓbÕ±éÍhä¡$[ó!`Ûœ˜;´<-­$=ƒ%].ä,ˆ‹m4Äú¯“Š#quN@ÞZˆp¢oß$Û½Ÿ‹}ñB]ê¼0–”CöòÁøp&Á+¾c0Ú àÈñN8 Ñác;ÇÄ­‘‘äà®G}ú™Øœù¼Å×l¤hûfŠ@qA6h"ã¬É'utÁ;Ý ‰Ë—;Ä—è౜U6¤k('d&™I'‰úg¯jRý‚CµSšY‹a«úHÁÝ)AT -É™‚Ü}çAÉ…hÉÞàs,„#Oq¨¥cE}áK Wh‹ÅÇë-.ÙD°  Z'u¨ÿûLužrÆ©d³;n8EU¦ŸÅ™#ÃV~)¢‰ƒ[ÖQôÚßtiÇŠ$²³®;¸¤~¨4-Vqïÿ$%5óµ¤óòkr¿ïÄ39áô–p9îÔó®Ê„ Ñv£ÚS>ÑŸÿNÔA›‡bW"Þ çðfë)Û&I’!;âÎdíb€{“äÎñœq½”nßá1Çófå¸LMæ–ëc¹®ËS€ÈñW)©î¾—‘':Òíæ»£“Ã.u%ZÏÓž¡'Éjæ:º¯Hã,â¬ê³\ñöŸ¯Õ;Ô)À‘¢)ÎTág—Χ»C)áÍ¡Wæe-ìZ|\ïDe”’X×î«C` Ùàî¿JÚ¿5„^1…מ0ÄמÈ^eêØ3@‡y¸h¢?¾VÔf=£K‹ÿkñ‹ÿÛ–kó—]Tj IDAT1‡€§ïRCgpÌÞœvaÆ{׊¤@våï”Ôc×$ÜK?3ÄAãuóÈ3ûÇœ8ò\W²aµ!&ê,~љTèLˆ>LÖ¾óvwŒ!ú"µÿM¸Ö? ¬ïô\`¬CrY×1—ª5…MoDÛÎ×΄8Ȇ\žn/ê{‚±°ØÍz¿7ؽŸá/«ÿy1î¹öç]Ÿ“$~|&g^ØfÙ£ b°y}&ö²¸<·¨p4&ãŽæc™òf”Æ‹]ª¸ìbàHL~÷²œbݸï'MçÍ Ó2Js½fðxÎ8Ιd 0Ó`Ð"ðd«¸´½¾° “ŸþÝÝ]e—Â!œuÑ_|Í4ÇB @&þ÷G¿ôF)À2 ‚ÖÄ©\!SdµüY%fKcÜr0â2o4ó}tmÁ r:8AN–I–ÁBnkÚ®×¶`©a2؃F ‘•šcäͦλÆ(êÛ³Tå”3vw„CývŠ^xÄ]»¢ D¯}À¡fk bÓ÷„W¤åá×µµÌ ß¾Í@´vƒÅeýõnÓ¿¡ƒñMºXý8Ûóœ‚H¨‘A/ºÜÀäé˜æ¡9jBnOÏIçp֘íÝ5üA’»îׂ>ýLÞüf®ÅmÛDñÎ %yAHTczÄ œY\ѵºÓÙ׉úѧðÆú¥„‹42¨§3kŠClûŠÇOåMxcúÅÙ…÷€áœuùo•Äöõ„Sc£Z«ç˜s¥t:Å`^–<(©æ¬ÉÉ)W0÷çžðc9¥§(Ê+í(Z ‡òæÄ󔤨3˱#“6®~'éjÞl Zœ _•`FˆZÉŽtÓZC4×»é‚ÈFß–ßX÷eR‰m4DµÎäåBÎôVKzñ±ÎdCשž¸{ hD×k¢ZgñzŒ`@ýØr–òFÉ4W²s8hOPë,^ ö¬ÐDo±z?ƒwaš4mèÍk3dkbÔÊ«ÏZ\cƒyAÈFMìY|• ¶ üý '@†zY‹¸¯À6ý’Ð’´œ7*÷d¤TˆÚâºtØÑŸÀ*„hÙ@dÐöµñÛ-¶å|ÿZ¢:r¡ÀaŠúî Ï·w-íjcÓ×4,T•Òå.à?Ì™šùJÒuY7aW66}‰ZorC&ù{žÛÒlÞñŽ“ÕÞO9Ͼ¨í1- àì× žýz׌xAàŠ›yóìËz[eÓ¿ /K;^àÍÙtÁ9É•øë_uéßGïÏ«²é70vÎKºFÜÝ:$x0Nl­\Êà¨qß&o_R9†7§_ÒÁòÞU|88|ˆVŸáŠï‹rpû3AºÃ¼CrÇX‹y˜¨V&ìªÄ»²ÙÔRò“÷ªKswؽ"òÖ†4‡ôú3öðŸÝÍY‡ƒèÒ…†›L¥(,¬¼ ±£ŽG¤¤%þÍ6u°™·ZJï¯:Fœ #Npw‹ÜE£f.o-^hrã³4›ŽÔRÜ•¢Þu(p’¯ù¾_†‚ï|¼o’¤VŽå¬c9‹æ8qø0u‘;¾7 s_úJOpí¶U¼2Ö™|÷­„û†Û`¿Ù8Ùô_Bߥ•q¿Êßg-²lÞOñë‰þå1ïoÿœ1˜»=€ýè4D¦ŸŠc“D°C4Žg»3¿qý [gǼ=YЂ3|á_ßÝXøágýFgØì¨Åàöãî±wvßqÔ;DÔ£ Dí— á¼K÷M©1›C›èz]Ôš,nØO³'‡¶'0II½÷dÜsÃmûãÊlNrÅ •"¥({ ÒýE|£.ˆyØÊ–ãÕWôCg— WÔ‹!Ûž¥ùÍ¡ZoqÉí&_p„²[×›\Ä[ºWÔ>zŸÚãÈfµŸ$\Á ²Ê»vŸ\rv^ä©¿1Ô¶ç"›¾…Q6¾Öœ7øÒìe;ûÅ‚þî›}Û1Öæà¡üDW²êLO¬·åûšÍ3£¾ò“Ý»5‚í ö îÇNñEºŸ„58l$›ßnöVžåéq÷ÈüS}Íü=€Ö!¤`“ K¥¨~~J);¥g‹¯ ü‘'úà¯í¹È¦oÙþŸ¸Ë;TÔœe=·–•þëŸJìSéds(SÿuJ<˜øFH½jjÔ[lÞñ “ôš&l­ZÊìßÑq»Q@ß0©Ç +&Ê$WüÎÛl«Ó¡c¬y&ä¯8ÝëMùÏü£É•ß#òÅgö²é’ÛM¾~~ÊYy–·ÇÆܘú¦¸cúM÷(mlö+MѶ9qÏ  }9 0ôödÚÏ)8=/rï/ :P]Ôl¨ÉàÆ×›óžÛ» ‚ǹãËwˆú?ž´̦֡ך}® ÑØ“ÊW›ø5CKÙcÈfïPë-nÍS¡àðkü!,ö>½ð8WbÁ·®^f¯lú–ÍoE½ÅÇ;{ÒÔ¨·Øƒ·Ÿ£”ò&?Ö¿óg¶µàPgÝsaÁQΤRºgÉ3¥úC/½É¼¹ö¢v(R÷ERÑ£WþØÛ“÷ ^L^—ßð‹›)Z·ÒC6{†!xÕÿ5å¹"/ì,ö,P|ž?|÷íÛÆ+›¾"¾I5†P:=w=þ¾ÄžDäëJlUEí©ÇìÐC•­ïÅÜœŒHÉTçn;ªåb\•ßx÷¯Ø´ÁNø;”ˆ®×ź/SÎaWï¾RFwHAΪ¸*¿ñæ« Þ²ÑC6½ÃL´â±Æüçy#ÝÕ<ï ® ÁàG:ã7^iqÄ®/d³—¨u&·î_‘@õOüáýU.Ùè?ÊÎ÷‡ßÿ¤ÿýض\j4}§:bëuið%y{Ç»-¹4Øxå% ­Zn ¬CÈ*MÚøj$oÄ êEÜy.”RÞ,½4Ðxý¥„«Ùl!›žaÄ ZþhS~ù)£O’ï OðÄÊœºãÂõ·Úï6ZØÂ«Ÿ«¯Ê )%û¯¼£-æ~ @@åOóÿô E˾µc@¢t±æ½¸wÄP_ut–ñféÕõ7\ÇÀ³ì ß™ÚO®š÷bžÑ·ç7о¾‹©tW‰FñEÆk.µ¸÷ÞìÚÝØÆ¦=‘Uš´ô …å'»ãGî¾Þ”?èb_Ä]%ûóÜöâû‚“­º¾ þž_18ëßv¸Ëº/’Êæ·¢ÞQ·9y÷õª{ƒ#Ÿ³ÝRX÷ØÓˆ<òE¶õé‡# ¬!’ßlcîÌoÜ OîA¢1äö¢ºç_ÇäŽë-NMõõlú;Œ0°é¨wëì¨wô­ÿÏÞ}Ç·UŸûÿ|µ,oy¯8q†³7  aÂ#Œ”ÕR Ð–öÒÞÛqÛÒÛûkKÛKK)eïU#0CBÈ$“8‰ãÄ{ËKó|<–Ù>ÒÑxÞ¯—^::úZ–Ž}Ïó}žÂ¦Â“"Ó6}ôмÖíÕFÇ~ÅkµXð\½Šî«š Æ^bé¥2šZ8@O0)9z÷„»Šž|Eïºÿgnƒ±WL+RžnÏiÛmK}¯º3Ÿ¾ôfÿݢƘûn¾VÑ77rºB"èktvü¡¹0µÀàšzk^«Î0ò´–¡Òtʸ[ ›ês2­WŸç4íÝÉg÷ékr¶ÝßX sþ£¨!µ0‚gPqCAËÇ;Î;nt»:ùXƆ×Ye7m»¿±¨|Y–5vjŸcàƒeÒ…{SAó.kjÏmW»ŒÝÖh-i`‘æêUt;ÿÐThÌÔ¹§}'¿EgŒ\pÀ¨KsÛ;'å´­¸DŠÿù•[ß•õëLmŽN·þëµåî}¤%ÌY£Ïl<_E‹3»K¯+l¼÷.E÷ó{\ÆÚ’•£Ó­?ø|‡e矚 +.ÊCè#ÿt:`Ü· šÛJ2;Wœç2r9Pæ—ª_ï̪zº=wÚwò›Ã)9«>Ý“ÀJηt¶n59®¾ =÷êôîK®Ñ»ÿÇã’tKÔ~ÐYÿqwFÅò쎂Ò¢ú‰>oNj_îìÔ¾Íwe,?Çš½âZ¡\ûm½b0Fs,n›"޼iÍjÝÙ—6zYVçÄëƒk›®¶ôQF生”ÖÕlîMûî –ÊJà–ïëÝã'©›žÅb“­Åe¨yËšÕ±ßn.]’Ñ5ÿW%õŸ`ð£pQfwF¥Ùö³{[ —ç·Ý£w NeìínýÞ‡[òÓG™sZÔ("xv1®%¸¼¹i}–©©uÿ~·#ûŧzÓo»[ï:}™žßãHÓ½i5k¬Ùy3̽sZÜ 7ksЂfB dô¼õ^gÖóKz2,rÙ:eö‰:E­Eªlä¤KŠÖݶ”æ­}iUö”ÒEÝó~^\™Êa  ðÄ´ÞÂÓz[wô™|wgNqžSãwõîYóunp ”pº;LGßëÊì9æ0Zši­\™Û¦õ±"­Äèšü£âúO^iÏÙpa_ê]÷éÝ'œùÆ3,6))j×wgÔÔ9þ K{žF)-ƒq€žôf!Ë.ÊépœžÕõè –=l7ûkÞ©·žg@c’â’¢m§Í|ô]kVZ±Ñ9ó,ä ‡Î$dÙ·,òÜlëݶ”ödtÿÀž2iºN.ûäì“tŠ%OÛ™‡d#%`ksë[÷ÚRš¶ö¥ö4¸Œ–‰)¶ÂÓz&^ŸÛ&b0¡$oVª-oVj}Ç×ö”?üÅšÕ]ã0©Ô)‹Îʉ‹ôîâr~Å#{»[ßöUŸ¹m·-µûˆÃ”VltšÞ3õ–èÕ†0Y~En[Ç×ö”ßÞßaIU¦[ïÖ»O936޳,òÜ6E[Û•ÙðYOFÞìÔÞÙ?)l0eÅÎ&èIÄ”­w¹6¯µ·ÞixäÅ®¬?ýÖf.%pÖyB9ù ½;ËÂoˆZrv¹u-ÛúR[¶÷¥ÙÛÜzˤÛ¤r[ÓJŒ‘[<5 ¤'ȲAUö”G_ïIsþÑâèvu€0ÜB! Š SÓúŸ_RÒç{€~öý€g; HøÞÀ€Ÿ¥ðþNØÆ{_>ÏlÙ{Ÿmý0ø>|·•ýã?ø½Þç~ý=î6þf†mãMçó{Ÿ8~/ÐõAß'H1àorõ*:ÅEgh f!Yz·)SïNÉÑ»KOÏèΞhnÇ3 3ÆcŒ Ø®S OJïÅIé~ëb»z]²¦ñiIÀ®S´(Ùi 3ÆcŒ€!M§Ò´K$q8éÏcŒ1ÆXââ1ÆcŒ±Â:cŒ1Æc1„tÆcŒ1ÆbèŒ1ÆcŒÅÐcŒ1Æ‹! 3ÆcŒ1C8@gŒ1Æc,†p€ÎcŒ1ÆX á1ÆcŒ±Â:cŒ1Æc1„tÆcŒ1ÆbèŒ1ÆcŒÅÐcŒ1Æ‹! 3ÆcŒ1C8@gŒ1Æc,†$o€.…Ö#`Œ1Æc‰F…3itGg]ë10ÆcŒ±Äâè,qŒ™´zO}eÖc`Œ1Æc‰Cè©›8â3it§5Ï©8SœZƒ1ÆcŒ%Å–Þ§8ÌÊH÷“´:¸ú2y1ÆcŒ©ÂÙ›¥Jl™ÜzOv·Öc`Œ1Æc‰ÁÕ›ÍúH9»sy1ÆcŒ©ÂÙ¥Nl™Ôz×á™ Z1ÆcŒ%ë¡9õjì'©ôº¯:,µcŒ1Æ‹{R YÿñŠ#jì+©ôÞºÊ^{˨F­ÇÁcŒ1Æâ[_SE½­y´M}%u€ûO>¬õcŒ±pH|*˜±˜Ð±oj1eÒèŸ_Ì:cqʸ¶Ãâ¶«ÐS™Å w£Ã8þÁÚQ–—Z ´K¬s[]åõ£Æ>RWæêS’þýœ1­5|z9èjißsZ»½£°Mëq0Æ‚§8¥([ÝPrO‡5{ÖêÚ2Ùè4j=&6rºÝ½ig½ÞXr÷8·þ.}oZÉêúbÅÉÀüÕvÓIOוÝ9Ú­ÿ~™ËpÂãue²ÁÁ¯Æ4bk-k¶œkUkI @íÚU›´C¼pÛ¥(ýgCIÚÆ®L­ÇÂ’“lsê§>ZWvw¡ÃTžÜV¡èÏx½¡Ä´³'Më±±ð¥~Ô‘}ùæÖüKË) /0?,t¦Lz¬®ÌÕîÖk=¾XbÞÜ•±l]Sñ ŠN'ƒ¸¹BÑõF#¿˜¦”­=éú·Ús„iWGß½EÕX’t5oçp_Ó.¹€ltg­®-ûa¡ÃtõÁöÜœ7Zó´K.ú}}©‹^l(»½Â­7û„l—–Kqé¶Öü¬uííFÇ•órKþª:«eaÑÀÙò=pç·þ´êJåA{ŠVã‹%Ùo·å®Ø×žw^Ùñg..—â’­­ùYtdk16–¼œÝŠ.ýá†â+¶·æßÙÝ••ûçÚQ®êäyÍöÖ?V»î†5÷ɺÇá×îÙ õb™igOÚ™¯7”ÜV¡èM:`v.p›£'£ä_ ÅÒŧ Yäe|Ô™}ñç-WŽ–ÂßîÔBˆ[º² žk*LÆÙ›x¤8¥(þgCñ¢7}Ê­€kGKÝE5¥lìʈêcˆ”@á3ME7uugÎËz»ÓŠ ®k´Zò_læ~ºµÙ3«-ûy‰#åô`|ðËInýÙï4¥<Õ\˜èijÀ¡—ü¹ÚûåÝ£iãò†®ê‡´G,ÊZ×n¹l[kþ%å_deéÀ÷s)•Ö•¹:Ýü\b“ÿbsþõu–Ó‹‡?ÐWfwšm©£­+uÛ”„~Sˆw.«[7ñ±º²8R So¿´DŠk«Úó²ÞlÍüèb‹Û&ÅèGêJïH³™Ç‘\8Ù|Wß—VöX}I¢GL;Ž—Áò׺²[›­–Û'J]š¡ÿ:àÒ1Rü$½/uôƒµ£”­=éš 4¬çhÙzN³Úûå ÊÇÁçÿósÅmpk=Ž˜!‚çš oléÊ>¥~òéàÎÑný©ÏÖ•éö™£=D–؇£«/¹ÝÐ7ä ë`9)ÀÝ¥.ãœÖ’õSdGÈÂ!ÛSNy¦¾ì®1S•™“|§¯'3ÿ©Æ"©Dn|±ÄÝè4Î\][vO™Ë˜BÂ@a*pw‘Ó4ùѺ2¥ƒsø™z¤_jÉ[ür}é/Ç» Ór†Þ¶À üd’¢»rGk~úà ÅÎîĪ6¤¸L΃Ïþ|c$öPÔHuì?©óÈw­ã³ã4c3æÑºÒ;ͶÔÊÙŒ:¬#u|ÔR˜¶ÁÊ‹G™*”V§~úcµew9M!~ô3ê€[*ÝYÿn,6nïNØ™›x”²©+ã‚õME×QtáLí–g?Ȳ›+­+sõ&Ö›ý`†Ý½©K^k(ùÎXEo ã/5ë;*ÜúSž«+å ¦÷>›¹ø/ÇFý½׌“Bä‹xq1ðóGʬÇjËĺ΄X#!~õ묇fwEbÿBªÔá@ˆÜ[²ÙªìOKSn½s~ÑI¯Ï×zZ‘ ã¬W›Šo­èB}SØÑ¼œžÖݶ<¿52£cÉÀ¸§/õ”O[ ./ùéùÍÀE™Vë99íjŒ…/kM[î¥mÝ™'¨°¼Ü-ÇŽè”íË u%&ÇÈ÷[Ò?ìÌ^vØš(­+XïÖ ùþ4K{ïÉ™ &XbsÛ¤Èx®¹à §-uÙ¨‘íkw;ðl«ÁÕ|Ea£±ÈàRg„Ñ×°áÒûýãÖðn½Êüó)åö¡¶à}s¶|iÖ¸m´G´™vt§/ú¢=où¨ðßê{Ǭ&{ÝÊ¢FAð ’ÌõÙç±f®è1‡¬À“n³­éªÂF¿+LYDI <ÓXt£Én£ò2ÏW ùÑI¹-®™é½êîY;9/·ä_iïMŸ>Lê@8¶¶¯X2º:¾•˽?Xð6teŽßÜiYU¡è²UJtKàùj!7¦ö8.Éoqv.¬³ê„¯·ýö•uáïô°R»ôó~yîÅæüc…Z%Z²ßkϹ ¡+ëdÖþ÷¹€Gjõî=+Šë =çõ³À$PðRsÁ W_Ú¤Kìp4\W×RuI’Á¬=WŸ¢ûdCñmE.£Zoîƒ}Ö$äå™Öž3,‘¹‡èP\R”<ÑPts¶3¥8BÕÌkºÇm)¶ÆkŠã-(bÑåìpëóžm*¼8Ýiš¡š@µ=ÀêZ½»öܼÃ$³-2÷¢.[˨Æ/þÎk®¾ÌÄ6 ˆ9¯6eÆ=ד^R5Â:±MJ è¹¦¢ku6ó¸,÷ à™#BÙtF~‹21µO½=³DãvHQñDCñMyNS^3e] ðϲý‚ÂF1*ñÒ"b›ÒåŠn­è ÷wOR{Û./P½šB4¸:ܺ‰Ï6”Þ6Ê­÷­† Vðƒëàuüa•ù!ýšöœ™‡º3WŽ“Â…roƒüÀd¶u_YЬ7Çî™÷žú ǾúãïÚZËì#Ûè#&ôN1ýŽ›OÍ›¹~¦Öc‰WŸ¢›ðTCÉÍ….C¤f·Þoò Ù} ³TkˇÒä4Î|¥±hU9ÕØ†7Ž ùáÜœ6Ç ÝѹÇäcØÕ›vÚ†ÖüËTXG¬fðp›ÑQ»²¸AgŠÝ7ùã¶§œðNSáõc¤.¬•³ap)”ÿóâÂF‘€9ü,<®»©øµ–‚•EnÃx'ì‚ÑéVW딪³Ûł̘;6·î\²s×_ùLº*[8@W͸+~;¥|飋….ê}×9L³ßh*º¡\ÑEzvkW;ð‚9­§í’ü–ÈÞ‹'†]½© 7´\Å ÎkS3ðzAF—uçãª-c}GöyGº²©¸Ž Xv7ðÐQ½{ïŠâzCNì§×¥lêÊX²³#÷[#X÷3¯òÓ“s[ ”ÃÏB§¸¤H{±%ÿ”N[êÅcü7ƒ‹–ÍÍÀ«=FGëU…MÆH‘§ IDAT•Š^9úÞMzá¾½êí•tU-xµxÂÕ??Û˜Þ÷¥S¶vgœ¾µ=÷?í¢#¥©x´Ãè8¶²¸AgŒ£Ù-YïwX¾u¬+ë”Bí©Twÿr¤Øš®æ|\µä¼Ôœ•³/}jÖKxúˆP¾8+¿Y™³y­Yoµæ.oíÉ5ÆŽ†ÛZ—²Ó»;/È‹‰Þ ®>E7æ ªjK¯Às–³Ýè8vŸåLtb}göÔ]Ö¬ë+¤.Ò‹’GJxµFÈ Ùæ¾ÞËò["Y¾Yq]-Û–î¨zöÛ…|/à=âÒJ¤]þÇY•_ŽMÉnÊÕzŸK´¥Ì_Û\xíèè-p UM7ðx_нñÚ¢-S™dƒÃ8å妢[+b÷5`sÿ¨Ñ»÷_]\¯‹ü_¦.G“ËPôbSኗqšÊuö#­Å¬>ªsW/ÎiÓÍRwÍ„½³°Íz`ÞáïÝóUo]eÖcp€UÙ•_fúÒXˤÏǦ)B»ÜZ_â˜Ã4÷ͦ¢ëF+º`ÛòFËžvàYSjoûe|Z5‘™vö¤-ÚÔ–?’XѲ¥x97£Ëz>/ ļ©+ó̯:r–Eq-K¸¬à¡zƒëÐuÚ¤2éwõ¦-ø´5ŘØ¬$€§ª…²yi~³2>vsøYð¤L¯µæXß›~e…±úa:7k“½ãªÂ¦p_ËR Ù×4¦¡cÿ‚ß]v¸óÀ¼(Or€®sAÙ2yc®9ïXzJNCº)«9ݘٖnÌèH×mQ›ÃNßÑ·´êèèóbø ´Ù<Ü”Úwhù´ý:ç'šüOŽ”^ØÑT´  vŸƒƒÕt«­]Ç.šR%ô|ªßŸüuË/³µœ ÁZ–p¹`uµÞ½yɤý(ɈZà™õYMñ·šK–”ÄÏkÞ©rmEÙ1ë ¥<Ç”C™c?9Tqã(§±,]ëѨ£×üë°ÎýÕ¸âç©£‡ÚNqšÎnK³+·Ça-è±·÷ØZGõtì;¹ÍÖõõ©_¬­ýä­ÇÂBÕl\RºèÚóGïŸt®¥l£aO;ðTUAÓ;5/¯v( ÛµOð8@ORÝ%c§}û¦ÉUFgh=–Ð<Ðèx¦ê¿ž=hýÙ­ÇÂF¢Þ´²rÖ·Nm.´ÄÈš‡p(xüksß3U¬¡÷†£ZG{µ)×Tκó»S[ó#ÕØ,Z>mòéªó×Rÿïw#s†‹+¦}÷öiµeÅi‘¹‡h9Ò <²wܡת÷>˜ø,g˜}ǼÓKûÖªI}©±²î,R <PïZW{ê—›šÞ 0ÄÁYOГŽAw8meå ß»uJ{^†QëÑ„çãzòÔ‹ÞßÐøÊ:­ÇÂB—›òFñu¯ºå†‰½é‘n€-o×èÝOVÝôÆÎÖ‡6j=­XLk ®©¼ì»7OîKK”ÿëþà±ý3v¯©Ùù„šû5é¶g®˜°èŽïMëÊŽõ Á²:€¿íÉm}êÀŽ¿£8/=fíM?oôÙ«®WW6/ŽÒ ÕPÛ¬ÞŸÙùAíïŸm¶ÝvXëñ ô¤RœöĨ••·Þ´²Ò–Ï @`ä£ûfî~ûè¶§]|f0-çdz¯­üãeŒqÅéÇámkòñ}§}ñ~ÝÇIwª²å?g¬ÿ»+/—xÿ×fð÷ÝÅõ/Úó@Žk¤û+IÿÇØ+ÇÝuãªIö”x?æR€G÷¥ö>SõÜ?ÚìªÜ¸…Ôœü Î9£ô…×TºŒ¦ùŽwŽ ¹¦fʾê>~Èsj=ÿ8@Osó¿{âÕ^~n¹[¯õXÔÒjþ¶§¸þùƒ; bôEƼ–”.;ïºÊ÷Î/Tö­¡¶øûž±‡_«þê Í+bÏiÅËϾzÂg.гŽ¡°¹‡ödXŸ:°þÿlîð«9ÌÊ»yÁ•ãÿyÁ…cç8ìÏˇ ®çÞûüÞößD<‡Ÿ–i|¥äìQ7]wÃÄöÜx\ï V°zjßúº›ßÙf/÷u«#ÃÏ+OÿÍÔ%¥÷_ºjrOF¾YëÑÄ6‡{iø-™/7ûŽ;§·æç¤Dgl±¦©xhz9ü,c楣_·|ìáq‹Š“ãlZY}ÛúޱǞèr.×x Eà=I¿ñ­4}õ˜••·¯ºf‚Íœh‹†sZ±Ô¥þûÌ,ãô’·n">J)%®ÊìŸ×ëʲ>´'ËÎí»¶”ïMïÌÑúæêýæ¾>WvØyÇy©oͪ¶¢Ð:òfäÞq¶Y—e}üëì°þ¯6—[waE}éxg“_¯6:êz‹šÂ½}–ñpY–éÙ2«ãªÚ¡¶™bùÙ<ƒ.£û‘}Y]áÞÏÉ…ÕZç÷¯9ªwÕ:º&ü=(bBÖϪ²þq‹z£bƒM͹~á¥ÏŸuýD»9Y?<ĸ,àóÚòæä_vç;Gçîü´aý‹±¼–ˆÿÅqfnþ- ®¿ú‚³ËÜzg˜ÕhSb`ù’[RE€PÎ~0s÷Ô4Cé=/ÞñW Ä¡þèX064î{lÃýÛ›l¹uAŸëáåZèÍ}% O8ô¶£ˆ››?{ióˆV{|šsZÉŸŒWk@aêtdZÜ}ô¯‘¼½º·A¦Âø7Ùbü àÒôUÐé08ù5»Lº –e£/YǘƢdXIÀò ·þ´âÍsVï/žð~í¼\Ûsß^­ÇåèqÆ¡¤9_¯>ÿÃ׫û}³3í§s?Z0Mãù_wetín?ã‹‘ì#ÃX“Úíä1ÆX"r‰ –^°´ü““®ç2$ÓóHË7÷ÎêÎ\Pø³ë_­^}ð½c=”Ùµ—/ÐãÌ®¶FX.èí—òÑuF>³Álû¼ñuÎ%gŒ1Æü0é>·¤›r¶4O:°EåfeéUc¿?ÃWËK_>¤S66MÙ¯ö~%$ÊÓ©<Ú[E(8@gŒ1Æ‹1eaû‡u»þ‰}ß4Ùò ¾t«SÈ·îz\ëqDKÂ6aŒ1Æc,q€ÎcŒ1ÆX á1ÆcŒ±¢bº[ßwÙ\›:¦5:µX%pW`IbÛc乨ÈÏEUµÆÄÿ¢ñðpkþž)¤“_I'Û6( ô<Ýp‚\Åݺø(®$©qZŒ:G+-w™2Mˆ‰Š¾)†Þ6~.ªª €•ÖL¼cð;­Ç Ó)îxx¬˜z :üˆcp°¤’`ÏÓúá®T-@—RVk_,r„šÏÖ€AÀ!¥ŒÉæ,:Î%4nµLL:ôòsQ=Bˆ>­ÇÄÏ1æ÷'kL€;+¦žÛ§‰Ø8Ó ™LÏSÎAgŒ1Æc,†p€ÎcŒ1ÆX áFEB¤( @¢“³9* ÷CA†âB­ÇÁ´333µôºç¢Ð  @ €V)¥Kåû`Œ1– 8@2!D€“Ì ×`Üçq\&“µÓŽÃb­Ç.iˆüs±O±ÀV)eÌV!ðLd{.YÂYø_¢ê ÂäT*„8Uëqò»µ?“­Hèãá±bê¹u R´CÈ$tQ~žºXA“-VÝRFoÉèQ"„08ÚæAQ$Ðé K‡è°{¾:€N;Ðîó»NpjðÃYZš±˜— `€ùBˆ6Gó@?!D)€™*Aã©„p6Ð¥ÿØ |P4ôeéÀù£••€nç] Ìb>èTb ÕT¡X æ+¦·Œ¿]ÒkEËç©Kq ÀN¤”îHÞèQ „Èp9bdöz°n'pÅ: º»tn>X£3"6,ÆQ*€%Š…k"}€Šg¦|h O‹1xÝ» xï}¯ÀžvºÔö÷ÍÑrdŒ16€@…çbBì°]JÙ©;c$„Èç™Ze(i ¦Ð × XLžK íó½Åäù9eà6ŒiÉ©ëjƒV À ÌÎ&Y´U@S¤ !^•RÚ£yÇBˆqΠùÇë}œO;7 8±ø¸øË.ใÀõiF1ÆbŒÀ f !6Ø$¥TÔ¼Ð#H‘ à ¶O%é°q9aÔz$ŒÏîVïv´R0>+¸qi¤–ßý¨îx›[¦wL׸SN`£¬B<¤ž4»3€ØXœ »Úèë­S€EžÌõ¥£€6;ðë­Às€Î‹az TzΊ¶¨µcÍsß•BàBÄxpîÅÁ9‹Ev7pÓÇÀƒ»O€MMÀÃ{U½.àÎÏúƒsƒÏÑìá½Àˇ4r¨Šœé;BX… ÎJc1ê€Ó->£”¾6ÄDË#Æ ¨ÀõBˆÙjíôÈY PëAD’å¯3)oÖÛZh&|a1pQP” ìíþwPe—k–Û.ÖžO àñ¯iÑs˜%„˜©{Òì®Ue‰)N…ô”AËæ½NUO3ÆXDé,U+Hç—BT˜«õ8ÔÐí>k¶·G{¨‚‹·Â‹Õ³ tÇe1ŸJÀâÔ+‡éëήOßw9+×/rR€?,Ìž¯$ ¸ÿD`gͬ×õ£â#Eâ!Dƒ”²]Í !ò¬€:ÕY"B‚fÒ}õy–ÎZÇ_gÒÓZÆ‹QK….)宑ì„t•yR[ÎÒzjØÖüh•>ó'Õ@o”6}Ϙڪ»(×ü’±ý¿óþüÀWÀ)EýÁ¹—N‹Š§«€ºž¸ нeX_Uk‡BˆÐÌyšZûŒ„>pÎÿ×=¼—.¾¦åÏ'Ä–1–À–y‚ô}áî€Ã*õÍB Wl –S~°h·Ó‚¼¯;)PÿÝI@}/ðüA ªxh!ç,2ú\t¶f²e`~9Tx^aECÌ çyfXã+‡¹RQ$¥léŽ<— *µ gBv¾y°FÇýÑ•1–€ó…­RÊæpvÀ¡•Š„P—и÷A-ÐÔürpéXàÏ»€£ÝÀ ]Î(~º¸áCàíeÇç26R6OšCºŸ£”÷w¦!žwFO@æS Î,ú©Ð¸¾y0–WÐ…v´Øb}½.™ñ0ÆÒ8Oñd8%9@W×,ÄøŒU°Žzò>½ î²MTúÌ×]Ó׫­-À‚¢¨±D5âYt!D1€“T‹ÛZŸmí6FèŒi¬4q»!Ôr€®!Ì×zjépP£oߥ҂ÑWÿìea*}ÿE茩h€·Â¹¡'µå<ðºí„3=øqˆµ!tü,`,,B5Õ…tõ”"˘…«ÀL9Àހܛ󻳵?¯é¦ë¹‹¤†>àÑAËljºé붖㯀-aeüÅŒJ!„^Jéã¶óä«= HY½¿¿RO°&f\™ñIJqYta,^½_ üëkàp•ŸWÜ3È2i=²ˆÓX àéPnĺz&k=5M̦ògû:€ò±Y@n ð·=À”Àáþôm;–ß4XÕöPÅ65Ñ%Á˜ŒPÊ„:Pëé¸Ñf£J= ²+GOÄ{®2ÆÔöz5­[èÌN»^û_6¯.í_7”ÀÊ„¥Rʺ`oÀº <é-“´‡šæP9»7P€nÒ«&ÿopÚëôS$¥¹xóÔ“ÀxåZŒ¨¾) L'€ÒL‹ß#Üd„ ƒŽAqµÆ»ÈW'€o¦£•Ùš‰1ÙE È~08·8dÛ|P¼z¸¿ÏE‚›à`7Žß·¯ØR†(­è˨¾?hõYzÃDj=ûj5•¿›™ ühÖñu¨£d!€‰ƒ~'ôhÐ ¢ÕLÀ™`±g¿jèÞ‚v„?f#7‡ƒÒR¶ xï|­G¡™ Bƒ”2”ùâyM„Ü9jÙ¿Z ¼pˆNOË. œWž§¾ƒöAðÇ¡ÝÆ¨£ÙIÆÔVß |RœQähÖl£’·—Œ.G¿›•üd=¯w«Úž-¦MBdJ)»‚Ù˜tuŒ ¼Iü¹ÒÏ'Úë&Ò%ãø}°&PɺC‘Έ˜üÂóý_Ô†¹ŸÓ,PàÁ‘‹iÈZ×RÌÆBˆR%Q„Ì+ Ë}s€wRNú¯·¿ßNoþWк—d_ðØí¤”€ cðemM‰Ÿ6À4òu'ð«­tÆ+P€îí<¸çAi0ÉBÁ~’кÌÌÆ «£Pë$±6¯ùüœ 4›X`%€tªp_[`Ua_ŒR„ t3#9hH7Ð Û%c)}µx£xç(¥Ò­^Ü¿X=¥x‚mE ‹)h^¦Cb,(Ïr÷ ãñ×e—Œîx46Cñ‘”ÿhÐÕÁEµc°Ðï¶€RPVÚœ/ð¢ ÷Õè¹°"ìi§Î¶Ù&`Fn'Ñ8Êÿ„ªv]‘Ié/³ò€ßl¥ÓãíöäÐÏ)§*.¯Vÿ>¼V ”gP ~Ñ x„ë5‹´V[ÿlº—ÝMgÇÿJÒòÌY¨ÒVÀ¶c ÂŒË?÷ªëšû(ß¼ÃtØ=_@§Ï÷¥iÀ_OÕz´ÇÙšíZ#0˜¥”€^,íêÔr¸ý±€JiöâøW€Ï~º@¹àå Ù|‡g¿‡Añ¤¯|¾ËâŠA%™¼ìü¡@ú-žŸS ^ºÛâ·/úƒ<ÐÙïcÅ3¿jZñYxÖØüÇT‹ßתI´†"ÎîAèBˆ, |ŵTñáÍ Å”¥·OãÅ£=÷΢ÒtÕS:Ѓ»érR!¥YÆYlúÁÆ¡¯;gÍñ¿ûøBª—€Fƒô¨H˜Ùó•ë)úõ|ÊûÉ&êˆ=œjÍÑÑ Ð×…¶¸À8?·©ð,üŧ ‘蓃®» ô¢û7(-áÛÏãð€nŸß `†ÏÏW ºM5€¿û‹¯4·ûü\4ègxÀŸŸs¬€ÿ™×c Ç ÄÆâê{à+*£ø§ÇèÀ>¶·»Õû=×ÇwâG^õÐ㾎R‡XSC³Â{ÚiáùÒQ´Xt^w]Ì £@üÌ2Z€÷ï#¬ß»‰R~2¥Á0 rRŽÏ?F¯¡(°5ÐF \Ü4 ¤ÃN ‘¼ùbsò)eÀb²Sè«Åäù]ÊÀïc”·B»ïŠé\w‚ª¦ôøŒ˜šM¾ÀÃ>ÿ××PJM+€}žû.åÄW¸›ìÂK@tR‚š@³÷sŒpèƒAÐ5[ÕæT(øÎ0‹ýÜß®éÎÏ(®š¥ÏTÏ^8Ü:ÅÞcœÐÎlªô·úWmôaêÃ:úÏÉ~5Ò9ÒùÝ)(f:c4'¸;}À©ó“.À˜V*³¿ÄÞYv- f#>Ž\ºÖPËsg©úþ´€»g ¿}Œ³ †Ü7À9œw€‚pßù×OA3ß%.@x•Pf‚‚ì×00Mäh–}(@ööºÜ ä}ôP«¸8¬p:úôµÃl¿ œ·x?|à&Ï¿zŒ4Qe¥³3§ùŸIyßó(ÍxðÿÙ\àH7ðy#msQET†)Ád—G|òîQ`í1Ê­^^ŒñTqß8LR—·!zûóÑtÓL嵕À¸¬çôÔb®~?øím!œ9¯é¦‰“ã“{‰t!D®”²m¸8@¹Ø?Ñà«CVàXìãè³4“|>¨Tð‰çº³<߯ÅÀà Ùä¸4+9<‡ÂêÙÇàî- ™ |q­šÑ—˜æùþ?Cßà-7€3p0Zƒ0O þR? lðq—ûITºt,èÇâ&qØå®‚º¤Å}/ßCÖàk|OËž?+²ã‰ev7°¶–òô75ѱù´bàî™Àé%”þÂX¨ X¡Ý&ÃÜ:Ÿ†^à©À¢Ð}dƒÖ} ‰ô‘KŒz~üj+*ñf1Å~ãó³ïsZ‚R?¼Ë@ïgÛ†ØßAPÊI>è4T¨ú×üæÏ~3¡í‚>ï‚Ù^_ ±Í>Ð,|6è1Ð$@ïô´ZòWc÷PÐåy”/ð“E?Ú3ÛØ™±EQ  3âêóó@KÊ€¢«ª¹œ¨j{€‡÷ï£TÄqYt–ó‚1Éû˜0õ¦—k=Ф’hÐG.a“,tj*Æ |;ÐßIt=æ^çy¾¶cèJ-ÍnçûlŠáfƽUT´,ˆæý›Úp|E/ ú0‘ðUx»Hvù鉺Åó(§ÑiýÁÒ<ψîg„bP ãKÀƒ|,;!Ÿ.,°--Àˇ)Z1¾Ñô?‹¤½ô8=ŒÅyL3§ƒÎZî0LÍ–8@‚„Iqì”"àé”ÛèÍ A þêóóp­Ñ½3×>vx…3ÓJkv-Dã1PE©çcÌÞŽã¯{û(}3ÄÇojK”è ôpõë$ÓÔ<äy=£Øv©jw½´.  Ž?aèEìyîñ|ÿ Ô]l^ J_¬¥ä%’P‡l­R Yôrñ áÔ"ªŽqïFZ„W4Äç=o££6¹ÁÅÞ 3ÐÂ^o–\"v ö1ð~$Óì1(M§©ZËlO0^ÛÓ_þó”!JÉõ|ü(ˆëùe/ñÿ² LÏ~<;´Û¨Ü@‡þ˜ÁªVõüÛúžÙT;ëR9 æs_ª¼ï¤ew„ñ1êäBÿg1ý¹• ÖC ûϤ& žA‚¸Íÿ ¤ËI§Gÿôpî:¥ša¤ÕÛ>‹ìnà'Si´ç=œz³ª·FÄ0'#&ÒÏ'ïߔ빯¡Ò\¼‰Z<¨r˵•Ԅ妀óFnI­ßIÁûPµu½ H§Xü_G=â:@é-x EE&ð!ª‰`\]bÈa`!‹SàGa$Õ<¹$øýpWàm|¹†zwJ  ³ðÕöÿå3?á¯iQªÚñŠøø˜R HSL•8¬ýz¨åÃå0€N  s?Þš%Ãå{ÿ¦t“àìDЇßí5±²’fз´P#/šMÌö3»ÒÐK] º¤(tüô IDATÇ×5;ª»€ÏB ï¼Õ}’™ÍMg‰ R©E”õ€RKÊ@ÖúXĤ¨IØ,?›ý)!ýõÁÓhÆ=X Þ7à±›t6¤’4à†I> ‰ü4(гN_ Ê%3A§g€ª•x¥¸Ðóý1{£8¶Ьu9vü uß¹ð_©”O½¹ÃÀ|ô P‰J8 ê~ª™ #ðØbàŸû/šéCcE&ͬŸ2DßO¨ŠËdKÿbQ›îž Ü1}øm$€7ª¿ï¡®™1Ü-âºÀo¥®«ÞÉÅ©9ÀïNŒúìúÛ ~ S@“Õaî'TöµÔ,®4#¿ Çç·Wx.Þ…Ó“ÑŸŠÐ$ǧ NÊSôúJ 6ý“Ÿàø‚ãÐߊ}—ŸÛyÆ\ì¨õ6Ï}ús"èýeŸçï`¬ç~R<š¨ $ÀI à®CW# Y¶ ¸zðV •ðÜÙJ|/CͰÔ`Ô%|Э*~ûbCÊ3?ŒïVéþ¼š9.u ýtÀÌuṵ̂ÿ¡Ó?"a€³,½ññŒ£ÀÇAîã0(H·€hAŠÊ蟙_zòvMõí¦:4{îðæHþ µtÀÍSèŒËÇù¯Îb^úaÞ°××þŠšVå›ÿœ \–ÄÿÛ_o£J'('ý@'u½õàÍs£ü•_j|ö÷0ö1 ÀÅ8þTÿ,‹¼êá5tŒôšá¹xÙ@ºôŒI‚Î’š/@ÿâ÷# ã¦¯3=÷õèÀi ‰ ߨ©Âóu1€çüìÞsºAgoÏt}0ÿ¹,7‚zY4XÄm‚fÖ÷Í~8 X_ ¼R <ð]N+.® ”WÖš%%ÐY²ið€Æ€ƾš<‹è·¸ÿ´8u>è@îMΨFðº À3 ³£@o2<×D€ÞàÏ®¦Üö¥ô ÓÏ‘±ÈÙÚB‹¶·ÒY”;§×M¤ "YµØhæ|z.ðÀ*1Úãþ°xá­Íˆr×Ü5 ø½©ÛI.Ñ[<—ÐäÂ<ÐŒó EêÞ³x[·2|`¿Ï>½kŠš<·Ëÿ|û=``eª PÐpWa ÓÐ_Åf;hv¾tÖs©gß·€Þ_†:vž °¿°Ç3ÎR.tP`èñ©ð$è‰êL:ZOvN9õ‘x½xµ¸ûs:{~þš™[ë!è,( ½À;Ç€m-ô&ù?'Ñï75ÏnŸõ쇠v0§kð(ˆ-Íž·ƒÓ£ú`¹ô&ä¯?å« S¯Ã-ª|4Ãã¯4˜Ó³7@o Y Ó˜¡„€ÊN¦‚æÞÙ©Áo- ògÞÇ ô¸xƒ93¦Ž4c÷Q= ×O¤3'äYÇœÃ]´8úâ Îêþ|ÝD ЫC\€§‚c àwhVy?‚;ë˜æÙ^ÃÀzß cX7€3@³Ý€‚ïvÏÅ{|nìþ-b {',ö‚Ò`&€ºJ{U€â";è8èeöŒ ÀüŸëÚA"¾ ¤ÏÇгۥ^è^ÖùŒ°t,ßâ¹}TŽÏE©À-ž³—[š©èÓ€æ>à ‚ßO—“’ZÃy·Nb ³€>¬îÛÜÿâòmÕ;>‹®/K£ScQÔŒ‘ÕŠUÔx.ÁjÃЭyƒ™qfFÚà~‚Õ‡¡ó!}ïëˆçsú\ÀCA®H7Ðórl&='U./Ç"¬®ø¿]À›žWãÅô¡¿˜«¼£ÑÓæì¤A‹ì*2)jŽÈ|j@ï¢?‡|6‚ˉ^ Òbèf<€f¬‹@Am¨ ç«Ð ûªô|ý 4ó=TSÝ1èúCXåkžgÌNPêË`}ž1_J¡,‚ÿã}=€Í!üs\JyˆûŽ( `sðZ5ð™ç/ µ|m¦h·'tÉÄˆà «¾øÁFÊm¼wp´Xës¨Ì7‹K¨TZ”t–àlnà±PNš{ÌÊþk.uÂe±­ÝNíëŸ;8*›y× úÅòF‹þr€ :š]×@3hVw>(Íc'Ïî–y¾·Ý ʳ :ÃN€Ïm- ³–:Ðl´ ”ÖRúP1ýé9ÝÞË›rxC7yû À•žû)„ÿý‚_Û´À9žíϺGÜÑnàõ#´@»®—ÞëWŒ§Ï1Vî3aq€Î†õÈ^ª=ýÄJayñÐÀrw0-x¿–f ž÷~õþ~<¨RK7Žo¾äMêT…¿ 5µl+¨¤¢ÀT_#ÂUÅÚìÀÒ·èÌå$ ð“ÙÀù£ƒoBÄÔÇ:V›}àé,oY—Èõ¼pûŽê¡Ç¡?ƒòÀ TÇw2¨»Ü2Ð þ‘jð Ï÷ª×±M4ðÑZBs&âºðàÅÀ_7¬®›HgMž=HÇ—Áé-ðUk{ÆXäln64Ð÷:AU^ê{Ã;{5ÉB]AûÝvz­ß3ÓÿíBé:‡»9@¹„›A?l¥¯ µp*ý³[ÏŸÕ¿8äìQÀåkéVœènôÏ»TƒfÍo5¥8´B¤³• Ô­Ë_  €¸~>Ï¢Sê|üeýN€r_/PÛÚB3íߟõa2–4ºÀÿì ™óÜÊ5/K§2Ëÿ³Xw øõüÐ*de›€Óü´•Ê4©ÿ×%€ÇnÐG.áfÐk=Ÿë&Yþ~é㳮ܞlæRîh‘6ƒtšU?æs½uvªÇëÕÓ=`¨Bp RbµÅö]`\š­ï°@6€©žûÏ;ï}xðUj^á-1VŽã»ÛŽü ™$zVM¢7èÍÍ€ÓMÇ)J.…‚óóFclä>m~þ%ÐØœSül¥¶´€ô¡½À#{KÖ÷ÌVLP1Uá= âzË»›j›uòòà KŽ¿ÍT Õ4v)tÛÑáó}–Ï÷W‚:Ùùš JÙj®18¸Ê¥ËÀ'˜æRá¹î€£n-úôj£ý€}~_ì³O€ú±ƒîw›Ÿ±°øèø’ÇŸ‰ÙtΉ…taŒ©¯ÙÜö ä8™t_¥¼žáY@ú›mTrñ B[0ʾÁ).QÐx“ø"Hy|°]íéÎè/4Õ@¹iŽÄ Ð}_´y¾êA©/¹ ³'ofÍS@3Ùg€ºÜeXÆ}fzöß ò›=û:4Ã~€ÝžßÃsßÿp.¨ÙÇNÐŒ¹¯áê ³Øæ¯±Ê7¤”.!„´v‚1ÆÂ¢H*›øó(µe(Ssh6ýo»ÇöSJì-¢X  GASàMâËØ,jørÀ LñIsÙìùK'd›vÏü¬)q‚s#€Ežïèÿ v2(8wx”ÖâU *qx5¨Ë\%¨ L(²A)2ƒë¤ð#Ï}Ÿ à5Ïï{<×y_쮋}íRÊ`Ö=Ôƒ>œ1ÆXX S)ß<Fp× è³xj  ‚ˆ9@!)e·¢Ôn8!LõåÿÚüÎS:±Ýìj§ÕÛƒsÓ ®—jÇéìy h ™È A…àmô/"õí[008÷ÚáÙf”çkÅ¥ð&Žob¤Ø à,Ÿq±Äì‡ÿ£à16áä’OÏ n;»hñ³ZÏ¡:7P;Ä9Þ’´„-©Ú ¥tÚˆtu4rˆÂô\ªÆòf `uRJËkÕt ìÜòãsÓ{]À¦&ª§®â÷nPkë žŸ üs€RI†² ‡“1Û‹¡ ysâý|Db (؆S5cŒÀŽV`ÕGC_Îÿ¿ÿøÂáÓmâØÑ`6â]H  Naßùø¸ž.µM¿kúñÛ~ÞHå’Äï®zP ÐßI´´¸²Íg;ß ¾ HóÞ&ô ¥ÉQû0×yzòIÅäì z=è9ÆÇsÆXÌÉI¡Å¥¡J ”ÙÁ8@¢ñÕG3€ YÀ+K7ª©Moy•7ó÷iöÌ2ºÄ±gÜleªÏ÷Õ×ôVÖ Ô™Ptx–Ü 5 I)ÝBˆZpš c,Uf9UëQÄ ‰%›‡Äº:Ž€¶ “LJL#pM¥Ö£ˆ)¾³éÙ šåþx«8;ÀTXxK)C)y 3ÆX¬« rñ?÷BI)„·Å+úgˆÙÎ[)~Øy*óμë‡ÝŠÅ‹ý!n¿ \ëž1ÆbÝ–`7ä]=û´‹8 à çû¡ R¥¢¿[hUÄGÔÏêùšœM“‹!>w<32Ã-\fŒ1¦­vôÇq€®oš KloƒõQÎÇÀêTf×x¾öø ŠãòÎÖ—fð³8Ur5½Åk+x cŒÅª-RÊ Ñœƒ®)¥"„ØjdÃW=€õ Ž¡‹LP *Á8Ô Ô ê°S˜Š¾ôŒ)À÷=÷íÍÿ†Î—g±g[87’Rv !˜¨òxcŒŒ”Š4ÐÕõ€¹à2x‰î]Г‹ä{.^u^ô|¦^°4»o5\øLY<©“RÁí?0ü?gŒ±X²1ØÅ¡^ «HJiBl°@ë±°€øñn¿Àý2@)%N >Í©n˜ûüÌsÎN ŸgÜà¹û`±-Ðs`XRÊf!Ä縨cŒÅ†zÐnHx–E}_€«)$“nP°_ ^ƒÀFf¤³ç^ŸhVa?Œ1ÆFÆ `M(¹ç^ «Ì³¸kD³`Œ±¤#|¨ÊލìëŠûcŒ1¶Ï¤”a•\æ=2¾D¥tcIïS)ePÝå‚!¥lð©ZûcŒ1²#6…{cÎA)%„kܪêÁX™‘‹ÅfÒ½½KA©>ŠAv9‘=¿ËÂÞ67c=ÂOq: JKQ•”r£Â®,ÅcÑV àÕpR[¼8@)eŸâßV€ÏT°TŠ”K+°øä¢ðj® ˜bà•ñ‹ë–8=œÛéßÃBZï£ À[#8†KJù±B`~Dî€1ÆØ` ^ µjË` G”ò˜âeA¥Ò‹'âÊÙy(Õ…9óØë‚.7E‘ŒL~ ²¿3w‡{û£ÝÕã™.'Ô Þµx¯±Ç:¸ú¢ µO¸ ºð„››!Ÿ>€Ï×Õâõ0ïº th½|)åzO>7˜ígåáÚÅ%›滃K(Kï¶j*Ï@Îgã?ýýŽVÈ xÜêÀ©G“,X27§Ë ÷^€Éèÿµ:¦æ å§sñËpo¿¯ØÑŠ7ª¬Á·1g,A5xa¤Á9ÀzÄI) !žpþ?{çGu®ñ÷LÙ¾«²ê½Zr·1î¸`0¦ƒ)!ôKrC NBnHO HÕˆmŠ)Æ…fÜ»lIVïu{›™sÿXuí®VÒJÚµç÷ØÏ³£sfæ;gNyç›S€aw[GšñÁô8|wMb´üôVæ@!nKÿU=èé6|)‹ó±çT+Nÿц¿49ñÈ=ã  øhðA„·KñîÑâ%jl¢”:Bi—?(¥;!Mð®“ÐYp¬oåðð#‘ž¨ ëF†ËSÀ» ê ÙR±Îމs8ÛŽÏSµÐ_‘†y³â#w÷Þ©± Sc¡ʹ_Õƒî©ÇvYœËÈà8€ÏB!ÎY  ”ÒBÈ:WÈεÜìÏŸÆÌnî¸-74óh‘Fðì)Ôl*ÃsðvºCBÁ@wW>¾óðDGòÄ@˜ÜÀŠÐôÒYüÞ”eFž1^ï®HâÚ7.Ìü!Nð+JÍÀÎ:ˆ¡¼¦H÷ÊA]"Þ«³ã@§5Àû%ô4¥tÄ6(”úÀµ™dÖô8l»-Qšçæœ ô2|°®7g¹"™ÑåÖ\rít#Þ¾!kèË0† û!í¨ÁßÖÓµ-#!D ¯XÕã¿ ô<VçãÛ‡Ž¿`^»¼µƒþ»Ç·Váõ]’]™ƒG¾9É_ú{Sa^9‡sJño÷¢*3|KR°fy:òWdD– wN¶ëJд£/9EŸ_‚x-¦ÿ«:ö™qd~¢%Ñ·dãðê|dë"v=Þ쮇ðE-~÷f ýåXÛ"3x$“¬Y Ø{W’”ú}çƒ 8÷5âï•Ñ7ÆÚ–±†¢_3ûñ¡öæ»ë!l¯Æo7ž§kC}íÕùdýõY¸câ2†ÿH ¤ÏkðÜŠèwÆÚ–‹‘9‰dYޝ­‡ä‹mîY¨± À«ç`+jÇïvTÓߎµ=þú!„}pν®cÚ/œ}nGì>qýÚàó˜ö»õ·ÇññÃÍiµ_”~ë¶,Iî“G‹ÚA?·ëޝøæÜWB(zæqÿüïb€g@;~××q×ùC8¨òì¹iêY²¡¯ýƒ8×›†þåopÇ=ò¿t÷á †3‡º9S ;Q·¯‘ÐýLÚÖ‚;Vv yDù£ƒh?ü—Õ¾aÁ—]Ÿç÷|vÎí:ßç}ü¤Ÿ‚ îÀžÜ¤Ÿüð®1ìÛ˜J+°¾ºø9¸ IDAT>æ”þ®Ÿý‰åy)¨:Ñ¿îö˧€Ç=êž­½M©y÷W¿Z“åH‰ »Òß›F°¾ÆPÖvË_žàUjqÈý—ï¶3ø6mèm§ß¶ßç}ú‡ ·í†ÑvvÙà'ý=ÛÊ´iÛ~ðøUÆöüEÉ}*üEN©x½LÝ|fÂw~/N¿·ÄGž ðNü40Q3ó¹_Œêþˆ~vøxK,£ ‹¤ c÷~•"p%H ÷o<'Ðû7àƒè½Ã/лl°ÙEòÙËÇt7Ç;™p<º« =7Û>}I®Ýg>úndƒyI $Ѓédj¤ê¬èþEºÒèYÓÑ $ÐÚ€;™ËrŸü·´™ù†ï§Þ™æ`ÃeòèûU,m¿dImâÔI½†´ô|ÃèýÛ¥ÞqèAÕù¾á}z gŒ@÷ù¬mf3çy繂Ò- u˜zñ7º]}iSÔU«»6¨íD×Q€ðþ×êw~ïç!QÏ»š¸RScf¢Fˆ¢v`³4¥Ñ½â'§G¨í ªÝfÛ9@®>è#Öv)Ð{¥'%ûÄ¿'?cSDÒ|Ÿ‘À-ëJ9ñ@ô•¥M‹ÿp´Óg;ˆ¶ÓF))p ÞÝA+‹Ÿ7êb9¬ú¹ù—¸ŠRr%€i‚©²@ïeƒ$[_?¥½Œ1qCÚøzäx§Š¡q—O6çLHð~òQèÓ7€ }í÷i£0ŒN¦+ÜGþ{<")^ÿ^êMÑ-Ê„1ÜNÞ#¯UªDݵ++¢“âÜ>óBèýlè×ãHëŸÏ¿ÛX§Ëçé‹­µ,=–½¢Â0sY#Ùvú¨»ÃèèxîO_ξܺ/yV|xy;áîl7Vcø x­Bã©\úøQ>!ËÞËFY êcÂV wÞ]½ã7“¦µ}žuW®ÀDÊää¡ðI5¡Û]…åËÿ³‡QºÇŒ@ïŒë°ÀG¥Oäµ…&%½ ^²1÷z òGx·›îßQÈ}`}„ûëdꪭ̉wNénMÈh¯£zº 8¢It/¾ur× ²@÷~±t´±¸RCvnOº9]q²§‘ÐSIÓZ3—,lª>ɽŸ ~âv…·:•zxSö-¨˂&'ðjS’MsëO³œ¢o¾„•@§ p™Z9ý¿v†Y9Ú+q˜ÜÀ«µFGëOâUêÎ-Ôe>pÛ?  FÛÞq¾»­N™ºí{ónŠ­Ó_ Šðr¹ÁYtÉã_»Ç]Óôo?GP wþtø”‚¼}þ‰\×ðRÔ›1è%sUþàစ,жÑGx NÆæɧ/Õßçd £´+à—õ–‚lûôE9øIƒ,Ð/Næ6[¿ñôÕivN5B“Gß­â$ËÌ¥õI“ -=lz ú4NA`i¨W)¶ü+ÿ¾LÏ’L?ÛNð¾TØ}ÃC%ÀÈ´Îõyþ€ýIÝôä”ÕÆ]ò(­–^a6XsÛ\7üò8H¯ ' ôÛþmÀ0ÚÎ.B Ð;ù/f”¾Ux_Ž“­>¤(ðv+íUÍ«¬»â郄áü¶Ÿ!h;ƒ­ó5ž9ÿDnåPÓÕ—1è%sÇX`0@G! ômô>P'#R`ÛºSÚùhçGrW@ `SK“—N¶d=A7.Dz@œ¾lð!úÛèÇ £“é R S‚HνñnÚ-ÑÍ!<ê–€×+UbÔu++ ‰Æ®upƒªO²@ïgƒŸ¸ýlpÙŒmã? ïKnQE°(ØÝ@èÞøEµQ‹n¨é²#Bz'žžw-NÄOŒÙ~Ù»ªÍÜ:qé·Š}Ø! ôÛþmÀ0ÚÎ.B(Ð@tZÙø-ß›½„?g¼*-¼&(Ëé6àÍúxëÙ…ßM’&Y>ƒÑèàðÆù'r· :a>^º1ç ò {gµ€…,жÑGx°Ìž-çUÉ5uªKãB_œ"°¾Z)ͺëRSl¬ªçDɽŸ ~âúl;)¥hÚø¯¼•šrÃH9ޫ⤒)·7LšÙÚËÆèàÚ³)uný¶ÌÅI#3†[-KfÝ\‚K®¯õc‡,Ðnû´Ãh;»l±@ï ?·#>çàŸ.Y“eQ¦DÈþ¶xµT!Iº±¨ý²ŸŸéøsàg0ú½3ì€ÊžÈÖ$ÒQè¥s~à‰A%VèÛè#|0Ì™#¼eo‰æê”Ðu `³E/,»÷R³‚gúÚ, ô;™^Mô:ïÏè{Gg”0^> ¡rÎlG–5ÿæëjˆµpƒªO²@ïgƒŸ¸ÛÎæíÓ[ÅÏ0†Îs'RàårÇ|å·‹ôI)Ž~é‹@NA¨óÌÞØ‚c¯®Ìð„lpðf¹R,™óÈ >kš)(á) ô@}Lä tT‰aëÿNåØŸv{¶È„óFc»ê ýÈ’ÙVrÅv³úøž;†«@€jPüºlíÐEú¨ ôÒ9ø+0ÈÄÊ}`}„¶“©«³1Ç7œÔßžîöäÑSmÀ1]¢ëò•­~v3”úÀL¯&zŒ÷g„ th,®ÔàËmI·fxÝìn ôlÚ%­‹æ7û³1¨ú$ ô~6ø‰;PÛI[펛P¼%㪔á/-hñÿ©‰u²·üð´J£á+}*ÐÀÙT­Šßú‡©k²ìüpWàpŠÀK•WãŠ_VFy‡x%ù cÞiƒXV›ñùcsïHjÖNˆAXÑâ^.Ó¸O>rÔ9eu%0Èg0¶ (ðÛ²µ9Î@éôǨ ôó³ï¦ ÿ:sd‰•úÀ6úJ'cwŠdÇKGt·Ä9Ø¡Žý¼žPÇ„ûô™Î¾ö”Ÿù( ôÀéÀ†é°è €µÝÊÔm|'cuª v§Êwª8É>gY}Ò„|+¥C¨ó=eÞÏ?qè`./ÑÆì|=ïÎÌ¡/-XiÞ¶f™¢ny¤˜0lç}/(n§‹Q½óÄô5ÉÍêe¿¨AÑè^oN±8núÍQ–㩯4øµQèÁå“¿ðè¿5;ÿT0©þƒü{r<ìXïLl®`è—˜Z[sÕ?÷^ík¯` g0öNQ?”¯ÍöøLlFE Ÿß˜}€ÿR.?À + ômô>ÔNF¢”n{ã”n>mã³1f”ØXÉҴ哬Yù=&âÉÝß}dÞù~ò¸ã x‰¿ùNÚ͆&UR€1“.x­J-ÄÞ´²:*.Úë)”úÀ6ü BÑvv…[Íf޾ûÌøÒ-ŠÁŠ#-„~ª¾¤)úª;+ú¦¡_ú"\ SJJ!½÷ÔÄ•š’˜Án:SÔ¼/Nj¢×c}†a?u¢yoq;¯4·ù\á¥Ù ¼Öë̺û® ^+v‡Œm}|Ü ¿œóJ•ÄNš×´kÿCoUhyÅö:–îÍXQ½°{‚ãÐó5žA÷!dü‚¦•-œÒT­KÕø<©»Ýse–>\:<‡÷B»Ox÷Ýý«ÐHÖ‰·Vœ²imgÏœˆ+Ð{ØÑZ›ß#¯—rÒ&nYiåuëvÓèLG06GȳN øþ☓#îA?¿)ûSP,‚z“ñþ„ß7ï¡ÿsû…ûx —=èA¼vþ.:ÖÈ›vŸÓ^›ê¢^½øÈf®\3Ýܹàq?/ûÚá3ezàô `Ã^”ˆð ÷ «Øw$:ãÜþ¸%ÉÝe²¨ø”ɶÞrM]¿kÉôm Î 2zÏs›7¿šu ÎÇGûï‹(€7+”býü{β ­½Ã<ƒ ÁƒÞ×Ñ퉗”lÈ»:5ðþ÷*9ñÄä5gùñ ºæ_Ùõ¶Qö —OþÂ#̃Þ3\r;IÜ'ߟ¹'’®ËHÀ7L7ï´¤˜‹—<÷kÌ´÷(Á—U_ááãA§D?+_›]… Q~~Söj¯ ¢“ñþ„ߌòT)ú‹œžÇ²@¢Àõ k¨µ1G6ׯJó0}79Ñœ6$ºÝ<Áæ/ ²@÷{Y wþ„Ÿ<ö!n þ|†|þqòífW¡Å—¶d-œÝÚï|@è=¢† “ñ™† ÛN¿u¾m×ÇI3ëw¦,Hìïp‰À*õnñºïVÇÄôÇð\`‚ÀUyF›±ûéÉ«³úá$àÕrµ§féOŽ*’²~ÚY Ëݧ ½ÂiGú*öÅäì];ëî´vu¨—Iµz€WÏ+…c™w´ÎúN©/|ØÜËÜ€Ï ¼:œð›òµÙA ïèe›²¢)Èi ƒèd¼?á7£¼‡UŠþ"§ç±,Ѓ(p}í·9%²ã•Ãú•F;Ý1yôÓzB=“rÓægô›¡óQèÓ7€ 4Ò)Ð),f;[¾éýÔ¸¹sš“ sú¾ öH€,дqpLÿû×v¬ó¦3G ™‡ÿ›{SºÐåhr¯5'Ùt+¿WÄñ< ((|¥ïè¨Ûjâ¢Þ_{Éý&eç³x©Öè°Üø›Ã µF ÐöÈ]è>mèN{–ЍÏ~1e†é‹¬UÙB?GÝPø´†Ðí®qÍåËÿµ‡Q6ø°¹WÔ€Ï ü:ü«|mö—‚‘èÿ  L'Ó{ˆL¿p•\èA8_öK”bûú“º9b+ÄÌÑÌ«'Y³r£=}¯Ó7 ²@÷{Y wþ„Ÿ< ÐýÙÐ/\èÛ8ð3EÛ9`·6Ö«Ô[ž/X“içÏ[€Ít|kÌuœïWè÷0ïüjêÝÆ½KÞ¶ä´K7ýü!L¿¶3 ýíz`ûƒ²Ãh;»lÞÐR¥Îþô»soŽ«3L3ö=38êíÀ+zWÑ´ô\Sßצ¾6ø°¹WÔ€Ï <º…‚üOÅÚ¬×G^¶)+@í\µEèƒmàÂR wòõ®jUþÔ$g´$\d>üN¦W=„NÆûSè²@ÈÆŸA(ÚΠê¼Óáb웞)$ãÛ£\[;ˆg}Q ôÎc²åﯱìá’~6ÈÝß}dîÆ^á>zç±fÿ?s&V¼9áÞ§ r‚·Då ݧš]ÕpÅßñ½äg_¤è„§@y§bmÖ& ÀH ôø} #•XY l£ðát2ìïkc(:_6tË=pú°a€FZèÕ'Y ÷³ÁOÜôÁ´}Ïï—¾‹@ ´Aèþî# t6ô  Ð@tZÙ´O¾5g™¢8nijà%϶oÖÇÙJþi/’&›û¦!@Ûy¡ ô†ŠµY?Ä„lá>¬¡ëÊÈÈÈÈÈÈÈÈŒ!¬J'ÖÞ¸n÷›¹¿Üûû3:W½½§¼XÌ‹O“›‹JnÛºµSœË 1smyî@‘B¾ÂeÙ¦¬BÓB}]™ðÁSpMéüå[þ¸í3æ4|vK–Dìi øÈœÑVzÕ¿vsº¸Aï¢y0@i #±ý#pM™0ƒ0,mZþÌÁÍõ'Îúò±9:êàÏè¸ãŠÕ•ë ËÈEU­ot0í1YJêõ/4Z›Ûù¦º…Íjãlk6»X«ÙÍÛ,NÎns²N‡“¥’ÔõÔü=˜%‘’q}ýö÷¸Òˆ×›aÒ·Êõ¶#T×€¡¤(TjA©ÕxTG©ÕzT: Öé=*^Ðã]ÆÔtÇØ*##s# t™QA’(ŠË-ܱ3&îÐ §¢´¢U*â ReR†ÄRBŒà¸(ð¼ò|¢ ).VK ! ñö›ÞåU)@¨W Òññ$ê=F×߀0=ÿÞu.é8=®KIgÝBA€îøÞëR‚60Þë ã÷í¸.¨÷>A÷}»m÷Þ£C€P€‚!”vÆñÚÜi‹÷¾óÚÕ}¯  ”!^YÖ‘7 6èx"‘ºÒÖËžŽpâ½í|¤ãŸ›ÃFìv;k·Û‡ÝÁ´¶Ú¹êj“Âa¯gš›[y»ÍÁdfÅ»òÇ%:Ƥ8SÒâÜdä„VXQWݨ,=[¥)>Ó¨«©¬×0LU(s%†ÄR0l4åØÊòÑ’J-ê Ñ!Œ·¢ºÇó è~Iì#Ð Ð_ “! ôÎ2ã-sÁ ôî—Å>èd½»N#Ð;EyÏß´û%„tÔ³žy„@'~º·Îu ôŽëj qâ}.Ý¿ëÅš@\¤©òŒ¶¡ü´îà‡dš[ê4†¸x{ʸñm,iTéôî×%JÑV}^ÛpîxtcñùXS]¹01”SÎÉaÃ%†3J mœ[et2\”®z€®çÖqÁ®òÒ'Ü¿³ìöp>ô*Ïhßx=_6{¾€u_›v;EºlèQGº^â¼Eµû¸ë7z–=ôHG¯kö8§³®÷ ë°¿óż—-e „JÞ‹w—ûçvÔ'B;êk÷K,CA(¼`W¾öHOWÞÚYß—‰lM¼èhRö&^p´)D{+o7UiZíȬ e\V»6cr³>gV³6gf;a/œ6$ˆxÎkÓ‰"z"aÑp·Ÿ¦.S¥Ò…ÛÖEÅåöí›U‡Nšø(ýrjÐ]-ææ{Âôö‡Ê (<-(ÌqŽŸåDI$•å5üყuŸ|üv씩™ÖEK&™’SbÍN‡Ná®rÙ¿˜`y…”œ7Õ’œ7Å •hKM©¦üÄžØwÿøëi Y¹í-­OÊɷ޵­¡€J"©8´+îüÞ}©–Æ*=«ÈY~¾È)n I³-„ÑHŽ„^_ñÐ},Y°Ê(UE „ä9@(¥Þ¯ßðþ†ä‚­j´­rlݧ/Npmü¹A•l‰š°¸Ú8÷ÎJV©Ç: áFØ tCî½ «Œ#¢«™šJ^“úE bLJλŸég/8híÞoyÜ–R¹’"AÂç{›>nQ›-Z¢×­¡ù™WŠ ËõòdËÈt²,ÍÉMwåä¥;Ý.³oïIÝ ÏnMŽŠÖ O4MŸ‘kc"à“(¥'ŸÓïÜ~ÆØÒäPh´÷ˆ‰)·:Ö@»¾ôÈ‚\¦„0ˆKϳÇeäÛ§/_USvøËؽßÌ(ÆÍ]PW0gA3Ë+ú÷{aŽÃÔÊŸýbkjÅ¡}©,?MP¨¢’æ›ÀpÝ_»¾nÈ\LVAuÙ Út9 Ú@)(lUû mGßÊhþËõãuys«ãÜ[ªNgk[Ã…°è §C성 ,Ì¥¯úl¤ Y·2Ñùßè·j §N$i ^UTí\íöXËåV`ؾ»QñÂúZu)5è~J3S'H]Ÿýq}ŒÌ R)¥ÅK.5/^:Ã|öL™z÷Σ†ÞÝo¼õŽËš&Nδµ}þ8{ªL³éõÝ) yT­ù¹˜˜r™“t|B–‘ –ãiþì¥Íãæ,mnª8«=óÕÖÄã;¶dN]¶¢¢ð²¥cm_04œ;a(úlGF[Me´B½Ê­5>ic¹¤¿\dú@´™sLº¬Ùǧ‰k9øzZùºÌçõq¶ØÙ·–ÄL½¶ÍH–]7›ñjokõýÃé;þ{]¶ ÎFj«Ù*) ãˆ:~6ÃðzÄŽ{m8ü3y¬ËÒÐìd~÷ÏJ]}S2“œ°^R*’zŒý”‘…²…²ÕÕõü†õŸÆïß{NëªÍ:½:l>ÚmNvãºÝ‰å%­!êEB9I"Œ»5®õÐÛ9M_¼81åÚÇérg·µ}cEX t>ª@Eܦ3´ï[”&ñ2†áõkÍ'RãáÇ…Îqç ÓÃê3n`uiW³-gž=ŽF¹¡1¥øïGõª·?jQE~D“®½Â\ö–Ë„ŽôŒD÷|GÍgŸ6üá·ÓW\7³eÎüBËXÛuhïý‡›$©ÔˆÆøo¸‘· -zc‚ûŠRTzp—qÛó™˜7s^ý´«n¨ÆEF%‘œØ²)½tß¾ ¥ö×.MÌMÈo§2!À0þÊfý„«š¬Å_ÄÖ¼ÿëYšŒ©õÉ×ýìÔÅ8F=Ô…VK@pÔù¬ðš¤Å]v·Ÿ}^ì9)´­øEa¡Œ*¼¸¿ŒeU6öÿ[dØüi®*5ñ=1J¿BVå2#!K¯˜azô·Ô:P¢æ/ï§47™ÇıÐÖbæžûã‡[ÞkOŒŠyÏ£Ó?,Ê{³ÉŒ$¹—.h¹æû¿9fnnV¿ÿ§µSš*JµcmSÓù"ÝÇ¿ûù¬ŠC\Š6f·M¡¾9b'uË„/úüE­y¿÷9áµbÉ37/k?öaâXÛ4Ú„§@WÆDg“Ïpuü¯€·WÓ¾“A=Ö * Þ9œ:Iè!äx‘‰ûáÿ•éA~‡¸˜§$–5ŒµI2 ±Fƒðð£7ÔÍœ3ÞüôŸßK«®lRŒæýëjšÏ<õn¶ÅüM>*æMÇ¥ÊÞB™QA©ÑŠ V}³ôÒko/ÿâ•çÆW<=v 5Ö IDATP*‘ýo½œ·ûå—§1Ü_%•áya¢åz 3bN)¥¬øÅ™ô•Þ×´ë¥ å¯>³=C§ÿ¨T-¸èÆ@Ê„±)Ž+þŸG¶¼›]ôÕg £qOÁådvüãS­-34jý .BTr 3ú‚¤«;=õšò²ÿÜ»ÐÝV=*š±$Lz V•HTÆÝ^pÂ@—zµ×f*ÁѰ»Ÿ@gÑ„á¼ÃôDgó¨Ø{!³yG£âﯚ4‰Æ7EŸ.7Ì2aAVN’ë¡G¯©}{݉ǜ‘q¹§—i_}~WšÞð† P^"ϵ ñI®åüäÄ™ÛÓN|úaòHÞËm·rÛþþûéNËÕJ¥ö÷—ŒL0/{°Ü8guQùK÷/t6ùœŒ‘$,º£~‡DEïªRq—üU'. ÊØ©$qæßXVOÀÙzXÝýWßáuÙ]‚^p4È‚rìüº…i£C“÷–ÈqÝü ™0'5-Îýíï__óÞÆÝñe¥¡îR]Ñ |ëå})†¨ ž/‹s™°Bct/ô§ÇËïO,Þ÷eÜHÜÃanã·þí©K÷N©y\ç2aCììUÕ K9^ñÚà í•G/ØÉpa)ÐEW+,%À;Ñ3iÎ?¹”opšä%öR´=çós³.uy—‡ÝÙ~RîX‡Hk»›ùËKµZcô ËŽêpG™ ‰OˆòܵfYýúW>Mt»…Ì9ñxòê _¤jtÏ —-¿äË„%j½AXòÀ÷Nß¶9ËÒÚIÓ”Jäó矙B¥ï…ú[ò*-2aGôôê“WüïÁê·8_°µòcmÏH–ÚNÿY´V¾Û_`S mEÏŠŽæý:Nª O¿–—ù•½çª˜)ŒÇZ!9ÛNJÖêOä ]CdÖemC6£×Þ. ™ˆàšç´VW5©ŽÞ:ѧOœ×ž:fÖ«5Ëí‡LD?ga³JgpýäÝÔP\¯©ì¬®tÿÁt…æo²8— {’®úŸs¢¥YÓºo}ÚXÛjÂ~=IgËêh>ØÓ5ÐogÑNÍ_KÕ;著p™©¨±3¯½ÛªNˆ}M(2Ë2ôž®¬þéR³s“+õQÚA—_›ÕÁ¾ýÊžd­î-‘äe"…ùwÀDˆi8~¬Êóæë_Ù,g/ñ1qR:Ïý—ëTªîóA¤ O”JžÕ³…éŠI“³ÿxúÝ‹ZŒøcþ¢IíßÜ™0¾sG‰‘çÿvQ–}_44ºÄv“G–#HJP²:-Ç,˜kTÏ›«þé¯O7ï=Øêòun]ƒS°Ø<Àó âTœFÍ2‰ñ*.1^Å-š› =Slv>õÌÉæÚ›ü’BÆÍ_Òøá1}æw•Žºšã.-Pë§ä*õf›èùçí5]éa Y:]““¬P'Äp ›CÚl’ûX™Ãr¤Ôa•h †¡úñKË[÷¼–tíÏÏŒµ9Ã%lº6e9Ó)Ωäùü’£a—D%M^°Îï¢ôæŠMbÜäŸp*ãt†á åµÐƒbë’çÿw¬Í¸ hl°H'ŽÕxÂH >ÿ´ÈyÏ}s5sçç) '¤ðS§eðÇUx`ÞüqJµFI7}öïYÚÛm"@ÁÀãh}]«X_×"îÛ{Ú©7¨ä Þ˜=‚iÏΓ†ÛV_Þ4PÜ]ŸÅpüyÞÅ|¶³Ù¥çM?z47&6FÁΜ­ülWcP+&X¬éàÑVç¡c-õ×_•¦ýÞƒqQžýÅ÷§Ä?ü¿ûjGÚö‹‰qó.¯;¾ýƒŒÁô³;÷¤1ì“W4B@§e˜ä†­o”†ä”›s‰RõÇŸ’Ø~ÚbÏ!—í±ß·Õ7·ö¾v´a^ûC\¬x°¾ô¦+´úoÓ't†—×ÎO÷Ù­/?ŸC°æçM¥ûûé„ÿþeb^B,«xe³¹®èuw¯!3'¨´¿}$.;%Žë7ÑõÈ9—é'Ï5–Öµ½¾~¨ ó¯Ç’'Àê'kŽ,œª‰y躘¬ÎðÆvÑÙ)Я›£7þp¥1?VÏúßt¾Þm¾ù·•|……±óî)«xiÍâ¤0áÿR€°èŒ"Æ©kYÝm¨ÛµZðX+(pÚô€ç:[I@ UôxÆÖôuÄ5:cAYµ…‹¹TΫQàýwŽ8çÎÏS@z†‘ëèé™qTV4 íí¶€ÏÂjqHòj¢þ)˜î8r°8¨- «ÊÛU þ2¹ìÁ®½-Î=š ÈÊÐ ©ÿؼµÚzé´Xõü™ñÚ¼l½rþÌxõžƒMÌÒhcMò¸I–¯Ö¿ô~vSomnתô üÚnœ-<€×[üí5ZýÿýÝjrº÷}ñž›µ†ŸGÏ0ÀÉsçί]¶šÑ““Î)nºRm˜7C©}ïù„Ìå÷5–[ì¾_]m0®X¤‰>zÆm;Zä¶;\’”Ërµ‚ûø9·mjB{Ë2MŒ?¾`ºJ—Ë*(þ»ÝÒÚ3ì¶+ô±?{À˜Å2 %UnÛΣŽöšfÁ•™È)¯™¯‹Ÿ>NµáÉÔÉ×ý¸úX›Uð9dlÍÕÑ©ËfhNW¸L'λÌV‡(&9%LÈTj~¹:~‚‚'LEƒÇzªÂe:Sí²0Hˆf—ä©c &bvêT3|LªÙ|z[¼aâòƱ¶g8„¥@WÆL% ¯´û•Ø)Î` ÝÒ<öšîI£ªDYÂ(Rˆ"yíçÑÁlvHv›‹j´JŸ`è©Tp ,!u™Ä'D{LmÖ Ú7S»‰×ê²ä±ÐA`sˆ]¥R­b‡Ü¾>ûò¹¶¹3â´ C°`v¢V衃0 e8Nò¸œ ¯P øâÙVS¡aø 9tóV—ãÑ4BJ"í¾E¥¿éj¥ö³Ý.Ç'_¸ì{\M-=ê F†ýу:#ÃO¿biùÇkÖ6JPàÅ·­íëÿfLÍNç”ß½WûÛš|~‘[±Hý»™ê^}ÏÒÖ1,Œ  ÀûŸÙÚ¦(´—ÏRG©”¤Öåãâ¦%úX8^ì¶TÔ îNç‹1Šå~pgL:Ë€¼ö‘¹öëZk)(…we/úâfSýË¿H*ÌMUh¸*6õÿö½JͲÚ„çÞk=ÿümµç‚tŒ][>S§à Óf]wü®êˆÝEE0„‚R ‚ò¤>b:¨ǵ9ëŠ ‘.ÐÃò3¹ÂP@€ vØëvôi`«ÉcäñÎ ãÔ ãÊx©ov1JE‚,G …‚#j‚€Åìè*ßµµm"ÄÄê˜YsÆ)½Ë+Ê Žc)à]>1P'7™¿ládÕÛë?µ––ÖFԚţMŒQïim±;ÙÖbæœÑ2)b˜?;F5vL?g± Òª,omªñëq ­Æ+öE‰RYœ‡]l¼³¥º<(—­½UŰÙLþ pê¬à9uNp¿¸Þn¹þJ•æ/k ñ ~ô6ºS §%1,ËzuøÔñ|P/3Ñ߃\šÛÄ€cÝwvØêšEwr«Xy¥.ú™7½Ãf2“9Åä|…6}jë5¼%!†å•ÞìÂ,E?ï¿/bt¾‡ò´ZD¿"»±]ðÜþÛªƒ?¹=.gv¡:N£b¸ÅS´I‹§h“~xs=Pìhþ¿·‹Ë=3O„NqI.käì¸å‡°èΖ¯%CÞý !ôÙw2¦ÒuA¿ÝÇ>ÚQ(ìÍû/˜Fg$q{Ð1AT&”0 Ãt·çV‹‹67[Ä’s ÂÖ-'œ»ÓçJ,‹ƒnßzÔ¹}ëQ‡BÉ‘ŒôX&¿ E1uZ®")9†‹‰Ñ±÷Ü·Üð§ß¿Õjµ^íåb†ãX*°»¼ ˆ„B±Ÿ÷GŠƒGL®Ò2›‘¢¡Ñ%Ö68„“§-.›ÝãmW‡™kɉjlvÁo&$ð]ùÉù„åyI„ rÇemQªté·‚K°|°Ýi¿v™Ò¶lR›—Õ=D¥±E%ê-CßøI[íþ£.§×=®I¢ðΣ¤Þ‰Ÿ€K Cno?Þio{àf}⊅ژN¾ò ] !@M£àÚwÜaëYÚ[L’ ˆ”r,!=ÝTüÅ!»¥Ó¶Žx^çNc·8´ÝšfÁýÝçêŠÔJ†™?QcX0Y»h²6!FÇ*gSÇ¿üƒ4ÃÍ¿­Ü×f#f2=ᔂè0qŒ::â¾tž½i?M`Uñˆ.üëh:@Ýæ³Ï_ù#ˆ÷«2NgÀV¿S]­tؽÈE€ÜÉyÇÔv¢P°Þiî~à8cÈÑ{¸å£SÎ7ŸpyçÛ‹T%oÃO¨·M "ßÝ.=_Z/”¯¶o=l¿æºYšË—NÓh4J²ôŠš÷ßÙ9lOæ…Ì@m C(¡òz9}Ù¹§ÕÙw£¢Ž‡h¨æ.'Æ«x¨¨¶õúd¶zºDPl´’m·¸ý¾eE”]oÀ«ÿx”äPbIô0•!£¬ÊëAV« £à q  v¥õ¢'%‘å r9Å—ûŽ:%CWÿýÄÚvÿMúÄÌdN5c‚R}è´Ë¾â2ï˜ôwööž€ RZÛ$83’xuA¦B³eOÇøtÒ½ŠK_â]‚f¨8Ü’´ãˆ­mÇ1k”ܺ0*áñÛã'ÆèXåÊË¢ÿ½µµfà«„T î%5\ ËÉTr¡ýì³"0œ)‹Þâb ay]V¿Ì& Uìt’zÙK|ìøï²Þó´=1ozcM¢’$Õñ¦©ÑÞ% Œqꀕ::¦{“ –fÿlI¤ð¸Eêq‹T‡ï覔â£öÙÛÚ¬"$'ÇÊ“ë`6Ù8}”& h3´"HDõ9W.NÖvq9~º­×øØê:[—Ç+!NP9ÆÇªºÂ«ë­ë) 5vS›B¥75Ž0"óÝ&Ø—Å‚\¯ç¼²Fô¸=Ý/äÇ‹<.¸ù*µçFvÙ¬ÊzÁsì¬Û ·,ÓÅΛ¦Ö&dzJIݸÝ÷ 0'KÝ6¸zžÖ¨VŽîäMJ·wšjZ<6H‰åÔ£yÿáâÝ hçKX t°Tl,ÿõn:Äðˆ.ø“¶ôC.õòM]cJ“æ>Ïe]³_‘ºàU^eœÑU¹šOþAp™Š"úÁŒ&)‰J‰eŠÇÚŒ1¥®ÎÖU^“´ëE\¼¶«¬5Ô[Fuˆ ¥@K³I•Z¶õ7hm¶ðqñ?oÆ%D{$ZÑ^–HC­bɃwæÆ€Û-ÑÍÛªzm÷\Sg¤Ž/õ“ úƒïÉÄq±]áUuVyNF––&•Îïwb`O”Z£[k"²üô»ZßžÐÇLÇùk²êFµnÁ,ïÐÃ'zO–|ê9K«ÃI¥ÜLNñëE¼o‘®×1ÌŠÅýW,ïnk€%³ÕÑ·_¥5Àá3.Km“ï1â^×ZksHbj<§úÍÃqJ…oûtj†¹n¾®ß 1Á°|¦.Öhà|¾OÈPj’b8 ”Öy_"Ip³¬&:¢µaý]«åدEÁ^C£ ¿Ëâu2|TW¸BŸÛ«°RÑ…¦c¿ÌUD¦;`ŒHNPI-k3Ƈ] ÍM).^Í,\œÎ}þi¹_a·èòl¸\mlÝð`ü7„$&ưÐPßÑÏHÓÖjá㢠tc|”Gp×Dü|ÿÈà’ɱÊoß—£äàÍ÷ÊÚ[Ú\bϲï$zäd‹}Æ”8ÍòËÓ ëß/mw8=ýꙂgÈõ˲bàtq«ÝjóÈ»ëv`kmVgϘçwi½žhcb–æ ƒŒˆë7Õ*ÂÜrJ{Ë5*í©³‚»¤\ð”W‰«]¢©I;w¯êôžW׉ž'Ÿ¶öòTW׉Â_^´¶<þˆ>~åÕꨙSêO¾tXËkD7–Äð¹Y¿h–JWß$ }é°ø4$H6a7ÿø¾(É eØ+æjbà½Ï­­þâ×·ˆž¿¿Ù^õÓûb³–ÏÕÆM§ÔoÝgk®l\¥4-žSd¥ðêy“Õ1;>ØãÛˆ5WF§ÿfbÂþ"{óÙ*—¥ªYp¨”„š­Ö_š¯2² !Ífѹõ°µy8iMK“‚Qh"þ‹ZX t€ÂTü¢d­ÞJ£rW3êÄ…„×fôk‚=¶*j«ÿB2•¼" Î&Iw>8RT’ÓUJ¢ô÷6¢›6œsóÛSUã cÙ«VäðÛ¶”öóÈÍš“ÆÍž›ÎÀÇ›‹ÜOèú´¾BW_×*~ùå)—Ålï'H†à¦•—iõ eçë"¾)(¥p:ÝŒZ£”0çyŽ‚¸ûæ0BÈÉÔñf‹ B¡P0HNTqI *.7KÇO›Óõ™ü£5æu›Îû\?úŸ¯µ<ÿÔ|µ^˳Ï>9/í¹×N7:×êp¸DªT°d|~´êÛwOˆVq’Dé?ןŒèC­­EeHHʃ®‹²›ʵÀ‚‘6+äì9àvN*dùÉãyåÄN1± ÿ:傺õK§íÏ/XÛMIê«^Þ`3Ÿ)ÜOþHŸ•Æ)¾y§.¶ï5œ.*:éî·|á`±;$éóŽö ¼K+Zl’øÑ.ÿk¨ÀúOÌ-ÅUnÇ3³Rxͽ×D¥öãòPéÈ9GÀëø£É$¸ 3”Q &iLÒöÛÝñlË´v]ã™&“à‰iåj,Örú¸ˆòøû""z$Á^M[N<%âÄS`xeU „á DgÜícmbD£Q³T”ðŽC¼xGM|ùY¥°dY†˜?.†½ç¾ÉʹóÁËžW IDATS¹Òs­bS“]ЉU19¹ÑÌ„I Ô×Y¤?<Ò•tz%³lÊTÅåË&«ËË= íbS£I$„Âg`&NÎTFEy‡ßœ=SéÞ·÷TPðň©ÝÆéôê ¾0èôAkËf^Ô/¨¡â®•Qw­Ìð^Zau­ÛXfÚµ¯ÁîoTmU­ÍóÌ˧º³ >%QÃ?ùØ¥)”›GÔky¶Óãîp ÒKŠÏWšÝ¢F—ݦÐDÅx‚q¹bíÕ§"ó ê–ÏÜÎ-Ÿ»œQ†Ì½”W¦%3lrÃ*„Ô6ˆB]£$:îvÕ4t¬>â§ì;ìr®X㪞6‰WdóŠœLާEc‹(”×î\v—‹Òžç›Ì’ôŸ5W€µþ—1ìËŸ^n¯ßü…­m·H‚ËMœx}à´Óvãcµg.)Tjò3xUNªBE4µ žªFÁùŻɿ”Äžö9Ü’ôÿìg`\ÅÕ÷Ïܾ}W«]õb5Ûrïl°1Íj€IH ¢¸Ä¡ª÷ù]‘gú½V«K–Ölõ8 3€0`ÜGÚÉŽè¬Ñ±æL"Ð £ƒ•Ktâ‡_¾Ísì½cZ¤H’ ï¼qLüⳓRA¡‰ÊÈÐQ©6-joâ–&Z]Ý®:Z|J8ì\ïi<ùø&?…ïDΗŸ ~õÅ Ù¢¥ ‹Ó³Y‡Ìf-¥ª*¸:¼J{»W-?Q/ƒ!ŒQçý±ã›£Æé³‹5^tÞ¢×{ÿy9`Õ˜Ž)/Š*¾õÎÃN„p$¤â qvˆêOîÙÛ€¨N}Ô™O×bt(´w„”¿={ж 0ކyÄ€(< ð‹œØ¶Áž=iÆ ÆŸX2rüм–êwE/aj*³˜f¬®J´§ è¸ô¼TñÍ? ú»1½¯£=ˆ÷îÈû"}Ä€B*X?Ö"£ˆçåT®ÝáS÷ìª` Ž䃋Ÿ>–ñxtmu‹pÓm«;tÊŒ"ï;¯½ªÒ‚hÆ6¦¨„‘ Æ*Tïß•váÏ~µ°çèRl¢ÆÈ…äÐnšfg¸‰¢B,²»™ µT¤§¬Úžh[N—¤pœ¾ð%&ïâ½lÖ²5ƒnDPŒòWmâ /ÙÉÛ§ý†4>†€ÉÀâI%zÙïÿŠÈ?ˆfëׇÓgyhš”ØFÁì…ÁЫI[ƒ¡zÿN‹ÉžáÓY¬C 99~ÉüZE~†”ˆ§í›—óõãÏ©A4;â-IùBÒ‚ ©³¢8ðÖ}4èngUöƒ¯ñKQè2—S€ˆ'x(\}‘)$É/&Ú á´Ø¹õ˜qÑÒ)CŠh°ä¼I’ø:ÕϲBÒS¾mczñ‚¥C=oÜì%UÞHcì&ÂÈcðú´ eÁ÷Fæ¬çn$¥@צ/‹NÉðÖ~8¤/¦§f @sf¤I™š”÷—¬LŸh’)ª $¹.Ѧ§Äñ#µ“Y'Ûìý/PÔKŠANÏ0„B¡¤Î ŒH¼ÎVÎÝڬ˛:{ȱ°)†ÁY“¦6Ë¡WIÏ3aÄâ>ô©1Ø||ZɈŸ ¤Ñf#Uò€l’@ÝåQ;«Ë!Þ€!rãå©A·÷ñ$F²¬ ÷ÞüÚ¾üÂY}.üÑ«VOn ~IcL¢WFß¼ñ|ÑøEç×ê$• KÏkP¤çèp¸]aäáÜùf¡iæå#;$Q I)ÐiM:Ⱦš!«DUö‚"¶cZHfËF?-³‰9MŠÛ÷ï¤,B_|ðöæÔ¢’Lÿ„I¹S9¿°$;0}¶Ýðý–Œ#Œ(}µ.«M:ï[ƒšÝ挜€5×Ö!þLºF»ßËP|íó¬o7$Ú–á")EBá^6ÄhOål@´€(FC<è§ÀonÏñCÏ€(–“ü#ŒŽ®Ñœ8V¯]ýÝ%§µõeßßÊrq(´ž”}ˆ ½¡VslÓç9K®¿­ìtÓZxý÷«ÊËH‘ö%¥6 zCr5ò-_þ}zÖ•í@3jºÿ“ò%TÄð:V—‡Åé\V—ƒ(ZÓ™NǨyPg“Å¿¸-Ëßîþ…1YMžÜü!ê­×7¤]ÿƒM,KŸÖ;O34¾ñGËê¾»Um'"Ô¨Š‚¾~íÙñ3/ùN…ÖœrÚ•5§Õ+ó¯½þˆ¸…2Ô‹0B¨ûžÙ)ó®>&dLô&Ú–á$)ºè:Ö¹Òû‚!}$µi‹£÷$z*‰@?EæMO‘ÎÄb»ûÿ’²ŒáÕ__{éË´ù‹J]9¹¶>—³ Y©âÒ '8|Þ;h€1½v!ÉÙñÞ«yæ´L_Á¬…§4ï¢72'LseO-n¿â†+MáLѺá™q§ž{Û¨ˆÜKRН`Ë×pøÃhz?MsæAÇêrQÊÄ;¬Štì&_×Óàç7eR-Õvן’²œÆ6ªŠáÅg?Mg ¯X5ôÈýqÞÊíã û–ô"’‘mレßVWmXpÕ÷+†;íÙß¾¦’f7(rècÕ…´øNî4·ïxsBÖwþ¼;Ѷœ ’RxÉFìkøo8\¢`Cé žeëÌ~=éÚôs©ŒO3Tç¸u×É7U9¥¹b„N8ŽÂý¦Ð“›±QiëøE¼‰„dA’ôÏ¿¯ÍÐhxåÆ^Øt&VW½á¶ó&Ln÷¸]׳$² !iÀ¾~í¹BWsƒvåí¿8Ìò°WÌMãsøãƒŠ|/%ß&"txŽo´Ö½}ÏÂŒË~·ƒ5¥Kïi²‘” ýè#jd,:g.E‹_fÒü“±LømÈ]MiÓ–PƼ+©”ÒŸÓYK^f3æýau¹ <´ÅyüŸrBo`”À2÷ †æ­ ±/Bû¢~C IÞ“¸aÛí’*yΊc !øíOó}¾P¥Ýº÷6Ön}REˆ O$œ}|Þ õ'ÖdMQà]yÑðké‹K®œßªÕíQ¾þòr«Ùò–DÑ2·…pÖQd }õâã%ƒQ\rí-€Ð_­Bk¶Šüü¾Ý_<ù×é¢ÏÉñú{Ä3|I¡_Úw½•éØôìä¼ëžÚ,dNôŒæÊ8i=è¡ö}¸aÃ’·ö#«ý;ıW唺 W‹²¿a4?³„q×Í9þ‹–:C -«)`ñ&Î*{v—éyè­œ‹'ºÎ–8pþª™Î•—´´;W±ÿG$N:á¬Òxâ°á£G~;Í’‘í[rÝ+Nu1¢SAÐå ~þ˽¬vôþ‚#C ‰¢uÓsùŽÍÏ—æßôÜ&!stElé¤õ GPBhÝsŸâ<òˆ¢±- ø”iˆæSbt Jƒ-8Ø~Z¶F‡Äíx¦¸ñÛÁE³½ÒŸºG×êœK[Í¿PhZŸh³£˜v§‡yû¯l’$£Ÿüü’›Ý$%¢>ÿœI®’Òlÿ^x<³ÝùÖ`ü‹Ì0Ä@8c„ü>z燯æ:ª ‹®ºñ„½`|BD +h”wÜ»oó‹ÏLpÖk ÿÑì$2Þ‘pV5BýG¿ŽÅ3î–odic¢''éz%Ø ÞÚUoí‡wàÝÎ,Eyzåù‡'ºÞøè„ðÎ'«‹énl2\HÜ*„ac ¾Ücúzã>Ó…Ín[¸¤4áãÖRRMÒí÷\Z½sËaãº÷/K´?T †ÈIÞITìÚœ²ÿó÷ò gÍo^rí÷+)šÆ‰l Ò,§ž{ëGjöo·ìyÿª 4s¥Âëî ÚZ% ÁZ¿~fœs÷Û%Ö×±-¹¹z,é½#Ð É…\{iFð¼ñÿž~BßÐü&m2üX5èg"á´ÀÃþ½'´ë¿ØiNµ¥{~ùíZ½Q—TÞº9‹&¹K§øÞ~ù£ôš“ïhµºŸË:à "Ô §MýñýÆ#›>ÎTe-¿õç‡-éYIB(gÚ|gÆ„iÛw¿ûzAãÑ…vÁðDˆ’  „a%ÐpÐаöþiŒÁê/¼íÕ¯XSƨŒÔÒD N‹t› >þ»ñîíûÚØÿ|ô€æd-¦†ëpŠé•¦4‰60‚ðzýô7›÷ví8bÈʶ†®øîGÉø¬ „×#J:tzrãOVÖW–ÕjÖú”µ¶ê÷Z­î*Yo¼^¦é”d4™¤H¡ U¾ãËÔòÝÓ4zƒ8añ²†‚™sÛ1NNo!Ãk”y×þàDk屯oÜQ*¹& ¬p»Ä ó‰P'œúÇŽ—óuûmé+~¶Ï<íâæ±ä5…t°0oºUš7Ý*54¨·Ö½+lÚþ,gÐ/ÅÓµªÀç±Bè“ê“ ü–Í»ÕUÂì9Ežÿ¹÷Òz³ÅþЀ’SP’((É©uuø˜¯¿ØeÙ³íu ÇÏR5º[v:,„>éh®Ž³ÎÞP~0%{¤¶ó¿ÿ£cæŒì³€‡­`‚wÕ}ÿ·³z××Ö²M÷æz¢Ž~(sú«BiGÀÛKH°"¡Žïe´ï'KÚ2󲊜o߀bµcºþDx}S'ßÍÿ?¸'ò;ÚêÁÑß]£8°Lùu´?8èØ‰}õcÀ¬¾ oìê+Ž5qÀÐR]×p~TV$oÜùq6@üøõοq}¸Ç±¸s{ݸDZ}ÚÐë~<{ÙßÝþ>÷÷rýÙßÝÆÞZ·AQ…Ï65sï~âÔøƒ,Òig`ž‹úi˜çí£Ns…'@áR1 èܾEáðñ¨3WPx_døïÎs;ÇþOQ‘íI ß.BÝl@GK ±­ëøpº8ΆÎ{ ÿÓy/”Î%Ôy,Ä_·ËvÜyí®œ§Æáí›;ÏA¶¯sÀ1×éLc aÀ]yƒ»òŽæBjx÷¼‰îÇ€pÌ3èÌ( TØî˜| ßsظ®t8ZleE-_UUË×T7 ƒ Ÿ³´Ô5{^±¦©ÞÊn¿óNº¿/=öãSxçcãÞß—nÛ¢6*ŠŠöï/96¼ GÊk´ŒuÚ-+T´<‡ŒÂUwĦè{M…ËT¤¼Gm×um@p×±Ñ2ÜYØâþŽ€:ß³Ø<€è»}]ùÞy,‚H;ŸÎã#ïÑg)(ü\:‹#ê´#öoo{ Û\uHßZsÌÐV_©G xÞ¦‰‹–¶DŠ«[»yÐû+¯½~ƒzÖýñû{I§ëf»®4Øï—«±Fsô«ÿæ4;dc…•2§½Ed¸‰r\†bî,’÷©“!ò,pd7Ä–gŽÔÕÑýЕ^W¹ŒÛpg%Úý¸®²~΀ºÒÆÑ:7zÛѲݹtýŽÍ¦®²1÷—fÌ9Ñ|ŠÙ×iäûgK¤¼ ^ŽÇ”û˜s;ß'„Q´ÊÅÑ  ÂþFó5æ~¢y‡p¤lãØg„0Æu–ÀοŽÉŸpÛ«xçîWrÝ'¾ÊÖdLp¤.¸¦ÂP¼°=¦ü ¾¬ö¶÷¯Uú}ŸºÞ½õRŸ6ö¦}ãõWߟÿCèƒÄyÐú¼ïFE8ÆŠÚ)ÐÖ¤!ï;§=˜ÓyüYE‘¼¤ŸxŽÆ—,Ï ]²<3äh©ƒG«˜]°{X¯_¡ŒúÉ*ÇÍÁ>³Œsœ XÖ„iDÖà ˆ¢„|~íóÏ磛›[™šê:¡¾¾™3µJAQZ`Öìßµ×Ïsèô‚л‰Ð4…gΟäž1²Ûï ÐÇj4åG×èNžxN×Òà4Ú‰ Ã.Ty¾H¥ f‹J³)˜¦4¤®(²ˆ‚^ôw0!Ÿ‹ñ8›xGÝq½³ñ¤ŽaYÕ–7ÎURÜ1÷’‹kt)©£*…)#70ïº[ËäPðDåÖ/Ò+·ý Û×æÖ±Â ‰b«¬f‘Äð¥cÚ+:– 9Nh½U[Rü {,Áæ£)ˆ¦ÓÄ¥5Å?~í+ÖhUïÂp@†¸Î8V3¯.]&.]"€Ç¯ CÇÛ™½‡×°õM2íöHT«SD>I²‚hšÃUU¥îÝ|CïîîÙÍ×_·KOñ{Û§ ½îC\N§›6$*ÈåÁã•(%ÜiÜý6b~öÙõÕeC¯Ý´=ìí¶u –®ÎÖîç.»Ÿó³·ëÆ¥ƒû{Ö}ÛW»Ùßýúí"Àñåºîõ¢)ÐëÅ`Ô¨ Cã¸ý½ä¡ûó‰+»#jˆË`²*Ë ò¸}´ßëgEíµîì½þÃÝ~GŒê¿;¸ëœ~Þ§>»i»Û=mŒ{ö}Ø8@ÝÍ~Ÿu¯YugÍ¡Ô!,è ²Æh”YŽWºŸß_—üp×±·Ò³îÔ÷+ÞÆ^ëο_]göüöªÈà¨Vôû89èe9DF€¨ÈjÕ§(RúpJôÍ0‰¡Èõ"5_ŸŽÓ¦§Á±¯Yô1us°œ CTVb8…ô"#B´Æ(ÒZcˆÑ™CÚÌÒCÉB'Íë£Ñ¸¥EÈ—U:¶÷š1ªä† cG_•ÔÀ‚"î÷ ßNB’Às4¶§j°=U£Æ~dº?çîÛ†&ÐOï#Ó— Ý÷÷ó‘ìÚ߇ ô+žðŸ½7°e@wÞ· ½í' †¡±9Å$›SLq“d‡á#þyf>2}Û€Ø?€ ½¼{ôn÷wö„ hÛ‹§ºíÅSÝý•GY’(P{oÔA¯e9.>ëþ¸ý½ïë:³ïº¿†§îŒÚÐÇ·£»ÏªÏ÷©—ºû>õÿ݈;1¼ Ýó~úqn† q!TÐ,§ž†sc” ôA6xÙô…ŒÏGÂpCVÕ @ ’ˆ¤èœy²L¼‹fõãH¿@ aL‘”C\(Ö¦â[(Sñ-T¨ýöÔ¬Q}õŸ¨ªäN´i@ Â%)z,¼e*â-Sië”{hãÕSûêoÙªV>™@ @a$¥@]GpGÙ?T}ö¥ˆÑf!Dñ ËZIé²VRJ°{êÖªÞš5ªè© s@ £†¤èªè‚ŽcO*ÇžÁ:ér.£t™P£Z°!sÑ÷isÑ÷éPÇ!ì©ùPñÔ­#C`@ #ž¤è]`¶íÄÇ.¥íÀŸ]ÆrJŸ{ҤΣ s…IÞ<ñæÉŒuòÝàkÚ zj>T|-ß!0@ „I’ ô.°oÝZÕ[·Óšt¤Ï¾„2ä\B±†‚Î!0è3/ ô™PJÐ=uëÔ¶cOÉX %Út@ aÐ$e˜Å͸ãÄ¿”Ú¯.“ê7^#¹N¾¡(¢+ºŸR‘¹èšæÌ$L#@ aD1b<è}ê8„ƒ‡•¶CQ ¹—S©“ïf-$Ú,@ $c:àÐ!¿Ÿ ùýL0 C -t( Å@CAZ••ø%ã@1{úXf¾—ãW_ĸû¹=ýÑëua åÕûLC‡‰¡ûJœCK'r]<ý¸gZ1Ç|Ý~n¿ïtN þYe‰ûøôz®$ €hÓœV¦½D :‰t2£1H´Æ(3ƒÄj2­5ËŒÆ(#Š"4F8#^ #Š]ú2Ê{¥±/¢¢m@ Î2žöv¶©¦NÛZW§uÔ7kÛ[u~w'ËA*NÎ!4­U)Z‡)Z¯R”#Ê¥b¶‡~F½iÁðŽN0Ĉw·G/¿!žÞV\ï°nëûàðu0êOYFLí¡qOØëqÝŽ_Ý>j$Šjt¨§æï™Öú»ûÔ¾½èö>ò<Êcèõ¹G~w>‡¾ó¯Ç)µG0ÆR¥F‘ü´*ùU 0ª )ÈVcR@@±œÌê,!5Ç­µsë2‹Ý†ÜÉ.ÞšìïJ„äaÄ tÞ2r/£õY+)Š5õØê8‚UÙGZ0Š% µÔÖiZjëµ­õ ÚÖz‡¶£µY+½4ÍèT–/VhfªÊr¥Š)5#@ÑFLQFLÑFÑLSº°‚ŠˆÝXYƒG…ŠìЍS±‡'?á.e¨K°E…y—Hì¶½óz‘´c·A×1¨ëïÞl†¨g>rí8©†;ÿé–^¬ÐDqçtÚˆ»%ÿwd?õÙ¸s?Žè‘<ê:t˜ß×A£h~uÙuÞ£èß‘c»ò p·ëÄ\ãH†vå êz(ü´Qçù¸G^vÚy#QLÏ Š<Δ!›/(|N¸ áÎýQpÌ=DÎUD­]Œt1Šèb”›’wЇæµ$²F’1¢:£IGúœK)}Î¥«ÏïÑÞ OýXu׬Qºâ£“aè0’i¨¬Ò–íÝo©I¡Ù\ VhÇz¯‡à©&F4oPhÞ p¦ìPgcH”…‡:ª_ÓAC å˜>è¬Ð{N~&z[´œ)Ík*˜Ùd™|~£¹dA;ÑN‰'é:¢5 ËXAés.£4©sQ$¼b¬ŠáרþpxÅ!û"Br1†ê£eú²½,•‡N¤ø}¬ ™&³üÍrJú\ËMR¥Á(ÎJÄ7м%/ÈYò‚–‰·"€¨ªè§}Mõ¾ºÝf÷ÉÍöÖ½Ÿ1‚!d,˜Ùh™t^ƒeÒR¢Yòr%€$è„Ô¹HŸ³šÒe¬ £íqD¨ý öÔ®Q¼uŸ¨ŠäI€@.dIBËö¶T-7Kbæ5óeŽ¿W±›/ ÑŒcˆR@4pºPœV1äÎsóæ¹2ÿ¸Jô:ØŽ£ŸØ]í•ï>0}ð jÈŸÖd™xnƒeêÊ2æì‘”]°ÍGi žïa›lÅÞºTwÍUòTÆ \#ÂÈ£¹¶^Øöéúôª£Ç-ªJ!^³TÖèn‘-©eÀDŒgVo“lsn¨·Í¹¡N½tDZÏR]åëÓj>y|fõÚG(}ÞÔ¦Œsn8a,š×‘h[G;I)ÐcÁjüëUOÍj õ X%CX#YVÑþƒÍü‰ ëhS˜¶v‘v¹ƒ”$a¤ª(ŠŠúñ‘pMý0˜@`ñ z¥hf]“ã´ œw i‘Š IDAT3Io° ˜¢)@€XÕ`ÔÊ&³V6[tRÉ„N~Y±,ɨ¯¬Ònùx}F͉ãfA»LÒ›þGä„¥2B˜ÔòBb¡y½bvE³uÚMX‘ëÄz«óà‡Ùeÿþù¹ºÌ ­éçÞtÌ\º´-ÑvŽV’V ‡Ú÷coíÕW¿NUDo¢Í!Ç+R;÷4ñ_Ó¡mqÈ´À/À58ΆY6ÛRSe 1áà€B#¤B„‘"“ý©ðvŒPxò>¢:ƒ áp·PdgºãH a@HÅ1û¢ÑM·3BCÔžèoŠêvlg4‚èuâ®­bÔ½už±!r½üÆT¼ý¸ó:Q›;ó‡ó(’.‘ë!¤‚()·»ƒö¸]´Ûå¤?ùè@ª³ÍÁæ¤û§Íç-ž˜ i#8QT;¡ß²ncFcU¥QЮ’RìOùn’J9¼ šÃæ‰+æ‰+–ãeÍ›ÿYXùÆ}‹û8gú’Ž[¦­jI´£¤èÁÖm¸±u›Üµ…TÝ„ägßÁþõwêŒ^ŸñÜ Ðj/PÒí¹rŒŽŠoÒeæàyAµÙÓT{Zšˆ 3çÌó`QUE…°gÇãšw¶Ú³smÁo]6Ïa¶šäS$ '1nýdSFK}­NÐ^.§¤ý;@3EãYÉû@ Œ4iã}ùß~ä@ÈYUÖôõ3…UïýaAãúç]öÅ×Oµº16táÔIJÞÏêBÒQ^ÙÎ>ÿJ•Éã-  †gU›5KE”Ú˜p(ŠÂ…ÅÅ¢ñ…~@åÇk^øÇ§™ù…i•—ÌkÓé5dâÓ¢üàQÃÆ¿ÊnoixíÕ²%íŽMç`ò 'F6¼5?·ú¡C¢»¡¬iÓ? ëÖþmvÓÆ—üéçÜtÄ:çŠÆDÛ7ÒIRÞˆV›…h].¢hü-›U¬¡¥„ÄÐÐäež失¾ÑÄêõS,– JÜ¢„¤¥xBI xBQ`ï®}ú§þònîÔY…îó.œíd9Ž<ÀaB–$ôùeÚ±Û&ho’,¶Ÿ(&½k0*àL™¡œK8’qÞ'š7ý£ ö£‡çvÝX—÷íö1ZÒKyª$¿@Gr¯ LE7RŒ6!ªËäšÏVHr )ZÓ ÖÈTpÐQñªtîW`1a ðÅÆÍ›ï; :í¯qJÊ…BÑ1²@Á̹3<ÓfMö|³q«ù‰‡ÞÎûÞ­«êíé)R¢mé4ÕÔiÖ<ÿÎ8Ÿ8cÊ»A–_ÔÏlh0`õ©RöE¿>fž´²±æÃßÍ:öäUçç®þÍNCÉbg¢m‰$µ@çÌ¥(uÚ4gš8¨ÎPÑ]ŽuiçPˆ@ݘtÂp£b Ïþ»Ü¸÷@ª`µ¼§P”DwŒl†Áç._Ü^2±Àÿï~”uÑ [K§øm×Hc [ÿ»Á¾å“ YwbJy<„(i¼&:-…|~uXÄ¢ÙzÍ‚™ZÍ–]^ÿÖ½þàp¤I=èóç¸J~ôÎÆº;©òջενòhÖ·þßqD1¤"I+ÐiÎisŸfhÁX @¨ãV‚¬ËZIõvŽ*yÀÛð™bȹ”Ö¦ŸCÁ~2È‘0|øýú㣇S:\«(³ù.%\ºH}3ZÈÌÎÝxÛu oþûôƺ6îüUsÚmÓHÂÝîb?|áíüÆšF½VÿIÐ^Gº¶ˆVC¡ŸÜd5Θ$ðÅydvË–WsmÓH )_LÎ8¡SXcpüÓŸÉfŒqøF°N8mzü˜EQA„Çâ[—Ÿïhjpò;¶2&Ú–d¥âp™~í+ïÓè~%kõ¿•“xÔä˜á¾;íf–AhãVŸÿûwÕ¶´w(qs±ªjEùªÛªOœ ‰fMßù›y8®KÓ2ì,3”ð™,ƒPVÇžJÈÍŒT–1éizègB±â®òôónß×øÙ³œ{>LO´IÉNRÖ¦œq<ý X 9!>&ú + „Ú0#Øè„ÓåÅÿœ446ŸËhµW Çc „\sóêÆç{-'Õn–ÆçmS2QWY­}ÿ_oòÂmŠ »“ iIÎ] ÎÖjT ð·gZ]ªÚûŠ">¿ŠŸ|ÑÑñøYöï^b6¼ü®ÓU]/Ê߹ج¿ýF›…¡Ã’yý[%¹‘sTãó¯9QÓ=­ülŽýÝ©SÆk4Z E‚ªúñz—ëÁ48Bbï]Þ×^b5]}qJJnÇ1 BÁª­~ÿd}cEmHŒ=vñLƒî÷wdeí<èóþéÙúÆßÿ4;sæDÎjfØoöyÜ?zàdõifá,b_tSìoçê>üÓp¶?ôL£CU”Œ¸·Ÿ,Ƚ`±É˜—Õ쨪ïÑ7^aµ,ž¥7<ðßþ@u½³C–#‹»]¾Âb|àÎì¬ûnËL¿ü§e•ÝBN¯Õí?î÷]rûñ²v—,cÔÙ;@úÉG$Ù—ÝX¸Øªÿܽ¸àûÏlÐd–zmS²‘”tÑSŽ(Î ¬¾`H¯Ÿ.sEôž‚íˆç“pJ¼òVµ¡ï„t‰6…L˜\èÃ*@EY­&Ѷ$’€/@¿öøËÅŠ<…Òêþ%'é'dÌ’—ŽÊRÛ ªû¸¾)|\^vX •šQ|è™æ65æK[v2(~³Çë£(€É%Z>²]+PÔ­WÙ¬²‚ñÿ{°¦ÁÙ!ÇÍ/{ÿóv÷Ö}^OA/,žeèQñª*àß?]×àtÉCš—FH^ò®þÛ>!}¼ãä+?[":k…DÛ“l$eíhÞm;[§þzГ@M:J+ „Úp°ãq{†Œ£-@ïÚ'ó<¿š4ðQV­^Úúé›ÇtT—ÏÞú8+à33ýk .ÑæºÁvN·”¤ÁÅ•äðq§6M`÷A ·XêUu¢ag£Â¿xœÀéµ4]Q 5´ôÞ€Ø{Äç(Èz®úQÝ4yy ň~ƒ$貿7o»MN›÷M iˆâÌ ËZEÅúmlÓßÃ~Ñ}7í¸KÆ*il†ÎÛ6ë(ê‰6ƒ¤Mz9'?#pô`¥®tj¡/Ñöœ-önÙeñºƒœÁô€8ðÑ„DqàhP¼ê2€…st›•¡[Ûú¯=u¢†+){µ÷ œñçzâd( ©øÿþÑØÚÙ¹#“D ~2êØiÿ ë¢{{ôâÜ–ÏK¿àç剶'HÊ1èD×Q\¿á ÙSó¾ŠÕþ묄ÀUùºR¿ézQ¶÷aÈ` p섇ã¹9¤üúdÊŒbÏ¡½' ‰¶ãl²sÃn;ËÞ¤ d"ïFóþ:·¿¢Z”x¡ÝÒo½ã©f€ [½þ}‡qc»AŒ°˜†o•Ñ£¢b<.‡çó2OmR*aôBñ:Å<ýâòö½cU&Í3Hr ŠàØû¥ö³Rëî_(îÊ×_Ãçj u›êküRuŸ|SiÙóK¹úó•¢ãàCŠªµD§Æñr'KÑ3`¼=®ÑciØÆ©R8>×_[Õ4f¢¹T=¡ëp´œæVÒíœä( †‡þÞÒpÝã}?³›©nÕ™VC¡'þ˜e[4G§•$Œz²¹Ç‚#-ŽðØòi¥]QXN—GQßXëlg„¾7'Óbì}Щ㵂Q?| ÂÈÁ¾ä¦“JÐÇ9w¾›™h[’¤âÒ%ÔÞºµª·n-ÆÐ}ЄÓgÖvªü Ñf „Î]šÊ¬\™Æfd”ÍÎSn·„Ž zä°K9pÐ)ïÝÓ&áî…cHMÕP+VäòS§Y™ôt­Õ²(pk«_iiñ«[¶Ô„vïnìÑ}uùå“´ÅÅV&:‘¨ª bHVA ·´¸åêê6¹¬¼)¤*£kè(EQ–i UW6¹YÁDÛs¦Ù½iw*ǯR(*‹ÌËlÚê þáÑfçÿþÄn¹é*‹qÅ9zí£PM½$ã¹é“4¼ÕBÓí.EùÍŸÕu¢Üýúù&·ð{|Êc÷g§) ›Z%YÅîþc}ËéØöØKÍŽùÓõÚÉÅÍGÿ,÷é×.WMCHä8De§qìôR­¶(W.ýqY¹Û§áƒÑ[%ã„sªÛ÷¬)°Î»ª>Ñö$š#Ð „3Ížƒ.žçÎIzb00è¯LÒŒ+ÐE½L¨*†”¥¤ptI‰‘^}yw˶x­ !€«¯..[=N`˜.g•ªbÐhX”—gbòòL0gNW_ïQ^yeŸïèÑ–¨PÏÍ53'¦ gÏë ©ßl-÷¯ûdŸO”ä¤ÏÛÁR:µÐspÏ ÃXè5'jŒ4{Çèjer^}·Ã»u·?ø»ÿIK™2QàVgŒNßr{õ«Í^ßý49û£þÏ×.„.8ר/)àùé“4Œá´z ¨â+Z^ý“ëí)×_jµ^}QŠ5v¿Ó%ËoèhoëÉ2àcSéy µïþf¡*)ŠÆt#tšZ|4ÆÙ€Éì%DàÞ_óã t”$©ðÖ›u¡¯¾l–[Z‚*Æ6Gåæi©¥KÓØ m}¾ßwÞ9M»xIÐÚP?ø ú|dhLÏ„žz¥Åùô«ÍNˆt¯Å É?þRsÛ/7·e§sLQ.ÏBªZß,Êõ-¢ ÓyÚç߸¼S.=xPÿ6FÆ’%m€1ò”}m5•®8­áH'i:Åè·ÎB‚u6Åé@qF„`Ù‡Ér Ûvã`ÛnU‘Æ\HbÂ0ÓÜê§)Tœh3$7WCÍkaþñôÉàº%ŠÂ€! --Aµµ5 îÞÝ&ëž¡‘$õ_²úòq|DœoÞÜ >÷ì!0$a…±(*pð`«tèP«ôÙgÁ«¯ž¤Å}ÄSóùD¼ƒèa ¤b“Q V¯ž®›?¯Pk·™Û¼Üòç¿~Ô ‰ÉÛ$‚À©Š¬Œz¹Pqä„ã‹DÙGü3«` p²F”OÖˆáFV’”ZŒjE©®)Ôr-Il#$Š×)¼mœÓ[¾ÍFz’AkÒ‘©øVJŸûm Q½Nôîz…‹n¬Êà©ýPm/{V‘ý ä#B8%ÚœAJVrapуÇøñúèx”Mýz5ý~9,šc¶éõ,úÎw €º:¯ò÷'öû0Æ€ú˜’ Êø¥—öûx~è³GÝž úò«[Ý>ŸˆW,/ÕÙíFæ¼e“´ë>Ý;*ZÔM¢¨ˆ¦©Q[ï´·¶ñ€¦&Ú 0†àí…!G•1Ñv$š¤š)­I[в–­e ùW÷%Î{€(ŒyWP¹ç­au™+’ê~#‡Ö6‰Æ8;Ñf ˆ ¡£B97O;äò~þòlNÂi¼ñƉ`_žñîˆb/ëy’?Þïípù€%‹Çš%(õ­ìîðö‰b´àuYÄ{N ÎŒÞT®a‹ 4RIA«Í\‰ìsŸ¤£ÙŠAòVb_ý'ªûäëjGÙsªûäoý'ªä©Œ‹(€hÒgÿ…1ä\œ4÷D94·`†¦ÒmÆ€TôG‡¬\ñíLN§£‡äÙž25…ðz%¼}[ÓYYÍ+$ÊxÇŽÊ€Á ¡²2-IÞO18Œ&ÜÑîÕñœ½î‹({¢Í cVŸTü.!Ñv$š¤øPÒ‚¬Ó~OGúÙ±ìWå+ªçäkŠjŒã4H4Ì"ͧ€qÜ5´©ð{4Åè©S~Éú»E9ÐD¼>„AãpJMg$ÚŒ9tÈ­=âQ&–èÅ‹­Ì¼¹óô;w¶Ë›7·JG¸”Ö^"¶Ä’Ÿo ZZú?n¸iiéššee#~Ò¡Á¤“\íÞ¤¨CÏA¡P ©K ÂYƒÑ[CJÈ7`”°ÑNR|\Lî (6<ÜHöUã–í·+¢÷ä€%ä„öcO)žÚµjú¼'ÎP€(VÖÒ;éæÝ÷x@8{¸<"MQ¶¤"ª ðàŸÊ‚÷ýªD˜Xj YŽ‚…‹¬ÌÂEV ¡> îÜé?ÿ¼Q¬©ñöáK$@ ·º£ï£NÏŠ©`£Nñº}£zˆ Æê[$ÉEaÀê¨øNœ B1:Ðe}‹ÀJZ¶ß®HÞÊ! %ÉWƒ›¶ÿLV;£¹è3—Ó4gó—0xTC¼ƒ¢©)„ïúùÀC–6mtH^oW|ñÌ, uÙêî±Çç诺*?n E!ˆÄ<÷ù¤³Úñû»"·°ÌІå$+!¬ªxTÜKß$}›•@ F% ÷ kÒÏGˆ¯ší®|Eª8 ùjqGÅ¿•” ·Óˆâ@Ÿ½ŠrU¾>*b.Î<…@A+^b °þ«VyÃú™a2³jò$#5o~ ;w^*Ã0®»¾€w:Cê—_Ô‹áFH c†A¶TÍY–¶TCÔ +ÖG2cDQhTÜK_°§ý£»—€@ $jÈÇ –ó£ î2ä,S£BÁSõÆi)$wÕÛ àp‚¹t”{¶ÉÉÀ)ŠÚ4"ËŒªb¨«õ«ÿýo£ô‡ùï½g/ G]ùÞ÷ ã&Ú´µ…T›]sVE—ÍÖ%ÐÛ;|£¢áìqûh½Q7*î¥/´zŒÕ–ù^„‘‰ìq´` %ÚŽD“xnš„d_ VM§•–r‚è {àyÓø„ßaäšÂªªÒœh3†…£G\ÊÇkÃ^s³…C&XÇw(6›@ ìY^ãÆÙ8€pc¢¢²¹ÇФ#ËÇš,úQíåÑ›4¢ª¶N‘\tŽÅt÷™é%yš1²o$!y­1yžð!.Œ.ˆ®£Ã’^ÈuTåŒE4£Í& I³#yßá¦Q3k¼¾¾+£ÁÀ"—;<¬dÛ7Mâùçgq CÁß.^þ÷ÿ™¶¥°ÀÆNŸ–'TT4‹€ˆ‡¾ìQòávù³ÅxVBU& ½Q+ªN´gU+´Ü]?5ib·É2†æVE­«—•×ßöŸGu£l î¼ÙnY¾È ¬€†1€(bìõ+jC‹$«†>ßìö6;ŸOçÏ3™–Ï3YWeÕ1/øF ’«YËèS‰¶#Ñ$\ S¬±cXÆr*¡¶pºŒ&}œqÅ„Äa³² Bu‰6c@‚AÍÛËË×Ó¢¨â††.±¾gC>yÒ£Œg /¸ —ûü³ê`c£¯ß—Äl¨ü|}ð`Ó=ß‹–ºæšùF„Âò×íõ 5dÅçõ3F³~TqÉ.ÈñïݲŽ`T‡|‹™Bã‹YFUü~Ð ‚I9àú« š¾èò?üX»/±–&Ž´T†)ÌãyYÆ8$†W:ã9„¦³Ù½àž[Óí_mu»ÿDC‹Ó¥$ì‘eŒ%cu°+²’‚`Ã1[ê¢ë$ÚŽD“PŽ("+†byxVÿV%w狈€bt H£FÎ ÖAeèäè—\šÎNŸn¤ß¯A:|ØÝ«‡jÆÌfÕªL ì¸KQU ã±~üñþ‡ž¯çy=ôð"ã /ñoÚTÛ«ø^¶,Ÿ¿æšIº ª‚ƒè¤XtÔ’%EšåçOÔqƒ>ùï~oyE³£À{ (*Ð45ª?ü¥%^¬¾‡di7ŰóÇŒ·ãàaQ¾ôêFW¸¨bl·ÑÔO~hÒÜtQó£›MÚí»‚ÒÆÍQ1TëTyÿ³×ý680  iŒ3í“—ͱ—œo2\´ÌlZ¾ÈhœT¬Ñ\ugeµ³]JˆH¿çÑêZP3Úc-&Çt²¿]c˜°´%Ѷ$šÄ t¦«'ãáy±Ú¥Y(F‹É3ª? „áÁžªUõxÒûiÁ’s¬Ì’s¬Lu•_=yÒ§44T—KÄ©©*db&M2Ñ„ŸøûÑÝ„µ5^塇öøî¾{ºN§cÑí·OÕ­^] ÔÖz”úz²ä發<m2ñôá&+ËÄÜ÷‹óÍaan4 ”Å¢¥†Š~EQÆk>ÚãY¿á¨Ÿ%J1$Q4=ú§¹ht%%-3àso`Ç’@ïNK«¢Þÿ ÓWRÄ1 ç ì—èùÞ:E¤§3Ts‹¬$Âol2ÒEnw -$C#”™ÁÒ M’"+C÷6«*@]“(×5‹ò–ÝÞÀ«8;ž{0/;ÃβÞ•~Û¯ªêû;ߨ§)–E¨­CNȰšB(;cIöøÕŸË d·°l}›L¦†§¨Œžó¥Í#É ñè÷‰ûØ&;£·ú{¡¬GyMø!ÈHÓ)ŽÆA@¼ó‰ÊŽ{Õ½{]ÊäÉ:/_Kååk{¨DŒvíj“_{­2ÔÜèuµ‡ƒÚä{ïýÆsݵ%Âüé\V–žÎÊÒÓñ«©Ö×{”µkO¶n­îuü¦F⢢ÔíYVpCƒKÞw &øÍ7å·Û§ŽÏ9ÀáýåºqEÙcb˜ÃÄÅíÛ¿x-à€ÑôOÍ[âÂy[TÈÆ};çÌØ_ÿoŠn|ÇðýÊŒìãX„Ö½™g…0¾øºªæXÝb4PÔ/ï´ÎÑjÒl ÐÖ®(ë¾tûþüT«K’âÅàÿ›žrî|½öG›Úüßñ›yb1çhh{ IDATÏ <…îþc}óÇ_ºO»\* „žx©¹õ×?ÍL_4K¯ŸT¬á—Ç×k)ê·eØÌÐëÓ¬, ÐáV䯶»Ýþ³¡9(ªQ»ÿþë¼ìÒ"öÏÔ×oØáîµ»ý¿Ÿ™vá"“åµuŽÖÞouüîGÙ™çÌ2šþø\}íú®¸ît„n¸Ø–zår«-ÓÆ ¢Óæ’Å »]íü«®¶û5nºÈf»b™Õžmã44PPT•cUï/Öž¬lö¨#¯_i³_w-+ÃÊE£i)*Æ'êž¿¼ÑP¹»Ì;g††ËÍb™M[ýAŒ!ÚN*ϳÏþ5+ÕžÊÐe•!ñëí¾Bófh…ï]i1Ξ¦®ºµªY”»ìµ˜h:ÍÆ0ç.Ðk.½Àhhj‘寷û|Z-E…BÃçÙ}c­Ó}ËU6kºeÏ™kÐÅ ô¢<{漜tËVÕ‡B~ÙÞ®b€Y“tÚ+VXRf–juWÝU^éª*À΃>ßÒ¹FÓ•+R,½ t†FèÒe«QG3ëwt q“al–ÓðTœ#ƒcú×o ‹¦•è ¢„Õ}Ç}îªÆPШ£éñyÝ¥ç¤Øc:C#ôÌ/ ‹æ”êÍnŸ"oÚçnsºe©8GÐN/Ñ™^¿¿dÊUÛqĽö÷¿•–vçw2 B’ª>éw« xi ¡L+L+Ô™ 3 èñ¸®O µÕšó®yt[¢mI’F  ÖYÈ<þ§Ttfx¬§&®Ê@½Ô ]Ç )ÓF™ œ-–.N ì=ø>pA¢M`P…;Ûå°pÆ€!Ü)–ñn·ˆ7o®`@TøõBH„úÖµµ.!¬t ôøkÔ8É`Œ¡©¡MÈ/Ì &mKnÑêuÊ„¥Î‡ž4eNQË—i9€òÊð˜j[*MÝ÷?)zЏû×­î÷×zƒ^» ¯Z¡ãÐnþŸŸ¤è?ùªo”€o¶Äï}פ[0Wýô†+ê­^0[ÃÉ2†¶vE™7KQjá\-°m·?*røã/Ò,öT†~â_ŽŽ§_tzpøc‰y¡Çÿi]¶H¯ýÁµ)†¼Üæê~?ß½Äl|ü_­Îg^q¸År‡‹šQL·±ìø!®[òþ;2ÓÓm,ûÒ{Ç£/5¶ª¥ŠeüùžÜÌå Œ¦_c·þíŦV€5_¶»ï¼!=}ÁtƒÑl i—G‰ ³b¡É`ÒÓÌ¡r¿ïd}H¨üñwÒÓ¦•è õ-bð®Gª*WÂ!„1 3'èâ"ùüpuš}N©Þ|°ÜïþÙ#•YÀðÕËSSï»!»ðÞë³ó¯üÕ±ƒ‘:á;ËR3~÷|mÙ§;œÎ¨MƒAKSZat¬¨<œ´ny¥XŸ?«ž·œñèb#¤è|Ê,ħÌ"–0&¥HŠºTë]ù„¾©8^£ÉÉKS!À¬XÔrd÷3V‰û\aù£^¤s@nNx"Ë„ÿ¾ù{Fͤ #J¿øš;pëM&VK¡õ_ûCï¯õÆ qøäs_hÕr_ðâ•záÇ7[´¿þS«`ËŽ€¨(a¯8E…Ço3 ‚YÓþÐÑXY-ÉW\lÐæd±tm}¸!0v8Ž÷æí]ýÒ•FÍä ·}·?øÔ mžpË8¼/$bü뇛ÞÓi®»ÂÒ«@?t,zæ•6÷ÊB¨oE˜¦Óõtt¡² ›ôÓ'jµ‡Êþ¿>ßÔÛ—dŒï¢®qñÌ †ï\h t—WQ¶ìñx–Í3š.;/ÅôòšÖ¶Øë\ºÔbX»±Ã9MFM_÷-[:Àž««‰ŠóöóE¢AKÓ7^dÏ’dŒïþ{Ue»[–cm~ó ‡cå<‹uæxyÑ£aóA·ÀnayYÁxûOHž€¢xêXhß÷ñMV_ÍþŒq7<¹>Ѷ$ £–0H˜Pd”Bâ.¢Î }rpo™qòôâ1Êž•š¾hvs0ð? Æ£¿m2q<Ç|ýi–eÓ§Y–/×fY^üGšiÉB ×áRÕûîoó>Žñ]:>~ñƒµ¾^çh|¸ÎèL/:OÃçSñ¡£!Ñh ¨)y`ÆÓi)´uW ´eGX„/™öšëu*-á9g‡¢;ŠÆÝŸ95,Ú7lõõ˜§¬ÖÔIrª•¡õ:ªÇ·~ã6ï÷RÊ4öê3Jµ€Í»=½Žuwyµ².Ôi(Úníëÿþ—ík6Ço51ô¼©zCHÄêGÛ{4Dº3±@#ð,¢j›BÁ­<1™8N#hxŠ®¨ úšÚ¤^×=Ø_îód QÏ{eCÐÇÐýí§ùE̱˜#cÜ =Á²H5|üðLãø%U†¢cbèà`H¨«"øj×toE†ûèúõ`Æïë–@ô§*ûI •0$®¼Äæýã£ZÞK´)„$Äíò2Õ• šÕWŸ7:–ç]¾²©lÿßS‚ö÷ _„æLÒáRñÖA K»¡QVªjdõ£O}!ŸOö äæ°4@UMïa«jÃÛó²Y:vû¶]qÚdž[¼@Ëÿÿöî;<ŠóÎøofv¶¯zEB !z± ‚;ÁÆ5ärö9É;ÅN.¹‹“ó“\ ¹Ä¹KâÇ—K¹Ä‰KÜsgp97œ8vÀƒc0MB]B½í®¶ÏÌ{l¯Za µßÏóð°«wvæ7uûÎû¾sä¸ÛwÑ*“–=ûÞÓ­^cDŸYiÔ=»Ãê\{¡Q'8Ýɲj¶?yýö× ²ÿù«Ùh]x9bRe¹Vs¼ÁÕYµ§Ï7åû°¬ØÿÃÄ>U¦ªL«%"ºë–‚;o*,à8 TþBˆ»ªL§íòǹë€mlpDò-¨1çVêuþN™×ååhŽÛuÀ:jwŒ?zÍœr½žˆ¨³Ï›ð‡M¬ÚrÒ=¯Ò`>øÇ¥+øp¼ÄqÄEܸ òBm¨)ÏÏŸ;Óöà=Õóϯ3çž_gÎuºéã‡íÝmÃ;v¸<“4tÝ Ðóç_Ì•\V]Ù ß?6ݱ¨Éô&è’“†ß'E%äq :‹Ä”Å&ï1ïcÊñ£ÒW]™-ÕTöøÚ»v‹zÝÅøQÞü¿w .¿êÂ!>¾BrÆÓê´ÊU·lèxñÑßÖh4«Q{õŒmêÒÞ!É_ýÖ€=8:…›G|¥hÿ_}RâKE°¾UˆinüÞ>§÷+_Ì¡5+ Úßx~aÉúo-…ýlXªiƒ wÝQa¿ïGÿ–Gº?Ow( "}=ƒÚÁ¾aÝ-_¸ªwºc™.ó–/²­ÝpI÷Þ7·Ì2e=ïµet-`G—$—køÊÙ"ª1>·¨œí•%Ø–<èàa·×åflù½6;‹ç—.Ò‰û¹¼’äïd½ÿËóù[rÌ êtšÕÄ·?'"jnóJËéu¸<=;hƒš#—Hž —¯É2VÌò'è{…›Ó´tz<+–˜LÇ]î_=Õ7Èq¡qQ z\ÌÛÿ<Ó}fÅsöâ£ååŸÏ_ò×ÖÁŽ>[§åø²Bnù\SVU©ÎxýwO~8æ’e‹Q¹oî’¶ã`ÃØh×€ÛMÄÑâ£eõBKÑ‹»‡»'¸ÉgŸ½_ÛòÔÝkµye¶ê;~y`Æ>0#=)‡ì_`0c\¶¶ÔÕÚѪÙðÇ£ñ_‘¤g ÇË?ûØ‹e›n»¢77?kF\2QÇѦ»nmîWOÌéëÜh2g¿êåù’ŒKÒú‹aGK»Oþþ½yæ¿«·ˆü•٧¶ýtÈöÔŸl®Dc;éñZ%'›ç?8ìò°˜ç‹í;èôô=1íσÞÝïð\wG[ï¿[”³æ“aÙ¢ð#:Õäñ¾ò¦uJnäj4¹þšf¯—1‡KVNœv»ZÝž»¬ö=‡ìN"JøÔ´G®_;Ýrÿ×ËŠ?sžÙ2¿ÆÕL§¹Ãã~m—XÅXv‡¬¼÷¡Í¶nµ¿öüÕ]éu™‡\ÿ†S÷~~Vé —änº"¿(X&ÉŒ81µl‡KV6ßwêø7n™UzóºüÒú+òK#ˇm’÷}#ý#vÿèœnE9|Ú1º°ÊU_šoŠœvÐêó<úZ_ûã;û3®/‹ä›ŸüêAoñÌùÂïç1ã®1†Rrl;r·î¨šGD¡¶D¡Þ,ôžÅ•Å¿ŸÔQ\qQã0FÆ·ÿëøYÜ´ñ1¦(#7mÒ–³4bLPÒòë*þØ ‰UÎ’Çûù„Û1~û'\‡¤1&ø|û ôÉþìx^ßÀ—ƒ¡žqœBq¼x„=#âñcx’f ÜÿtM >U“…¦õÿ=üÔMžc,üäÎÐ4ÄqÄü¼ñþ¿ûŸÖéâ'ùŸâ™ìI¢\IJË!ŽSXDqór|h¾1O ¿çù˜iýËIò$Q…ù‡O n%Cp=¼g|tüÁíŠ9z[ç˱àò‚qSpûq qŒãÃû-rýñÇn7çžüýó¥Ë.¨µ]¸v±-Éqz™ô|bñåiŸOé_;ŸOé_;“ŸO bˆ,÷y½üÓÿùÇ9ƒ=£F£ù—’V­èHÆÎäˆÅÊYDyäôáȹðôŒ";å…;ëçzÉù¯Z× #n“„?ÇEÄ7Š cÁ·iùÇú®(ùªJÐÑå“¢†^ŒÜ}‘1—œwäßÂqÆn‹Ø˜‰ˆ1–çjªEMi‘È÷IJg·O²Úd%®“h\ÇÆ@yäv¢Àùñ™Ø×ÁrF\è:9@9cÄ;ªË#§e:-qs*ôÚÒ"Q38"I]½^ß°U’ã:‰®'—Œq¡íŽ;t*p¡×ÁiÃÛŠXä¼y¨¦L¯«*Õé†í>_S—ÇcsŸTÊ+ÊB3à8F%:±¶L¯wx¹{Ðëíðx‹ˆ!°î¢ÀssgôÅy‘ˆ£Î~§µ×í‘SBEðâ"· ç_WÞ_‘ëÀ"Ö!ôÙ¨óˆ8R‚ÇfàsQéÇ1ŽBOÁëuôI³m£ŽÛèÿýÇAx1 Œã1âÈzê­ü3¯ýäÁ˜åž{×cï Æ\_0¼×Îñ¾çSçƒlR®ãæKIcçÚID;Û·U=EILv‚n"¢ÐƒR9¡•E‚>~Œ ÊcãOZž`RÅc’ÄeÆ$èÌ}gÛÑEyˆôúåþ„ úŒOÐ_ñÍAC솛.íOtŒÄ¯)¾dâÊÓ>Ÿ> :‘,+Ü;/¾Q|h×¾RáVÉhù‰—ç#*3 A',´^áD|êô`Ìsˆ¨=AO{—Àõ$ðrÒô¨cÁ3tÔÇ&è‘Ç]ÒQ\‚û!$q„ îÌHÐIöð¯?0äèkµ¹K®lž½ñÇxQ¯¤•‹Ìüý™ömU¯S“ÚI´zs›ƒˆð(˜QtZÝï‚aYþ&¹ÜoÆgi0£(²Â½ðì+E.—“¿þ¦K¦;žO^غúëzn¾ç‹ ‚ð³\ª—|‡3z„€Lçì>fnxøæ‹m»gWÖÿhOåMåE=š‹†¥lƒ>Ð=S0O€i•ŸgüáòA‹ùçŠÃù $é3”cÌ)<þÛgÊŠJ³¼·iC/—Ù˜&¬j~­ã®û¿q¢j~Ö¨uèjsì!‘ßÇ™¦o÷ÃUÍO|é2Ñ’ïœÿõ?½•»tCÆQƒQcª ¦"An æ 0íŒF‘=ðýeC ç¿íµÚîËØQ²f¤Þî>í¿{zÖeWž7ôÙk.LY³Éé zùÆ/ßÖ¶áö[}žÿâm#×éd¹¿t2€×Ú­kzbË…ý{[TrÙ—ŽÌ½óûĬ"AS‰ó`†ñŽvéûvÿvÎè©·*u9¥¶º/?ú¶±t}ºãR±½ãM0©DƒZwT=ID·¥j(?¡÷è$:~Œ ÊcãOZž`RÅc’ÎsáòOy'Ñûˆˆº{Ç„?<ÙšÕÓ—'ZLß“uú:†N¢Áe«¿“(‘BG>+ë;ŠV,cã¦ÓéS†Ý}'ovU‘ëkß¾±Ól1ÊDQiL¢ºåKluË—X[ŽŸ²ì}cwiOÇ¥½aƒd4ÿ“W£]†FêŸ2ÎÞ¦¾÷®µ7ï)ÓVŽTlüÞþüó¯íŽkiJGÇKΉ¦.A‹ˆÚˆ¨jŠæ uµy¾Ÿý0oàÀá^ÝsÛïÌ5 zÝ•ÌdZ/ë´¸N©€¢(ÔÑÚªo8yÜØÞÚf˜UžïùÂW®êÎ/ Å S¯fÑ|{Í¢ùöŽÓ­¦½¯¿SÚÙtU¶Þx‰d0Û§Õ](?˜NŽÎ³z÷þ~ÎXûcIÝ`õ-ìÉ]¼n Q-9$õv:MI"¢ÖU×2â^&¢‰Ü¦ M}–·iãÊ£îéPÂ[ ±¯“Þ¦Më–F¢Û!ñM$Æk^]Ž&.‰–3Þ­¯p >ŸÆ>2~ÿ%ˆhxÔÃï;Økxïýýа¬1.VDÍrÅ&Š…L«-PxN@—ø÷“ÒÄÅçõð6û¨`·Yy»mDÓÑÖ¬ïëéÑVT»—ž_mŸ¿¸Ê)ŠšÈ}—ú|B—¸’L;Þµ3®¼§­Ã°çõwJÛNÌÕj«eQw¬3\+‰º¥rhKÒ˜1rhâ:ÌÐÄ…&±‰‹³û¨iäÔÎ{ëÞbÏpg¶yöâÞÒË·4fÕ­Š›8ò=š¸ßGˇ۶U?Hi˜²ˆ¨eGõ Dtþdü/)é†ò¿MµC}"AO]ž`RÅc’“/côÈr·GæíÕµ¶¹µC’0<âl67Sž(Á¶ Íš‹¼¼ú%žš%+ŒúpÔ÷]²ÅÆ,/4yÜôá$$y€Á4&Ñ;}hÚä¤P=Ix)þïVN£X²ŒRVŽIÊÉ1J5µ³œÕµ¥n.âÛ9üÙ4Î'$èq1$™v zðõPoŸîÄæ4=7ÜwÆ(j‹eQw¬7\#i «e" zò˜1rHÐC‡tú$ :cdkÛ›mmøK‰½}_‘ÏÞoÒå•[³jWõœwÍÓìÅöÀÊ~Òkg¦%è^"únÛ¶ê´ž¯1Õ z#FF¢´6”ÿ%%ÝPþ·©vH¢/B$è©Ë¬ôˆ?A‚žàø‹š4Õ±yK¹~ãÄdÿ¥±Ç_Â’–'Þ¹À·â.b1׉Ÿó‘ï‘ ÇÅdÚ³NÐ#c°Š ‡>Ê9}¤!¯¯³ÍÌóf¦Ó_)ëŒ×HZÃe>žÓ!AŽ9#‡=t˜!A§ &è¤x¹ÑÓͳž~§x¬ó@‘ä¶kÅs†²ëÖôä/ßÐc(ªqNø; zð}pÏ·m«~‰Ò4UmЉˆ¨fskGËŽêÑO¦r903dååúV®¿|`åúËÜcN¡á££Ù§Ï=ÓôÙŒñz­n©$h1v¬Õ-’5Úy2Çé§;l€O ¦Hœ³ï¸ÉÑwÜìh°¸‡Z,î¡–l¦øxSù¢þÒ‹o;‘¿lC˜]Œ!g'O½:‘Li‚ðŸDt5]|–3„Þd”—®]3¼tíšaÉçãš>>žÕÓÚfèÚcî{Éh6pO¢®RÒˆ A\¨ˆÚ…²V¿P4³0: d<É9$:zŽ˜]'ÍîÁ&‹{¤Õ쵞13Eæ5Æ·>¿Üj*«.^umsþÒÏöóz‹¿³6KÕÞ&ÈKD¿kÛV-MäCSÚÄ%¨e{uý•·<ø· Ý.@—ñcLPÒòë*þØ“ܾBB†&. ×!í}™M\&rít;Bo{§¡¿ëŒqðLa°{Ðdì7È’›4E#–(oa<ŸÍˆ²‰²Ï[Ç[/d3^0+¼ÍøÀ{Ž\dÿÂÂM Ònâ2YM\‚³ˆøÃ„›¸g¹Ù'ÒÄ%´ÜTM\ ÆÅ(¢‰Küò'ÔÄ……?:±&.¡˜ƒÓ±Èå¤XÏ4›¸D~€£ñ›¸Dîààú'iâ\#"¦ø8ÅcÓH› {¬‚ìµkd]#{ÇÙk¯C}âÓÈ>§FvÛDÉ5ªç4ZYŸSj7VX Å56Ó¬y6K囯œWCqîŽ÷Ý0þu M\ˆˆdF܃m۪Гˆ¨e{u!#î"šG4Á•E‚>~Œ ÊcãOZž`RÅ2Û<íŸIDATc’“ :!AgHЮCÚû úDö5cÄcŒFúúuýƱQ«èq¹¯Û¥ñ¸Ü‚ÇåÑxœ^Áëöh¼n·àóºÉëdÉÃÇÎ@­xVæEƒOÐê%Agô ZƒÄëŒ>Þèt&Ÿ 7K½É+šó=æò…v㬹.8ôWºçôˆ¹œõ÷#¢_·n«yŸÎ¹hâBDD5õ­ÍÛk6Ñ."ª8WË€ÌÁqå•{òJŠ=éVn(ŠÂ¹NA–¤è²¨×áÿÒþ²Nýã8Iâ±ÔIªÜH´*i%=iTnÄÇ?nåFZ 1õÌã&f ªÒÆ +u ãU΄cL¿ÿíYVn„ÂŽY^™hÊñ‘ 2J²ãUÎÀ9õèÙ&çDç0A'"šSßÒÕ¼½æ*"zƒˆªÎå²áyž,‰$ØAiÕøŸ §¼“øÛ¤ß}Lc|JÐÏ"ÁM3AOtŒ ÊYŠ»§±ëðÉô”5Ìg}÷1C’õ?7íàPˆèéÖm5i=(™s~[oN}K]@Dÿs®— 0E†ˆèG­[kv~ÒMK»»9õ-¶Úúæ¿'¢»ˆÈ11L’Dt_ëÖš†É˜Ù9mâ«¶¾ùMÛç¼ODÏÑÒéŒ`‚|DôTËÖ9oMæL§½çzm}s­!¢{ˆ¨}šÃDDoÑw&;9'šæô Úúf/ýþôöÚLjèV"ú Ç ò'æ¯6o­žª…¨"Aš[ß$ÑÓ§·×>KD‰è›D´ŠTPÓk˜ˆÞ%¢×›·ÖÚ¦zaªJЃæÖ7)Dô½Ð¸}n.]ADëÿʧ36˜ñ¼Dt‚ˆŽÑѦ­s»ÎåÂU™ Gª«?=BD;ÿ¨áùº:"ºŒü‰zQaÄÿæi >-d"²þÙÿÛ‰h”ˆZ‰èÔé­u¾é ŽcqÏ€邶Ý*‚@E ¨tA‚ "HÐT :€Š AP$è*‚@E ¨tA‚ "HÐT :€Š AP$è*‚@E ¨tA‚ "HÐT :€Š AP$è*‚@E ¨tA‚ "HÐT :€Š AP$è*‚@E ¨tA‚ "HÐT :€Š AP$è*‚@E ¨tA‚ "HÐT :€Šü?Ú4óèÐÝB>IEND®B`‚nut-2.7.4/docs/images/warning.png0000644000175000017500000000621612640443572013637 00000000000000‰PNG  IHDR00Wù‡ UIDAThÕ™ypÕU–Ç?¿ßÛ²AÂÒHˆ!DY[Ç‘nÑ A±0#«a¦ˆ0caSeBX"B5:"28IJqþPm§í²íцîšaz -šÈòÈ3ûKÞö{¿åÞùãåÅ·&/Lÿ3§êTR÷Þß½ßï¹çœ{î}Š”’ÿÏb¿™dDBĶÝEQPUEQP¢ ʈ H)eww7Û¶mò,,ËŠ¶t*l6UUUÌž=›¼¼<ìv» )eÆ*„]]]rË–-òÒ¥K2*Bˆ!Õ²¬´êv»åSO=%Ïœ9#= ‡ÃR!3Å4bð•••²­­í/Þ²,iš¦lmm•ëÖ­“gΜ‘mmm#"‘1ø7nȪªªAð± 5M“¡P(NƒÁà †! ÃH >VW®\9bÊp¾+¥”===¼ð ;v,Îßu]'ÐÙÙ‰aƒß¸\.ìöøðRU•œœœA¿ÏÏÏGªªƒc<Ï?ÿ<«W¯fîܹŒ7‡Ã1d`I@J)={öìáØ±cq …hooçÚ¯~…¶yóFH%¥µµL¯­Mµ&7ndíڵ̙3gXjªÆXðû÷ïO éììäâ{ïÚ¼ ƒš©4íÝËŸëê’À?~œ£Gòõ×_ÓÝÝaÈ4–N¹Qðuuu¼ñÆI–ïèèà?ûö#Jâ@éîÝ̨®NÙ·víZÖ¯_Ï]wÝ•v'’H)¥ÛíæÀ9r$®/ ÑÔÔDÇ"wî!ÔÔ¢¥;v0óÅSö¯X±‚mÛ¶1cÆŒ”$âH)ååË—©¯¯çÕW_›( ÒÖÖÆ7'Ob¤±@3ðÚÀ_ < ,J€¬!ˆÜVSì]»âڢ؞|òI*++SïDlª¼~ýºÜºukR.òÊ•+ò£]»äÏ!I_ùã½ä?Õl–-W¥·»]¾s¸NNù7 ‚ÜòÈS)ôÕÕÒ0 ©ëz’VTTÈÏ?ÿ<)ŪQË766røða:g…¨Û\:qcÏž¸€•À5 Øÿ›_ðïç`êì¹d¹)$S§ÞÆ »¶òÖ…ßñÖÕËäÿýÓœ‚ óHàÚ¾}üñ'?I¹C/¿ü2¯½ö—/_Ž lÛîÝ»¹~ýúîǧt›ÖÖV?úˆÐ@ÊK\ô]àã ¿åöYsùÞ„[ïÒ)r…EÅh}:¾mBÇÉÝ?˜OÞ¨Ñü`î_ñ› ŸâmêåÖ¨«ÄhïïaÜòÐCqXFEEEÛ·o§¸¸˜üü|\.WdV­Z•Òò \;yu5 WvÂÔ ûº?f4ŠBËÔP¥‚ÓnGUíXzo'÷Ü?ï»7`%Ì{õÀ.ÖÔÄÅB4^yåêêêhnn&DLž<9¼®ë´´´pãôi¼µµIÀ£j PI8èEó~‹ð‚4AJUQPDd¤ôò§ g™qÏBÛopô“SÚb Õ†Ÿþ”‹ÕÕIUî„ ÈÍÍÅívãóù’2Ó4ihh ñý÷é­­Mr™TjSRXHF +Æ9,„i …iawårïçãpf3sÎ]lzö|# tåàA.îÝ›€@ €®ë© ØívºwïNkùXÛf!±LË4A Ë22zg€,W66%2N ú(a|iæ–À•}ûp8I„ f¡Xƒÿgþ»mW@QÁfCuº" H‰e ,!°,kЯ^úo„°zˆ`7ýÞöŒ”ˆbnd‰‰Bú2AX-@ 2qÒTlŠOs#¦î§ß¦Ú–‰ ¦iFHK ) „°PI]G%¶¥*{Òs)4íH0 l®<&•Ì$oìD|>¦´“7æVlv;îÆKX††Ã‘ƒ‚‚¢D bËhÒUÍiïĉ;`½@`€µ (Œv* £¿w+clЦª(ª Љ ËB ùémo£«»Kµ#`™)BGI±^¦EaÆ~,zqrÜ:º»¹ðÙ/9ûåâôa€Âæpͪ:PU›Í…ÝáÀåÊÁçíbbÑm„|}¸\„Œ¬’ªÏ´4O"0x]Kh? ì)_Îø¢b¤”øW­æ«û9»¶×òÍÿœeö_Ï#+;gÎh»›ÍBµ;Qm 6U§“ü1ã¹ÿÁÅ„ú; zû0ÂAo'y) –©Äˆõ³Ä oÚZš)˜8 ‡ÝΨq…,\½Ž;§MáÍ£k€½L¹ãûŒ¹¥WnR•X¦…DÁ¦8QUŠÓIöè186´@}}½@(îTNcAMבH÷žŽvL-€0¡cÏÊ¥èÞyümÕq._ä—½‡·«p(8p˜‰Hò'ò€¥ ‚”(TEE‰P£Ç]¼¦;k%mJÌ4Óë7áq·`„CHË@‘&ά<î¼ç!y¢’QcyûènÎùïô{{ÂBJ1¨–0±Œà@¤£…ƒèÁëÃf¼tàS¼'$LææŸ}ðF(ˆeèHË¡ãpe3¶h:ó•±rM%S§á÷vôû¢wÈáeHËÀÔÃèZ-køCs(I"àt:QU5åDÓ€w^9Âùß~… `™a¤0A8&”~Ÿñ“n''w4y£ó°« ÌHmd™"ìÃ44B}}½ôõv!4²t:"RJ (Z¼8¥%žj7üWÿ| Ë#M)-&ª"ÈWĨÑc®}‘°L`:z(ˆ Ðïõòù˜ @îÖ¿»ª*sQ?+((à¾7ߤpÑ¢$kä € ­Â}í á`ÿ€• °tT¡áÌvátea³© ¤îG×üè¡ZÐGwG;Þžv¤@¿’žÀœgžaÞ¡C„Ãá$Œqb/ yE+,,ä‡ÇŽQ¸paÒÄ“€ÕÀ?.çO<î÷b†KÔ8RF²Ž”Xf˜°@Ó|=´{nð­§Ëòá±Áè‰H4Ôí>ÊCõõľ¢D1ƾæÙúûûSnÏäÉ“¡¾žÿÚ´‰æ_ÿ:®¯X T­¨düX¨ÿÅnj΋Ýé(,ËB×u´P-ࣳÓMo‡ÅÒøàŸ£7Ãí|—F£r÷ºu<üöÛH)Ñu=Î¸Š¢ >IªªÙãÇS^^žD@JIQQ÷Ö×3éÁ“RkðpG,¸ï1N½ÿ¯x»;ù}ô÷tÒå¹A—Ç·½•O}ß^GëïàÄ?Fiˆœ-¶Ëßñè£iÁ766²hÑ",X@AA.—+ò.ä÷ûeSS;wîääÉ“)snKK çž~šÖ/¿Lê@;‘«á¨Rø  B c²€b"©8‡ÈÉîL3ý±Ç(;})eœßøý~ÊÊÊX²d Ó¦McÖ¬Y”––F!¤ßï§¹¹™šš^ýu I·Ûͬ_ϯ¾Jê»Y‰zøô¥K)ÿøc„躞þ‘G¡¼¼œiÓ¦1sæLJJJÈÍÍýîe.–Duu5§NJZLJIkk+ç6n¤é&I¤*“g,YÂòO>@Ó´¸¾ÆÆFÖ¯_Ÿ¼ÍfSÃYUU%//’’öíÛÇòåËñx-D;wªŠx`°?ö»TÏ™H4UÆÊùóçÙºu++V¬üIÔÔÔðì³Ï2þüaAe<•œ?žçž{ŽåË—3eÊ”aÁK ‘D]]?þ87~¨þÓ§OsðàÁŒÜfDI8p€eË–±téÒ¿xEQøâ‹/Ø¿?eee[~DIlß¾ŽŽB¡Äóväâr¹B°fÍJKKG~Dà;---\»v¾¾¾Á7Êÿ‹D7ž2e “'OÎ<ŒDHƒAúúúÐ4-©¿QU•¬¬,òóóÉÉÉAUÕŒìü_倘²@QëjIEND®B`‚nut-2.7.4/docs/images/blue-arrow.png0000644000175000017500000000106212640443572014243 00000000000000‰PNG  IHDR ©¬w&sRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÙ *wì˜tEXtCommentCreated with GIMPWIDATÓMνka€ñçîÞ{{!1-zT›VœDZ2bü Ð è¦›Š›ƒB‡B÷ Eì¢Ò¡k—NÅhD± 1IMî.ï]ïîuÄgþ q¤µÞüpÂöÇ~‹*¹é` Ç¢&–Ï/rgå"VKßê—ï:Pž‡8CêéÌedñTÛ<¿¹„5)7^ô9Ku.@… Dcι9©yŠJ2ÀÇ+$¨0t‘ºÇB-§½q‹ÝW .ˆä ÛŽˆráßÛÒÑ¥ûý]ɦ,g],ë3o7ŸqõÑñ°„_ 1†“X»%—ÿs˜¢pq;P¸ €¸öd‡N6EòK«`!Ÿ²³Õänë5‰wÄW„•F –@ºŒ¨Ë1Ÿ6nó¸Ñjóë´¢ÇìD!réCd¬3tÇ)«­6!ÒðÈÜÄPµcÌõµ^6‚üPP™åxfž°l2tðx‚õµâi3àýþú”0PÔ¦?é[‹u—æŸæJÀ0¿˜]Y×ÍIEND®B`‚nut-2.7.4/docs/images/asciidoc.png0000644000175000017500000000341712640443572013750 00000000000000‰PNG  IHDRXTúÒsRGB®Îé4PLTE     !#! &!'#)).-4*0073;### +/)))#.1).0$16'59(7;(8>666678:::7@8A"H+am>erCCCKKK@W_TTT[[[D]eIdmFgq@huEn{KirGp~HrQoxSq{bbbzzzJtNy†OzˆVvS}ŠZ|‡\ŠV€Ž\W‚Yƒ‘]ˆ–_Š˜a†’eŠ—c›f‘žj’Ÿl•¢n˜¦pš§rœª}š¦|œ¨w ®}£¯{¤±Ý†~ã„{î‚vê„yótù€r„„„ŠŠŠ—Ÿ’’’”•œŸ“—œ”™šššƒ™£Š— ‹˜¡‘˜ ‚¡­©¶†®»ˆ¯¼Š±½¯‘¥’•¢˜¬‘“´ºŒ°·šœ£¢£ £ª¬¬¬¿§©³¯´²²²»»»Ž´À¶Â•ºÆ›¿ÊžÀÌ¡ÃΧÇÒ§ÈÒ«ÊÔ°Í׳ÏÙµÑÚ¸ÓÜ¿ØàÆŠ‡ÂŒ‰ËŠ…Ì’ŽÀ—–È“‘ÍžœÖ‡€Óˆ‚ÚŠƒéŠÃÃÃÉÉÉÛÛÛÄÛãÉÞæÌàçÏâéÔæë×èíÚêïÝëðäääêêêâîòåðôêóöîõøôôôñ÷ùþþþ×gbKGDˆH pHYs  šœtIMEÙר3TIDATHÇ­U]OG½ÛZ1UXì&µë %;›bÒÄEþˆˆ )$lùðîŽdù-ˆÞ OÔ’C±TဓZ;4Ëýs™Ýõ®m„P›ƒg÷ÎøÌ™3wîøøéôÓ?'6œñÄñŸoËyøûääã_ÇÇG 8 ¡ÿáý~¹ Â‡££÷ïö?'þØ+ç—¦`ÿÝþ›ÝJù\TOgÐÊ Ø.ooå—~ŽÀ[ºÀïëë…Ba?/ Æ(˜Ïµ5û›|N} 曧ȱ0ä/ ƒÚ:!§Ê™Ñ ìínSÝ\ŽB¹k烘oÆ$>aDSäÔè°·Ã…‰«™‘ñiôí'±«ui.æÂk†a‡p“jS¿•â4Ã?D¥ÂÉð-Ø«PÇ–®Ö²:€mÒnĬi-»ä޹0Oq8nʈc‹ÄvLÈŽÍu¸ã¦°qv|uhüMY܌Ƈ¸K–53µvŸ‘ˆáXŽ4[¹ø_0tY*"­9NH’”8sR\’âfcˆÿ¢µëš†©pÆHÅš•cÅ…ˆ.ŬrÞ F˲ª0„‡73<: 1ºÇj¯§Ö…V¿mÂ.¬óÎü¼À†-¯ ˱™Šž¢)¼Ù“è«¡MЩB¶¥ÜŒTŒá—ËfYR€gÁºy–cCXe™ ›q¡.еÁµÌ&ÔÐw–c-ðŸu°T̿ੰxìGh‘ ïV¶ y¶é(­ z[¡„QÊ‹Šcôð`7nÓCšËíûÞ"Ö]q ‡çûöWmÞ’J…gYUì”·è)UQ~Ãh@QT7Á,74ʨ m7Oƒ…;F¹ÕÝÒ ïÔàÉŠ²(OEBÃPÞ*eñWyNÐëðT–å§B]:6êX[¤çÐwˆõ¢¯EX¯®úà‰< >~AàyƒGæf“£Á!XÏ+òT&“yÄŠ%Ng’,ø‚—ØWÉôCã"?†6<¤dsøQÚâER©d2Buû,NE?…B^Q1 t‰ÞÐÍnÑ;ÄzÁ¢÷f0äEO@ìöз(vu{†ƒœèî6f¯‰Þïx¼5Ôïñ€œ ÷_ó\Þs†hðµvuuv^…TxÈsõËËŸ—.]îüӥǿ"ƒIpIEND®B`‚nut-2.7.4/docs/images/old-cgi.png0000644000175000017500000002100112640443572013475 00000000000000‰PNG  IHDR ÃàÁ gAMA± üa8tEXtSoftwareXV Version 3.10a Rev: 12/29/94 (PNG patch 1.2)Ý.I IDATxœíÝ;–Û¸Â.PüwU á8è ‚”‡âAuPÁ < n [`‘¾Ø{yuWQ|@"D~E€àÿÝ÷@!„ÿ·wD6ˆd#€H6ˆd#€H6ˆ>Ê/ý÷µM9Vòõ¿¯Ÿ?VÎü&…>ÿýœW€=}ýoĵmj‘lÉF‘lÉF‘lÉF‘lÉFÑûq±áÔ~ýúµwX×ç?ŸåáûÕËSx[F‘¸¾úgèpF5}T®M`ÙÇ¿jSˆd#€H6ˆd#€H6ˆd#€È=ü°©ÛÇm8ñþ¸o¼Ý ¶ÈdÏ5ÜG¹éÃWs?³—š}Zyp¨ÿ.o¶ë¯WÇ\7‚ý%‰¹9{3§Ô¬¿~‹VÍ®_j)ö2ü¾—g`®Áº`=m·ÛJu½¯õ¯½Ev‘Û›öò¹¾Ë›‘`g÷ǽü§aï8RÇÊ·Çî«c·Øß!ø r•¡²Õæ¹Ôp‘ë5ŽœÚð«Þ}—Çê.ÞÛï寻k_¯Ò¦;[õšynÙûã>Œ\5Eºöޝ×*Úý.OXUî×òaáòÇÙvð<ºuq½£Ûóx7œøö‡¤šCgr‹½W_…»6ÖV¹ëËKºs…+7½Èœ•¾ÝÉÃBrÁ‹Ñ¦Ç2làØ}‹Ýªóè…iP;‚·hap gÎ.Ë}»?,ÈF°ƒr_ìÅ4ož×;´QéybÓ™÷°’ûe›¾Ø-d#ØYﯮ^+ÛÌÃSîOºdÿ\-,°»Qã­wͦñÂþF°³mFƒ|Ûòí9· +yÛI͹z´ªEöé²}¢Ëßîf ®ÁÊ] ’3ô^Î_èFýºð>g‹¹Y\n—…Ì-úöHw©Kö9šQûtìJƶ˜¿zâ×VsX(¼—ËpÝö7ìkÙû¹0¼På=h“·X˜™õ”{™æ™¶æ9+¤RÍ>°’E:b­?]²Ú¸n›ej…ÓâQý+7Á²æT•±UˆmÌk£~¶Ê9+³rýßW×àº@äº@»< †\7h`]®YN™—gÃëF‘lÉF‘lé‹Íõ}ý÷µwØ™:€:@=Ùˆ‹ûùóçÞE`gêê£hSˆd#€H6ˆd#€H6ˆd#€H6ˆd#€H6ˆæŽ‹ýëׯEÊÓ|þóùùïgaUôòÔÔÞÖQxfˆ±ØÙQÍ3’TÑkSPXöyyÚÔ"Ù ’"Ù Z /60ß-Ü[×a¿ÖeVs÷eVt4ËÕË{¨Í[³¸nD»n™ÃÐíã–|)7ó*ìPu •Çç¯Ý[öLXQùŒxÜ»?”§sRås[.©W2ö8`§·Ãu#ZtÜæW¨¢O#ru —‡\"jlQî˜èX‰:Ðû·qÚÔ"(T€WfRI®M6€´nVÎýÌõhSøÃ ®^}èý¬ÝíÂd#xChnŠ65€¨7¤MpRl•‹ˆ-“þHŽhEƒ†{_PnŠ65ÈroZƒd#ˆjÆ4r9¡Mê@;’û·×ùZ¸6mj´¨{’ þlR¡È@¨©]Ýx¤b\›lD‹ ǵ×á¯7On:'U®¹éêÀ•vôØE¸Ùú&1i„:-Ðß ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù Z>õ_œ|šñ„õÌYÕ‚kØký·ÛÛ•/òɯýÀÁ­rÝèu~u¢]Š'\À6ŽÛ¦¶F˜¹NQ.ïc•Þ÷gŒxý¾‹gF¹}ܺ3tƒËkñÞ²¹éÓÖ™œ³;å9OwzwJ½a9GMœ©rC¹š²J6êEŠ×¯½ðñú¹›ZÂ÷ܳÔ:“sæ– ƒ¨4ó½×O»­É[O~ÈКµ²Qåùu߆³±[_/4<×<ýÃëd¹Ùz©qævà26ºn´—Óõ7ëÒÔm÷Üf‹p ÏFcåú*=­ÝÞ4lêZo[!ÓR ;î}j„†d0ZoÓß×ë˕ܺ›òhÓF×’}«sr7LõšYçp¶Ü¥£Ê¾Ø½HñŒYÃrÖL[ø¤ú­÷æw1 €6-ŸzçÔB‡ßú9ß.R˜2vëÝŸ‡·ªå 6vëó'¾­üyÖÈÐý¾u- ¸Ù¨O€–²/6ÀJö{Ømùõêpbå:'&9CyÓo—}ZðTe7ðÅ?äÜœ¹Å“ÎÝœÈÎmj3Ÿ›Ñ}NȲ¥š¶ÂùýXÜyÔ³MÊm9ȧeÚÔF0äOAòÑuåçÙÀ½/vùJCw0žÞ @½Å“Cjÿžìë ß-d¡Y*9ºÒœÁÆ–³ò%—¸°Ãe£™c¾mêFQí_5c? ÿZ¼²Yêõó RÓ>äd´”p1;g£ä•›îõ’ÉÃ-æŸÜ²S³­ÊÂ%2)×ɺ<ç´Úq ¾ØÉ—V½,±^÷—šÂ¿ÝúÛkT£ SxiÂMp…‰pj‡kSÛòt;vC57ÿOG`ûgÍjM€¤Ãe£µåRHå G»‚í PŒº¨^?$'I·ÇÞ%8‡VµQ6ºW?§¬~ÎúÅ{7 omën(w_ب¾ØÉÂwÛí©¦÷ôò+—-|ÎÉw*p Û]7JÞºU9gÍl½^Æ…™ëçœP°9[ŸßÁhíùí²îùàìŒýÉF‘læ>µÂƒß§Ý6¿’ä“@†³%‡^ÌõÎΠœ˜ãÙè˜Ï·J–³÷ áÌÝ;Þ‡s&ÓY>#K>b¯û’´¹!*ž?¨-È?sûZhÁ9²ÑA,A†¨÷MÛÒð¹u×ì”k«êLݸ¶Â@*¹}­4â”٨רTh™*ÌY¨ñ½Å»ËŽú>v€ïÆõv¢lÚ¦d“·ºÑ”B·wŽl”T0¼k]û(ûÂÚFý¸öÁÔ€Š+q=u£¹}­´ãÙ(Ì~¾}ýVzµìÊ+Ëfÿ±R¿!NáÇçðá#üùoáù‰¿ßÑÛãvÿ¸ÿ™þñ÷ÕÞ á>Âïð{»rñãŸñÓ}!¼Þà£ók¸›çõÉ|­UÂ}¿J Öoëy„ðÑÿx»+ï¿4X6QªÎ¯6:\U·ðÃZöõù:P[V­§ÉFO5W€ægŽù£¦œÃ»•6DO/äÓûýµ\ 9ê×ú÷ÿÚr6®KÖÎI8ˆ£D¿›|£þ´Ì‘ 5Ï£ÝæHzÓùyÝ¿?boßÂp4ê4î:c?ÖǠ᜽ÎÚO÷ïd>`•ózuD»|è/-P7 qç¸ntÏ?7¾×V8œæì¾TÞPe_ìáâ“%׹ƆÚá^$*©Цsd£0¦ÿõ´9sý¯+×|©7‘Kº}Ü’;:WÔËè^îûpºÝÝ ÙˆõΈÝ__?çΚ\CòÔøz)YÔ‹IþQ4œNƒ´©ÑºÂ9’ »?î¹+F¯éÉ«ÀåÉF4§÷G÷é¼HÈÄeuãb†\.âE›ÂÏ~z„[¸Ý?îÏŸÃÇßÿ†B¸=þ¾:Ó!|„Ûãv÷[¸}…ƒ>GåÇçî;úó^ºïâé£óÆÃ÷OãùêšïoÇä‚u ±lï“÷¾¯ùïÇûm¿ä¶þèï¦okû^ªoÅû¾ì1ë@د}M8HhÔÑúüþú½ØºŽúµ^ò=nbãk §û|®*ÙqêÀAhS£EÏãÝóŸÆ’4²´cØÎÞýÙ!¢A²-rì’b‚l0䢴L6øF0jÙíãæB2²@–Óäå £°dŒlDszŒrãÖ¸xЦÜ~W7Q8>ÐŽ£Þì kêþøÚÔ :á{5p­¨qŽÈF4*wÈ{¯­P ‹¨°‹'AŸÃ"9§©‡=´?Þϲ±ÓìS6¤¿@$D²@$DËwØ[d4ˆš•ôæyþÚ»ù¶û,åáÄÜj+ç ©»ËÓ“[©ßÜp%3?êÞã¦çf‘2Àî{3ÃtÉÀ4œ˜\¶~Î×¶†)çµTañn"9N2õÞà’´©­h¯laä:˜l»ëFɶªáÄÂ`µK©_ç„&¹™æJÏ) ^õ©,Ò´‰.Mp4e£\ß áÄ·­QÔf›œsT“\÷‡™ý‡¦}JÃEæ¨/Ò´‰.qp4e£Â³»ù\d[É ÝË-o»UÎYXvñ`7êSš¼õáGW¿ã¦íÍd‡-ØÑ>mj/k<Ÿ¨ü8¤P—]zsnvþÞìSJJ®¿¾H¹«nËV³[›Úëç5®µ”·>aÎͺŔïk«|#õïwÂ@ å" 'êQÀ¹ìyŸZïD›¼ÀpÀ«_Cû) —z»‰iëiÚÞ<àþ q«\7Ž"˜öÉ£ul\ªHK%¿ã$H®íÜ×ÊÃ…AÓÌäˆvÜPn¹¬×HŒ‹$‰îhI…‹FaäÃÅà Nœêü>êGÛPMÛÖð’Ï‚™£fΞ™HŸ-E°·©åÛÍyòÖßЂƒY/²ž§WD[µ»ÏsýÏb;:ñu£°áSë÷ÝÐÌ9ç(\‘Ê…˜Êp3œ-·”´À–Nœ†íJ¯Ÿg¶m •;Ó¬Ôݸœ~édzxæXª=örâ6µ¤iOƒŸ°‰74*¼Ýаx•k뾋·EJÎPÿFÊsÖ\€•œæºQïdŸëSè4¹ ,¹Îùz-^sç|oo·>-O K^ß­*»SÊŸIwCÂ;:G6uxáÌZÒ­é 3gCîx¯ìS³õœ5S’Ù«þå¢ÛØÖpµ65€9d#F›y-Ç¥ ŽL6ˆd#€H6ˆd#€H6ˆd#€H6ˆd#€H6ˆd#€H6ˆd#€H6ˆd#€ècïÀžn·ûã>œBNçz’ <=¨—ðÜ•]½Ýj_·L6¢]ÃcèœsgG®!¹÷ Óƒºq9…h_7N›rÈkÜýqOV€Üt ²DÝÀtÜ —hºÑûmj´è€~|þB¸…ÛýãB>þü÷ö¸ÝÃ=~__?îÝ)O¿ÃïmË^ëÇçÂóm¾ÞÚŸ)™ÿ†?óüy³Ïé_»uêÀöuà•{r@X°ô^MÎÖøíãí.ûø[˜Ç·]ömþä:‡{3|ÿ!¢$ÉFp¿¿–;™õk½ä{¼"u`cÝ¿‘ò÷Ò¡>Ÿ–iS£99ûê´Ñ%Ð QpÔ ¯°¦îˆF1 klôõßE;FrQݤ} Úä" æf£Ÿ?.RØ’`DŽþFuûcº¤D—ºq1½Î×¹1ìë6éoD£û×­¡s !7ëéf ;š.ÙˆF兯åcåµ*@auãbH’ ÏÑu£öuËô7ˆd#€H6ˆd#€H6ˆæÞ§öëׯEÊÓ|þóùùïçÞ¥à:¸‡ßcCØ‘Çù°,mj‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘lÉF‘l}ì]Bá¾Üñø¶Øš¶Ø{|,´HqÝ ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù Zî¹Ïp6·[áþ¸WNçzn·äŽV7ÚÑÛ§Ï_»ìîÉF4êuRìsÓ¹˜á)°û’ºÑˆä>µsѦF‹zÇÁÂi’«º?îNs G6¢QÉã c%¹: n\’ýH’65ZtÀ ?þù>Bx„â÷òö¸ÝÃ=|„Ûãvÿ¸‡gø3ånáϯ‰¿Ãï]ÞÂ[ñ=¾þâ¾Âß÷¾Oùß¶åÞÊçÅê@øßAë@|óêÀ×…)^™iãcÅϯO »[¿íÜxõϯ!”>ÆïæëÕÔŽxU¼×Ê¿ÕÀá&¾ï¦×ÌýuövôßoáïæB¦Ø9Ÿ+Yó8 Ѻƒôù½àÉì¨_ë%ßã}ý^ìó9ìÅß㲺ÇÜÏøýuÐϧ5G=ˆÂ&Œ€õ޽ŸÏz”8ìéýñ~–Ýíùá%ïéµþ&ï«,WÓú9‡ekþw&¹†á'óú¹æ"ðÛ™swß¼V2aëà(G8f°L¦Ü¹¼§~Î#ÈuîýZùŽzÓ'ô1ÞÎz®Ïs¬‹½`džܧ¶Šš\RsÅhNÆ®§µÛpáqàöq»üÑ·N™&œõ‡¿¾¾ÝoBwJobÍâ½&¼‹áÄÜ]Äëõ¯& ½îMã€Î‘žù`ÙØkä·ÞpÏ`ñüWØbnñð÷{µà·ëõö+ƒQxw²Ÿ·.&÷®}2ÔŒi¤n\Cr?öŽüöu›ŽØß(Ùù÷Õý%¬êYù²%õ¤‚9·ž6uù¤©7ËPï°ó_›’ÇnÌhø—ëzï=w{Úõ”똓e &ÔuãbÞžkhÖ9ÚÔNwžÞ¥À£6:ªnÚ&ÎË“¶ÈÕuZpŽl´ ÜÙ=×ózìâ¡®ÇÏR +°™=ÛÔz]Þj.qO›³×xÜ›¡w‘|ØÒ¯ù*ÿºM‘&¨|Ülï1&¹UõþUn´w“Ý´­À5qìÇzÝ£÷.Ë•ÁhøH“äÚ†OLœPžÞèšõ[€kغMm³3ëÌûÚfªÉ%Éœ±Tø˜öÀä@ДM³Q¯]¦ûûa³NM#NnpçÊ çìNÉÙžæ±rìÇÊÛõ'<ár½«;.py›f£Üe’î‡Ý)“¯^Ôl(7gèŒTÎrNN(ya+õÁ(¼Kfò4âpc?®´†OÿËn.×û§¼éiÍjÓJ—qľØÎÐ]Ãn@ëå¼áI 5GÌFhâÙ¥¡jÔF“O‚[vpFGûñhW/*o@«œs¾£}>pv«\7ê°sO˜O.;ÿÁï•*ÏÙûù5ç°ƒvîβ·%On}8±ðt{¯˶Èž5Ö G¶|6ª™p8eìƒß뵡dBªŸXXs¹Tc'†T•]Êe»;àŽÞ¦F™àË’"Ù Z>½}VüJš¼æ}oõª|ÜlåCT*ç,|t¹‡¨Ôl.àˆãÕë½wY¦¨ F½{Ór}Œêç,”§7÷üuÀ¹lݦvÀ3ëEª)÷Ü·ÅËSc8÷.Å€}mšzí2¯áy†Ó+q’a.>¡ ©;[nñ᜽÷õ¶ð5õ\ØF[XÖ¦Ù(w™¤;ÂawÊ„«cÏ”çz*ÃYr€ÄcD2–•K~¨¼‡²s£ùgåšA»c$n–ÝDeáËãwWnhr!àŽØ{æ:¹ø´G«Á°P9ÍÙÖð$К#f£9'øaߣÞôm®íÒP5j£Ó¢8.ïèc?.rõbÁ;°*o@«œs¾ÜV„˜f•ëFÉ'Õ';?4óÁO¡H½Ÿ ýxrw–½-yrëÉ…¤Û{}XÎòÖ+ÍÜp:Ëg£Bè)Oûà÷úÅGm(™ê'Ö\.ÕØ‰!ÂFe—r™Ç£·©Q&¸À²d#€H6j—ÇÇÀÐïáJ>ñ4×'º²ïpn¤¹e­–,焎Ïݧ~ñäce{óh° AçÈF9ÉÀ4œX^öc-|wžò:G•Jmj«Ølp£ÉifÂ3Ú ÏFNüÏÄÐë‹óúµ7þÐpÎîKJ;ç°æŒ çÜmjC3Ÿ›Vh–zµy%?2ê©gÃN(yÓ.->t$œÎ¹³Q²ëq·gñªçø9Ï|͵½ñs߆¥Úx‹p4çÎFoÓÉ„„±ÙmíÂÕ_|Zã¢Ξ†æÇ‚ERÅÛbÌ/gî5Áæ¸x_ì™Ê×r¯Ží‹]™fz ˆ¯aïñàJÎqݨþið3Ÿ_^|øk²órM_ìä²…­¿òÖÛÑ,ç¼}àÙ(¤Nóouœ¶æäâ¹gÝÏ)ÃÛ õ¦ç‚Q¯úd‚„e#d—Åmp3#GV´BÝh‡}Íl4‘/ÒÙí;\»+ÜÁ n´Ã¾&I_l 9½á}ðìj eÛe£ä³8 3ÏÜÖ¨òT–jq¹GŽT&9gÍâɇœ4e8”Ô¾åá8ÔvØ×älÔ¦6ÿñkØ· É¡þoùÑ%ù„©÷ãóGáö¸Ý?î!„ðá#Ü·{¸ÿùáãþœ_ú¸ ð;üÞóäýøüÑ} !„ð·Ðkaø¦Bø6Ã×^ï`]Ÿÿ,VÂïƒÖÏ–©­¿#aðáüý@ºÕàÛ”ðýƒ Ç><=þüÿõ»cex~áÞŸyÍJ ¿Ñ*¦ý8m\ìáƒÞÞ.Î}}]õhO-uuà öÌFÉ‹I½Ýw¯‚ôn*©_|l‘†w¯$ ê=­7ýtÑdx=ù\å‡zÉï,ÀÓá®åš„*‹1ªE)yïpC¹–;°Ö_³)ÜB¼¶r:]°ƒ±î™ç=.å¬qª5úby=Ãxôv©}‚6t;Y0¸0õH:M6ÚŪ—Üy\ŒhœlTeZ¿¥• Ìä eìÇò3í 3T.¾ äBÃ_'lº×—|Øãªa[ü5¹/yr=­– МQá;¨n4¾&I£ÓðÕ]œ”u£ö5CGéoTvüº{ü5Α¶!D²@$D²@$D²@$D²°µ5 Ó¿ï²\Vîtû¸%_ÊM/¬¿rþ±kf)…:œ8jOÕÏß›m¸ÔÛò¨?“%÷Ñzû:·Åsñ<5¸ ÂéõÐâÞÓ‹sÓÇ®ç휕ëg¦Q©(|ß5ûhìü½Gƒ÷¢Ò°bôÖ_^99Éïéªûúßw×à‚îûG,(W’Ó{'°^v;ÿL*ó4ÝÝôÚGß×!AC’ÇÊÂô±ëE /6ˆGÓønN#Û9évz܉¯*!d/.ypË/ày¿ï²°®ç9ï¼GÉ$;Õ/þvþ™ëg¾Üg¾ø¾¸Æ÷]_l`u£zy³‹n|©ÜMcçg/ï^ý¾ëõ§®¹¶wï»lÀè{—Bç´zê³`›ÆÞcØÍR-4}ÊFÀ^7ô—Ž'y/Rýnšy«v çÚ•wMå¾»sÏþ}×ߨÈópÙÈß×6a&ï!U‰×¿±Ã2QVhMÛfÓ'ý¾ËF@ÖR5m.çµË‰í¤'Ôƒ«›cÂRÉEÎþ}— !5cu‡µ ™#cn=œTî>²\˜vßÙyGI>©ä§½Í¾>;ýà‚ºÁ%œ³½Ÿ™ru 7}ì}jîkË•3yTOóöo›-÷õ‰ÈFpAo»^çN/ÿ•_ßÑr8¸ÊÆÈ[UIDAT_#‡×}v\ý"oëÀÌRú•±vÙ××ø¾ËFМúÃßÛý–Ú4‡e—µcñ}}ÞÊ£¿°®ó±®ñ}—"Ù ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù ’"Ù ú˜¿Š¯ÿ¾æ¯֣Т Pïÿî{áå¯ÿ¾>ÿýܬ4‹ûõë×ÏŸ?+gÖ¦ÉF‘lÉF‘lÉF‘lÉF‘l½fȯ_¿6(À¼yf@S´©D²@$D²@$D²@$D²@$D²@$D²@$D²@$D²@$D²@$Dÿþ[¿¤È`‚½tIMEÓ 1;̵‡]IEND®B`‚nut-2.7.4/docs/snmp-subdrivers.txt0000644000175000017500000002453612667573057014143 00000000000000How to make a new subdriver to support another SNMP device ---------------------------------------------------------- Overall concept ~~~~~~~~~~~~~~~ The SNMP protocol allow for a common way to interact with devices over the network. The NUT "snmp-ups" driver is a meta-driver that handles many SNMP devices, such as UPS and PDU. It consists of a core driver that handles most of the work of talking to the SNMP agent, and several sub-drivers to handle specific device manufacturers. Adding support for a new SNMP device is easy, because it requires only the creation of a new sub-driver. SNMP data Tree ~~~~~~~~~~~~~~ From the point of view of writing an SNMP subdriver, an SNMP device consists of a bunch of variables, called OIDs (for Object IDentifiers). Some OIDs (such as the current input voltage) are read-only, whereas others (such as the beeper enabled/disabled/muted status) can be read and written. OID are grouped together and arranged in a hierarchical tree shape, similar to directories in a file system. OID components are separated by ".", and can be expressed in numeric or textual form. For example: .iso.org.dod.internet.mgmt.mib-2.system.sysObjectID is equivalent to: .1.3.6.1.2.1.1.2.0 Here is an excerpt tree, showing only two OIDs, sysDescr and sysObjectID: .iso .org .dod .internet .mgmt .mib-2 .system .sysDescr.0 = STRING: Dell UPS Tower 1920W HV .sysObjectID.0 = OID: .iso.org.dod.internet.private.enterprises.674.10902.2 (...) .upsMIB .upsObjects .upsIdent .upsIdentModel = STRING: "Dell UPS Tower 1920W HV" (...) .private .enterprises .674 .10902 .2 .100 .1.0 = STRING: "Dell UPS Tower 1920W HV" (...) As you can see in the above example, the device name is exposed three times, through three different MIBs: - Generic MIB-II (RFC 1213): .iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0 = STRING: Dell UPS Tower 1920W HV .1.3.6.1.2.1.1.1.0 = STRING: Dell UPS Tower 1920W HV - UPS MIB (RFC 1628): .iso.org.dod.internet.mgmt.mib-2.upsMIB.upsObjects.upsIdent.upsIdentModel = STRING: "Dell UPS Tower 1920W HV" .1.3.6.1.2.1.33.1.1.2.0 = STRING: "Dell UPS Tower 1920W HV" - DELL SNMP UPS MIB: .iso.org.dod.internet.private.enterprises.674.10902.2.100.1.0 = STRING: "Dell UPS Tower 1920W HV" But only the two last can serve useful data for NUT. An highly interesting OID is *sysObjectID*: its value is an OID that refers to the main MIB of the device. In the above example, the device points us at the Dell UPS MIB. *sysObjectID*, also called "sysOID" is used by snmp-ups to find the right mapping structure. For more information on SNMP, refer to the link:http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol[Wikipedia] article, or browse the Internet. To be able to convert values, NUT SNMP subdrivers need to provide: - manufacturer-specific sysOID, to determine which lookup structure applies to which devices, - a mapping of SNMP variables to NUT variables, - a mapping of SNMP values to NUT values. Moreover, subdrivers might have to provide additional functionality, such as custom implementations of specific instant commands (load.off, shutdown.restart), and conversions of manufacturer specific data formats. At the time of writing this document, snmp-ups doesn't provide such mechanisms (only formatting ones), but it is planned in a future release. Creating a subdriver ~~~~~~~~~~~~~~~~~~~~ In order to create a subdriver, you will need the following: - the "MIB definition file. This file has a ".mib" extension, and is generally available on the accompanying disc, or on the manufacturer website. It should either be placed in a system directory (/usr/share/mibs/ or equivalent), or pointed using *-M* option, - a network access to the device - OR information dumps. You can create an initial "stub" subdriver for your device by using the helper script *scripts/subdriver/gen-snmp-subdriver.sh*. Note that this only creates a "stub" which MUST be customized to be useful (see CUSTOMIZATION below). You have two options to run gen-snmp-subdriver.sh: mode 1: get SNMP data from a real agent ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This method requires to have a network access to the device, in order to automatically retrieve the needed information. You have to specify the following parameters: - *-H* host address: is the SNMP host IP address or name - *-c* community: is the SNMP v1 community name (default: public)" For example: $ gen-snmp-subdriver.sh -H W.X.Y.Z -c foobar -n .mib mode 2: get data from files ^^^^^^^^^^^^^^^^^^^^^^^^^^^ This method does not require direct access to the device, at least not for the one using gen-snmp-subdriver.sh. The following SNMP data need to be dumped first: - sysOID value: for example '.1.3.6.1.4.1.705.1' - a numeric SNMP walk (OIDs in dotted numeric format) of the tree pointed by sysOID. For example: snmpwalk -On -c foobar W.X.Y.Z .1.3.6.1.4.1.705.1 > snmpwalk-On.log - a textual SNMP walk (OIDs in string format) of the tree pointed by sysOID. For example: snmpwalk -Os -c foobar W.X.Y.Z .1.3.6.1.4.1.705.1 > snmpwalk-Os.log NOTE: if the OID are only partially resolved (i.e, there are still parts expressed in numeric form), then try using *-M* to point your .mib file. Then call the script using: $ gen-snmp-subdriver.sh -s For example: $ gen-snmp-subdriver.sh -s .1.3.6.1.4.1.705.1 snmpwalk-On.log snmpwalk-Os.log This script prompts you for a name for the subdriver if you don't provide it with *-n*. Use only letters and digits, and use natural capitalization such as "Camel" (not "camel" or "CAMEL", apart if it natural). The script may prompt you for additional information. Integrating the subdriver with snmp-ups ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Beside of the mandatory customization, there are a few things that you have to do, as mentioned at the end of the script: - edit drivers/snmp-ups.h and add #include ".h", where is the name of the header file, with the *.h* extension, - edit drivers/snmp-ups.c and bump DRIVER_VERSION by adding "0.01". - also add "&" to snmp-ups.c:mib2nut[] list, where is the lower case driver name - add "-mib.c" to snmp_ups_SOURCES in drivers/Makefile.am - add "-mib.h" to dist_noinst_HEADERS in drivers/Makefile.am - copy "-mib.c" and "-mib.h" to ../drivers/ - finally call the following, from the top level directory, to test compilation: $ autoreconf && configure && make You can already start experimenting with the new subdriver; but all data will be prefixed by "unmapped.". You will now have to customize it. CUSTOMIZATION ^^^^^^^^^^^^^ The initially generated subdriver code is only a stub (mainly a big C structure to be precise), and will not implement any useful functionality (in particular, it will be unable to shut down the UPS). In the beginning, it simply attempts to monitor some UPS variables. To make this driver useful, you must examine the NUT variables of the form "unmapped.*" in the hid_info_t data structure, and map them to actual NUT variables and instant commands. There are currently no step-by-step instructions for how to do this. Please look at the files to see how the currently implemented subdrivers are written.: - apc-mib.c/h - baytech-mib.c/h - bestpower-mib.c/h - compaq-mib.c/h - cyberpower-mib.c/h - eaton-mib.c/h - ietf-mib.c/h - mge-mib.c/h - netvision-mib.c/h - powerware-mib.c/h - raritan-pdu-mib.c - huawei-mib.c/h To help you, above each entry in -mib.c, there is a comment that displays the textual OID name. For example, the following entry: /* upsMIB.upsObjects.upsIdent.upsIdentModel = STRING: "Dell UPS Tower 1920W HV" */ { "unmapped.upsidentmodel", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, Many time, only the first field will need to be modified, to map to an actual NUT variable name. Check the <> section first to find a name that matches the OID name (closest fit). If nothing matches, contact the upsdev list, and we'll figure it out. In the above example, the right NUT variable is obviously "device.model". The MIB definition file (.mib) also contains some description of these OIDs, along with the possible enumerated values. //////////////////////////////////////////////////////////////////////////////// FIXME: Shutting down the UPS ~~~~~~~~~~~~~~~~~~~~~ It is desireable to support shutting down the UPS. Usually (for devices that follow the HID Power Device Class specification), this requires sending the UPS two commands. One for shutting down the UPS (with an 'offdelay') and one for restarting it (with an 'ondelay'), where offdelay < ondelay. The two NUT commands for which this is relevant, are 'shutdown.return' and 'shutdown.stayoff'. Since the one-to-one mapping above doesn't allow sending two HID commands to the UPS in response to sending one NUT command to the driver, this is handled by the driver. In order to make this work, you need to define the following four NUT values: ups.delay.start (variable, R/W) ups.delay.shutdown (variable, R/W) load.off.delay (command) load.on.delay (command) If the UPS supports it, the following variables can be used to show the countdown to start/shutdown: ups.timer.start (variable, R/O) ups.timer.shutdown (variable, R/O) The `load.on` and `load.off` commands will be defined implicitly by the driver (using a delay value of '0'). Define these commands yourself, if your UPS requires a different value to switch on/off the load without delay. Note that the driver expects the `load.off.delay` and `load.on.delay` to follow the HID Power Device Class specification, which means that the `load.on.delay` command should NOT switch on the load in the absence of mains power. If your UPS switches on the load regardless of the mains status, DO NOT define this command. You probably want to define the `shutdown.return` and/or `shutdown.stayoff` commands in that case. Commands defined in the subdriver will take precedence over the ones that are composed in the driver. When running the driver with the '-k' flag, it will first attempt to send a `shutdown.return` command and if that fails, will fallback to `shutdown.reboot`. //////////////////////////////////////////////////////////////////////////////// nut-2.7.4/docs/contact-closure.txt0000644000175000017500000001053212640443572014061 00000000000000Contact closure hardware information ------------------------------------ This is a collection of notes that apply to contact closure UPS hardware, specifically those monitored by the genericups driver. Definitions ~~~~~~~~~~~ "Contact closure" refers to a situation where one line is connected to another inside UPS hardware to indicate some sort of situation. These can be relays, or some other form of switching electronics. The generic idea is that you either have a signal on a line, or you don't. Think binary. Usually, the source for a signal is the host PC. It provides a high (logic level 1) from one of its outgoing lines, and the UPS returns it on one or more lines to communicate. The rest of the time, the UPS either lets it float or connects it to the ground to indicate a 0. Other equipment generates the high and low signals internally, and does not require cable power. These signals just appear on the right lines without any special configuration on the PC side. Bad levels ~~~~~~~~~~ Some evil cabling and UPS equipment uses the transmit or receive lines as their reference points for these signals. This is not sufficient to register as a high signal on many serial ports. If you have problems reading certain signals on your system, make sure your UPS isn't trying to do this. Signals ~~~~~~~ Unlike their smarter cousins, this kind of UPS can only give you very simple yes/no answers. Due to the limited number of serial port lines that can be used for this purpose, you typically get two pieces of data: 1. "On line" or "on battery" 2. "Battery OK" or "Low battery" That's it. Some equipment actually swaps the second one for a notification about whether the battery needs to be replaced, which makes life interesting for those users. Most hardware also supports an outgoing signal from the PC which means "shut down the load immediately". This is generally implemented in such a way that it only works when running on battery. Most hardware or cabling will ignore the shutdown signal when running on line power. New genericups types ~~~~~~~~~~~~~~~~~~~~ If none of the existing types in the genericups driver work completely, make a note of which ones (if any) manage to work partially. This can save you some work when creating support for your hardware. Use that information to create a list of where the signals from your UPS appear on the serial port at the PC end, and whether they are active high or active low. You also need to know what outgoing lines, if any, need to be raised in order to supply power to the contacts. This is known as cable power. Finally, if your UPS can shut down the load, that line must also be identified. There are only 4 incoming and 2 outgoing lines, so not many combinations are left. The other lines on a typical 9 pin port are transmit, receive, and the ground. Anything trying to do a high/low signal on those three is beyond the scope of the genericups driver. The only exception is an outgoing BREAK, which we already support. When editing the genericups.h, the values have the following meanings: Outgoing lines: - line_norm = what to set to make the line "normal" - i.e. cable power - line_sd = what to set to make the UPS shut down the load Incoming lines: - line_ol = flag that appears for on line / on battery - val_ol = value of that flag when the UPS is on battery - line_bl = flag that appears for low battery / battery OK - val_bl = value of that flag when the battery is low This may seem a bit confusing to have two variables per value that we want to read, but here's how it works. If you set line_ol to TIOCM_RNG, then the value of TIOCM_RNG (0x080 on my box) will be anded with the value of the serial port whenever a poll occurs. If that flag exists, then the result of the and will be 0x80. If it does not exist, the result will be 0. So, if line_ol = foo, then val_ol can only be foo or 0. As a general case, if 'line_ol == val_ol', then the value you're reading is active high. Otherwise, it's active low. Check out the guts of upsdrv_updateinfo() to see how it really works. Custom definitions ~~~~~~~~~~~~~~~~~~ Late in the 1.3 cycle, a feature was merged which allows you to create custom monitoring settings without editing the model table. Just set upstype to something close, then use settings in ups.conf to adjust the rest. See the linkman:genericups[8] man page for more details. nut-2.7.4/docs/new-drivers.txt0000644000175000017500000006620112667572020013225 00000000000000Creating a new driver to support another device =============================================== This chapter will present the process of creating a new driver to support another device. Since NUT already supports many major power devices protocols through several generic drivers (genericups, usbhid-ups, snmp-ups, blazer_* and nutdrv_qx), creation of new drivers has become rare. So most of the time, it will be limited to completing one of these generic drivers. Smart vs. Contact-closure ------------------------- If your UPS only does contact closure readings, then go straight to the <> chapter for information on adding support. It's a lot easier to add a few lines to a header file than it is to create a whole new driver. Serial vs. USB vs. SNMP and more -------------------------------- If your UPS connects to your computer via a USB port, then go straight to the <> chapter. You can probably add support for your device by writing a new subdriver to the existing usbhid-ups driver, which is easier than writing an entire new driver. Similarly, if your UPS connects to your computer via an SNMP network card, you can probably add support for your device by writing a new subdriver to the existing snmp-ups driver. Instructions are provided in the <> chapter. Overall concept --------------- The basic design of drivers is simple. main.c handles most of the work for you. You don't have to worry about arguments, config files, or anything else like that. Your only concern is talking to the hardware and providing data to the outside world. Skeleton driver --------------- Familiarize yourself with the design of skel.c in the drivers directory. It shows a few examples of the functions that main will call to obtain updated information from the hardware. Essential structure ------------------- upsdrv_info_t ~~~~~~~~~~~~~ This structure tracks several description information about the driver: * *name*: the driver full name, for banner printing and "driver.name" variable. * *version*: the driver's own version. For sub driver information, refer below to sub_upsdrv_info. This value has the form "X.YZ", and is published by main as "driver.version.internal". * *authors*: the driver's author(s) name. If multiple authors are listed, separate them with a newline character so that it can be broken up by author if needed. * *status*: the driver development status. The following values are allowed: - DRV_BROKEN: setting this value will cause main to print an error and exit. This is only used during conversions of the driver core to keep users from using drivers which have not been converted. Drivers in this state will be removed from the tree after some period if they are not fixed. - DRV_EXPERIMENTAL: set this value if your driver is potentially broken. This will trigger a warning when it starts so the user doesn't take it for granted. - DRV_BETA: this value means that the driver is more stable and complete. But it is still not recommended for production systems. - DRV_STABLE: the driver is suitable for production systems, but not 100 % feature complete. - DRV_COMPLETE: this is the gold level! It implies that 100 % of the protocol is implemented, and a full QA pass. * *subdrv_info*: array of upsdrv_info_t for sub driver(s) information. For example, this is used by usbhid-ups. This information is currently used for the startup banner printing and tests. Essential functions ------------------- upsdrv_initups ~~~~~~~~~~~~~~ Open the port (device_path) and do any low-level things that it may need to start using that port. If you have to set DTR or RTS on a serial port, do it here. Don't do any sort of hardware detection here, since you may be going into upsdrv_shutdown next. upsdrv_initinfo ~~~~~~~~~~~~~~~ Try to detect what kind of UPS is out there, if any, assuming that's possible for your hardware. If there is a way to detect that hardware and it doesn't appear to be connected, display an error and exit. This is the last time your driver is allowed to bail out. This is usually a good place to create variables like ups.mfr, ups.model, ups.serial, and other "one time only" items. upsdrv_updateinfo ~~~~~~~~~~~~~~~~~ Poll the hardware, and update any variables that you care about monitoring. Use dstate_setinfo() to store the new values. Do at most one pass of the variables. You MUST return from this function or upsd will be unable to read data from your driver. main will call this function at regular intervals. Don't spent more than a couple of seconds in this function. Typically five (5) seconds is the maximum time allowed before you risk that the server declares the driver stale. If your UPS hardware requires a timeout period of several seconds before it answers, consider returning from this function after sending a command immediately and read the answer the next time it is called. You must never abort from upsdrv_updateinfo(), even when the UPS doesn't seem to be attached anymore. If the connection with the UPS is lost, the driver should retry to re-establish communication for as long as it is running. Calling exit() or any of the fatal*() functions is specifically not allowed anymore. upsdrv_shutdown ~~~~~~~~~~~~~~~ Do whatever you can to make the UPS power off the load but also return after the power comes back on. You may use a different command that keeps the UPS off if the user has requested that with a configuration setting. You should attempt the UPS shutdown command even if the UPS detection fails. If the UPS does not shut down the load, then the user is vulnerable to a race if the power comes back on during the shutdown process. Data types ---------- To be of any use, you must supply data in ups.status. That is the minimum needed to let upsmon do its job. Whenever possible, you should also provide anything else that can be monitored by the driver. Some obvious things are the manufacturer name and model name, voltage data, and so on. If you can't figure out some value automatically, use the ups.conf options to let the user tell you. This can be useful when a driver needs to support many similar hardware models but can't probe to see what is actually attached. Manipulating the data --------------------- All status data lives in structures that are managed by the dstate functions. All access and modifications must happen through those functions. Any other changes are forbidden, as they will not pushed out as updates to things like upsd. Adding variables ~~~~~~~~~~~~~~~~ dstate_setinfo("ups.model", "Mega-Zapper 1500"); Many of these functions take format strings, so you can build the new values right there: dstate_setinfo("ups.model", "Mega-Zapper %d", rating); Setting flags ~~~~~~~~~~~~~ Some variables have special properties. They can be writable, and some are strings. The ST_FLAG_* values can be used to tell upsd more about what it can do. dstate_setflags("input.transfer.high", ST_FLAG_RW); Status data ~~~~~~~~~~~ UPS status flags like on line (OL) and on battery (OB) live in ups.status. Don't manipulate this by hand. There are functions which will do this for you. status_init() - before doing anything else status_set(val) - add a status word (OB, OL, etc) status_commit() - push out the update Possible values for status_set: OL - On line (mains is present) OB - On battery (mains is not present) LB - Low battery HB - High battery RB - The battery needs to be replaced CHRG - The battery is charging DISCHRG - The battery is discharging (inverter is providing load power) BYPASS - UPS bypass circuit is active - no battery protection is available CAL - UPS is currently performing runtime calibration (on battery) OFF - UPS is offline and is not supplying power to the load OVER - UPS is overloaded TRIM - UPS is trimming incoming voltage (called "buck" in some hardware) BOOST - UPS is boosting incoming voltage FSD - Forced Shutdown (restricted use, see the note below) Anything else will not be recognized by the usual clients. Coordinate with the nut-upsdev list before creating something new, since there will be duplication and ugliness otherwise. [NOTE] ================================================================================ - upsd injects "FSD" by itself following that command by a master upsmon process. Drivers must not set that value, apart from specific cases (see below). - As an exception, drivers may set "FSD" when an imminent shutdown has been detected. In this case, the "on battery + low battery" condition should not be met. Otherwise, setting status to "OB LB" should be preferred. - the OL and OB flags are an indication of the input line status only. - the CHRG and DISCHRG flags are under replacement by battery.charger.status. See the linkdoc:user-manual[NUT command and variable naming scheme,nut-names] for more information. ================================================================================ UPS alarms ---------- These work like ups.status, and have three special functions which you must use to manage them. alarm_init() - before doing anything else alarm_set() - add an alarm word alarm_commit() - push the value into ups.alarm NOTE: the ALARM flag in ups.status is automatically set whenever you use alarm_set. To remove that flag from ups.status, call alarm_init and alarm_commit without calling alarm_set in the middle. You should never try to set or unset the ALARM flag manually. If you use UPS alarms, the call to status_commit() should be after alarm_commit(), otherwise there will be a delay in setting the ALARM flag in ups.status. There is no official list of alarm words as of this writing, so don't use these functions until you check with the upsdev list. Staleness control ----------------- If you're not talking to a polled UPS, then you must ensure that it is still out there and is alive before calling dstate_dataok(). Even if nothing is changing, you should still "ping" it or do something else to ensure that it is really available. If the attempts to contact the UPS fail, you must call dstate_datastale() to inform the server and clients. - dstate_dataok() + You must call this if polls are succeeding. A good place to call this is the bottom of upsdrv_updateinfo(). - dstate_datastale() + You must call this if your status is unusable. A good technique is to call this before exiting prematurely from upsdrv_updateinfo(). Don't hide calls to these functions deep inside helper functions. It is very hard to find the origin of staleness warnings, if you call these from various places in your code. Basically, don't call them from any other function than from within upsdrv_updateinfo(). There is no need to call either of these regularly as was stated in previous versions of this document (that requirement has long gone). Serial port handling -------------------- Drivers which use serial port functions should include serial.h and use these functions whenever possible: - int ser_open(const char *port) This opens the port and locks it if possible, using one of fcntl, lockf, or uu_lock depending on what may be available. If something fails, it calls fatal for you. If it succeeds, it always returns the fd that was opened. - int ser_open_nf(const char *port) This is a non-fatal version of ser_open(), that does not call fatal if something fails. - int ser_set_speed(int fd, const char *port, speed_t speed) This sets the speed of the port and also does some basic configuring with tcgetattr and tcsetattr. If you have a special serial configuration (other than 8N1), then this may not be what you want. The port name is provided again here so failures in tcgetattr() provide a useful error message. This is the only place that will generate a message if someone passes a non-serial port /dev entry to your driver, so it needs the extra detail. - int ser_set_speed_nf(int fd, const char *port, speed_t speed) This is a non-fatal version of ser_set_speed(), that does not call fatal if something fails. - int ser_set_dtr(int fd, int state) - int ser_set_rts(int fd, int state) These functions can be used to set the modem control lines to provide cable power on the RS232 interface. Use state = 0 to set the line to 0 and any other value to set it to 1. - int ser_get_dsr(int fd) - int ser_get_cts(int fd) - int ser_get_dcd(int fd) These functions read the state of the modem control lines. They will return 0 if the line is logic 0 and a non-zero value if the line is logic 1. - int ser_close(int fd, const char *port) This function unlocks the port if possible and closes the fd. You should call this in your upsdrv_cleanup handler. - int ser_send_char(int fd, char ch) This attempts to write one character and returns the return value from write. You could call write directly, but using this function allows for future error handling in one place. - int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) If you need to send a formatted buffer with an intercharacter delay, use this function. There are a number of UPS controllers which can't take commands at the full speed that would normally be possible at a given bit rate. Adding a small delay usually turns a flaky UPS into a solid one. The return value is the number of characters that was sent to the port, or -1 if something failed. - int ser_send(int fd, const char *fmt, ...) Like ser_send_pace, but without a delay. Only use this if you're sure that your UPS can handle characters at the full line rate. - int ser_send_buf(int fd, const char *buf, size_t buflen) This sends a raw buffer to the fd. It is typically used for binary transmissions. It returns the results of the call to write. - int ser_send_buf_pace(int fd, unsigned long d_usec, const char *buf, size_t buflen) This is just ser_send_buf with an intercharacter delay. - int ser_get_char(int fd, char *ch, long d_sec, long d_usec) This will wait up to d_sec seconds + d_usec microseconds for one character to arrive, storing it at ch. It returns 1 on success, -1 if something fails and 0 on a timeout. NOTE: the delay value must not be too large, or your driver will not get back to the usual idle loop in main in time to answer the PINGs from upsd. That will cause an oscillation between staleness and normal behavior. - int ser_get_buf(int fd, char *buf, size_t buflen, long d_sec, long d_usec) Like ser_get_char, but this one reads up to buflen bytes storing all of them in buf. The buffer is zeroed regardless of success or failure. It returns the number of bytes read, -1 on failure and 0 on a timeout. This is essentially a single read() function with a timeout. - int ser_get_buf_len(int fd, char *buf, size_t buflen, long d_sec, long d_usec) Like ser_get_buf, but this one waits for buflen bytes to arrive, storing all of them in buf. The buffer is zeroed regardless of success or failure. It returns the number of bytes read, -1 on failure and 0 on a timeout. This should only be used for binary reads. See ser_get_line for protocols that are terminated by characters like CR or LF. - int ser_get_line(int fd, char *buf, size_t buflen, char endchar, const char *ignset, long d_sec, long d_usec) This is the reading function you should use if your UPS tends to send responses like "OK\r" or "1234\n". It reads up to buflen bytes and stores them in buf, but it will return immediately if it encounters endchar. The endchar will not be stored in the buffer. It will also return if it manages to collect a full buffer before reaching the endchar. It returns the number of bytes stored in the buffer, -1 on failure and 0 on a timeout. If the character matches the ignset with strchr(), it will not be added to the buffer. If you don't need to ignore any characters, just pass it an empty string - "". The buffer is always cleared and is always null-terminated. It does this by reading at most (buflen - 1) bytes. NOTE: any other data which is read after the endchar in the serial buffer will be lost forever. As a result, you should not use this unless your UPS uses a polled protocol. Let's say your endchar is \n and your UPS sends "OK\n1234\nabcd\n". This function will read() all of that, find the first \n, and stop there. Your driver will get "OK", and the rest is gone forever. This also means that you should not "pipeline" commands to the UPS. Send a query, then read the response, then send the next query. - int ser_get_line_alert(int fd, char *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler(char ch), long d_sec, long d_usec) This is just like ser_get_line, but it allows you to specify a set of alert characters which may be received at any time. They are not added to the buffer, and this function will call your handler function, passing the character as an argument. Implementation note: this function actually does all of the work, and ser_get_line is just a wrapper that sets an empty alertset and a NULL handler. - int ser_flush_in(int fd, const char *ignset, int verbose) This function will drain the input buffer. If verbose is set to a positive number, then it will announce the characters which have been read in the syslog. You should not set verbose unless debugging is enabled, since it could be very noisy. This function returns the number of characters which were read, so you can check for extra bytes by looking for a nonzero return value. Zero will also be returned if the read fails for some reason. - int set_flush_io(int fd) This function drains both the in- and output buffers. Return zero on success. - void ser_comm_fail(const char *fmt, ...) Call this whenever your serial communications fail for some reason. It takes a format string, so you can use variables and other things to clarify the error. This function does built-in rate-limiting so you can't spam the syslog. By default, it will write 10 messages, then it will stop and only write 1 in 100. This allows the driver to keep calling this function while the problem persists without filling the logs too quickly. In the old days, drivers would report a failure once, and then would be silent until things were fixed again. Users had to figure out what was happening by finding that single error message, or by looking at the repeated complaints from upsd or the clients. If your UPS frequently fails to acknowledge polls and this is a known situation, you should make a couple of attempts before calling this function. NOTE: this does not call dstate_datastale. You still need to do that. - void ser_comm_good(void) This will clear the error counter and write a "re-established" message to the syslog after communications have been lost. Your driver should call this whenever it has successfully contacted the UPS. A good place for most drivers is where it calls dstate_dataok. USB port handling ----------------- Drivers which use USB functions should include usb-common.h and use these: Structure and macro ~~~~~~~~~~~~~~~~~~~ You should us the usb_device_id structure, and the USB_DEVICE macro to declare the supported devices. This allows the automatic extraction of USB information, to generate the Hotplug, udev and UPower support files. For example: -------------------------------------------------------------------------------- /* SomeVendor name */ #define SOMEVENDOR_VENDORID 0xXXXX /* USB IDs device table */ static usb_device_id sv_usb_device_table [] = { /* some models 1 */ { USB_DEVICE(SOMEVENDOR_VENDORID, 0xYYYY), NULL }, /* various models */ { USB_DEVICE(SOMEVENDOR_VENDORID, 0xZZZZ), NULL }, { USB_DEVICE(SOMEVENDOR_VENDORID, 0xAAAA), NULL }, /* Terminating entry */ { -1, -1, NULL } }; -------------------------------------------------------------------------------- Function ~~~~~~~~ - is_usb_device_supported(usb_device_id **usb_device_id_list, int dev_VendorID, int dev_ProductID) Call this in your device opening / matching function. Pass your usb_device_id structure, and a set of VendorID / DeviceID. This function returns one of the following value: - NOT_SUPPORTED (0), - POSSIBLY_SUPPORTED (1, returned when the VendorID is matched, but the DeviceID is unknown), - or SUPPORTED (2). For implementation examples, refer to the various USB drivers, and search for the above patterns. NOTE: This set of USB helpers is due to expand is the near future... Variable names -------------- PLEASE don't make up new variables and commands just because you can. The new dstate functions give us the power to create just about anything, but that is a privilege and not a right. Imagine the mess that would happen if every developer decided on their own way to represent a common status element. Check the <> section first to find the closest fit. If nothing matches, contact the upsdev list, and we'll figure it out. Patches which introduce unlisted names may be modified or dropped. [[commands]] Message passing support ----------------------- upsd can call drivers to store values in read/write variables and to kick off instant commands. This is how you register handlers for those events. The driver core (drivers/main.c) has a structure called upsh. You should populate it with function pointers in your upsdrv_initinfo() function. Right now, there are only two possibilities: - setvar = setting UPS variables (SET VAR protocol command) - instcmd = instant UPS commands (INSTCMD protocol command) SET ~~~ If your driver's function for handling variable set events is called my_ups_set(), then you'd do this to add the pointer: upsh.setvar = my_ups_set; my_ups_set() will receive two parameters: const char * - the variable being changed const char * - the new value You should return either STAT_SET_HANDLED if your driver recognizes the command, or STAT_SET_UNKNOWN if it doesn't. Other possibilities will be added at some point in the future. INSTCMD ~~~~~~~ This works just like the set process, with slightly different values arriving from the server. upsh.instcmd = my_ups_cmd; Your function will receive two args: const char * - the command name const char * - (reserved) You should return either STAT_INSTCMD_HANDLED or STAT_INSTCMD_UNKNOWN depending on whether your driver can handle the requested command. Notes ~~~~~ Use strcasecmp. The command names arriving from upsd should be treated without regards to case. Responses ~~~~~~~~~ Drivers will eventually be expected to send responses to commands. Right now, there is no channel to get these back through upsd to the client, so this is not implemented. This will probably be implemented with a polling scheme in the clients. Enumerated types ---------------- If you have a variable that can have several specific values, it is enumerated. You should add each one to make it available to the client: dstate_addenum("input.transfer.low", "92"); dstate_addenum("input.transfer.low", "95"); dstate_addenum("input.transfer.low", "99"); dstate_addenum("input.transfer.low", "105"); Range values ------------ If you have a variable that support values comprised in one or more ranges, you should add each one to make it available to the client: dstate_addrange("input.transfer.low", 90, 95); dstate_addrange("input.transfer.low", 100, 105); Writable strings ---------------- Strings that may be changed by the client should have the ST_FLAG_STRING flag set, and a maximum length (in bytes) set in the auxdata. dstate_setinfo("ups.id", "Big UPS"); dstate_setflags("ups.id", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.id", 8); If the variable is not writable, don't bother with the flags or the auxiliary data. It won't be used. Instant commands ---------------- If your hardware and driver can support a command, register it. dstate_addcmd("load.on"); Delays and ser_* functions -------------------------- The new ser_* functions may perform reads faster than the UPS is able to respond in some cases. This means that your driver will call select() and read() numerous times if your UPS responds in bursts. This also depends on how fast your system is. You should check your driver with `strace` or its equivalent on your system. If the driver is calling read() multiple times, consider adding a call to usleep before going into the ser_read_* call. That will give it a chance to accumulate so you get the whole thing with one call to read without looping back for more. This is not a request to save CPU time, even though it may do that. The important part here is making the strace/ktrace output easier to read. write(4, "Q1\r", 3) = 3 nanosleep({0, 300000000}, NULL) = 0 select(5, [4], NULL, NULL, {3, 0}) = 1 (in [4], left {3, 0}) read(4, "(120.0 084.0 120.0 0 60.0 22.6"..., 64) = 47 Without that delay, that turns into a mess of selects and reads. The select returns almost instantly, and read gets a tiny chunk of the data. Add the delay and you get a nice four-line status poll. Canonical input mode processing ------------------------------- If your UPS uses "\n" and/or "\r" as endchar, consider the use of Canonical Input Mode Processing instead of the ser_get_line* functions. Using a serial port in this mode means that select() will wait until a full line is received (or times out). This relieves you from waiting between sending a command and reading the reply. Another benefit is, that you no longer have to worry about the case that your UPS sends "OK\n1234\nabcd\n". This will be broken up cleanly in "OK\n", "1234\n" and "abcd\n" on consecutive reads, without risk of losing data (which is an often forgotten side effect of the ser_get_line* functions). Currently, an example how this works can be found in the safenet and upscode2 drivers. The first uses a single "\r" as endchar, while the latter accepts either "\n", "\n\r" or "\r\n" as line termination. You can define other termination characters as well, but can't undefine "\r" and "\n" (so if you need these as data, this is not for you). Adding the driver into the tree ------------------------------- In order to build your new driver, it needs to be added to `drivers/Makefile.am`. At the moment, there are several driver list variables corresponding to the general protocol of the driver (`SERIAL_DRIVERLIST`, `SNMP_DRIVERLIST`, etc.). If your driver does not fit into one of these categories, please discuss it on the nut-upsdev mailing list. There are also `*_SOURCES` and optional `*_LDADD` variables to list the source files, and any additional linker flags. If your driver uses the C math library, be sure to add `-lm`, since this flag is not always included by default on embedded systems. When you add a driver to one of these lists, pay attention to the backslash continuation characters (`\\`) at the end of the lines. The `automake` program converts the `Makefile.am` files into `Makefile.in` files to be processed by `./configure`. See the discussion in <> about automating the rebuild process for these files. [[contact-closure]] include::contact-closure.txt[] [[hid-subdrivers]] include::hid-subdrivers.txt[] [[snmp-subdrivers]] include::snmp-subdrivers.txt[] [[nutdrv_qx-subdrivers]] include::nutdrv_qx-subdrivers.txt[] nut-2.7.4/docs/macros.txt0000644000175000017500000000505112640473702012236 00000000000000NUT-specific autoconf macros ---------------------------- The following NUT-specific autoconf macros are defined in the m4/ directory. - NUT_TYPE_SOCKLEN_T - NUT_TYPE_UINT8_T - NUT_TYPE_UINT16_T Check for the corresponding type in the system header files, and #define a replacement if necessary. - NUT_CHECK_LIBGD - NUT_CHECK_LIBNEON - NUT_CHECK_LIBNETSNMP - NUT_CHECK_LIBPOWERMAN - NUT_CHECK_LIBOPENSSL - NUT_CHECK_LIBNSS - NUT_CHECK_LIBUSB - NUT_CHECK_LIBWRAP Determine the compiler flags for the corresponding library. On success, set nut_have_libxxx="yes" and set LIBXXX_CFLAGS and LIBXXX_LDFLAGS. On failure, set nut_have_libxxx="no". This macro can be run multiple times, but will do the checking only once. Here "xxx" should of course be replaced by the respective library name. The checks for each library grow organically to compensate for various bugs in the libraries, pkg-config, etc. This is why we have a separate macro for each library. - NUT_CHECK_IPV6 Check for various features required to compile the IPv6 support. dnl Check for various features required for IPv6 support. Define a preprocessor symbol for each individual feature (HAVE_GETADDRINFO, HAVE_FREEADDRINFO, HAVE_STRUCT_ADDRINFO, HAVE_SOCKADDR_STORAGE, SOCKADDR_IN6, IN6_ADDR, HAVE_IN6_IS_ADDR_V4MAPPED, HAVE_AI_ADDRCONFIG). Also set the shell variable nut_have_ipv6=yes if all the required features are present. Set nut_have_ipv6=no otherwise. - NUT_CHECK_OS Check for the exact system name and type. This was only used in the past to determine the packaging rule to be used through the OS_NAME variable, but may be useful for other purposes in the future. - NUT_REPORT_FEATURE(FEATURE, VALUE, VARIABLE, DESCRIPTION) Schedule a line for the end-of-configuration feature summary. The FEATURE is a descriptive string such that the sentence "Checking whether to FEATURE" makes sense, and VALUE describes the decision taken (typically yes or no). The feature is also reported to the terminal. Also use VARIABLE and DESCRIPTION for defining AM_CONDITIONAL and AC_DEFINE (only if VALUE = "yes"). VARIABLE is of the form 'WITH_'. - NUT_REPORT(FEATURE, VALUE) Schedule a line for the end-of-configuration feature summary, without printing anything to the terminal immediately. - NUT_PRINT_FEATURE_REPORT Print out a list of the features that have been reported by previous NUT_REPORT_FEATURE macro calls. - NUT_ARG_WITH(FEATURE, DESCRIPTION, DEFAULT) Declare a simple --with-FEATURE option with the given DESCRIPTION and DEFAULT. Sets the variable nut_with_FEATURE. nut-2.7.4/docs/outlets.txt0000644000175000017500000000665412640443572012465 00000000000000NUT outlets management and PDU notes ==================================== NUT supports advanced outlets management for any kind of device that proposes it. This chapter introduces how to manage outlets in general, and how to take advantage of the provided features. Introduction ------------ Outlets are the core of Power Distribution Units. They allow you to turn on, turn off or cycle the load on each outlet. Some UPS models also provide manageable outlets (Eaton, MGE, Powerware, Tripplite, ...) that help save power in various ways, and manage loads more intelligently. Finally, some devices can be managed in a PDU-like way. Consider blade systems: the blade chassis can be controlled remotely to turn on, turn off or cycle the power on individual blade servers. NUT allows you to control all these devices! NUT outlet data collection -------------------------- NUT provides a complete and uniform integration of outlets related data, through the 'outlet' collection. First, there is a special outlet, called 'main outlet'. You can access it through 'outlet.{id, desc, ...}' without any index. Any modification through the 'main outlet' will affect *all* outlets. For example, calling the command 'outlet.load.cycle' will cycle all outlets. Next, outlets index starts from *1*. Index '0' is implicitly reserved to the 'main outlet'. So the first outlet is 'outlet.1.*'. For a complete list of outlet data and commands, refer to the <>. An example upsc output (data/epdu-managed.dev) is available in the source archive. NOTE: The variables supported depend on the exact device type. Outlets on PDU -------------- Smart Power Distribution Units provide at least various meters, related to current, power and voltage. Some more advanced devices also provide control through the 'load.off', 'load.on' and 'load.cycle' commands. Outlets on UPS -------------- Some advanced Uninterruptible Power Supplies provide smart outlet management. This allows to program a limited backup time to non-critical loads in order to keep the maximum of the battery reserve for critical equipment. This also allows the same remote electrical management of devices provided by PDUs, which can be very interesting in Data Centers. For example, on small setup, you can plug printers, USB devices, hubs, (...) into managed outlets. Depending on your UPS's capabilities, you will be able to turn off those loads: - after some minutes of back-up time using 'outlet.n.delay.start', - when reaching a percentage battery charge using 'outlet.n.autoswitch.charge.low'. This will ensure a maximum runtime for the computer. On bigger systems, with bigger UPSs, this is the same thing with servers instead of small devices. NOTE: If you need the scheduling function and your device doesn't support it, you can still use <>. WARNING: don't plug the UPS's communication cable (USB or network) on a managed outlet. Otherwise, all computers will be stopped as soon as the communication is lost. Other type of devices --------------------- As mentioned in the introduction, some other devices can be considered and managed like PDUs. This is the case in most blade systems, where the blade chassis offers power management services. This way, you can control remotely each blade server as if it were a PDU outlet. This category of devices is generally called Remote Power Controls - RPC in NUT. nut-2.7.4/docs/chunked.xsl0000644000175000017500000000153012640473702012360 00000000000000 images/icons/ images/icons/ nut-2.7.4/docs/snmp.txt0000644000175000017500000000270012640443572011727 00000000000000Desc: Information about SNMP and Network UPS Tools File: snmp.txt Date: 16 March 2003 Auth: Arnaud Quette or This document presents interactions between SNMP and Network UPS Tools. This work is mostly sponsored by: MGE UPS SYSTEMS There are two part in NUT for SNMP support: 1) the agent or server side An SNMP agent provides information for its hardware. In general, agents are embedded in SNMP card. This part has to be implemented as a Net-SNMP sub agent, acting as a NUT middleware (upsd level). Such software make a bridge between NUT data and SNMP, thus allowing to monitor, through SNMP, standard point to point UPSs (serial and USB). This is part of the "UPS Sub-Agent" project with Net-SNMP team. 2) the client side * Introduction An SNMP client communicates with an agent to acquire data. This part, which has to be a NUT driver, is implemented by snmp-ups. snmp-ups acts as a bridge from SNMP to NUT, thus allowing to monitor in NUT any SNMP Agent (embedded or on a host). The new snmp-ups driver now support multiple MIBS (SNMP structures declaration). For more information on snmp-ups, have a look at its manpage (man 8 snmp-ups). * Extending existing mib2nut information To be written... * Adding new mib2nut information To be written... References: - NUT SNMP Protocols Library Available at: http://random.networkupstools.org/protocols/snmp/ nut-2.7.4/docs/download.txt0000644000175000017500000001233012667537407012573 00000000000000Download information ==================== This section presents the different methods to download NUT. Source code ----------- [NOTE] ================================================================================ You should always use PGP/GPG to verify the signatures before using any source code. You can use the ifdef::website[] link:docs/user-manual.chunked/ar01s09.html#verifySourceSig[following procedure] endif::website[] ifndef::website[] <>. endif::website[] to do so. ================================================================================ Stable tree: {tree_version} ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz[nut-{revision}.tar.gz] - link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.sig[PGP/GPG signature] - link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.sha256[SHA-256 sum] - link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.md5[MD5 sum] - link:http://www.networkupstools.org/source/{tree_version}/new-{revision}.txt[Release notes] - link:http://www.networkupstools.org/source/{tree_version}/ChangeLog[ChangeLog] You can also browse the link:http://www.networkupstools.org/source/{tree_version}/[stable source directory]. Development tree: ~~~~~~~~~~~~~~~~~ Code repository ^^^^^^^^^^^^^^^ The development tree is available through a Git repository hosted at link:https://github.com/[GitHub]. To retrieve the current development tree, use the following command: $ git clone git://github.com/networkupstools/nut.git The configure script and its dependencies are not stored in Git. To generate them, ensure that autoconf, automake and libtool are installed, then run the following script in the directory you just checked out: $ ./autogen.sh Then refer to the ifdef::website[] link:docs/user-manual.chunked/index.html[NUT user manual] endif::website[] ifndef::website[] linkdoc:user-manual[NUT user manual] endif::website[] for more information. ////////////////////////// NOTE: Users that need the latest developments to support new devices *must* use Git or <>. ////////////////////////// Browse code ^^^^^^^^^^^ You can also browse the code at link:https://github.com/networkupstools/nut[GitHub], or at the link:http://alioth.debian.org/scm/?group_id=30602[Alioth mirror]. The code was originally kept in Subversion, and the old link:http://trac.networkupstools.org/projects/nut[Trac site] will be kept around for a bit so as not to break the URLs in the mailing list archives. [[Snapshots]] Snapshots ^^^^^^^^^ GitHub has several download links for repository snapshots (for particular tags or branches), but you will need a number of tools such as autoconf, automake and libtool to use these snapshots. If our Buildbot instance is behaving, you can download a snapshot which does not require auto* tools from this link:http://buildbot.networkupstools.org/snapshots[builder]. Look for the latest *[tarball]* link towards the top of the page, and be sure to check the 'Build ##' link to verify the branch name. Older versions ~~~~~~~~~~~~~~ link:http://www.networkupstools.org/source/[Browse source directory] Binary packages --------------- NOTE: The only official releases from this project are source code. NUT is already available in the following systems: - Linux: link:https://aur.archlinux.org/packages/network-ups-tools[Arch Linux], link:http://packages.debian.org/nut[Debian], link:http://packages.gentoo.org/package/sys-power/nut[Gentoo Linux], Mandriva, link:https://apps.fedoraproject.org/packages/nut[Red Hat / Fedora], link:http://software.opensuse.org/package/nut[Novell Suse / openSUSE], link:https://forum.openwrt.org/viewtopic.php?id=26269[OpenWrt], link:http://packages.ubuntu.com/nut[Ubuntu], link:https://github.com/voidlinux/xbps-packages/blob/master/srcpkgs/network-ups-tools/template[Void Linux]. - BSD systems: link:http://www.FreeBSD.org/cgi/ports.cgi?query=^nut-&stype=name[FreeBSD], link:http://pkgsrc.se/sysutils/ups-nut[NetBSD], link:http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/nut/[OpenBSD], link:http://doc.freenas.org/9.3/freenas_services.html#ups[FreeNAS]. - Mac OS X: link:http://pdb.finkproject.org/pdb/package.php/nut[Fink], link:http://trac.macports.org/browser/trunk/dports/sysutils/nut/Portfile[MacPorts] - Windows (complete port, Beta): link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-6.msi[Windows MSI installer 2.6.5-6] Java packages ------------- The jNut package has been split into its own link:https://github.com/networkupstools/jNut[GitHub repository]. - NUT Java support (client side, Beta) link:http://www.networkupstools.org/package/java/jNut-0.2-SNAPSHOT.tar.gz[jNUT 0.2-SNAPSHOT] - NUT Java Web support (client side using REST, Beta) link:http://www.networkupstools.org/package/java/jNutWebAPI-0.2-SNAPSHOT-src.tar.gz[jNutWebAPI 0.2-SNAPSHOT (sources)] Virtualization packages ----------------------- VMware ~~~~~~ - NUT client 2.7.2 for ESXi 5.x (offsite, René Garcia) * link:http://rene.margar.fr/2012/05/client-nut-pour-esxi-5-0/[blog entry (French)] * link:http://rene.margar.fr/downloads/NutClient-ESXi500-1.3.0.tar.gz[VIB package (v1.3.0)] nut-2.7.4/docs/Makefile.am0000644000175000017500000001171012667574772012266 00000000000000IMAGE_FILES = images/asciidoc.png \ images/hostedby.png \ images/nut_layering.png \ images/nut-logo.png \ images/note.png \ images/warning.png \ images/blue-arrow.png \ images/simple.png \ images/advanced.png \ images/bigbox.png \ images/bizarre.png \ images/old-cgi.png # Only track here the local deps SHARED_DEPS = nut-names.txt asciidoc.conf USER_MANUAL_DEPS = acknowledgements.txt cables.txt config-notes.txt \ configure.txt download.txt documentation.txt features.txt history.txt \ outlets.txt scheduling.txt security.txt support.txt user-manual.txt DEVELOPER_GUIDE_DEPS = contact-closure.txt design.txt developers.txt \ developer-guide.txt hid-subdrivers.txt macros.txt new-clients.txt \ new-drivers.txt net-protocol.txt nutdrv_qx-subdrivers.txt \ snmp-subdrivers.txt sock-protocol.txt CABLES_DEPS = cables/apc-rs500-serial.txt \ cables/apc.txt cables/ge-imv-victron.txt cables/imv.txt \ cables/mgeups.txt cables/powerware.txt cables/repotec.txt \ cables/sms.txt CABLES_IMAGES = images/cables/73-0724.png images/cables/940-0024C.jpg \ images/cables/belkin-f6cx-rkm-xu-cable.jpg images/cables/Lansafecable.jpg \ images/cables/mac-940-0024C.png images/cables/mge-66049.png \ images/cables/mge-db9-rj12.jpg images/cables/mge-db9-rj45.jpg \ images/cables/mge-usb-rj45.jpg \ images/cables/SOLA-330.png ALL_TXT_SRC = nut-names.txt $(USER_MANUAL_DEPS) $(DEVELOPER_GUIDE_DEPS) \ $(CABLES_DEPS) FAQ.txt nut-qa.txt packager-guide.txt snmp.txt NUT_SPELL_DICT = nut.dict EXTRA_DIST = $(ALL_TXT_SRC) $(SHARED_DEPS) $(IMAGE_FILES) \ $(CABLES_IMAGES) $(NUT_SPELL_DICT) \ common.xsl xhtml.xsl chunked.xsl ASCIIDOC_HTML_SINGLE = user-manual.html \ developer-guide.html \ packager-guide.html \ FAQ.html ASCIIDOC_HTML_CHUNKED = user-manual.chunked \ developer-guide.chunked \ packager-guide.chunked \ FAQ.html ASCIIDOC_PDF = user-manual.pdf \ developer-guide.pdf \ packager-guide.pdf \ cables.pdf \ FAQ.pdf SUBDIRS = man SUFFIXES = .txt .html .pdf all: doc doc: @DOC_BUILD_LIST@ pdf: $(ASCIIDOC_PDF) # also build the HTML manpages with these targets html-single: $(ASCIIDOC_HTML_SINGLE) html-chunked: $(ASCIIDOC_HTML_CHUNKED) clean-local: rm -rf *.pdf *.html *.chunked docbook-xsl.css *.bak ### TODO: automatic dependency generation # Add other directory deps (not for local EXTRA_DIST) and generated contents FULL_USER_MANUAL_DEPS = $(USER_MANUAL_DEPS) $(SHARED_DEPS) ../README \ ../INSTALL.nut ../UPGRADING ../TODO ../scripts/ufw/README FULL_DEVELOPER_GUIDE_DEPS = $(DEVELOPER_GUIDE_DEPS) $(SHARED_DEPS) \ ../scripts/augeas/README ../TODO ../lib/README \ ../tools/nut-scanner/README user-manual.html user-manual.chunked user-manual.pdf: $(FULL_USER_MANUAL_DEPS) developer-guide.html developer-guide.chunked developer-guide.pdf: $(FULL_DEVELOPER_GUIDE_DEPS) packager-guide.html packager-guide.chunked packager-guide.pdf: packager-guide.txt asciidoc.conf # Note: without the "-v", asciidoc (circa 8.6.2) sometimes hangs when # generating the chunked HTML. In this case, export the environment # variable ASCIIDOC_VERBOSE to "-v", ie: # $ ASCIIDOC_VERBOSE=-v make A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) --attribute icons \ --xsltproc-opts "--nonet" \ --xsltproc-opts "--stringparam nut.localdate \"`TZ=UTC date +%Y-%m-%d`\"" \ --xsltproc-opts "--stringparam nut.localtime \"`TZ=UTC date +%H:%M:%S`\"" \ --xsltproc-opts "--stringparam nut.nutversion \"@PACKAGE_VERSION@\"" \ --attribute iconsdir=$(srcdir)/images \ --attribute=badges \ --attribute=external_title \ --attribute tree_version=@TREE_VERSION@ \ -a toc -a numbered --destination-dir=. .txt.html: common.xsl xhtml.xsl $(A2X) $(A2X_COMMON_OPTS) --attribute=xhtml11_format --format=xhtml --xsl-file=$(srcdir)/xhtml.xsl $< .txt.chunked: common.xsl chunked.xsl $(A2X) $(A2X_COMMON_OPTS) --attribute=chunked_format --format=chunked --xsl-file=$(srcdir)/chunked.xsl $< .txt.pdf: docinfo.xml $(A2X) $(A2X_COMMON_OPTS) --attribute=pdf_format --format=pdf -a docinfo1 $< if HAVE_ASPELL # Non-interactively spell check all documentation source files. # This is useful for Buildbot and automated QA processing # FIXME: how to present output (std{out,err}, single file or per target)? SPELLCHECK_SRC = $(ALL_TXT_SRC) ../README ../INSTALL.nut ../UPGRADING ../NEWS \ ../TODO ../scripts/ufw/README ../scripts/augeas/README ../lib/README \ ../tools/nut-scanner/README spellcheck: @for docsrc in $(SPELLCHECK_SRC); do \ echo "Spell checking on $$docsrc"; \ LANG=C $(ASPELL) -a -t -p $(NUT_SPELL_DICT) < $$docsrc | grep [^*]; \ done # Interactively spell check all documentation source files spellcheck-interactive: @for docsrc in $(SPELLCHECK_SRC); do\ echo "Spell checking on $$docsrc"; \ LANG=C $(ASPELL) check -p $(NUT_SPELL_DICT) $$docsrc; \ done else !HAVE_ASPELL spellcheck: @echo "Documentation spell check not available since 'aspell' was not found." spellcheck-interactive: @echo "Documentation spell check not available since 'aspell' was not found." endif !HAVE_ASPELL .PHONY: html html-single pdf nut-2.7.4/docs/scheduling.txt0000644000175000017500000002522312667537407013116 00000000000000Advanced usage and scheduling notes =================================== upsmon can call out to a helper script or program when the device changes state. The example upsmon.conf has a full list of which state changes are available - ONLINE, ONBATT, LOWBATT, and more. There are two options, that will be presented in details: - the simple approach: create your own helper, and manage all events and actions yourself, - the advanced approach: use the NUT provided helper, called 'upssched'. The simple approach, using your own script ------------------------------------------ How it works relative to upsmon ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Your command will be called with the full text of the message as one argument. For the default values, refer to the sample upsmon.conf file. The environment string NOTIFYTYPE will contain the type string of whatever caused this event to happen - ONLINE, ONBATT, LOWBATT, ... Making this some sort of shell script might be a good idea, but the helper can be in any programming or scripting language. NOTE: Remember that your helper must be *executable*. If you are using a script, make sure the execution flags are set. For more information, refer to linkman:upsmon[8] and linkman:upsmon.conf[5] manual pages. Setting up everything ~~~~~~~~~~~~~~~~~~~~~ - Set EXEC flags on various things in linkman:upsmon.conf[5]: + NOTIFYFLAG ONBATT EXEC NOTIFYFLAG ONLINE EXEC + If you want other things like WALL or SYSLOG to happen, just add them: + NOTIFYFLAG ONBATT EXEC+WALL+SYSLOG + You get the idea. - Tell upsmon where your script is NOTIFYCMD /path/to/my/script - Make a simple script like this at that location: #! /bin/bash echo "$*" | sendmail -F"ups@mybox" bofh@pager.example.com - Restart upsmon, pull the plug, and see what happens. That approach is bare-bones, but you should get the text content of the alert in the body of the message, since upsmon passes the alert text (from NOTIFYMSG) as an argument. Using more advanced features ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Your helper script will be run with a few environment variables set. - UPSNAME: the name of the system that generated the change. + This will be one of your identifiers from the MONITOR lines in upsmon.conf. - NOTIFYTYPE: this will be ONLINE, ONBATT, or whatever event took place which made upsmon call your script. You can use these to do different things based on which system has changed state. You could have it only send pages for an important system while totally ignoring a known trouble spot, for example. Suppressing notify storms ~~~~~~~~~~~~~~~~~~~~~~~~~ upsmon will call your script every time an event happens that has the EXEC flag set. This means a quick power failure that lasts mere seconds might generate a notification storm. To suppress this sort of annoyance, use upssched as your NOTIFYCMD program, and configure it to call your command after a timer has elapsed. The advanced approach, using upssched ------------------------------------- upssched is a helper for upsmon that will invoke commands for you at some interval relative to a UPS event. It can be used to send pages, mail out notices about things, or even shut down the box early. There will be examples scattered throughout. Change them to suit your pathnames, UPS locations, and so forth. How upssched works relative to upsmon ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When an event occurs, upsmon will call whatever you specify as a 'NOTIFYCMD' in your upsmon.conf, if you also enable the 'EXEC' in your 'NOTIFYFLAGS'. In this case, we want upsmon to call upssched as the notifier, since it will be doing all the work for us. So, in the upsmon.conf: NOTIFYCMD /usr/local/ups/sbin/upssched Then we want upsmon to actually _use_ it for the notify events, so again in the upsmon.conf we set the flags: NOTIFYFLAG ONLINE SYSLOG+EXEC NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC ... and so on. For the purposes of this document I will only use those three, but you can set the flags for any of the valid notify types. Setting up your upssched.conf ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once upsmon has been configured with the NOTIFYCMD and EXEC flags, you're ready to deal with the upssched.conf details. In this file, you specify just what will happen when a given event occurs on a particular UPS. First you need to define the name of the script or program that will handle timers that trigger. This is your CMDSCRIPT, and needs to be above any AT defines. There's an example provided with the program, so we'll use that here: CMDSCRIPT /usr/local/ups/bin/upssched-cmd Then you have to define the variables PIPEFN and LOCKFN; the former sets the file name of the FIFO that will pass communications between processes to start and stop timers, while the latter sets the file name for a temporary file created by upssched in order to avoid a race condition under some circumstances. Please see the relevant comments in upssched.conf for additional information and advice about these variables. Now you can tell your CMDSCRIPT what to do when it is called by upsmon. The big picture ^^^^^^^^^^^^^^^ The design in a nutshell is: upsmon ---> calls upssched ---> calls your CMDSCRIPT Ultimately, the CMDSCRIPT does the actual useful work, whether that's initiating an early shutdown with 'upsmon -c fsd', sending a page by calling sendmail, or opening a subspace channel to V'ger. Establishing timers ^^^^^^^^^^^^^^^^^^^ Let's say that you want to receive a page when any UPS has been running on battery for 30 seconds. Create a handler that starts a 30 second timer for an ONBATT condition. AT ONBATT * START-TIMER onbattwarn 30 This means "when any UPS (the *) goes on battery, start a timer called onbattwarn that will trigger in 30 seconds". We'll come back to the onbattwarn part in a moment. Right now we need to make sure that we don't trigger that timer if the UPS happens to come back before the time is up. In essence, if it goes back on line, we need to cancel it. So, let's tell upssched that. AT ONLINE * CANCEL-TIMER onbattwarn Executing commands immediately ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As an example, consider the scenario where a UPS goes onto battery power. However, the users are not informed until 60 seconds later - using a timer as described above. Whilst this may let the *logged in* users know that the UPS is on battery power, it does not inform any users subsequently logging in. To enable this we could, at the same time, create a file which is read and displayed to any user trying to login whilst the UPS is on battery power. If the UPS comes back onto utility power within 60 seconds, then we can cancel the timer and remove the file, as described above. However, if the UPS comes back onto utility power say 5 minutes later then we do not want to use any timers but we still want to remove the file. To do this we could use: AT ONLINE * EXECUTE ups-back-on-power This means that when upsmon detects that the UPS is back on utility power it will signal upssched. Upssched will see the above command and simply pass 'ups-back-on-power' as an argument directly to CMDSCRIPT. This occurs immediately, there are no timers involved. Writing the command script handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OK, now that upssched knows how the timers are supposed to work, let's give it something to do when one actually triggers. The name of the example timer is onbattwarn, so that's the argument that will be passed into your CMDSCRIPT when it triggers. This means we need to do some shell script writing to deal with that input. -------------------------------------------------------------------------------- #! /bin/sh case $1 in onbattwarn) echo "The UPS has been on battery for awhile" \ | mail -s"UPS monitor" bofh@pager.example.com ;; ups-back-on-power) /bin/rm -f /some/path/ups-on-battery ;; *) logger -t upssched-cmd "Unrecognized command: $1" ;; esac -------------------------------------------------------------------------------- This is a very simple script example, but it shows how you can test for the presence of a given trigger. With multiple ATs creating various timer names, you will need to test for each possibility and handle it according to your desires. NOTE: You can invoke just about anything from inside the CMDSCRIPT. It doesn't need to be a shell script, either - that's just an example. If you want to write a program that will parse argv[1] and deal with the possibilities, that will work too. Early Shutdowns ~~~~~~~~~~~~~~~ One thing that gets requested a lot is early shutdowns in upsmon. With upssched, you can now have this functionality. Just set a timer for some length of time at ONBATT which will invoke a shutdown command if it elapses. Just be sure to cancel this timer if you go back ONLINE before then. The best way to do this is to use the upsmon callback feature. You can make upsmon set the "forced shutdown" (FSD) flag on the upsd so your slave systems shut down early too. Just do something like this in your CMDSCRIPT: /usr/local/ups/sbin/upsmon -c fsd It's not a good idea to call your system's shutdown routine directly from the CMDSCRIPT, since there's no synchronization with the slave systems hooked to the same UPS. FSD is the master's way of saying "we're shutting down *now* like it or not, so you'd better get ready". Background ~~~~~~~~~~ This program was written primarily to fulfill the requests of users for the early shutdown scenario. The "outboard" design of the program (relative to upsmon) was intended to reduce the load on the average system. Most people don't have the requirement of shutting down after n seconds on battery, since the usual OB+LB testing is sufficient. This program was created separately so those people don't have to spend CPU time and RAM on something that will never be used in their environments. The design of the timer handler is also geared towards minimizing impact. It will come and go from the process list as necessary. When a new timer is started, a process will be forked to actually watch the clock and eventually start the CMDSCRIPT. When a timer triggers, it is removed from the queue. Canceling a timer will also remove it from the queue. When no timers are present in the queue, the background process exits. This means that you will only see upssched running when one of two things is happening: 1. There's a timer of some sort currently running 2. upsmon just called it, and you managed to catch the brief instance The final optimization handles the possibility of trying to cancel a timer when there's none running. If there's no process already running, there are no timers to cancel, and furthermore there is no need to start a clock-watcher. As a result, it skips that step and exits sooner. nut-2.7.4/docs/user-manual.txt0000644000175000017500000000734312667570775013232 00000000000000:titles.underlines: "__","==","--","~~","^^" Network UPS Tools User Manual _____________________________ :Author: Russell_Kroll,_Arnaud_Quette_and_Arjen_de_Korte :Author Initials: RK, AQ & ADK Introduction ============ The primary goal of the Network UPS Tools (NUT) project is to provide support for Power Devices, such as Uninterruptible Power Supplies, Power Distribution Units and Solar Controllers. NUT provides many control and monitoring <>, with a uniform control and management interface. More than 100 different manufacturers, and several thousands models are <>. This software is the combined effort of many <>. This document intend to describe how to install software support for your <> (UPS, PDU, ...), and how to use the NUT project. It is not intended to explain what are, nor distinguish the different technologies that exist. For such information, have a look at the <>. If you wish to discover how everything came together, have a look at the <>. [[Overview]] include::../README[] [[Features]] include::features.txt[] Compatibility information ------------------------- Hardware ~~~~~~~~ The current list of hardware supported by NUT can be viewed <>. Operating systems ~~~~~~~~~~~~~~~~~ This software has been reported to run on: - Linux distributions, - the BSDs, - Apple's OS X, - Sun Solaris, - SGI IRIX, - HP/UX, - Tru64 Unix, - AIX. There is also a port of the client-side monitoring to Windows called WinNUT. Windows users may be able to build it directly with Cygwin. Your system will probably run it too. You just need a good C compiler and possibly some more packages to gain access to the serial ports. Other features, such as USB / SNMP / whatever, will also need extra software installed. Success reports are welcomed to keep this list accurate. [[Download_instructions]] include::download.txt[] [[_installation_instructions]] include::../INSTALL.nut[] [[Configuration_notes]] include::config-notes.txt[] [[Advanced_usage_scheduling_notes]] include::scheduling.txt[] [[Outlets_PDU_notes]] include::outlets.txt[] [[NUT_Security]] include::security.txt[] Appendix A: Glossary ==================== This section document the various acronyms used throughout the present documentation. [template="glossary",id="terms"] ATS:: Automatic Transfer Switch. NUT:: Network UPS Tools. PDU:: Power Distribution Unit. PSU:: Power Supply Units. SCD:: Solar Controller Device. UPS:: Uninterruptible Power Supply. [[Acknowledgements]] Appendix B: Acknowledgements / Contributions ============================================ include::acknowledgements.txt[Acknowledgements / Contributions] [[nut-names]] Appendix C: NUT command and variable naming scheme ================================================== include::nut-names.txt[] [[HCL]] Appendix D: Hardware Compatibility List ======================================= Refer to the link:http://www.networkupstools.org/stable-hcl.html[online HCL]. Appendix E: Documentation ========================= include::documentation.txt[] [[Support_Request]] Appendix F: Support instructions ================================ include::support.txt[] [[Cables_information]] Appendix G: Cables information ============================== include::cables.txt[] [[Configure_options]] Appendix H: Configure options ============================= include::configure.txt[] [[Upgrading_notes]] Appendix I: Upgrading notes =========================== include::../UPGRADING[] [[Project_History]] Appendix J: Project history =========================== include::history.txt[] nut-2.7.4/drivers/0000755000175000017500000000000012670024741011014 500000000000000nut-2.7.4/drivers/usb-common.h0000644000175000017500000000747712640473702013205 00000000000000/* usb-common.h - prototypes for the common useful USB functions Copyright (C) 2008 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NUT_USB_COMMON_H #define NUT_USB_COMMON_H #include "nut_stdint.h" /* for uint16_t */ #include #include /* USB standard timeout [ms] */ #define USB_TIMEOUT 5000 /*! * USBDevice_t: Describe a USB device. This structure contains exactly * the 5 pieces of information by which a USB device identifies * itself, so it serves as a kind of "fingerprint" of the device. This * information must be matched exactly when reopening a device, and * therefore must not be "improved" or updated by a client * program. Vendor, Product, and Serial can be NULL if the * corresponding string did not exist or could not be retrieved. */ typedef struct USBDevice_s { uint16_t VendorID; /*!< Device's Vendor ID */ uint16_t ProductID; /*!< Device's Product ID */ char *Vendor; /*!< Device's Vendor Name */ char *Product; /*!< Device's Product Name */ char *Serial; /*!< Product serial number */ char *Bus; /*!< Bus name, e.g. "003" */ uint16_t bcdDevice; /*!< Device release number */ } USBDevice_t; /*! * USBDeviceMatcher_t: A "USB matcher" is a callback function that * inputs a USBDevice_t structure, and returns 1 for a match and 0 * for a non-match. Thus, a matcher provides a criterion for * selecting a USB device. The callback function further is * expected to return -1 on error with errno set, and -2 on other * errors. Matchers can be connected in a linked list via the * "next" field. */ typedef struct USBDeviceMatcher_s { int (*match_function)(USBDevice_t *device, void *privdata); void *privdata; struct USBDeviceMatcher_s *next; } USBDeviceMatcher_t; /* constructors and destructors for specific types of matchers. An exact matcher matches a specific usb_device_t structure (except for the Bus component, which is ignored). A regex matcher matches devices based on a set of regular expressions. The USBNew* functions return a matcher on success, or -1 on error with errno set. Note that the "USBFree*" functions only free the current matcher, not any others that are linked via "next" fields. */ int USBNewExactMatcher(USBDeviceMatcher_t **matcher, USBDevice_t *hd); int USBNewRegexMatcher(USBDeviceMatcher_t **matcher, char **regex, int cflags); void USBFreeExactMatcher(USBDeviceMatcher_t *matcher); void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher); /* dummy USB function and macro, inspired from the Linux kernel * this allows USB information extraction */ #define USB_DEVICE(vendorID, productID) vendorID, productID typedef struct { int vendorID; int productID; void *(*fun)(USBDevice_t *); /* handler for specific processing */ } usb_device_id_t; #define NOT_SUPPORTED 0 #define POSSIBLY_SUPPORTED 1 #define SUPPORTED 2 /* Function used to match a VendorID/ProductID pair against a list of * supported devices. Return values: * NOT_SUPPORTED (0), POSSIBLY_SUPPORTED (1) or SUPPORTED (2) */ int is_usb_device_supported(usb_device_id_t *usb_device_id_list, USBDevice_t *device); void nut_usb_addvars(void); #endif /* NUT_USB_COMMON_H */ nut-2.7.4/drivers/dstate.h0000644000175000017500000000530312640473702012374 00000000000000/* dstate.h - Network UPS Tools driver-side state management Copyright (C) 2003 Russell Kroll 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DSTATE_H_SEEN #define DSTATE_H_SEEN 1 #include "state.h" #include "attribute.h" #include "parseconf.h" #include "upshandler.h" #define DS_LISTEN_BACKLOG 16 #define DS_MAX_READ 256 /* don't read forever from upsd */ /* track client connections */ typedef struct conn_s { int fd; PCONF_CTX_t ctx; struct conn_s *prev; struct conn_s *next; } conn_t; extern struct ups_handler upsh; /* asynchronous (nonblocking) Vs synchronous (blocking) I/O * Defaults to nonblocking, for backward compatibility */ extern int do_synchronous; void dstate_init(const char *prog, const char *devname); int dstate_poll_fds(struct timeval timeout, int extrafd); int dstate_setinfo(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int dstate_addenum(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int dstate_addrange(const char *var, const int min, const int max); void dstate_setflags(const char *var, int flags); void dstate_setaux(const char *var, int aux); const char *dstate_getinfo(const char *var); void dstate_addcmd(const char *cmdname); int dstate_delinfo(const char *var); int dstate_delenum(const char *var, const char *val); int dstate_delrange(const char *var, const int min, const int max); int dstate_delcmd(const char *cmd); void dstate_free(void); const st_tree_t *dstate_getroot(void); const cmdlist_t *dstate_getcmdlist(void); void dstate_dataok(void); void dstate_datastale(void); int dstate_is_stale(void); /* clean out the temp space for a new pass */ void status_init(void); /* add a status element */ void status_set(const char *buf); /* write the temporary status_buf into ups.status */ void status_commit(void); /* similar functions for ups.alarm */ void alarm_init(void); void alarm_set(const char *buf); void alarm_commit(void); #endif /* DSTATE_H_SEEN */ nut-2.7.4/drivers/bestuferrups.c0000644000175000017500000003316112640444140013631 00000000000000/* bestuferrups.c - model specific routines for Best Power Micro-Ferrups This module is a 40% rewritten mangle of the bestfort module by Grant, which is a 75% rewritten mangle of the bestups module by Russell. It has no test battery command since my ME3100 does this by itself. (same as Grant's driver in this respect) Support for model RE added by Tim Thompson (7/22/04) Copyright (C) 2002 Andreas Wrede Copyright (C) 2000 John Stone Copyright (C) 2000 Grant Taylor Copyright (C) 1999 Russell Kroll 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 "main.h" #include "serial.h" #define DRIVER_NAME "Best Ferrups Series ME/RE/MD driver" #define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Andreas Wrede \n" \ "John Stone \n" \ "Grant Taylor \n" \ "Russell Kroll \n" \ "Tim Thompson", DRV_BETA, /* FIXME: STABLE? */ { NULL } }; #define ENDCHAR '\r' #define IGNCHARS "\012" /* UPS Model Codes */ #define UNKNOWN 100 #define ME3100 200 #define MD1KVA 300 /* Software version P5.05 dated 05/18/89 */ #define RE1800 400 #include #include #include int debugging = 0; /* Blob of UPS configuration data from the formatconfig string */ struct { int valid; /* set to 1 when this is filled in */ float idealbvolts; /* various interestin battery voltages */ float fullvolts; float emptyvolts; int va; /* capacity of UPS in Volt-Amps */ int watts; /* capacity of UPS in watts */ int model; /* enumerated model type */ } fc; /* Forward decls */ /* Set up all the funky shared memory stuff used to communicate with upsd */ void upsdrv_initinfo (void) { /* now set up room for all future variables that are supported */ dstate_setinfo("ups.mfr", "%s", "Best Power"); switch(fc.model) { case ME3100: dstate_setinfo("ups.model", "Micro Ferrups (ME) %d", fc.va); break; case MD1KVA: dstate_setinfo("ups.model", "Micro Ferrups (MD) %d", fc.va); break; case RE1800: dstate_setinfo("ups.model", "Micro Ferrups (RE) %d", fc.va); break; default: fatalx(EXIT_FAILURE, "UPS model not matched!"); /* Will never get here, upsdrv_initups() will catch */ } fprintf(stderr, "Best Power %s detected\n", dstate_getinfo("ups.model")); fprintf(stderr, "Battery voltages %5.1f nominal, %5.1f full, %5.1f empty\n", fc.idealbvolts, fc.fullvolts, fc.emptyvolts); } /* Debugging display from kermit: ---------------------------------------------------- time^M^M^JFeb 20, 22:13:32^M^J^M^J=>id^M^JUnit ID "ME3.1K12345"^M^J^M^J=> ---------------------------------------------------- */ static int execute(const char *cmd, char *result, int resultsize) { int ret; char buf[256]; ser_send(upsfd, "%s", cmd); ser_get_line(upsfd, buf, sizeof(buf), '\012', "", 3, 0); ret = ser_get_line(upsfd, result, resultsize, '\015', "\012", 3, 0); ser_get_line(upsfd, buf, sizeof(buf), '>', "", 3, 0); return ret; } void upsdrv_updateinfo(void) { char fstring[512]; if (! fc.valid) { fprintf(stderr, "upsupdate run before ups_ident() read ups config\n"); assert(0); } if (execute("f\r", fstring, sizeof(fstring)) > 0) { int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, alstat=0, vaout=0; double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0, hstemp=0.0, acfreq=0.0, ambtemp=0.0; char tmp[16]; /* Inverter status. 0=off 1=on */ memcpy(tmp, fstring+16, 2); tmp[2] = '\0'; inverter = atoi(tmp); /* Charger status. 0=off 1=on */ memcpy(tmp, fstring+18, 2); tmp[2] = '\0'; charger = atoi(tmp); /* Input Voltage. integer number */ memcpy(tmp, fstring+24, 4); tmp[4] = '\0'; vin = atoi(tmp); /* Output Voltage. integer number */ memcpy(tmp, fstring+28, 4); tmp[4] = '\0'; vout = atoi(tmp); /* Iout. int times 10 */ memcpy(tmp, fstring+36, 4); tmp[4] = '\0'; ampsout = ((double)(atoi(tmp)) / 10.0); /* Battery voltage. int times 10 */ memcpy(tmp, fstring+50, 4); tmp[4] = '\0'; vbatt = ((double)(atoi(tmp)) / 10.0); /* Volt-amps out. int */ memcpy(tmp, fstring+40, 6); tmp[6] = '\0'; vaout = atoi(tmp); /* Line status. Bitmask */ memcpy(tmp, fstring+72, 2); tmp[2] = '\0'; linestat = atoi(tmp); /* Alarm status reg 1. Bitmask */ memcpy(tmp, fstring+20, 2); tmp[2] = '\0'; alstat = atoi(tmp); /* Alarm status reg 2. Bitmask */ memcpy(tmp, fstring+22, 2); tmp[2] = '\0'; alstat = alstat | (atoi(tmp) << 8); /* AC line frequency */ memcpy(tmp, fstring+54, 4); tmp[4]= '\0'; acfreq = ((double)(atoi(tmp)) / 100.0); /* Runtime remaining */ memcpy(tmp, fstring+58, 4); tmp[4]= '\0'; btimeleft = atoi(tmp); /* UPS Temperature */ memcpy(tmp, fstring+62, 4); tmp[4]= '\0'; ambtemp = (double)(atoi(tmp)); /* Percent Load */ switch(fc.model) { case ME3100: if (execute("d 16\r", fstring, sizeof(fstring)) > 0) { int l; sscanf(fstring, "16 FullLoad%% %d", &l); loadpercent = (double) l; } break; case RE1800: if (execute("d 16\r", fstring, sizeof(fstring)) > 0) { int l; sscanf(fstring, "16 FullLoad%% %d", &l); loadpercent = (double) l; } if (execute("d 12\r", fstring, sizeof(fstring)) > 0) { int l; sscanf(fstring, "12 HS Temp %dC", &l); hstemp = (double) l; } break; case MD1KVA: if (execute("d 22\r", fstring, sizeof(fstring)) > 0) { int l; sscanf(fstring, "22 FullLoad%% %d", &l); loadpercent = (double) l; } break; default: /* Will never happen, caught in upsdrv_initups() */ fatalx(EXIT_FAILURE, "Unknown model in upsdrv_updateinfo()"); } /* Compute battery percent left based on battery voltages. */ battpercent = ((vbatt - fc.emptyvolts) / (fc.fullvolts - fc.emptyvolts) * 100.0); if (battpercent < 0.0) battpercent = 0.0; else if (battpercent > 100.0) battpercent = 100.0; /* Compute status string */ { int lowbatt, overload, replacebatt, boosting, trimming; lowbatt = alstat & (1<<1); overload = alstat & (1<<6); replacebatt = alstat & (1<<10); boosting = inverter && (linestat & (1<<2)) && (vin < 115); trimming = inverter && (linestat & (1<<2)) && (vin > 115); status_init(); if (inverter) status_set("OB"); else status_set("OL"); if (lowbatt) status_set("LB"); if (trimming) status_set("TRIM"); if (boosting) status_set("BOOST"); if (replacebatt) status_set("RB"); if (overload) status_set("OVER"); status_commit(); } if (debugging) { fprintf(stderr, "Poll: inverter %d charger %d vin %d vout %d vaout %d btimeleft %d\n", inverter, charger, vin, vout, vaout, btimeleft); fprintf(stderr, " ampsout %5.1f vbatt %5.1f batpcnt %5.1f loadpcnt %5.1f upstemp %5.1f acfreq %5.2f ambtemp %5.1f\n", ampsout, vbatt, battpercent, loadpercent, hstemp, acfreq, ambtemp); } /* Stuff information into info structures */ dstate_setinfo("input.voltage", "%05.1f", (double)vin); dstate_setinfo("output.voltage", "%05.1f", (double)vout); dstate_setinfo("battery.charge", "%02.1f", battpercent); dstate_setinfo("ups.load", "%02.1f", loadpercent); dstate_setinfo("battery.voltage", "%02.1f", vbatt); dstate_setinfo("input.frequency", "%05.2f", (double)acfreq); dstate_setinfo("ups.temperature", "%05.1f", (double)hstemp); dstate_setinfo("battery.runtime", "%d", btimeleft); dstate_setinfo("ambient.temperature", "%05.1f", (double)ambtemp); dstate_dataok(); /* Tim: With out this return, it always falls over to the datastate() at the end of the function */ return; } else { dstate_datastale(); } /* if (execute("f\r", fstring, sizeof(fstring)) > 0) */ dstate_datastale(); return; } static void ups_sync(void) { char buf[256]; printf ("Syncing: "); fflush (stdout); /* A bit better sanity might be good here. As is, we expect the human to observe the time being totally not a time. */ if (execute("time\r", buf, sizeof(buf)) > 0) { fprintf(stderr, "UPS Time: %s\n", buf); } else { fatalx(EXIT_FAILURE, "Error connecting to UPS"); } } /* power down the attached load immediately */ void upsdrv_shutdown(void) { /* NB: hard-wired password */ ser_send(upsfd, "pw377\r"); ser_send(upsfd, "off 1 a\r"); /* power off in 1 second and restart when line power returns */ } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { } void upsdrv_help(void) { } static void sync_serial(void) { char buffer[10]; ser_send(upsfd, "\r"); ser_get_line(upsfd, buffer, sizeof(buffer), '\r', "\012", 3, 0); ser_get_line(upsfd, buffer, sizeof(buffer), ENDCHAR, IGNCHARS, 3, 0); while (ser_get_line(upsfd, buffer, sizeof(buffer), '>', "\012", 3, 0) <= 0) { ser_send(upsfd, "\r"); } } /* Begin code stolen from bestups.c */ static void setup_serial(void) { struct termios tio; if (tcgetattr(upsfd, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcgetattr"); tio.c_iflag = IXON | IXOFF; tio.c_oflag = 0; tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); tio.c_lflag = 0; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; #ifdef HAVE_CFSETISPEED cfsetispeed(&tio, B1200); /* baud change here */ cfsetospeed(&tio, B1200); #else #error This system lacks cfsetispeed() and has no other means to set the speed #endif if (tcsetattr(upsfd, TCSANOW, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcsetattr"); /* end code stolen from bestups.c */ sync_serial(); } void upsdrv_initups () { char temp[256], fcstring[512]; upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); setup_serial(); ups_sync(); fc.model = UNKNOWN; /* Obtain Model */ if (execute("id\r", fcstring, sizeof(fcstring)) < 1) { fatalx(EXIT_FAILURE, "Failed execute in ups_ident()"); } /* response is a one-line packed string starting with $ */ if (memcmp(fcstring, "Unit", 4)) { fatalx(EXIT_FAILURE, "Bad response from formatconfig command in ups_ident()\n" "id: %s\n", fcstring ); } if (debugging) fprintf(stderr, "id: %s\n", fcstring); /* chars 4:2 are a two-digit ascii hex enumerated model code */ memcpy(temp, fcstring+9, 2); temp[2] = '\0'; if (memcmp(temp, "ME", 2) == 0) { fc.model = ME3100; } else if ((memcmp(temp, "RE", 2) == 0)) { fc.model = RE1800; } else if (memcmp(temp, "C1", 2) == 0) { /* Better way to identify unit is using "d 15\r", which results in "15 M# MD1KVA", "id\r" yields "Unit ID "C1K03588"" */ fc.model = MD1KVA; } switch(fc.model) { case ME3100: fc.va = 3100; fc.watts = 2200; /* determine shutdown battery voltage */ if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) { sscanf(fcstring, "29 LowBat %f", &fc.emptyvolts); } /* determine fully charged battery voltage */ if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) { sscanf(fcstring, "31 HiBatt %f", &fc.fullvolts); } fc.fullvolts = 54.20; /* determine "ideal" voltage by a guess */ fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; break; case RE1800: fc.va = 1800; fc.watts = 1200; /* determine shutdown battery voltage */ if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) { sscanf(fcstring, "29 LowBat %f", &fc.emptyvolts); } /* determine fully charged battery voltage */ if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) { sscanf(fcstring, "31 HiBatt %f", &fc.fullvolts); } fc.fullvolts = 54.20; /* determine "ideal" voltage by a guess */ fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; break; case MD1KVA: fc.va = 1100; fc.watts = 770; /* Approximate, based on 0.7 power factor */ /* determine shutdown battery voltage */ if (execute("d 27\r", fcstring, sizeof(fcstring)) > 0) { sscanf(fcstring, "27 LowBatt %f", &fc.emptyvolts); } /* determine fully charged battery voltage */ if (execute("d 28\r", fcstring, sizeof(fcstring)) > 0) { sscanf(fcstring, "28 Hi Batt %f", &fc.fullvolts); } fc.fullvolts = 13.70; /* determine "ideal" voltage by a guess */ fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; break; default: fatalx(EXIT_FAILURE, "Uknown model %s in ups_ident()", temp); } fc.valid = 1; return; } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/nutdrv_qx_megatec.c0000644000175000017500000001471712640473702014633 00000000000000/* nutdrv_qx_megatec.c - Subdriver for Megatec protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_megatec.h" #define MEGATEC_VERSION "Megatec 0.06" /* qx2nut lookup table */ static item_t megatec_qx2nut[] = { /* * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* * > [I\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* Testing table */ #ifdef TESTING static testing_t megatec_testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "F\r", "#230.0 000 024.0 50.0\r", -1 }, { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, { "Q\r", "", -1 }, { "S03\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "", -1 }, { "T04\r", "", -1 }, { "TL\r", "", -1 }, { "T\r", "", -1 }, { "CT\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* Subdriver-specific initups */ static void megatec_initups(void) { blazer_initups(megatec_qx2nut); } /* Subdriver interface */ subdriver_t megatec_subdriver = { MEGATEC_VERSION, blazer_claim, megatec_qx2nut, megatec_initups, NULL, blazer_makevartable, "ACK", NULL, #ifdef TESTING megatec_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/nutdrv_qx_megatec-old.h0000644000175000017500000000201112640473702015374 00000000000000/* nutdrv_qx_megatec-old.h - Subdriver for Megatec/old protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_MEGATEC_OLD_H #define NUTDRV_QX_MEGATEC_OLD_H #include "nutdrv_qx.h" extern subdriver_t megatec_old_subdriver; #endif /* NUTDRV_QX_MEGATECH_OLD_H */ nut-2.7.4/drivers/apc-ats-mib.c0000755000175000017500000007542312667774620013231 00000000000000/* apcats-mib.c - subdriver to monitor apcats SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2016 Arnaud Quette * * Note: this subdriver was initially generated as a "stub" by the * gen-snmp-subdriver script. It must be customized! * * 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 "apc-ats-mib.h" #define APC_ATS_MIB_VERSION "0.2" #define APC_ATS_SYSOID ".1.3.6.1.4.1.318.1.3.11" static info_lkp_t ats_sensitivity_info[] = { { 1, "high" }, { 2, "low" }, { 0, NULL } }; static info_lkp_t ats_output_status_info[] = { { 1, "OFF" }, /* fail */ { 2, "OL" }, /* ok */ { 0, NULL } }; static info_lkp_t ats_outletgroups_name_info[] = { { 1, "total" }, { 2, "bank1" }, { 3, "bank2" }, { 0, NULL } }; static info_lkp_t ats_outletgroups_status_info[] = { { 1, "OL" }, /* normal */ { 2, "" }, /* lowload */ { 3, "" }, /* nearoverload */ { 4, "OVER" }, /* overload */ { 0, NULL } }; /* APC ATS Snmp2NUT lookup table */ static snmp_info_t apc_ats_mib[] = { /* Device collection */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, /* ats2IdentManufacturer.0 = STRING: EATON */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, /* atsIdentModelNumber.0 = STRING: "AP7724" */ { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.5.0", NULL, SU_FLAG_OK, NULL, NULL }, /* FIXME: RFC for device.firmware! */ /* atsIdentHardwareRev.0 = STRING: "R01" */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* FIXME: RFC for device.firmware.aux! */ /* atsIdentFirmwareRev.0 = STRING: "3.0.5" */ { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsIdentFirmwareDate.0 = STRING: "09/13/11" */ /*{ "unmapped.atsIdentFirmwareDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.3.0", NULL, SU_FLAG_OK, NULL, NULL },*/ /* atsIdentSerialNumber.0 = STRING: "5A1516T15268" */ { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.6.0", NULL, SU_FLAG_OK, NULL, NULL }, /* FIXME: RFC for device.mfr.date! */ /* atsIdentDateOfManufacture.0 = STRING: "04/18/2015" */ { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.4.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigProductName.0 = STRING: "m-ups-04" */ { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* Input collection */ /* atsIdentNominalLineVoltage.0 = INTEGER: 230 */ { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsIdentNominalLineFrequency.0 = INTEGER: 50 */ { "input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.8.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusSelectedSource.0 = INTEGER: sourceB(2) */ { "input.source", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigPreferredSource.0 = INTEGER: sourceB(2) */ { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputVoltage.1.1.1 = INTEGER: 216 */ { "input.1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputVoltage.2.1.1 = INTEGER: 215 */ { "input.2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputFrequency.1 = INTEGER: 50 */ { "input.1.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputFrequency.2 = INTEGER: 50 */ { "input.2.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigVoltageSensitivity.0 = INTEGER: high(1) */ { "input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.4.0", NULL, SU_FLAG_OK, &ats_sensitivity_info[0], NULL }, /* FIXME: RFC for input.count! */ /* atsNumInputs.0 = INTEGER: 2 */ { "input.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* Output collection */ /* atsOutputFrequency.1 = INTEGER: 50 */ { "output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.4.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankOutputVoltage.1 = INTEGER: 215 */ { "output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.1", NULL, SU_FLAG_OK, NULL, NULL }, /* UPS collection */ /* FIXME: RFC for device.status! */ /* atsStatusVoltageOutStatus.0 = INTEGER: ok(2) */ { "ups.status", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.15.0", NULL, SU_FLAG_OK, &ats_output_status_info[0], NULL }, /* Outlet groups collection */ /* Note: prefer the OutputBank data to the ConfigBank ones */ /* atsConfigBankTableSize.0 = INTEGER: 3 */ /*{ "outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.13.0", NULL, SU_FLAG_OK, NULL, NULL },*/ /* atsOutputBankTableSize.0 = INTEGER: 3 */ { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.4.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankTableIndex.%i = INTEGER: %i */ /*{ "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.14.1.1.%i", NULL, SU_FLAG_OK, NULL, NULL },*/ /* atsOutputBankTableIndex.%i = INTEGER: %i */ { "outlet.group.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.1.%i", NULL, SU_FLAG_OK | SU_OUTLET_GROUP, NULL, NULL }, /* atsConfigBank.%i = INTEGER: total(1) */ /*{ "outlet.group.%i.name", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.2.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &ats_group_name_info[0], NULL },*/ /* atsOutputBank.1 = INTEGER: total(1) */ { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.3.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &ats_outletgroups_name_info[0], NULL }, /* atsOutputBankCurrent.%i = Gauge32: 88 */ { "outlet.group.%i.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.4.%i", NULL, SU_OUTLET_GROUP, NULL, NULL }, /* atsOutputBankState.%i = INTEGER: normal(1) */ { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.5.%i", NULL, SU_OUTLET_GROUP, &ats_outletgroups_status_info[0], NULL }, /* atsOutputBankOutputVoltage.%i = INTEGER: 215 */ { "outlet.group.%i.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.%i", NULL, SU_OUTLET_GROUP, NULL, NULL }, /* atsOutputBankPower.1 = INTEGER: 1883 */ { "outlet.group.%i.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.15.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, #if 0 /* FIXME: Remaining data to be processed */ /* atsIdentDeviceRating.0 = INTEGER: 32 */ { "unmapped.atsIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.9.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationNumInputs.0 = INTEGER: 2 */ { "unmapped.atsCalibrationNumInputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationNumInputPhases.0 = INTEGER: 1 */ { "unmapped.atsCalibrationNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationInputTableIndex.1.1.1 = INTEGER: 1 */ { "unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationInputTableIndex.2.1.1 = INTEGER: 2 */ { "unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationInputPhaseTableIndex.1.1.1 = INTEGER: 1 */ { "unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationInputPhaseTableIndex.2.1.1 = INTEGER: 1 */ { "unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsLineVoltageCalibrationFactor.1.1.1 = INTEGER: 487 */ { "unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsLineVoltageCalibrationFactor.2.1.1 = INTEGER: 488 */ { "unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltages.0 = INTEGER: 5 */ { "unmapped.atsCalibrationPowerSupplyVoltages", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltageTableIndex.1 = INTEGER: 1 */ { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltageTableIndex.2 = INTEGER: 2 */ { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltageTableIndex.3 = INTEGER: 3 */ { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltageTableIndex.4 = INTEGER: 4 */ { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.4", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltageTableIndex.5 = INTEGER: 5 */ { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.5", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltage.1 = INTEGER: powerSupply24V(1) */ { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltage.2 = INTEGER: powerSupply12V(2) */ { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltage.3 = INTEGER: powerSupply(3) */ { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltage.4 = INTEGER: powerSupply24VSourceB(4) */ { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.4", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationPowerSupplyVoltage.5 = INTEGER: powerSupplyMinus12V(5) */ { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.5", NULL, SU_FLAG_OK, NULL, NULL }, /* atsPowerSupplyVoltageCalibrationFactor.1 = INTEGER: 521 */ { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsPowerSupplyVoltageCalibrationFactor.2 = INTEGER: 1076 */ { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsPowerSupplyVoltageCalibrationFactor.3 = INTEGER: 2560 */ { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsPowerSupplyVoltageCalibrationFactor.4 = INTEGER: 521 */ { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.4", NULL, SU_FLAG_OK, NULL, NULL }, /* atsPowerSupplyVoltageCalibrationFactor.5 = INTEGER: 975 */ { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.5", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationNumOutputs.0 = INTEGER: 1 */ { "unmapped.atsCalibrationNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationNumOutputPhases.0 = INTEGER: 1 */ { "unmapped.atsCalibrationNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationOutputTableIndex.1.phase1.1 = INTEGER: 1 */ { "unmapped.atsCalibrationOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsCalibrationOutputPhasesTableIndex.1.phase1.1 = INTEGER: phase1(1) */ { "unmapped.atsCalibrationOutputPhasesTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputCurrentCalibrationFactor.1.phase1.1 = INTEGER: 487 */ { "unmapped.atsOutputCurrentCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsControlResetATS.0 = INTEGER: none(1) */ { "unmapped.atsControlResetATS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsControlClearAllAlarms.0 = INTEGER: -1 */ { "unmapped.atsControlClearAllAlarms", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigFrontPanelLockout.0 = INTEGER: enableFrontPanel(2) */ { "unmapped.atsConfigFrontPanelLockout", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.3.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigTransferVoltageRange.0 = INTEGER: medium(2) */ { "unmapped.atsConfigTransferVoltageRange", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.5.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigCurrentLimit.0 = INTEGER: 32 */ { "unmapped.atsConfigCurrentLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.6.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigResetValues.0 = INTEGER: -1 */ { "unmapped.atsConfigResetValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigLineVRMS.0 = INTEGER: 230 */ { "unmapped.atsConfigLineVRMS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.8.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigLineVRMSNarrowLimit.0 = INTEGER: 16 */ { "unmapped.atsConfigLineVRMSNarrowLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.9.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigLineVRMSMediumLimit.0 = INTEGER: 23 */ { "unmapped.atsConfigLineVRMSMediumLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.10.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigLineVRMSWideLimit.0 = INTEGER: 30 */ { "unmapped.atsConfigLineVRMSWideLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.11.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigFrequencyDeviation.0 = INTEGER: two(2) */ { "unmapped.atsConfigFrequencyDeviation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.12.0", NULL, SU_FLAG_OK, NULL, NULL }, /* Outlet groups collection */ /* atsConfigBankLowLoadThreshold.1 = INTEGER: 0 */ { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankLowLoadThreshold.2 = INTEGER: 0 */ { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankLowLoadThreshold.3 = INTEGER: 0 */ { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankNearOverLoadThreshold.1 = INTEGER: 28 */ { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankNearOverLoadThreshold.2 = INTEGER: 12 */ { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankNearOverLoadThreshold.3 = INTEGER: 12 */ { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankOverLoadThreshold.1 = INTEGER: 32 */ { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankOverLoadThreshold.2 = INTEGER: 16 */ { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigBankOverLoadThreshold.3 = INTEGER: 16 */ { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsConfigPhaseTableSize.0 = INTEGER: 0 */ { "unmapped.atsConfigPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.15.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusCommStatus.0 = INTEGER: atsCommEstablished(2) */ { "unmapped.atsStatusCommStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusRedundancyState.0 = INTEGER: atsFullyRedundant(2) */ { "unmapped.atsStatusRedundancyState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.3.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusOverCurrentState.0 = INTEGER: atsCurrentOK(2) */ { "unmapped.atsStatusOverCurrentState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.4.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatus5VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ { "unmapped.atsStatus5VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.5.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatus24VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ { "unmapped.atsStatus24VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.6.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatus24VSourceBPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ { "unmapped.atsStatus24VSourceBPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusPlus12VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ { "unmapped.atsStatusPlus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.8.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusMinus12VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ { "unmapped.atsStatusMinus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.9.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusSwitchStatus.0 = INTEGER: ok(2) */ { "unmapped.atsStatusSwitchStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.10.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusFrontPanel.0 = INTEGER: unlocked(2) */ { "unmapped.atsStatusFrontPanel", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.11.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusSourceAStatus.0 = INTEGER: ok(2) */ { "unmapped.atsStatusSourceAStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.12.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusSourceBStatus.0 = INTEGER: ok(2) */ { "unmapped.atsStatusSourceBStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.13.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusPhaseSyncStatus.0 = INTEGER: inSync(1) */ { "unmapped.atsStatusPhaseSyncStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.14.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusHardwareStatus.0 = INTEGER: ok(2) */ { "unmapped.atsStatusHardwareStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.16.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsStatusResetMaxMinValues.0 = INTEGER: -1 */ { "unmapped.atsStatusResetMaxMinValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.2.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputTableIndex.1 = INTEGER: 1 */ { "unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputTableIndex.2 = INTEGER: 2 */ { "unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsNumInputPhases.1 = INTEGER: 1 */ { "unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsNumInputPhases.2 = INTEGER: 1 */ { "unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputVoltageOrientation.1 = INTEGER: singlePhase(2) */ { "unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputVoltageOrientation.2 = INTEGER: singlePhase(2) */ { "unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputType.1 = INTEGER: main(2) */ { "unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputType.2 = INTEGER: main(2) */ { "unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputName.1 = STRING: "Source A" */ { "unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputName.2 = STRING: "Source B" */ { "unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputPhaseTableIndex.1.1.1 = INTEGER: 1 */ { "unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputPhaseTableIndex.2.1.1 = INTEGER: 2 */ { "unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputPhaseIndex.1.1.1 = INTEGER: 1 */ { "unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputPhaseIndex.2.1.1 = INTEGER: 1 */ { "unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMaxVoltage.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMaxVoltage.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMinVoltage.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMinVoltage.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputCurrent.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputCurrent.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMaxCurrent.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMaxCurrent.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMinCurrent.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMinCurrent.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputPower.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputPower.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMaxPower.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMaxPower.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMinPower.1.1.1 = INTEGER: -1 */ { "unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsInputMinPower.2.1.1 = INTEGER: -1 */ { "unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.2.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsNumOutputs.0 = INTEGER: 1 */ { "unmapped.atsNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputTableIndex.1 = INTEGER: 1 */ { "unmapped.atsOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsNumOutputPhases.1 = INTEGER: 1 */ { "unmapped.atsNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.2.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputVoltageOrientation.1 = INTEGER: singlePhase(2) */ { "unmapped.atsOutputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputPhase.1 = INTEGER: phase1(1) */ { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputPhase.2 = INTEGER: phase1(1) */ { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputPhase.3 = INTEGER: phase1(1) */ { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxCurrent.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxCurrent.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxCurrent.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinCurrent.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinCurrent.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinCurrent.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankLoad.1 = INTEGER: 1883 */ { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankLoad.2 = INTEGER: 984 */ { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankLoad.3 = INTEGER: 898 */ { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxLoad.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxLoad.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxLoad.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinLoad.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinLoad.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinLoad.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankPercentLoad.1 = INTEGER: 25 */ { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankPercentLoad.2 = INTEGER: 13 */ { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankPercentLoad.3 = INTEGER: 12 */ { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPercentLoad.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPercentLoad.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPercentLoad.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPercentLoad.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPercentLoad.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPercentLoad.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPower.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPower.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPower.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPower.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPower.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPower.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankPercentPower.1 = INTEGER: 25 */ { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankPercentPower.2 = INTEGER: 13 */ { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankPercentPower.3 = INTEGER: 12 */ { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPercentPower.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPercentPower.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMaxPercentPower.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.3", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPercentPower.1 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.1", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPercentPower.2 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.2", NULL, SU_FLAG_OK, NULL, NULL }, /* atsOutputBankMinPercentPower.3 = INTEGER: -1 */ { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.3", NULL, SU_FLAG_OK, NULL, NULL }, #endif /* 0 */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t apc_ats = { "apc_ats", APC_ATS_MIB_VERSION, NULL, NULL, apc_ats_mib, APC_ATS_SYSOID }; nut-2.7.4/drivers/victronups.c0000644000175000017500000003434712640443572013333 00000000000000/* victronups.c - Model specific routines for GE/IMV/Victron units * Match, Match Lite, NetUps * * Copyright (C) 1999 Russell Kroll * Copyright (C) 2000 Radek Benedikt * old style "victronups" * Copyright (C) 2001 Daniel.Prynych * porting to now style "newvictron" * Copyright (C) 2003 Gert Lynge * Porting to new serial functions. Now removes \n from data (was causing * periodic misreadings of temperature and voltage levels) * Copyright (C) 2004 Gert Lynge * Implemented some Instant Commands. * * 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 "main.h" #include "serial.h" #define DRIVER_NAME "GE/IMV/Victron UPS driver" #define DRIVER_VERSION "0.20" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" \ "Radek Benedikt \n" \ "Daniel Prynych \n" \ "Gert Lynge ", DRV_STABLE, { NULL } }; #define ENDCHAR '\r' #define IGNCHARS "\n" #define UPS_DELAY 150000 #define UPS_LONG_DELAY 450000 #define VICTRON_OVER 128 #define VICTRON_RB 1 #define VICTRON_OB 2 #define VICTRON_LB 4 #define VICTRON_NO_TEST 1 #define VICTRON_ABORT_TEST 2 #define VICTRON_SYSTEM_TEST 3 #define VICTRON_BATTERY_TEST 4 #define VICTRON_CALIBRATION 5 #define VICTRON_BYPASS_TEST 101 #define LENGTH_TEMP 256 int sdwdelay = 0; /* shutdown after 0 second */ char *model_name; static int start_is_datastale = 1; static int exist_ups_serial = 0; static int exist_ups_temperature = 0; static int exist_output_current = 0; static int exist_battery_charge = 0; static int exist_battery_current = 0; static int exist_battery_temperature = 0; static int exist_battery_runtime = 0; static int test_in_progress = VICTRON_NO_TEST; static int get_data (const char *out_string, char *in_string) { int ret_code; ser_send(upsfd, "%s%c", out_string, ENDCHAR); usleep (UPS_DELAY); ret_code = ser_get_line(upsfd, in_string, LENGTH_TEMP, ENDCHAR, IGNCHARS, 3, 0); if (ret_code < 1) { dstate_datastale(); return -1; } return 0; } static int instcmd(const char *cmdname, const char *extra) { char temp[ LENGTH_TEMP ]; if(!strcasecmp(cmdname, "calibrate.start")) { if(get_data("vTi5!",temp)) { upsdebugx(1, "instcmd: ser_send calibrate.start failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: calibrate.start returned: %s", temp); test_in_progress = VICTRON_CALIBRATION; return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "calibrate.stop")) { if(get_data("vTi2!",temp)) { upsdebugx(1, "instcmd: ser_send calibrate.stop failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: calibrate.stop returned: %s", temp); return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "test.battery.stop")) { if(get_data("vTi2!",temp)) { upsdebugx(1, "instcmd: ser_send test.battery.stop failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: test.battery.stop returned: %s", temp); return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "test.battery.start")) { if(get_data("vTi4!",temp)) { upsdebugx(1, "instcmd: ser_send test.battery.start failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: test.battery.start returned: %s", temp); test_in_progress = VICTRON_BATTERY_TEST; return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "test.panel.stop")) { if(get_data("vTi2!",temp)) { upsdebugx(1, "instcmd: ser_send test.panel.stop failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: test.panel.stop returned: %s", temp); return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "test.panel.start")) { if(get_data("vTi3!",temp)) { upsdebugx(1, "instcmd: ser_send test.panel.start failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: test.panel.start returned: %s", temp); test_in_progress = VICTRON_SYSTEM_TEST; return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "bypass.stop")) { if(get_data("vTi2!",temp)) { upsdebugx(1, "instcmd: ser_send bypass.stop failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: bypass.stop returned: %s", temp); return STAT_INSTCMD_HANDLED; } } else if(!strcasecmp(cmdname, "bypass.start")) { if(get_data("vTi101!",temp)) { upsdebugx(1, "instcmd: ser_send bypass.start failed"); return STAT_INSTCMD_UNKNOWN; /* Or Failed when it get defined */ } else { upsdebugx(1, "instcmd: bypass.start returned: %s", temp); test_in_progress = VICTRON_BYPASS_TEST; return STAT_INSTCMD_HANDLED; } } else { upsdebugx(1, "instcmd: unknown command: %s", cmdname); return STAT_INSTCMD_UNKNOWN; } } void upsdrv_initinfo(void) { if (model_name) dstate_setinfo("ups.model", "%s", model_name); upsh.instcmd = instcmd; dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); dstate_addcmd("calibrate.start"); dstate_addcmd("calibrate.stop"); dstate_addcmd("test.panel.start"); /* We need a GeneralSystemTest, but use this one instead */ dstate_addcmd("test.panel.stop"); /* We need a GeneralSystemTest, but use this one instead */ dstate_addcmd("bypass.start"); dstate_addcmd("bypass.stop"); } void upsdrv_updateinfo(void) { int flags; char temp[ LENGTH_TEMP ]; char test_result[ LENGTH_TEMP ]; int runtime_sec = -1; if (start_is_datastale) { if (get_data("vDS?",temp)) return; if (strcmp(temp+3,"NA")) exist_ups_serial=1; if (get_data("vBT?",temp)) return; if (strcmp(temp+3,"NA")) exist_ups_temperature =1; if (get_data("vO0I?",temp)) return; if (strcmp(temp+4,"NA")) exist_output_current =1; if (get_data("vBC?",temp)) return; if (strcmp(temp+3,"NA")) exist_battery_charge = 1; if (get_data("vBI?",temp)) return; if (strcmp(temp+3,"NA")) exist_battery_charge = 1; if (get_data("vBT?",temp)) return; if (strcmp(temp+3,"NA")) exist_battery_temperature = 1; if (get_data("vBt?",temp)) return; if (strcmp(temp+3,"NA")) exist_battery_runtime = 1; start_is_datastale = 0; } /* ups.status */ if (get_data("vAa?",temp)) return; flags = atoi (temp+3); status_init(); if (flags & VICTRON_OVER) status_set("OVER"); if (flags & VICTRON_RB) status_set("RB"); if (flags & VICTRON_LB) status_set("LB"); if (flags & VICTRON_OB) status_set("OB"); else status_set("OL"); /* Get UPS test results */ if (get_data("vTr?",temp)) return; if (get_data("vTd?",test_result)) return; switch(atoi(temp+3)) { case 1: upsdebugx(1, "upsdrv_updateinfo: test %i result = Done, Passed: %s",test_in_progress,test_result+3); test_in_progress = VICTRON_NO_TEST; break; case 2: upsdebugx(1, "upsdrv_updateinfo: test %i result = Done, Warning: %s",test_in_progress,test_result+3); test_in_progress = VICTRON_NO_TEST; break; case 3: upsdebugx(1, "upsdrv_updateinfo: test %i result = Done, Error: %s",test_in_progress,test_result+3); test_in_progress = VICTRON_NO_TEST; break; case 4: upsdebugx(1, "upsdrv_updateinfo: test %i result = Aborted: %s",test_in_progress,test_result+3); test_in_progress = VICTRON_NO_TEST; break; case 5: if(test_in_progress==VICTRON_CALIBRATION) status_set("CAL"); /* calibration in progress*/ upsdebugx(1, "upsdrv_updateinfo: test %i result = In Progress: %s", test_in_progress,test_result+3); break; case 6: upsdebugx(1, "upsdrv_updateinfo: test result = No test initiated: %s", test_result+3); break; default: upsdebugx(1, "upsdrv_updateinfo: unknown test result: %s / %s",temp+3,test_result+3); break; } status_commit(); /* dstate_dataok(); */ upsdebugx(1, "upsdrv_updateinfo: ups.status = %s\n", dstate_getinfo("ups.status")); /************** ups.x ***************************/ /* ups model */ if (!model_name) { if (get_data("vDM?",temp)) return; dstate_setinfo("ups.model", "%s", temp+3); upsdebugx(1, "ups.model >%s<>%s<\n",temp,temp+3); } /* ups.mfr */ if (get_data("vDm?",temp)) return; dstate_setinfo("ups.mfr", "%s", temp+3); upsdebugx(1, "ups.mfr >%s<>%s<\n",temp,temp+3); /* ups.serial */ if (exist_ups_serial) { if (get_data("vDS?",temp)) return; dstate_setinfo("ups.serial", "%s", temp+3); } upsdebugx(1, "ups.serial >%s<>%s<\n",temp,temp+3); /* ups.firmware */ if (get_data("vDV?",temp)) return; dstate_setinfo("ups.firmware", "%s", temp+3); upsdebugx(1, "ups.firmware >%s<>%s<\n",temp,temp+3); /* ups.temperature */ if (exist_ups_temperature) { if (get_data("vBT?",temp)) return; dstate_setinfo("ups.temperature", "%s", temp+3); } upsdebugx(1, "ups.temperature >%s<>%s<\n",temp,temp+3); /* ups.load */ if (get_data("vO0L?",temp)) return; dstate_setinfo("ups.load", "%s", temp+4); upsdebugx(1, "ups.load >%s<>%s<\n",temp,temp+4); /* ups protocol */ /*if (get_data("vDC?",temp)) return; dstate_setinfo("ups.protocol", "%s", temp+3; upsdebugx(1, "ups.protocol >%s<>%s<\n",temp,temp+3; */ /************** input.x *****************/ /* input.voltage */ if (get_data("vI0U?",temp)) return; dstate_setinfo("input.voltage", "%s", temp+4); upsdebugx(1, "input.voltage >%s<>%s<\n",temp,temp+4); /* input.transfer.low */ if (get_data("vFi?",temp)) return; dstate_setinfo("input.transfer.low", "%s", temp+3); upsdebugx(1, "input.transfer.low >%s<>%s<\n",temp,temp+3); /* input.transfer.high */ if (get_data("vFj?",temp)) return; dstate_setinfo("input.transfer.high", "%s", temp+3); upsdebugx(1, "input.transfer.high >%s<>%s<\n",temp,temp+3); /* input.frequency */ if (get_data("vI0f?",temp)) return; dstate_setinfo("input.frequency", "%2.1f", atof(temp+4) / 10.0); upsdebugx(1, "input.frequency >%s<>%s<\n",temp,temp+4); /*************** output.x ********************************/ /* output.voltage */ if (get_data("vO0U?",temp)) return; dstate_setinfo("output.voltage", "%s", temp+4); upsdebugx(1, "output.voltage >%s<>%s<\n",temp,temp+4); /* output.frequency */ if (get_data("vOf?",temp)) return; dstate_setinfo("output.frequency", "%2.1f", atof(temp+3) / 10.0); upsdebugx(1, "output.frequency >%s<>%s<\n",temp,temp+3); /* output.current */ if (exist_output_current) { if (get_data("vO0I?",temp)) return; dstate_setinfo("output.current", "%2.1f", atof(temp+4) / 10.0); } upsdebugx(1, "output.current >%s<>%s<\n",temp,temp+4); /*************** battery.x *******************************/ /* battery charge */ if (exist_battery_charge) { if (get_data("vBC?",temp)) return; dstate_setinfo("battery.charge", "%s", temp+3); } upsdebugx(1, "battery.charge >%s<>%s<\n",temp,temp+3); /* battery.voltage */ if (get_data("vBU?",temp)) return; dstate_setinfo("battery.voltage", "%2.1f", atof(temp+3) / 10.0); upsdebugx(1, "battery.voltage >%s<>%s<\n",temp,temp+3); /* battery.current */ if (exist_battery_current) { if (get_data("vBI?",temp)) return; dstate_setinfo("battery.current", "%2.1f", atof(temp+3) / 10.0); } upsdebugx(1, "battery.current >%s<>%s<\n",temp,temp+3); /* battery.temperature */ if (exist_battery_temperature) { if (get_data("vBT?",temp)) return; dstate_setinfo("battery.temperature", "%s", temp+3); } upsdebugx(1, "battery.temperature >%s<>%s<\n",temp,temp+3); /* battery.runtime */ if (exist_battery_runtime) { if (get_data("vBt?",temp)) return; runtime_sec = strtol(temp+3, NULL, 10)*60; snprintf(temp, sizeof(temp), "%d", runtime_sec); dstate_setinfo("battery.runtime", "%s", temp); } upsdebugx(1, "battery.runtime >%s<>%d<\n",temp,runtime_sec); dstate_dataok(); } void upsdrv_shutdown(void) { ser_send(upsfd, "vCc0!%c", ENDCHAR); usleep(UPS_DELAY); ser_send(upsfd, "vCb%i!%c", sdwdelay, ENDCHAR); } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "usd", "Seting delay before shutdown"); addvar(VAR_VALUE, "modelname", "Seting model name"); } void upsdrv_initups(void) { char temp[ LENGTH_TEMP ], *usd = NULL; /* = NULL je dulezite jen pro prekladac */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); if ((usd = getval("usd"))) { sdwdelay=atoi(usd); upsdebugx(1, "(-x) Delay before shutdown %i",sdwdelay); } if ((model_name = getval("modelname"))) { /* kdyz modelname nebylo zadano je vraceno NULL*/ upsdebugx(1, "(-x) UPS Name %s",model_name); } /* inicializace a synchronizace UPS */ ser_send_char(upsfd, ENDCHAR); usleep (UPS_LONG_DELAY); ser_send(upsfd, "?%c", ENDCHAR); usleep (UPS_LONG_DELAY); ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 3, 0); ser_send(upsfd, "?%c", ENDCHAR); usleep (UPS_LONG_DELAY); ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 3, 0); ser_send(upsfd, "?%c", ENDCHAR); usleep (UPS_DELAY); ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 3, 0); /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/compaq-mib.h0000644000175000017500000000021512640443572013134 00000000000000#ifndef COMPAQ_MIB_H #define COMPAQ_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t compaq; #endif /* COMPAQ_MIB_H */ nut-2.7.4/drivers/powercom.c0000644000175000017500000012074112667537407012756 00000000000000/* * powercom.c - model specific routines for following units: * -Trust 425/625 * -Powercom * -Advice Partner/King PR750 * See http://www.advice.co.il/product/inter/ups.html for its specifications. * This model is based on PowerCom (www.powercom.com) models. * -Socomec Sicon Egys 420 * -OptiUPS VS 575C * * Copyrights: * (C) 2015 Arnaud Quette * (C) 2013 Florian Bruhin * (C) 2002 Simon Rozman * (C) 1999 Peter Bieringer * * 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 * * rev 0.7: Alexey Sidorov * - add Powercom's Black Knight Pro model support ( BNT-400/500/600/800/801/1000/1200/1500/2000AP 220-240V ) * * rev 0.8: Alexey Sidorov * - add Powercom's King Pro model support ( KIN-425/525/625/800/1000/1200/1500/1600/2200/3000/5000AP[-RM] 100-120,200-240 V) * * rev 0.9: Alexey Sidorov * - add Powercom's Imperial model support ( IMP-xxxAP, IMD-xxxAP ) * * rev 0.10: Alexey Sidorov * - fix wrong detection KIN-2200AP * - use ser_set_dtr/ser_set_rts * * rev 0.11: Alexey Sidorov * - move variables from .h to .c file (thanks Michael Tokarev for bugreport) * - fix string comparison (thanks Michael Tokarev for bugreport & Charles Lepple for patch) * - added BNT-other, for BNT 100-120V models (I havn't specs for it) * * Tested on: BNT-1200AP * * Known bugs: * - strange battery level on BNT1200AP in online mode( & may be on other models) * - i don't know how connect to IMP|IMD USB * - i havn't specs for BNT 100-120V models. Add BNT-other type for it * * rev 0.13: Keven Ates * - Modified functions to work for BNT-other 100-120V models. * - Modified BNT-other type defaults to work for the BNT 1500A 120VA model. * - Documented the type[] values purpose in a condensed format. * - BNT-other can be used to perform a complete user override of values for all PowerCom models, detected or not. * * Tested on: BNT-1500A * * rev 0.14: Florian Bruhin (The Compiler) * - Added support for OptiUPS VS 575C * This probably also works with others, but I don't have their model numbers. * * rev 0.15: VSE NN * - Fixed UPS type assignment for Powercom Imperial USB series manufactured since 2009. * * Tested on: IMP-625AP * * rev 0.16: Arnaud Quette * - Fixed the processing of input/output voltages for KIN models * (https://github.com/networkupstools/nut/issues/187) * */ #include "main.h" #include "serial.h" #include "powercom.h" #include "math.h" #define DRIVER_NAME "PowerCom protocol UPS driver" #define DRIVER_VERSION "0.17" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Simon Rozman \n" \ "Peter Bieringer \n" \ "Alexey Sidorov \n" \ "Florian Bruhin \n" \ "Arnaud Quette ", DRV_STABLE, { NULL } }; #define NUM_OF_SUBTYPES (sizeof (types) / sizeof (*types)) /* general constants */ enum general { MAX_NUM_OF_BYTES_FROM_UPS = 16 }; /* variables used by module */ static unsigned char raw_data[MAX_NUM_OF_BYTES_FROM_UPS]; /* raw data reveived from UPS */ static unsigned int linevoltage = 230U; /* line voltage, can be defined via command line option */ static const char *manufacturer = "PowerCom"; static const char *modelname = "Unknown"; static const char *serialnumber = "Unknown"; static unsigned int type = 0; /* forward declaration of functions used to setup flow control */ static void dtr0rts1 (void); static void no_flow_control (void); /* struct defining types * --------------------- * See powercom.h for detailed information and functions. * * The following type defaults use this definition: * * "TypeID", * ByteCount, * { "FlowControlString", FlowControlFuncPtr }, * { { ValidationIndex, ValidationValue }, * { ValidationIndex, ValidationValue }, * { ValidationIndex, ValidationValue } }, * { { DelayShutdownMinutes, DelayShutdownSeconds }, * UseMinutesChar'y''n' }, * { FrequencyFactor, FrequencyConstant }, * { OfflineLoadFactor, OfflineLoadConstant, * OnlineLoadFactor, OnlineLoadConstant }, * { OfflineBatteryFactor, OfflineLoad%Factor, OfflineBatteryConstant, * OnlineBatteryFactor, OnlineBatteryConstant }, * { 240VoltageFactor, 240VoltageConstant, * 120VoltageFactor, 120VoltageConstant }, */ static struct type types[] = { { "Trust", 11, { "dtr0rts1", dtr0rts1 }, { { 5U, 0U }, { 7U, 0U }, { 8U, 0U } }, { { 0U, 10U }, 'n' }, { 0.00020997, 0.00020928 }, { 6.1343, -0.3808, 4.3110, 0.1811 }, { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, { 1.9216, -0.0977, 0.9545, 0.0000 }, }, { "Egys", 16, { "no_flow_control", no_flow_control }, { { 5U, 0x80U }, { 7U, 0U }, { 8U, 0U } }, { { 0U, 10U }, 'n' }, { 0.00020997, 0.00020928 }, { 6.1343, -0.3808, 1.3333, 0.6667 }, { 5.0000, 0.3268, -825.00, 2.2105, -355.37 }, { 1.9216, -0.0977, 0.9545, 0.0000 }, }, { "KP625AP", 16, { "dtr0rts1", dtr0rts1 }, { { 5U, 0x80U }, { 7U, 0U }, { 8U, 0U } }, { { 0U, 10U }, 'n' }, { 0.00020997, 0.00020928 }, { 6.1343, -0.3808, 4.3110, 0.1811 }, { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, { 1.9216, -0.0977, 0.9545, 0.0000 }, }, { "IMP", 16, { "no_flow_control", no_flow_control }, { { 5U, 0xFFU }, { 7U, 0U }, { 8U, 0U } }, { { 1U, 30U }, 'y' }, { 0.00020997, 0.00020928 }, { 6.1343, -0.3808, 4.3110, 0.1811 }, { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, { 1.9216, -0.0977, 0.9545, 0.0000 }, }, { "KIN", 16, { "no_flow_control", no_flow_control }, { { 11U, 0x4bU }, { 8U, 0U }, { 8U, 0U } }, { { 1U, 30U }, 'y' }, { 0.00020997, 0.0 }, { 6.1343, -0.3808, 1.075, 0.1811 }, { 5.0000, 0.3268, -825.00, 0.46511, 0 }, { 1.9216, -0.0977, 0.82857, 0.0000 }, }, { "BNT", 16, { "no_flow_control", no_flow_control }, { { 11U, 0x42U }, { 8U, 0U }, { 8U, 0U } }, { { 1U, 30U }, 'y' }, { 0.00020803, 0.0 }, { 1.4474, 0.0, 0.8594, 0.0 }, { 5.0000, 0.3268, -825.00, 0.46511, 0 }, { 1.9216, -0.0977, 0.82857, 0.0000 }, }, { "BNT-other", 16, { "no_flow_control", no_flow_control }, { { 8U, 0U }, { 8U, 0U }, { 8U, 0U } }, { { 1U, 30U }, 'y' }, { 0.00027778, 0.0000 }, { 1.0000, 0.0000, 1.0000, 0.0000 }, { 1.0000, 0.0000, 0.0000, 1.0000, 0.0000 }, { 2.0000, 0.0000, 2.0000, 0.0000 }, }, { "OPTI", 16, { "no_flow_control", no_flow_control }, { { 5U, 0xFFU }, { 7U, 0U }, { 8U, 0U } }, { { 1U, 30U }, 'y' }, { 0.0000, 0.0000 }, { 1.0000, 0.0000, 1.0000, 0.0000 }, { 1.0000, 0.0000, 0.0000, 1.0000, 0.0000 }, { 2.0000, 0.0000, 2.0000, 0.0000 }, }, }; /* values for sending to UPS */ enum commands { SEND_DATA = '\x01', BATTERY_TEST = '\x03', WAKEUP_TIME = '\x04', RESTART = '\xb9', SHUTDOWN = '\xba', COUNTER = '\xbc' }; /* location of data in received string */ enum data { UPS_LOAD = 0U, BATTERY_CHARGE = 1U, INPUT_VOLTAGE = 2U, OUTPUT_VOLTAGE = 3U, INPUT_FREQUENCY = 4U, UPSVERSION = 5U, OUTPUT_FREQUENCY = 6U, STATUS_A = 9U, STATUS_B = 10U, MODELNAME = 11U, MODELNUMBER = 12U }; /* status bits */ enum status { SUMMARY = 0U, MAINS_FAILURE = 1U, ONLINE = 1U, FAULT = 1U, LOW_BAT = 2U, BAD_BAT = 2U, TEST = 4U, AVR_ON = 8U, AVR_MODE = 16U, SD_COUNTER = 16U, OVERLOAD = 32U, SHED_COUNTER = 32U, DIS_NOLOAD = 64U, SD_DISPLAY = 128U, OFF = 128U }; unsigned int voltages[]={100,110,115,120,0,0,0,200,220,230,240,0,0,0,0,0}; unsigned int BNTmodels[]={0,400,500,600,800,801,1000,1200,1500,2000,0,0,0,0,0,0}; unsigned int KINmodels[]={0,425,500,525,625,800,1000,1200,1500,1600,2200,2200,2500,3000,5000,0}; unsigned int IMPmodels[]={0,425,525,625,825,1025,1200,1500,2000,0,0,0,0,0,0,0}; unsigned int OPTImodels[]={0,0,0,575,0,0,0,0,0,0,0,0,0,0,0,0}; /* * local used functions */ static void shutdown_halt(void) { ser_send_char (upsfd, SHUTDOWN); if (types[type].shutdown_arguments.minutesShouldBeUsed != 'n') ser_send_char (upsfd, types[type].shutdown_arguments.delay[0]); ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (stayoff) initiated."); exit (0); } static void shutdown_ret(void) { ser_send_char (upsfd, RESTART); ser_send_char (upsfd, COUNTER); if (types[type].shutdown_arguments.minutesShouldBeUsed != 'n') ser_send_char (upsfd, types[type].shutdown_arguments.delay[0]); ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (return) initiated."); exit (0); } /* registered instant commands */ static int instcmd (const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.start")) { ser_send_char (upsfd, BATTERY_TEST); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { shutdown_ret(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stayoff")) { shutdown_halt(); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } /* set DTR and RTS lines on a serial port to supply a passive * serial interface: DTR to 0 (-V), RTS to 1 (+V) */ static void dtr0rts1 (void) { ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 1); upsdebugx(2, "DTR => 0, RTS => 1"); } /* clear any flow control */ static void no_flow_control (void) { struct termios tio; tcgetattr (upsfd, &tio); tio.c_iflag &= ~ (IXON | IXOFF); tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; upsdebugx(2, "Flow control disable"); /* disable any flow control */ tcsetattr(upsfd, TCSANOW, &tio); } /* sane check for returned buffer */ static int validate_raw_data (void) { int i = 0, num_of_tests = sizeof types[0].validation / sizeof types[0].validation[0]; for (i = 0; i < num_of_tests && raw_data[ types[type].validation[i].index_of_byte] == types[type].validation[i].required_value; i++) ; return (i < num_of_tests) ? 1 : 0; } /* get info from ups */ static int ups_getinfo(void) { int i, c; /* send trigger char to UPS */ if (ser_send_char (upsfd, SEND_DATA) != 1) { upslogx(LOG_NOTICE, "writing error"); dstate_datastale(); return 0; } else { upsdebugx(5, "Num of bytes requested for reading from UPS: %d", types[type].num_of_bytes_from_ups); c = ser_get_buf_len(upsfd, raw_data, types[type].num_of_bytes_from_ups, 3, 0); if (c != types[type].num_of_bytes_from_ups) { upslogx(LOG_NOTICE, "data receiving error (%d instead of %d bytes)", c, types[type].num_of_bytes_from_ups); dstate_datastale(); return 0; } else upsdebugx(5, "Num of bytes received from UPS: %d", c); }; /* optional dump of raw data */ if (nut_debug_level > 4) { /* FIXME: use upsdebug_hex() ? */ printf("Raw data from UPS:\n"); for (i = 0; i < types[type].num_of_bytes_from_ups; i++) { printf("%2d 0x%02x (%c)\n", i, raw_data[i], raw_data[i]>=0x20 ? raw_data[i] : ' '); }; }; /* validate raw data for correctness */ if (validate_raw_data() != 0) { upslogx(LOG_NOTICE, "data receiving error (validation check)"); dstate_datastale(); return 0; }; return 1; } static float input_voltage(void) { unsigned int model; float tmp=0.0; if ( !strcmp(types[type].name, "BNT") && raw_data[MODELNUMBER]%16 > 7 ) { tmp=2.2*raw_data[INPUT_VOLTAGE]-24; } else if ( !strcmp(types[type].name, "KIN")) { model=KINmodels[raw_data[MODELNUMBER]/16]; /* Process input voltage, according to line voltage and model rating */ if (linevoltage < 200) { if (model <= 625) { tmp = 0.89 * raw_data[INPUT_VOLTAGE] + 6.18; } else if ((model >= 800) && (model < 2000)) { tmp = 1.61 * raw_data[INPUT_VOLTAGE] / 2.0; } else { tmp = 1.625 * raw_data[INPUT_VOLTAGE] / 2.0; } } if (linevoltage >= 200) { if (model <= 625) { tmp = 1.79 * raw_data[INPUT_VOLTAGE] + 3.35; } else if ((model >= 800) && (model < 2000)) { tmp = 1.61 * raw_data[INPUT_VOLTAGE]; } else { tmp = 1.625 * raw_data[INPUT_VOLTAGE]; } } } else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { tmp=raw_data[INPUT_VOLTAGE]*2.0; } else { tmp=linevoltage >= 220 ? types[type].voltage[0] * raw_data[INPUT_VOLTAGE] + types[type].voltage[1] : types[type].voltage[2] * raw_data[INPUT_VOLTAGE] + types[type].voltage[3]; } if (tmp<0) tmp=0.0; return tmp; } static float output_voltage(void) { float tmp,rdatax,rdatay,rdataz,boostdata; unsigned int statINV = 0,statAVR = 0,statAVRMode = 0,model,t; static float datax1[]={0,1.0,1.0,1.0,1.0,0.945,0.945,0.945,0.127,0.127,0.945,0.945,0.945,0.256}; static float datay1[]={0,0.85,0.85,0.85,0.88,0.9,0.9,0.9,6.6,6.6,0.87,0.87,0.87,3.29}; static float dataz1[]={0,1.03,0.78,0.78,0.72,0.55,0.55,0.55,0.5,0.5,0.43,0.43,0.43,0.3}; static float datax2[]={0,1.0,1.0,1.0,1.0,1.89,1.89,1.89,0.127,0.127,1.89,1.89,1.89,0.256}; static float datay2[]={0,1.73,1.74,1.74,1.77,0.9,0.9,0.9,13.204,13.204,0.88,0.88,0.88,6.645}; static float dataz2[]={0,1.15,0.9,0.9,0.75,1.1,1.1,1.1,0.8,0.8,0.86,0.86,0.86,0.7}; if ( !strcmp(types[type].name, "BNT") || !strcmp(types[type].name, "KIN")) { statINV=raw_data[STATUS_A] & ONLINE; statAVR=raw_data[STATUS_A] & AVR_ON; statAVRMode=raw_data[STATUS_A] & AVR_MODE; } if ( !strcmp(types[type].name, "BNT") && raw_data[MODELNUMBER]%16 > 7 ) { if (statINV==0) { if (statAVR==0){ tmp=2.2*raw_data[OUTPUT_VOLTAGE]-24; } else { if (statAVRMode > 0) tmp=(2.2*raw_data[OUTPUT_VOLTAGE]-24)*31/27; else tmp=(2.22*raw_data[OUTPUT_VOLTAGE]-24)*27/31; } } else { t=raw_data[OUTPUT_FREQUENCY]/2; tmp=(1.965*raw_data[15])*(1.965*raw_data[15])*(t-raw_data[OUTPUT_VOLTAGE])/t; if (tmp>0) tmp=sqrt(tmp); else tmp=0.0; } } else if ( !strcmp(types[type].name, "KIN")) { model=KINmodels[raw_data[MODELNUMBER]/16]; if (statINV == 0) { if (statAVR == 0) { // FIXME: miss test "if (iUPS == 1) {" if (linevoltage >= 200) { if (linevoltage <= 625) tmp = 1.79*raw_data[OUTPUT_VOLTAGE] + 3.35; else if (model<2000) tmp = 1.61*raw_data[OUTPUT_VOLTAGE]; else tmp = 1.625*raw_data[OUTPUT_VOLTAGE]; } else { if (linevoltage <= 625) tmp = 0.89 * raw_data[OUTPUT_VOLTAGE] + 6.18; else if (model<2000) tmp = 1.61 * raw_data[OUTPUT_VOLTAGE] / 2.0; else tmp = 1.625 * raw_data[OUTPUT_VOLTAGE] / 2.0; } } else if (statAVR == 1) { // FIXME: miss test "if ((iUPS == 1) || (iUPS == 13)) {" if (linevoltage >= 200) { if (model <= 525) tmp = 2.07 * raw_data[OUTPUT_VOLTAGE]; else if (model == 625) tmp = 2.07 * raw_data[OUTPUT_VOLTAGE]+5; else if (model < 2000) tmp = 1.87 * raw_data[OUTPUT_VOLTAGE]; else tmp = 1.87 * raw_data[OUTPUT_VOLTAGE]; } else { if (model <= 625) tmp = 2.158 * raw_data[OUTPUT_VOLTAGE] / 2.0; else if (model < 2000) tmp = 1.842 * raw_data[OUTPUT_VOLTAGE] / 2.0; else tmp = 1.875 * raw_data[OUTPUT_VOLTAGE] / 2.0; } } else { // FIXME: miss test "if ((iUPS == 1) || (iUPS == 13)) {" if (linevoltage >= 200) { if (model == 625) tmp = 1.571 * raw_data[OUTPUT_VOLTAGE]; else if (model < 2000) tmp = 1.37 * raw_data[OUTPUT_VOLTAGE]; else tmp = 1.4 * raw_data[OUTPUT_VOLTAGE]; } else { if (model <= 625) tmp = 1.635 * raw_data[OUTPUT_VOLTAGE] / 2.0; else if (model < 2000) tmp = 1.392 * raw_data[OUTPUT_VOLTAGE] / 2.0; else tmp = 1.392 * raw_data[OUTPUT_VOLTAGE] / 2.0; } } } else { // FIXME: miss test "if ((iUPS == 1) && (T != 0))" if (linevoltage < 200) { rdatax = datax1[raw_data[MODELNUMBER]/16]; rdatay = datay1[raw_data[MODELNUMBER]/16]; rdataz = dataz1[raw_data[MODELNUMBER]/16]; } else { rdatax = datax2[raw_data[MODELNUMBER]/16]; rdatay = datay2[raw_data[MODELNUMBER]/16]; rdataz = dataz2[raw_data[MODELNUMBER]/16+1]; } boostdata = 1.0 + statAVR * 20.0 / 135.0; t = raw_data[OUTPUT_FREQUENCY]/2; tmp = 0; if (model > 625){ tmp=(raw_data[BATTERY_CHARGE]*rdatax)*(raw_data[BATTERY_CHARGE]*rdatax)* (t-raw_data[OUTPUT_VOLTAGE])/t; if (tmp>0) tmp=sqrt(tmp)*rdatay*boostdata-raw_data[UPS_LOAD]*rdataz*boostdata; } else { tmp=(raw_data[BATTERY_CHARGE]*rdatax-raw_data[UPS_LOAD]*rdataz)* (raw_data[BATTERY_CHARGE]*rdatax-raw_data[UPS_LOAD]*rdataz)* (t-raw_data[OUTPUT_VOLTAGE])/t; if (tmp>0) tmp=sqrt(tmp)*rdatay; } // FIXME: may miss a last processing with ErrorVal = 5 | 10 } } else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { tmp=raw_data[OUTPUT_VOLTAGE]*2.0; } else { tmp= linevoltage >= 220 ? types[type].voltage[0] * raw_data[OUTPUT_VOLTAGE] + types[type].voltage[1] : types[type].voltage[2] * raw_data[OUTPUT_VOLTAGE] + types[type].voltage[3]; } if (tmp<0) tmp=0.0; return tmp; } static float input_freq(void) { if ( !strcmp(types[type].name, "BNT") || !strcmp(types[type].name, "KIN")) return 4807.0/raw_data[INPUT_FREQUENCY]; else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) return raw_data[INPUT_FREQUENCY]; return raw_data[INPUT_FREQUENCY] ? 1.0 / (types[type].freq[0] * raw_data[INPUT_FREQUENCY] + types[type].freq[1]) : 0; } static float output_freq(void) { if ( !strcmp(types[type].name, "BNT") || !strcmp(types[type].name, "KIN")) return 4807.0/raw_data[OUTPUT_FREQUENCY]; else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) return raw_data[OUTPUT_FREQUENCY]; return raw_data[OUTPUT_FREQUENCY] ? 1.0 / (types[type].freq[0] * raw_data[OUTPUT_FREQUENCY] + types[type].freq[1]) : 0; } static float load_level(void) { unsigned int statINV,model,voltage; int load425[]={99,88,84,80,84,84,84,86,86,81,76}; int load525[]={127,113,106,100,106,106,106,109,109,103,97}; int load625[]={131,115,107,103,107,107,107,110,110,105,99}; int load2k[] ={94,94,94,94,94,94,94,120,120,115,110}; int load425i[]={60,54,51,48,51,51,51,53,53,50,48}; int load525i[]={81,72,67,62,67,67,67,65,65,62,59}; int load625i[]={79,70,67,64,67,67,67,65,65,61,58}; int load2ki[] ={84,77,74,70,74,74,74,77,77,74,70}; int load400[]={1,1,1,1,1,1,1,1,88,83,87}; int load500[]={1,1,1,1,1,1,1,1,108,103,98}; int load600[]={1,1,1,1,1,1,1,1,128,123,118}; int load400i[]={1,1,1,1,1,1,1,1,54,52,49}; int load500i[]={1,1,1,1,1,1,1,1,66,64,61}; int load600i[]={1,1,1,1,1,1,1,1,86,84,81}; int load801i[]={1,1,1,1,1,1,1,1,44,42,40}; int load1000i[]={1,1,1,1,1,1,1,1,56,54,52}; int load1200i[]={1,1,1,1,1,1,1,1,76,74,72}; if ( !strcmp(types[type].name, "BNT") && raw_data[MODELNUMBER]%16 > 7 ) { statINV=raw_data[STATUS_A] & ONLINE; voltage=raw_data[MODELNUMBER]%16; model=BNTmodels[raw_data[MODELNUMBER]/16]; if (statINV==0){ if (model==400 || model==801) return raw_data[UPS_LOAD]*110.0/load400[voltage]; else if (model==600 || model==1200) return raw_data[UPS_LOAD]*110.0/load600[voltage]; else return raw_data[UPS_LOAD]*110.0/load500[voltage]; } else { switch (model) { case 400: return raw_data[UPS_LOAD]*110.0/load400i[voltage]; case 500: case 800: return raw_data[UPS_LOAD]*110.0/load500i[voltage]; case 600: return raw_data[UPS_LOAD]*110.0/load600i[voltage]; case 801: return raw_data[UPS_LOAD]*110.0/load801i[voltage]; case 1200: return raw_data[UPS_LOAD]*110.0/load1200i[voltage]; case 1000: case 1500: case 2000: return raw_data[UPS_LOAD]*110.0/load1000i[voltage]; } } } else if (!strcmp(types[type].name, "KIN")) { statINV=raw_data[STATUS_A] & ONLINE; voltage=raw_data[MODELNUMBER]%16; model=KINmodels[raw_data[MODELNUMBER]/16]; if (statINV==0){ if (model==425) return raw_data[UPS_LOAD]*110.0/load425[voltage]; if (model==525) return raw_data[UPS_LOAD]*110.0/load525[voltage]; if (model==625) return raw_data[UPS_LOAD]*110.0/load625[voltage]; if (model<2000) return raw_data[UPS_LOAD]*1.13; if (model>=2000) return raw_data[UPS_LOAD]*110.0/load2k[voltage]; } else { if (model==425) return raw_data[UPS_LOAD]*110.0/load425i[voltage]; if (model==525) return raw_data[UPS_LOAD]*110.0/load525i[voltage]; if (model==625) return raw_data[UPS_LOAD]*110.0/load625i[voltage]; if (model<2000) return raw_data[UPS_LOAD]*1.66; if (model>=2000) return raw_data[UPS_LOAD]*110.0/load2ki[voltage]; } } else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { return raw_data[UPS_LOAD]; } return raw_data[STATUS_A] & MAINS_FAILURE ? types[type].loadpct[0] * raw_data[UPS_LOAD] + types[type].loadpct[1] : types[type].loadpct[2] * raw_data[UPS_LOAD] + types[type].loadpct[3]; } static float batt_level(void) { int bat0,bat29,bat100,model; float battval; if ( !strcmp(types[type].name, "BNT") ) { bat0=157; bat29=165; bat100=193; battval=(raw_data[UPS_LOAD])/4+raw_data[BATTERY_CHARGE]; if (battval<=bat0) return 0.0; if (battval>bat0 && battval<=bat29) return (battval-bat0)*30.0/(bat29-bat0); if (battval>bat29 && battval<=bat100) return 30.0+(battval-bat29)*70.0/(bat100-bat29); return 100.0; } if ( !strcmp(types[type].name, "KIN")) { model=KINmodels[raw_data[MODELNUMBER]/16]; if (model>=800 && model<=2000){ battval=(raw_data[BATTERY_CHARGE]-165.0)*2.6; if (raw_data[STATUS_A] & ONLINE) return battval+raw_data[UPS_LOAD]; if (battval>7) return battval-6; return battval; } else if (model<=625){ battval=raw_data[UPS_LOAD]/4.0+raw_data[BATTERY_CHARGE]; bat0=169; bat29=176; bat100=204; } else { battval=raw_data[UPS_LOAD]/4.0-raw_data[UPS_LOAD]/32.0+raw_data[BATTERY_CHARGE]; bat0=175; bat29=182; bat100=209; } if (battval<=bat0) return 0; if (battval>bat0 && battval<=bat29) return (battval-bat0)*30.0/(bat29-bat0); if (battval>bat29 && battval<=bat100) return 30.0+(battval-bat29)*70.0/(bat100-bat29); return 100; } if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) return raw_data[BATTERY_CHARGE]; return raw_data[STATUS_A] & ONLINE ? /* Are we on battery power? */ /* Yes */ types[type].battpct[0] * raw_data[BATTERY_CHARGE] + types[type].battpct[1] * load_level() + types[type].battpct[2] : /* No */ types[type].battpct[3] * raw_data[BATTERY_CHARGE] + types[type].battpct[4]; } /* * global used functions */ /* update information */ void upsdrv_updateinfo(void) { char val[32]; if (!ups_getinfo()){ return; } /* input.frequency */ upsdebugx(3, "input.frequency (raw data): [raw: %u]", raw_data[INPUT_FREQUENCY]); dstate_setinfo("input.frequency", "%02.2f", input_freq()); upsdebugx(2, "input.frequency: %s", dstate_getinfo("input.frequency")); /* output.frequency */ upsdebugx(3, "output.frequency (raw data): [raw: %u]", raw_data[OUTPUT_FREQUENCY]); dstate_setinfo("output.frequency", "%02.2f", output_freq()); upsdebugx(2, "output.frequency: %s", dstate_getinfo("output.frequency")); /* ups.load */ upsdebugx(3, "ups.load (raw data): [raw: %u]", raw_data[UPS_LOAD]); dstate_setinfo("ups.load", "%03.1f", load_level()); upsdebugx(2, "ups.load: %s", dstate_getinfo("ups.load")); /* battery.charge */ upsdebugx(3, "battery.charge (raw data): [raw: %u]", raw_data[BATTERY_CHARGE]); dstate_setinfo("battery.charge", "%03.1f", batt_level()); upsdebugx(2, "battery.charge: %s", dstate_getinfo("battery.charge")); /* input.voltage */ upsdebugx(3, "input.voltage (raw data): [raw: %u]", raw_data[INPUT_VOLTAGE]); dstate_setinfo("input.voltage", "%03.1f",input_voltage()); upsdebugx(2, "input.voltage: %s", dstate_getinfo("input.voltage")); /* output.voltage */ upsdebugx(3, "output.voltage (raw data): [raw: %u]", raw_data[OUTPUT_VOLTAGE]); dstate_setinfo("output.voltage", "%03.1f",output_voltage()); upsdebugx(2, "output.voltage: %s", dstate_getinfo("output.voltage")); status_init(); *val = 0; if (!(raw_data[STATUS_A] & MAINS_FAILURE)) { !(raw_data[STATUS_A] & OFF) ? status_set("OL") : status_set("OFF"); } else { status_set("OB"); } if (raw_data[STATUS_A] & LOW_BAT) status_set("LB"); if (raw_data[STATUS_A] & AVR_ON) { input_voltage() < linevoltage ? status_set("BOOST") : status_set("TRIM"); } if (raw_data[STATUS_A] & OVERLOAD) status_set("OVER"); if (raw_data[STATUS_B] & BAD_BAT) status_set("RB"); if (raw_data[STATUS_B] & TEST) status_set("TEST"); status_commit(); upsdebugx(2, "STATUS: %s", dstate_getinfo("ups.status")); dstate_dataok(); } /* shutdown UPS */ void upsdrv_shutdown(void) { /* power down the attached load immediately */ printf("Forced UPS shutdown (and wait for power)...\n"); shutdown_ret(); } /* initialize UPS */ void upsdrv_initups(void) { int tmp,model = 0; unsigned int i; static char buf[20]; /* check manufacturer name from arguments */ if (getval("manufacturer") != NULL) manufacturer = getval("manufacturer"); /* check model name from arguments */ if (getval("modelname") != NULL) modelname = getval("modelname"); /* check serial number from arguments */ if (getval("serialnumber") != NULL) serialnumber = getval("serialnumber"); /* get and check type */ if (getval("type") != NULL) { for (i = 0; i < NUM_OF_SUBTYPES && strcmp(types[i].name, getval("type")); i++) ; if (i >= NUM_OF_SUBTYPES) { printf("Given UPS type '%s' isn't valid!\n", getval("type")); exit (1); } type = i; }; /* check line voltage from arguments */ if (getval("linevoltage") != NULL) { tmp = atoi(getval("linevoltage")); if (! ( (tmp >= 200 && tmp <= 240) || (tmp >= 100 && tmp <= 120) ) ) { printf("Given line voltage '%d' is out of range (100-120 or 200-240 V)\n", tmp); exit (1); }; linevoltage = (unsigned int) tmp; }; if (getval("numOfBytesFromUPS") != NULL) { tmp = atoi(getval("numOfBytesFromUPS")); if (! (tmp > 0 && tmp <= MAX_NUM_OF_BYTES_FROM_UPS) ) { printf("Given numOfBytesFromUPS '%d' is out of range (1 to %d)\n", tmp, MAX_NUM_OF_BYTES_FROM_UPS); exit (1); }; types[type].num_of_bytes_from_ups = (unsigned char) tmp; } if (getval("methodOfFlowControl") != NULL) { for (i = 0; i < NUM_OF_SUBTYPES && strcmp(types[i].flowControl.name, getval("methodOfFlowControl")); i++) ; if (i >= NUM_OF_SUBTYPES) { printf("Given methodOfFlowControl '%s' isn't valid!\n", getval("methodOfFlowControl")); exit (1); }; types[type].flowControl = types[i].flowControl; } if (getval("validationSequence") && sscanf(getval("validationSequence"), "{{%u,%x},{%u,%x},{%u,%x}}", &types[type].validation[0].index_of_byte, &types[type].validation[0].required_value, &types[type].validation[1].index_of_byte, &types[type].validation[1].required_value, &types[type].validation[2].index_of_byte, &types[type].validation[2].required_value ) < 6 ) { printf("Given validationSequence '%s' isn't valid!\n", getval("validationSequence")); exit (1); } if (getval("shutdownArguments") && sscanf(getval("shutdownArguments"), "{{%u,%u},%c}", &types[type].shutdown_arguments.delay[0], &types[type].shutdown_arguments.delay[1], &types[type].shutdown_arguments.minutesShouldBeUsed ) < 3 ) { printf("Given shutdownArguments '%s' isn't valid!\n", getval("shutdownArguments")); exit (1); } if (getval("frequency") && sscanf(getval("frequency"), "{%f,%f}", &types[type].freq[0], &types[type].freq[1] ) < 2 ) { printf("Given frequency '%s' isn't valid!\n", getval("frequency")); exit (1); } if (getval("loadPercentage") && sscanf(getval("loadPercentage"), "{%f,%f,%f,%f}", &types[type].loadpct[0], &types[type].loadpct[1], &types[type].loadpct[2], &types[type].loadpct[3] ) < 4 ) { printf("Given loadPercentage '%s' isn't valid!\n", getval("loadPercentage")); exit (1); } if (getval("batteryPercentage") && sscanf(getval("batteryPercentage"), "{%f,%f,%f,%f,%f}", &types[type].battpct[0], &types[type].battpct[1], &types[type].battpct[2], &types[type].battpct[3], &types[type].battpct[4] ) < 5 ) { printf("Given batteryPercentage '%s' isn't valid!\n", getval("batteryPercentage")); exit (1); } if (getval("voltage") && sscanf(getval("voltage"), "{%f,%f,%f,%f}", &types[type].voltage[0], &types[type].voltage[1], &types[type].voltage[2], &types[type].voltage[3] ) < 4 ) { printf("Given voltage '%s' isn't valid!\n", getval("voltage")); exit (1); } /* open serial port */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); /* setup flow control */ types[type].flowControl.setup_flow_control(); /* Setup Model and LineVoltage */ if (!strncmp(types[type].name, "BNT",3) || !strcmp(types[type].name, "KIN") || !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { if (!ups_getinfo()) return; /* Give "BNT-other" a chance! */ if (raw_data[MODELNAME]==0x42 || raw_data[MODELNAME]==0x4B || raw_data[MODELNAME]==0x4F){ /* Give "IMP" a chance also! */ if (raw_data[UPSVERSION]==0xFF){ types[type].name="IMP"; model=IMPmodels[raw_data[MODELNUMBER]/16]; } else { model=BNTmodels[raw_data[MODELNUMBER]/16]; if (!strcmp(types[type].name, "BNT-other")) types[type].name="BNT-other"; else if (raw_data[MODELNAME]==0x42) types[type].name="BNT"; else if (raw_data[MODELNAME]==0x4B){ types[type].name="KIN"; model=KINmodels[raw_data[MODELNUMBER]/16]; } else if (raw_data[MODELNAME]==0x4F){ types[type].name="OPTI"; model=OPTImodels[raw_data[MODELNUMBER]/16]; } } } else if (raw_data[UPSVERSION]==0xFF){ types[type].name="IMP"; model=IMPmodels[raw_data[MODELNUMBER]/16]; } linevoltage=voltages[raw_data[MODELNUMBER]%16]; if (!strcmp(types[type].name, "OPTI")) { snprintf(buf,sizeof(buf),"%s-%d",types[type].name,model); } else { snprintf(buf,sizeof(buf),"%s-%dAP",types[type].name,model); } if (!strcmp(modelname, "Unknown")) modelname=buf; upsdebugx(1,"Detected: %s , %dV",buf,linevoltage); if (ser_send_char (upsfd, BATTERY_TEST) != 1) { upslogx(LOG_NOTICE, "writing error"); dstate_datastale(); return; } } upsdebugx(1, "Values of arguments:"); upsdebugx(1, " manufacturer : '%s'", manufacturer); upsdebugx(1, " model name : '%s'", modelname); upsdebugx(1, " serial number : '%s'", serialnumber); upsdebugx(1, " line voltage : '%u'", linevoltage); upsdebugx(1, " type : '%s'", types[type].name); upsdebugx(1, " number of bytes from UPS: '%u'", types[type].num_of_bytes_from_ups); upsdebugx(1, " method of flow control : '%s'", types[type].flowControl.name); upsdebugx(1, " validation sequence: '{{%u,%#x},{%u,%#x},{%u,%#x}}'", types[type].validation[0].index_of_byte, types[type].validation[0].required_value, types[type].validation[1].index_of_byte, types[type].validation[1].required_value, types[type].validation[2].index_of_byte, types[type].validation[2].required_value); upsdebugx(1, " shutdown arguments: '{{%u,%u},%c}'", types[type].shutdown_arguments.delay[0], types[type].shutdown_arguments.delay[1], types[type].shutdown_arguments.minutesShouldBeUsed); if ( strcmp(types[type].name, "KIN") && strcmp(types[type].name, "BNT") && strcmp(types[type].name, "IMP")) { upsdebugx(1, " frequency calculation coefficients: '{%f,%f}'", types[type].freq[0], types[type].freq[1]); upsdebugx(1, " load percentage calculation coefficients: " "'{%f,%f,%f,%f}'", types[type].loadpct[0], types[type].loadpct[1], types[type].loadpct[2], types[type].loadpct[3]); upsdebugx(1, " battery percentage calculation coefficients: " "'{%f,%f,%f,%f,%f}'", types[type].battpct[0], types[type].battpct[1], types[type].battpct[2], types[type].battpct[3], types[type].battpct[4]); upsdebugx(1, " voltage calculation coefficients: '{%f,%f}'", types[type].voltage[2], types[type].voltage[3]); } } /* display help */ void upsdrv_help(void) { // 1 2 3 4 5 6 7 8 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 MAX printf("\n"); printf("Specify UPS information in the ups.conf file.\n"); printf(" type: Type of UPS: 'Trust','Egys','KP625AP','IMP','KIN','BNT',\n"); printf(" 'BNT-other', 'OPTI' (default: 'Trust')\n"); printf(" 'BNT-other' is a special type intended for BNT 100-120V models,\n"); printf(" but can be used to override ALL models.\n"); printf("You can additional specify these variables:\n"); printf(" manufacturer: Manufacturer name (default: 'PowerCom')\n"); printf(" modelname: Model name (default: 'Unknown' or autodetected)\n"); printf(" serialnumber: Serial number (default: Unknown)\n"); printf(" shutdownArguments: 3 delay arguments for the shutdown operation:\n"); printf(" {{Minutes,Seconds},UseMinutes?}\n"); printf(" where Minutes and Seconds are integer, UseMinutes? is either\n"); printf(" 'y' or 'n'.\n"); printf("You can specify these variables if not automagically detected for types\n"); printf(" 'IMP','KIN','BNT'\n"); printf(" linevoltage: Line voltage: 110-120 or 220-240 (default: 230)\n"); printf(" numOfBytesFromUPS: Number of bytes in a UPS frame: 16 is common, 11 for 'Trust'\n"); printf(" methodOfFlowControl: Flow control method for UPS:\n"); printf(" 'dtr0rts1', 'dtr1' or 'no_flow_control'\n"); printf(" validationSequence: 3 pairs of validation values: {{I,V},{I,V},{I,V}}\n"); printf(" where I is the index into BytesFromUPS (see numOfBytesFromUPS)\n"); printf(" and V is the value for the ByteIndex to match.\n"); printf(" frequency: Input & Output Frequency conversion values: {A, B}\n"); printf(" used in function: 1/(A*x+B)\n"); printf(" If the raw value x IS the frequency, then A=1/(x^2), B=0\n"); printf(" loadPercentage: Load conversion values for Battery and Line load: {BA,BB,LA,LB}\n"); printf(" used in function: A*x+B\n"); printf(" If the raw value x IS the Load Percent, then A=1, B=0\n"); printf(" batteryPercentage: Battery conversion values for Battery and Line power:\n"); printf(" {A,B,C,D,E}\n"); printf(" used in functions: (Battery) A*x+B*y+C, (Line) D*x+E\n"); printf(" If the raw value x IS the Battery Percent, then\n"); printf(" A=1, B=0, C=0, D=1, E=0\n"); printf(" voltage: Voltage conversion values for 240 and 120 voltage:\n"); printf(" {240A,240B,120A,120B}\n"); printf(" used in function: A*x+B\n"); printf(" If the raw value x IS HALF the Voltage, then A=2, B=0\n\n"); printf("Example for BNT1500AP in ups.conf:\n"); printf("[BNT1500AP]\n"); printf(" driver = powercom\n"); printf(" port = /dev/ttyS0\n"); printf(" desc = \"PowerCom BNT 1500 AP\"\n"); printf(" manufacturer = PowerCom\n"); printf(" modelname = BNT1500AP\n"); printf(" serialnumber = 13245678900\n"); printf(" type = BNT-other\n"); printf("# linevoltage = 120\n"); printf("# numOfBytesFromUPS = 16\n"); printf("# methodOfFlowControl = no_flow_control\n"); printf("# validationSequence = {{8,0},{8,0},{8,0}}\n"); printf("# shutdownArguments = {{1,30},y}\n"); printf("# frequency = {0.00027778,0.0000}\n"); printf("# loadPercentage = {1.0000,0.0,1.0000,0.0}\n"); printf("# batteryPercentage = {1.0000,0.0000,0.0000,1.0000,0.0000}\n"); printf("# voltage = {2.0000,0.0000,2.0000,0.0000}\n"); return; } /* initialize information */ void upsdrv_initinfo(void) { /* write constant data for this model */ dstate_setinfo ("ups.mfr", "%s", manufacturer); dstate_setinfo ("ups.model", "%s", modelname); dstate_setinfo ("ups.serial", "%s", serialnumber); dstate_setinfo ("ups.model.type", "%s", types[type].name); dstate_setinfo ("input.voltage.nominal", "%u", linevoltage); /* now add the instant commands */ dstate_addcmd ("test.battery.start"); dstate_addcmd ("shutdown.return"); dstate_addcmd ("shutdown.stayoff"); upsh.instcmd = instcmd; } /* define possible arguments */ void upsdrv_makevartable(void) { // 1 2 3 4 5 6 7 8 //2345678901234567890123456789012345678901234567890123456789012345678901234567890 MAX addvar(VAR_VALUE, "type", "Type of UPS: 'Trust','Egys','KP625AP','IMP','KIN','BNT','BNT-other','OPTI'\n" " (default: 'Trust')"); addvar(VAR_VALUE, "manufacturer", "Manufacturer name (default: 'PowerCom')"); addvar(VAR_VALUE, "modelname", "Model name [cannot be detected] (default: Unknown)"); addvar(VAR_VALUE, "serialnumber", "Serial number [cannot be detected] (default: Unknown)"); addvar(VAR_VALUE, "shutdownArguments", "Delay values for shutdown: Minutes, Seconds, UseMinutes?'y'or'n'"); addvar(VAR_VALUE, "linevoltage", "Line voltage 110-120 or 220-240 V (default: 230)"); addvar(VAR_VALUE, "numOfBytesFromUPS", "The number of bytes in a UPS frame"); addvar(VAR_VALUE, "methodOfFlowControl", "Flow control method for UPS: 'dtr0rts1' or 'no_flow_control'"); addvar(VAR_VALUE, "validationSequence", "Validation values: ByteIndex, ByteValue x 3"); if ( strcmp(types[type].name, "KIN") && strcmp(types[type].name, "BNT") && strcmp(types[type].name, "IMP")) { addvar(VAR_VALUE, "frequency", "Frequency conversion values: FreqFactor, FreqConst"); addvar(VAR_VALUE, "loadPercentage", "Load conversion values: OffFactor, OffConst, OnFactor, OnConst"); addvar(VAR_VALUE, "batteryPercentage", "Battery conversion values: OffFactor, LoadFactor, OffConst, OnFactor, OnConst"); addvar(VAR_VALUE, "voltage", "Voltage conversion values: 240VFactor, 240VConst, 120VFactor, 120VConst"); } } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/microdowell.c0000644000175000017500000007316112640473702013432 00000000000000/* * * microdowell.c: support for Microdowell Enterprise Nxx/Bxx serial protocol based UPSes * * Copyright (C) Elio Corbolante * * microdowell.c created on 27/09/2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 */ /* anything commented is optional anything else is mandatory */ #define ENTERPRISE_PROTOCOL #include "microdowell.h" #include "main.h" #include "serial.h" #include #include "timehead.h" #define MAX_START_DELAY 999999 #define MAX_SHUTDOWN_DELAY 32767 /* Maximum length of a string representing these values */ #define MAX_START_DELAY_LEN 6 #define MAX_SHUTDOWN_DELAY_LEN 5 #define DRIVER_NAME "MICRODOWELL UPS driver" #define DRIVER_VERSION "0.01" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Elio Corbolante ", DRV_STABLE, { NULL } }; ENT_STRUCT ups ; int instcmd(const char *cmdname, const char *extra); int setvar(const char *varname, const char *val); /* he knew... macros should evaluate their arguments only once */ #define CLAMP(x, min, max) (((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x))) static int CheckDataChecksum(unsigned char *Buff, int Len) { int i, Idx ; unsigned char Xor ; ups.FramePointer = Xor = 0 ; for (Idx=0 ; Idx < Len ; Idx++) if (Buff[Idx] == STX_CHAR) break ; ups.FramePointer = Idx ; /* Memorise start point. */ /* Check that the message is not to short... */ if ( (Idx > (Len-4)) || (Idx+Buff[Idx+1]+2 > Len) ) return(ERR_MSG_TOO_SHORT) ; /* To short message! */ /* Calculate checksum */ for (i=Idx+1 ; i < Idx+Buff[Idx+1]+2 ; i++) Xor ^= Buff[i] ; /* if Xor != then checksum error */ if (Xor != Buff[i]) return(ERR_MSG_CHECKSUM) ; /* error in checksum */ /* If checksum OK: return */ return(0) ; } static const char *ErrMessages[] = { /* 0 */ "errorcode NOT DEFINED", /* default error message */ /* 1 */ "I2C bus busy (e2prom)", /* 2 */ "Command received: checksum not valid", /* 3 */ "Command received: unrecognized command", /* 4 */ "WRITE: eeprom address not multiple of 8", /* 5 */ "READ: eeprom address (added with size) out of bound ", /* 6 */ "error writing e2prom address", /* 7 */ "error writing e2prom subaddress", /* 8 */ "error reading e2prom data", /* 9 */ "error writing e2prom address", /* 10 */ "error reading e2prom subaddress", /* 11 */ "error writing e2prom data", /* 12 */ "error writing e2prom address during data verification", /* 13 */ "error verification e2prom data", /* 14 */ "e2prom data are different from those in the write buffer", /* 15 */ "e2prom checksum error", /* 16 */ "NO CHARS FROM PORT", /* 17 */ "TOO FEW DATA RECEIVED: [STX] near end of message", /* 18 */ "CHECKSUM ERROR IN MESSAGE", /* 19 */ "OK", /* */ "" } ; const char *PrintErr(int ErrCode) { int msgIndex = 0 ; /* The default 'msgIndex' is 0 (error code not defined) */ switch (ErrCode) { case ERR_NO_ERROR : msgIndex = 19 ; break ; case ERR_I2C_BUSY : msgIndex = 1 ; break ; case ERR_CMD_CHECKSUM : msgIndex = 2 ; break ; case ERR_CMD_UNRECOG : msgIndex = 3 ; break ; case ERR_EEP_NOBLOCK : msgIndex = 4 ; break ; case ERR_EEP_OOBOUND : msgIndex = 5 ; break ; case ERR_EEP_WADDR1 : msgIndex = 6 ; break ; case ERR_EEP_WSADDR1 : msgIndex = 7 ; break ; case ERR_EEP_RDATA : msgIndex = 8 ; break ; case ERR_EEP_WADDR2 : msgIndex = 9 ; break ; case ERR_EEP_WSADDR2 : msgIndex = 10 ; break ; case ERR_EEP_WDATA : msgIndex = 11 ; break ; case ERR_EEP_WADDRVER : msgIndex = 12 ; break ; case ERR_EEP_WDATAVER : msgIndex = 13 ; break ; case ERR_EEP_VERIFY : msgIndex = 14 ; break ; case ERR_EEP_CHECKSUM : msgIndex = 15 ; break ; case ERR_COM_NO_CHARS : msgIndex = 16 ; break ; case ERR_MSG_TOO_SHORT : msgIndex = 17 ; break ; case ERR_MSG_CHECKSUM : msgIndex = 18 ; break ; default: msgIndex = 0 ; break ; } return(ErrMessages[msgIndex]) ; } int CheckErrCode(unsigned char * Buff) { auto int Ret ; switch (Buff[2]) { /* I have found an error */ case CMD_NACK : Ret = Buff[3] ; break ; case CMD_ACK : case CMD_GET_STATUS : case CMD_GET_MEASURES : case CMD_GET_CONFIG : case CMD_GET_BATT_STAT : case CMD_GET_MASK : case CMD_SET_TIMER : case CMD_BATT_TEST : case CMD_GET_BATT_TEST : case CMD_SD_ONESHOT : case CMD_GET_SD_ONESHOT: case CMD_SET_SCHEDULE : case CMD_GET_SCHEDULE : case CMD_GET_EEP_BLOCK : case CMD_SET_EEP_BLOCK : case CMD_GET_EEP_SEED : case CMD_INIT : Ret = 0 ; break ; /* command not recognized */ default: Ret = ERR_CMD_UNRECOG ; break ; } return(Ret) ; } void SendCmdToSerial(unsigned char *Buff, int Len) { int i; unsigned char Tmp[20], Xor ; Tmp[0] = STX_CHAR ; Xor = Tmp[1] = (unsigned char) (Len & 0x1f) ; for (i=0 ; i < Tmp[1] ; i++) { Tmp[i+2] = Buff[i] ; Xor ^= Buff[i] ; } Tmp[Len+2] = Xor ; upsdebug_hex(4, "->UPS", Tmp, Len+3) ; /* flush serial port */ ser_flush_in(upsfd, "", 0) ; /* empty input buffer */ ser_send_buf(upsfd, Tmp, Len+3) ; /* send data to the UPS */ } unsigned char * CmdSerial(unsigned char *OutBuffer, int Len, unsigned char *RetBuffer) { #define TMP_BUFF_LEN 1024 unsigned char InpBuff[TMP_BUFF_LEN+1] ; unsigned char TmpBuff[3] ; int i, ErrCode ; unsigned char *p ; int BuffLen ; /* The default error code (no received character) */ ErrCode = ERR_COM_NO_CHARS ; SendCmdToSerial(OutBuffer, Len) ; usleep(10000) ; /* small delay (1/100 s) */ /* get chars until timeout */ BuffLen = 0 ; while (ser_get_char(upsfd, TmpBuff, 0, 10000) == 1) { InpBuff[BuffLen++] = TmpBuff[0] ; if (BuffLen > TMP_BUFF_LEN) break ; } upsdebug_hex(4, "UPS->", InpBuff, BuffLen) ; if (BuffLen > 0) { ErrCode = CheckDataChecksum(InpBuff, BuffLen) ; /* upsdebugx(4, "ErrCode = %d / Len = %d", ErrCode, BuffLen); */ if (!ErrCode) { /* FramePointer to valid data! */ p = InpBuff + ups.FramePointer ; /* p now point to valid data. check if it is a error code. */ ErrCode = CheckErrCode(p) ; if (!ErrCode) { /* I copy the data read in the buffer */ for(i=0 ; i<(int) (p[1])+3 ; i++) RetBuffer[i] = p[i] ; ups.ErrCode = ups.ErrCount = ups.CommStatus = 0 ; return(RetBuffer) ; } } } /* if they have arrived here, wants to say that I have found an error.... */ ups.ErrCode = ErrCode ; ups.ErrCount++ ; if (ups.ErrCount > 3) { ups.CommStatus &= 0x80 ; ups.CommStatus |= (unsigned char) (ups.ErrCount & 0x7F) ; if (ups.ErrCount > 100) ups.ErrCount = 100 ; } return(NULL) ; /* There have been errors in the reading of the data */ } static int detect_hardware(void) { unsigned char OutBuff[20] ; unsigned char InpBuff[260] ; unsigned char *p ; int i, retries ; struct tm *Time ; time_t lTime ; ups.ge_2kVA = 0 ; for (retries=0 ; retries <= 4 ; retries++) { /* Identify UPS model */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_UPS_MODEL ; /* UPS model */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got UPS model */ for (i=0 ; i<8 ; i++) ups.UpsModel[i] = p[i+5] ; ups.UpsModel[8] = '\0' ; upsdebugx(2, "get 'UPS model': %s", PrintErr(ups.ErrCode)); break ; /* UPS identified: exit from ' for' LOOP */ } else { upsdebugx(1, "[%d] get 'UPS model': %s", retries, PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "[%d] Unable to identify UPS model [%s]", retries, PrintErr(ups.ErrCode)); usleep(100000) ; /* small delay (1/10 s) for next retry */ } } /* check if I was unable to find the UPS */ if (retries == 4) /* UPS not found! */ return -1; /* UPS serial number */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_SERIAL_NUM ; /* UPS serial # */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got UPS serial # */ for (i=0 ; i<8 ; i++) ups.SerialNumber[i] = p[i+5] ; ups.SerialNumber[8] = '\0' ; upsdebugx(2, "get 'UPS Serial #': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'UPS Serial #': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to identify UPS serial # [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get Production date & FW info */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_PROD_DATE ; /* Production date + HW version */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got Production date & FW info */ p += 5 ; /* 'p' points to eeprom data */ ups.YearOfProd = 2000 + p[0] ; /* Production year of the UPS */ ups.MonthOfProd = p[1] ; /* Production month of the UPS */ ups.DayOfProd = p[2] ; /* Production day of the UPS */ ups.HW_MajorVersion = (p[3]>>4) & 0x0F ; /* Hardware: Major version */ ups.HW_MinorVersion = (p[3] & 0x0F) ; /* Hardware: Minor version */ ups.BR_MajorVersion = (p[4]>>4) & 0x0F ; /* BoardHardware: Major version */ ups.BR_MinorVersion = (p[4] & 0x0F) ; /* BoardHardware: Minor version */ ups.FW_MajorVersion = (p[5]>>4) & 0x0F ; /* Firmware: Major version */ ups.FW_MinorVersion = (p[5] & 0x0F) ; /* Firmware: Minor version */ ups.FW_SubVersion = p[6] ; /* Firmware: SUBVERSION (special releases */ ups.BatteryNumber = p[7] ; /* number of batteries in UPS */ upsdebugx(2, "get 'Production date': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Production date': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Production date [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get Battery substitution date */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_BATT_SUBST ; /* Battery substitution dates */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got Battery substitution date */ p += 5 ; /* 'p' points to eeprom data */ upsdebugx(2, "get 'Battery Subst. Dates': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Battery Subst. Dates': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Battery Subst. Dates [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get working time (battery+normal)) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_MIN_VBATT ; /* working time */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got working time (battery+normal)) */ p += 5 ; /* 'p' points to eeprom data */ upsdebugx(2, "get 'UPS life info': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'UPS life info': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read UPS life info [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get the THRESHOLD table (0) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_THRESHOLD_0 ; /* Thresholds table 0 */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got the THRESHOLD table (0) */ p += 5 ; /* 'p' points to eeprom data */ upsdebugx(2, "get 'Thresholds table 0': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Thresholds table 0': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Thresholds table 0 [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get the THRESHOLD table (1) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_THRESHOLD_1 ; /* Thresholds table 0 */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got the THRESHOLD table (1) */ p += 5 ; /* 'p' points to eeprom data */ upsdebugx(2, "get 'Thresholds table 1': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Thresholds table 1': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Thresholds table 1 [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get the THRESHOLD table (2) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_THRESHOLD_2 ; /* Thresholds table 0 */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got the THRESHOLD table (2) */ p += 5 ; /* 'p' points to eeprom data */ upsdebugx(2, "get 'Thresholds table 2': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Thresholds table 2': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Thresholds table 2 [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get Option Bytes */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_OPT_BYTE_BLK ; /* Option Bytes */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got Option Bytes */ p += 5 ; /* 'p' points to eeprom data */ dstate_setinfo("input.voltage.nominal", "%s", (p[EEP_OPT_BYTE_1] & 0x02) ? "110": "230") ; dstate_setinfo("input.frequency", "%s", (p[EEP_OPT_BYTE_1] & 0x01) ? "60.0": "50.0") ; upsdebugx(2, "get 'Option Bytes': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Option Bytes': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Option Bytes [%s]", PrintErr(ups.ErrCode)); return -1; } /* Get UPS sensitivity (fault points) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_FAULT_POINTS ; /* Number of fault points (sensitivity)) */ OutBuff[2] = 8 ; /* number of bytes */ if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL) { /* got UPS sensitivity (fault points) */ p += 5 ; /* 'p' points to eeprom data */ switch (p[0]) { case 1 : dstate_setinfo("input.sensitivity", "H") ; break ; case 2 : dstate_setinfo("input.sensitivity", "M") ; break ; case 3 : dstate_setinfo("input.sensitivity", "L") ; break ; default : dstate_setinfo("input.sensitivity", "L") ; break ; } upsdebugx(2, "get 'Input Sensitivity': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Input Sensitivity': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to read Input Sensitivity [%s]", PrintErr(ups.ErrCode)); return -1; } /* Set internal UPS clock */ time(&lTime) ; Time = localtime(&lTime) ; OutBuff[0] = CMD_SET_TIMER ; /* set UPS internal timer */ OutBuff[1] = (Time->tm_wday+6) % 7 ; /* week day (0=monday) */ OutBuff[2] = Time->tm_hour ; /* hours */ OutBuff[3] = Time->tm_min ; /* minutes */ OutBuff[4] = Time->tm_sec; /* seconds */ if ((p = CmdSerial(OutBuff, LEN_SET_TIMER, InpBuff)) != NULL) { upsdebugx(2, "set 'UPS internal clock': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "set 'UPS internal clock': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Unable to set UPS internal clock [%s]", PrintErr(ups.ErrCode)); return -1; } return 0; /* everything was OK */ } /* ========================= */ void upsdrv_updateinfo(void) { unsigned char OutBuff[20] ; unsigned char InpBuff[260] ; unsigned char *p ; /* int i ; */ OutBuff[0] = CMD_GET_STATUS ; /* get UPS status */ if ((p = CmdSerial(OutBuff, LEN_GET_STATUS, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ status_init(); /* reset status flags */ /* store last UPS status */ ups.StatusUPS = (int)p[0] | ((int)p[1]<<8) | ((int)p[2]<<16) | ((int)p[3]<<24) ; ups.ShortStatus = (int)p[0] | ((int)p[1]<<8) ; upsdebugx(1, "ups.StatusUPS: %08lX", ups.StatusUPS); upsdebugx(1, "ups.ShortStatus: %04X", ups.ShortStatus); /* on battery? */ if (p[0] & 0x01) status_set("OB"); /* YES */ /* LOW battery? */ if (p[0] & 0x02) status_set("LB"); /* YES */ /* online? */ if (p[0] & 0x08) status_set("OL"); /* YES */ /* Overload? */ if (p[1] & 0xC0) status_set("OVER"); /* YES */ /* Offline/Init/Stanby/Waiting for mains? */ if (p[0] & 0xE0) status_set("OFF"); /* YES */ /* AVR on (boost)? */ if (p[4] & 0x04) status_set("BOOST"); /* YES */ /* AVR on (buck)? */ if (p[4] & 0x08) status_set("TRIM"); /* YES */ dstate_setinfo("ups.time", "%02d:%02d:%02d", p[6], p[7], p[8]) ; upsdebugx(3, "get 'Get Status': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Get Status': %s", PrintErr(ups.ErrCode)); /* upslogx(LOG_ERR, "get 'Get Status': %s", PrintErr(ups.ErrCode)); */ dstate_datastale(); return; } /* ========================= */ OutBuff[0] = CMD_GET_MEASURES ; /* get UPS values */ if ((p = CmdSerial(OutBuff, LEN_GET_MEASURES, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ dstate_setinfo("input.voltage", "%d", (int)((float)(p[2]*256 + p[3]) / 36.4)) ; if (ups.ge_2kVA) { dstate_setinfo("output.voltage", "%d", (int)((float)(p[6]*256 + p[7]) / 63.8)) ; dstate_setinfo("output.current", "%1.f", ((float)(p[8]*256 + p[9]) / 635.0)) ; dstate_setinfo("battery.voltage", "%.1f", ((float) (p[4]*256 + p[5])) / 329.0) ; } else { dstate_setinfo("output.voltage", "%d", (int)((float)(p[6]*256 + p[7]) / 36.4)) ; dstate_setinfo("output.current", "%1.f", ((float)(p[8]*256 + p[9]) / 1350.0)) ; dstate_setinfo("battery.voltage", "%.1f", ((float) (p[4]*256 + p[5])) / 585.0) ; } dstate_setinfo("ups.temperature", "%d", (int)(((float)(p[10]*256 + p[11])-202.97) / 1.424051)) ; upsdebugx(3, "get 'Get Measures': %s", PrintErr(ups.ErrCode)); } else { /* upsdebugx(1, "get 'Get Measures': %s", PrintErr(ups.ErrCode)); */ upslogx(LOG_ERR, "get 'Get Measures': %s", PrintErr(ups.ErrCode)); dstate_datastale(); return; } /* ========================= */ OutBuff[0] = CMD_GET_BAT_LD ; /* get UPS Battery and Load values */ if ((p = CmdSerial(OutBuff, LEN_GET_BAT_LD, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ dstate_setinfo("ups.power", "%d", (p[4]*256 + p[5])) ; /* dstate_setinfo("ups.realpower", "%d", (int)((float)(p[4]*256 + p[5]) * 0.6)) ; */ dstate_setinfo("battery.charge", "%d", (int)p[0]) ; dstate_setinfo("ups.load", "%d", (int)p[6]) ; upsdebugx(3, "get 'Get Batt+Load Status': %s", PrintErr(ups.ErrCode)); } else { /* upsdebugx(1, "get 'Get Batt+Load Status': %s", PrintErr(ups.ErrCode)); */ upslogx(LOG_ERR, "get 'Get Batt+Load Status': %s", PrintErr(ups.ErrCode)); dstate_datastale(); return; } status_commit(); dstate_dataok(); poll_interval = 2; } /* ========================= */ int instcmd(const char *cmdname, const char *extra) { unsigned char OutBuff[20] ; unsigned char InpBuff[260] ; unsigned char *p ; /* int i ; */ upsdebugx(1, "instcmd(%s, %s)", cmdname, extra); if (strcasecmp(cmdname, "load.on") == 0) { OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */ OutBuff[1] = 0xFF ; /* ALL outputs */ OutBuff[2] = 0x08 ; /* Enable outputs (immediately) */ OutBuff[3] = 0 ; OutBuff[4] = 0 ; OutBuff[5] = 0 ; OutBuff[6] = 0 ; OutBuff[7] = 0 ; if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ upslogx(LOG_INFO, "Turning load on."); upsdebugx(1, "'SdOneshot(turn_ON)': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "'SdOneshot(turn_ON)': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "'SdOneshot(turn_ON)': %s", PrintErr(ups.ErrCode)); } return STAT_INSTCMD_HANDLED; } if (strcasecmp(cmdname, "load.off") == 0) { OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */ OutBuff[1] = 0xFF ; /* ALL outputs */ OutBuff[2] = 0x04 ; /* Disable outputs (immediately) */ OutBuff[3] = 0 ; OutBuff[4] = 0 ; OutBuff[5] = 0 ; OutBuff[6] = 0 ; OutBuff[7] = 0 ; if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ upslogx(LOG_INFO, "Turning load on."); upsdebugx(1, "'SdOneshot(turn_OFF)': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "'SdOneshot(turn_OFF)': %s", PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "'SdOneshot(turn_OFF)': %s", PrintErr(ups.ErrCode)); } return STAT_INSTCMD_HANDLED; } if (strcasecmp(cmdname, "shutdown.return") == 0) { OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */ OutBuff[1] = 0xFF ; /* ALL outputs */ if (ups.StatusUPS & 0x01) OutBuff[2] = 0x02 ; /* Battery shutdown */ else OutBuff[2] = 0x01 ; /* Online shutdown */ if (ups.ShutdownDelay < 6) ups.ShutdownDelay = 6 ; OutBuff[3] = (ups.ShutdownDelay >> 8) & 0xFF ; /* SDDELAY (MSB) Shutdown value (seconds) */ OutBuff[4] = (ups.ShutdownDelay & 0xFF) ; /* SDDELAY (LSB) */ OutBuff[5] = (ups.WakeUpDelay >> 16) & 0xFF ; /* WUDELAY (MSB) Wakeup value (seconds) */ OutBuff[6] = (ups.WakeUpDelay >> 8) & 0xFF ; /* WUDELAY (...) */ OutBuff[7] = (ups.WakeUpDelay & 0xFF ) ; /* WUDELAY (LSB) */ if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ upslogx(LOG_INFO, "Shutdown command(TYPE=%02x, SD=%u, WU=%u)", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay) ; upsdebugx(3, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode)); } else { upsdebugx(1, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Shutdown command(SD=%u, WU=%u): %s", ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode)); } return STAT_INSTCMD_HANDLED; } if (strcasecmp(cmdname, "shutdown.stayoff") == 0) { OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */ OutBuff[1] = 0xFF ; /* ALL outputs */ if (ups.StatusUPS & 0x01) OutBuff[2] = 0x02 ; /* Battery shutdown */ else OutBuff[2] = 0x01 ; /* Online shutdown */ if (ups.ShutdownDelay < 6) ups.ShutdownDelay = 6 ; OutBuff[3] = (ups.ShutdownDelay >> 8) & 0xFF ; /* SDDELAY (MSB) Shutdown value (seconds) */ OutBuff[4] = (ups.ShutdownDelay & 0xFF) ; /* SDDELAY (LSB) */ OutBuff[5] = 0 ; /* WUDELAY (MSB) Wakeup value (seconds) */ OutBuff[6] = 0 ; /* WUDELAY (...) */ OutBuff[7] = 0 ; /* WUDELAY (LSB) */ if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ upslogx(LOG_INFO, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u)", OutBuff[2], ups.ShutdownDelay, 0) ; upsdebugx(3, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, 0, PrintErr(ups.ErrCode)); } else { upsdebugx(1, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, 0, PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u)", OutBuff[2], ups.ShutdownDelay, 0) ; } return STAT_INSTCMD_HANDLED; } return STAT_INSTCMD_UNKNOWN; } int setvar(const char *varname, const char *val) { int delay; if (sscanf(val, "%d", &delay) != 1) { return STAT_SET_UNKNOWN; } if (strcasecmp(varname, "ups.delay.start") == 0) { delay = CLAMP(delay, 0, MAX_START_DELAY); upsdebugx(1, "set 'WUDELAY': %d/%d", delay, ups.WakeUpDelay); ups.WakeUpDelay = delay ; dstate_setinfo("ups.delay.start", "%d", ups.WakeUpDelay); dstate_dataok(); return STAT_SET_HANDLED; } if (strcasecmp(varname, "ups.delay.shutdown") == 0) { delay = CLAMP(delay, 0, MAX_SHUTDOWN_DELAY); upsdebugx(1, "set 'SDDELAY': %d/%d", delay, ups.ShutdownDelay); ups.ShutdownDelay = delay; dstate_setinfo("ups.delay.shutdown", "%d", ups.ShutdownDelay); dstate_dataok(); return STAT_SET_HANDLED; } return STAT_SET_UNKNOWN; } void upsdrv_initinfo(void) { /* Get vars from ups.conf */ if (getval("ups.delay.shutdown")) { ups.ShutdownDelay = CLAMP(atoi(getval("ups.delay.shutdown")), 0, MAX_SHUTDOWN_DELAY); } else { ups.ShutdownDelay = 120; /* Shutdown delay in seconds */ } if (getval("ups.delay.start")) { ups.WakeUpDelay = CLAMP(atoi(getval("ups.delay.start")), 0, MAX_START_DELAY); } else { ups.WakeUpDelay = 10; /* WakeUp delay in seconds */ } if (detect_hardware() == -1) { fatalx(EXIT_FAILURE, "Unable to detect a Microdowell's Enterprise UPS on port %s\nCheck the cable, port name and try again", device_path); } /* I set the correspondig UPS variables They were read in 'detect_hardware()' some other variables were set in 'detect_hardware()' */ dstate_setinfo("ups.model", "Enterprise N%s", ups.UpsModel+3) ; dstate_setinfo("ups.power.nominal", "%d", atoi(ups.UpsModel+3) * 100) ; dstate_setinfo("ups.realpower.nominal", "%d", atoi(ups.UpsModel+3) * 60) ; ups.ge_2kVA = 0 ; /* differentiate between 2 type of UPSs */ if (atoi(ups.UpsModel+3) >= 20) ups.ge_2kVA = 1 ; dstate_setinfo("ups.type", "online-interactive") ; dstate_setinfo("ups.serial", "%s", ups.SerialNumber) ; dstate_setinfo("ups.firmware", "%d.%d (%d)", ups.FW_MajorVersion, ups.FW_MinorVersion, ups.FW_SubVersion) ; dstate_setinfo("ups.firmware.aux", "%d.%d %d.%d", ups.HW_MajorVersion, ups.HW_MinorVersion, ups.BR_MajorVersion, ups.BR_MinorVersion) ; dstate_setinfo("ups.mfr", "Microdowell") ; dstate_setinfo("ups.mfr.date", "%04d/%02d/%02d", ups.YearOfProd, ups.MonthOfProd, ups.DayOfProd) ; dstate_setinfo("battery.packs", "%d", ups.BatteryNumber) ; /* Register the available variables. */ dstate_setinfo("ups.delay.start", "%d", ups.WakeUpDelay); dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.start", MAX_START_DELAY_LEN); dstate_setinfo("ups.delay.shutdown", "%d", ups.ShutdownDelay); dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.shutdown", MAX_SHUTDOWN_DELAY_LEN); dstate_addcmd("load.on"); dstate_addcmd("load.off"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); /* Register the available instant commands. */ /* dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); dstate_addcmd("shutdown.stop"); dstate_addcmd("beeper.toggle"); */ /* set handlers */ upsh.instcmd = instcmd ; upsh.setvar = setvar; } void upsdrv_shutdown(void) { unsigned char OutBuff[20] ; unsigned char InpBuff[260] ; unsigned char *p ; unsigned char BatteryFlag=0 ; OutBuff[0] = CMD_GET_STATUS ; /* get UPS status */ if ((p = CmdSerial(OutBuff, LEN_GET_STATUS, InpBuff)) != NULL) { p += 3 ; /* 'p' points to received data */ status_init(); /* reset status flags */ /* store last UPS status */ ups.StatusUPS = (int)p[0] | ((int)p[1]<<8) | ((int)p[2]<<16) | ((int)p[3]<<24) ; ups.ShortStatus = (int)p[0] | ((int)p[1]<<8) ; upsdebugx(1, "ups.StatusUPS: %08lX", ups.StatusUPS); upsdebugx(1, "ups.ShortStatus: %04X", ups.ShortStatus); /* on battery? */ if (p[0] & 0x01) BatteryFlag = 1 ; /* YES */ upsdebugx(3, "get 'Get Status': %s", PrintErr(ups.ErrCode)); } else { upsdebugx(1, "get 'Get Status': %s", PrintErr(ups.ErrCode)); /* upslogx(LOG_ERR, "get 'Get Status': %s", PrintErr(ups.ErrCode)); */ } /* Send SHUTDOWN command */ OutBuff[0] = CMD_SD_ONESHOT ; /* Send SHUTDOWN command */ OutBuff[1] = 0xFF ; /* shutdown on ALL ports */ /* is the UPS on battery? */ if (BatteryFlag) OutBuff[2] = 0x02 ; /* Type of shutdown (BATTERY MODE) */ else OutBuff[2] = 0x01 ; /* Type of shutdown (ONLINE) */ if (ups.ShutdownDelay < 6) ups.ShutdownDelay = 6 ; OutBuff[3] = (ups.ShutdownDelay >> 8) & 0xFF ; /* SDDELAY (MSB) Shutdown value (seconds) */ OutBuff[4] = (ups.ShutdownDelay & 0xFF) ; /* SDDELAY (LSB) */ OutBuff[5] = (ups.WakeUpDelay >> 16) & 0xFF ; /* WUDELAY (MSB) Wakeup value (seconds) */ OutBuff[6] = (ups.WakeUpDelay >> 8) & 0xFF ; /* WUDELAY (...) */ OutBuff[7] = (ups.WakeUpDelay & 0xFF ) ; /* WUDELAY (LSB) */ if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL) { upsdebugx(2, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode)); } else { /* command not sent: print error code */ upsdebugx(1, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode)); upslogx(LOG_ERR, "Shutdown command(SD=%u, WU=%u): %s", ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode)); } } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* allow '-x xyzzy' */ /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ /* allow '-x foo=' */ addvar(VAR_VALUE, "ups.delay.shutdown", "Override shutdown delay (120s)"); addvar(VAR_VALUE, "ups.delay.start", "Override restart delay (10s)"); } void upsdrv_initups(void) { upsfd = ser_open(device_path) ; ser_set_speed(upsfd, device_path, B19200) ; /* need to clear RTS and DTR: otherwise with default cable, communication will be problematic It is the same as removing pin7 from cable (pin 7 is needed for Plug&Play compatibility) */ ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 0); usleep(10000) ; /* small delay (1/100 s)) */ } void upsdrv_cleanup(void) { /* free(dynamic_mem); */ ser_close(upsfd, device_path) ; } nut-2.7.4/drivers/libhid.h0000644000175000017500000001217612640473702012351 00000000000000/*! * @file libhid.h * @brief HID Library - User API * * @author Copyright (C) 2003 - 2007 * Arnaud Quette && * Charles Lepple * Peter Selinger * Arjen de Korte * * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com * * 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. * * -------------------------------------------------------------------------- */ #ifndef _LIBHID_H #define _LIBHID_H #include "config.h" #include #include "hidtypes.h" #include "timehead.h" #ifdef SHUT_MODE #include "libshut.h" typedef SHUTDevice_t HIDDevice_t; typedef char HIDDeviceMatcher_t; typedef int hid_dev_handle_t; typedef shut_communication_subdriver_t communication_subdriver_t; #else #include "libusb.h" typedef USBDevice_t HIDDevice_t; typedef USBDeviceMatcher_t HIDDeviceMatcher_t; typedef usb_dev_handle * hid_dev_handle_t; typedef usb_communication_subdriver_t communication_subdriver_t; #endif /* use explicit booleans */ #ifndef FALSE typedef enum ebool { FALSE, TRUE } bool_t; #else typedef int bool_t; #endif /* Device open modes */ #define MODE_OPEN 0 /* open a HID device for the first time */ #define MODE_REOPEN 1 /* reopen a HID device that was opened before */ #define MAX_TS 2 /* validity period of a gotten report (2 sec) */ /* ---------------------------------------------------------------------- */ /* structure to describe an item in a usage table */ typedef struct { const char *usage_name; const HIDNode_t usage_code; } usage_lkp_t; extern usage_lkp_t hid_usage_lkp[]; /* an object of type usage_tables_t is a NULL-terminated array of * pointers to individual usage tables. */ typedef usage_lkp_t *usage_tables_t; extern communication_subdriver_t *comm_driver; extern HIDDesc_t *pDesc; /* parsed Report Descriptor */ /* report buffer structure: holds data about most recent report for each given report id */ typedef struct reportbuf_s { time_t ts[256]; /* timestamp when report was retrieved */ int len[256]; /* size of report data */ unsigned char *data[256]; /* report data (allocated) */ } reportbuf_t; extern reportbuf_t *reportbuf; /* buffer for most recent reports */ extern int interrupt_only; extern unsigned int interrupt_size; /* ---------------------------------------------------------------------- */ /* * HIDGetItemValue * -------------------------------------------------------------------------- */ int HIDGetItemValue(hid_dev_handle_t udev, const char *hidpath, double *Value, usage_tables_t *utab); /* * HIDGetItemString * -------------------------------------------------------------------------- */ char *HIDGetItemString(hid_dev_handle_t udev, const char *hidpath, char *buf, size_t buflen, usage_tables_t *utab); /* * HIDSetItemValue * -------------------------------------------------------------------------- */ bool_t HIDSetItemValue(hid_dev_handle_t udev, const char *hidpath, double value, usage_tables_t *utab); /* * GetItemData * -------------------------------------------------------------------------- */ HIDData_t *HIDGetItemData(const char *hidpath, usage_tables_t *utab); /* * GetDataItem * -------------------------------------------------------------------------- */ char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab); /* * HIDGetDataValue * -------------------------------------------------------------------------- */ int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, int age); /* * HIDSetDataValue * -------------------------------------------------------------------------- */ int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value); /* * HIDGetIndexString * -------------------------------------------------------------------------- */ char *HIDGetIndexString(hid_dev_handle_t udev, int Index, char *buf, size_t buflen); /* * HIDGetEvents * -------------------------------------------------------------------------- */ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventlen); /* * Support functions * -------------------------------------------------------------------------- */ void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab); const char *HIDDataType(const HIDData_t *hiddata); void free_report_buffer(reportbuf_t *rbuf); reportbuf_t *new_report_buffer(HIDDesc_t *pDesc); #endif /* _LIBHID_H */ nut-2.7.4/drivers/nutdrv_qx_mustek.h0000644000175000017500000000175212640473702014536 00000000000000/* nutdrv_qx_mustek.h - Subdriver for Mustek protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_MUSTEK_H #define NUTDRV_QX_MUSTEK_H #include "nutdrv_qx.h" extern subdriver_t mustek_subdriver; #endif /* NUTDRV_QX_MUSTEK_H */ nut-2.7.4/drivers/nutdrv_qx_voltronic-qs-hex.h0000644000175000017500000000205312640473702016443 00000000000000/* nutdrv_qx_voltronic-qs-hex.h - Subdriver for Voltronic Power UPSes with QS-Hex protocol * * Copyright (C) * 2014 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_VOLTRONIC_QS_HEX_H #define NUTDRV_QX_VOLTRONIC_QS_HEX_H #include "nutdrv_qx.h" extern subdriver_t voltronic_qs_hex_subdriver; #endif /* NUTDRV_QX_VOLTRONIC_QS_HEX_H */ nut-2.7.4/drivers/belkinunv.c0000644000175000017500000010375312640473702013110 00000000000000/* belkinunv.c - driver for newer Belkin models, such as "Belkin Universal UPS" (ca. 2003) Copyright (C) 2003 Peter Selinger 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 */ /* SOFT SHUTDOWN WORKAROUND One problem with the Belkin Universal UPS is that it cannot enter a soft shutdown (shut down until AC power returns) unless the batteries are completely depleted. Thus, one cannot just shut off the UPS after operating system shutdown; it will not come back on when the power comes back on. The belkinunv driver should never be used with the -k option. Instead, we provide a "standalone" mode for this driver via some -x options, which is intended to be used in startup and shutdown scripts. Please see the belkinunv(8) man page for details. VARIABLES: battery.charge battery.runtime battery.voltage battery.voltage.nominal input.frequency input.frequency.nominal e.g. 60 for 60Hz input.sensitivity (RW) normal/medium/low input.transfer.high (RW) input.transfer.low (RW) input.voltage input.voltage.maximum input.voltage.minimum input.voltage.nominal output.frequency output.voltage ups.beeper.status (RW) enabled/disabled/muted ups.firmware ups.load ups.model ups.power.nominal e.g. 800 for an 800VA system ups.status ups.temperature ups.test.result ups.delay.restart read-only: time to restart ups.delay.shutdown read-only: time to shutdown ups.type ONLINE/OFFLINE/LINEINT COMMANDS: beeper.disable beeper.enable beeper.mute reset.input.minmax shutdown.reboot shut down load immediately for 1-2 minutes shutdown.reboot.graceful shut down load after 40 seconds for 1-2 minutes shutdown.stayoff shut down load immediately and stay off test.battery.start start 10-second battery test test.battery.stop test.failure.start start "deep" battery test test.failure.stop STATUS FLAGS: OB load is on battery, including during tests OFF load is off OL load is online ACFAIL AC failure OVER overload OVERHEAT overheat COMMFAULT UPS Fault LB low battery CHRG charging DEPLETED battery depleted RB replace battery */ #include "main.h" #include "serial.h" #define DRIVER_NAME "Belkin 'Universal UPS' driver" #define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Peter Selinger ", DRV_STABLE, { NULL } }; /* somewhat arbitrary buffer size - the longest actually occuring message is 18 bytes for the F6C800-UNV. But since message length is arbitrary in principle, we allow for some extra bytes. */ #define MAXMSGSIZE 25 /* definitions of register numbers for Belkin UPS */ #define REG_VOLTRATING 0x01 #define REG_FREQRATING 0x02 #define REG_POWERRATING 0x03 #define REG_BATVOLTRATING 0x04 #define REG_XFER_LO 0x06 #define REG_XFER_LO_MAX 0x07 #define REG_XFER_LO_MIN 0x08 #define REG_XFER_HI 0x09 #define REG_XFER_HI_MAX 0x0a #define REG_XFER_HI_MIN 0x0b #define REG_VOLTSENS 0x0c #define REG_UPSMODEL 0x0d #define REG_UPSMODEL2 0x0e #define REG_FIRMWARE 0x0f #define REG_TESTSTATUS 0x10 #define REG_ALARMSTATUS 0x11 #define REG_SHUTDOWNTIMER 0x15 #define REG_RESTARTTIMER 0x16 #define REG_INPUTVOLT 0x18 #define REG_INPUTFREQ 0x19 #define REG_TEMPERATURE 0x1a #define REG_OUTPUTVOLT 0x1b #define REG_OUTPUTFREQ 0x1c #define REG_LOAD 0x1e #define REG_BATSTAT2 0x1f #define REG_BATVOLT 0x20 #define REG_BATLEVEL 0x21 #define REG_UPSSTATUS 0x22 #define REG_BATSTATUS 0x23 #define REG_TIMELEFT 0x3f /* flags for REG_UPSSTATUS */ #define US_ACFAILURE 0x0001 #define US_OVERLOAD 0x0010 #define US_OFF 0x0020 #define US_OVERHEAT 0x0040 #define US_UPSFAULT 0x0080 #define US_WAITING 0x2000 #define US_BUZZER 0x8000 /* flags for REG_BATSTATUS */ #define BS_LOW 0x04 #define BS_CHARGING 0x10 #define BS_ONBATTERY 0x20 #define BS_DEPLETED 0x40 #define BS_REPLACE 0x80 /* size of an array */ #define asize(x) ((int)(sizeof(x)/sizeof(x[0]))) const char *upstype[3] = { "ONLINE", "OFFLINE", "LINEINT" }; const char *voltsens[3] = { "normal", "medium", "low" }; const char *teststatus[6] = { "no test performed", "test passed", "test failed", "test failed", "test aborted", "test in progress" }; #define ST_OFF 0 #define ST_ONLINE 1 #define ST_BATTERY 2 static const char *status[] = { "UPS is off", /* ST_OFF */ "UPS is on AC power", /* ST_ONLINE */ "UPS is on battery" /* ST_BATTERY */ }; /* some useful strings */ #define ESC "\033" #define COL0 ESC "[G" ESC "[K" /* terminal control: clear line */ static int minutil = -1; static int maxutil = -1; static int xfer_lo_min = -1; static int xfer_lo_max = -1; static int xfer_hi_min = -1; static int xfer_hi_max = -1; int instcmd(const char *cmdname, const char *extra); static int setvar(const char *varname, const char *val); /* ---------------------------------------------------------------------- */ /* a general purpose Belkin-specific function: */ /* calculate a Belkin checksum, i.e., add buf[0]...buf[n-1] */ static unsigned char belkin_checksum(unsigned char *buf, int n) { int i, res; res = 0; for (i=0; i bufsize) { return -1; } r = ser_get_buf_len(upsfd, &buf[0], 1, 3, 0); if (r<0) { upslog_with_errno(LOG_ERR, "Error reading from UPS"); return -1; } else if (r==0) { upslogx(LOG_ERR, "No response from UPS"); return -1; } else if (buf[0]!=0x7e) { upslogx(LOG_ERR, "Garbage read from UPS"); return -1; } n+=r; /* read instruction, size, and register */ if (n+3 > bufsize) { return -1; } r = ser_get_buf_len(upsfd, &buf[1], 3, 3, 0); if (r!=3) { upslogx(LOG_ERR, "Short read from UPS"); return -1; } n+=r; len = buf[2]; /* read data and checksum */ if (n+len > bufsize) { return -1; } r = ser_get_buf_len(upsfd, &buf[4], len, 3, 0); if (r!=len) { upslogx(LOG_ERR, "Short read from UPS"); return -1; } n+=r; /* check checksum */ if (belkin_checksum(buf, len+3) != buf[len+3]) { upslogx(LOG_ERR, "Bad checksum from UPS"); return -1; } return n; } /* read the value of a string register from UPS. Return NULL on failure, else an allocated string. */ static char *belkin_nut_read_str(int reg) { unsigned char buf[MAXMSGSIZE]; int len, r; char *str; /* send the request */ buf[0] = 0x7e; buf[1] = 0x03; buf[2] = 0x02; buf[3] = reg; buf[4] = 0; buf[5] = belkin_checksum(buf, 5); r = ser_send_buf(upsfd, buf, 6); if (r<0) { upslogx(LOG_ERR, "Failed write to UPS"); return NULL; } /* receive the answer */ r = belkin_nut_receive(buf, MAXMSGSIZE); if (r<0) { return NULL; } if ((buf[1]!=0x05 && buf[1]!=0x01) || buf[3] != reg) { upslogx(LOG_ERR, "Invalid response from UPS"); return NULL; } if (buf[1]==0x01) { return NULL; } /* convert the answer to a string */ len = buf[2]-1; str = (char *)xmalloc(len+1); memcpy(str, &buf[4], len); str[len]=0; return str; } /* read the value of an integer register from UPS. Return -1 on failure. */ static int belkin_nut_read_int(int reg) { unsigned char buf[MAXMSGSIZE]; int len, r; /* send the request */ buf[0] = 0x7e; buf[1] = 0x03; buf[2] = 0x02; buf[3] = reg; buf[4] = 0; buf[5] = belkin_checksum(buf, 5); r = ser_send_buf(upsfd, buf, 6); if (r<0) { upslogx(LOG_ERR, "Failed write to UPS"); return -1; } /* receive the answer */ r = belkin_nut_receive(buf, MAXMSGSIZE); if (r<0) { return -1; } if ((buf[1]!=0x05 && buf[1]!=0x01) || buf[3] != reg) { upslogx(LOG_ERR, "Invalid response from UPS"); return -1; } if (buf[1]==0x01) { return -1; } /* convert the answer to an integer */ len = buf[2]-1; if (len==1) { return buf[4]; } else if (len==2) { return buf[4] + 256*buf[5]; } else { upslogx(LOG_ERR, "Invalid response from UPS"); return -1; } } /* write the value of an integer register to UPS. Return -1 on failure, else 0 */ static int belkin_nut_write_int(int reg, int val) { unsigned char buf[MAXMSGSIZE]; int r; /* send the request */ buf[0] = 0x7e; buf[1] = 0x04; buf[2] = 0x03; buf[3] = reg; buf[4] = val & 0xff; buf[5] = (val>>8) & 0xff; buf[6] = belkin_checksum(buf, 6); r = ser_send_buf(upsfd, buf, 7); if (r<0) { upslogx(LOG_ERR, "Failed write to UPS"); return -1; } /* receive the acknowledgement */ r = belkin_nut_receive(buf, MAXMSGSIZE); if (r<0) { return -1; } if ((buf[1]!=0x02 && buf[1]!=0x01) || buf[3] != reg) { upslogx(LOG_ERR, "Invalid response from UPS"); return -1; } if (buf[1]==0x01) { return -1; } return 0; } /* ---------------------------------------------------------------------- */ /* some private functions for talking to the UPS - "standalone" versions. The functions in this section have _std_ in their name, and they do not use default NUT error handling (this would not be desirable during standalone operation, i.e., when the -x wait option is given). These functions also take an additional file descriptor argument. */ /* Open and prepare a serial port for communication with a Belkin Universal UPS. DEVICE is the name of the serial port. It will be opened in non-blocking read/write mode, and the appropriate communications parameters will be set. The device will also be sent a special signal (clear DTR, set RTS) to cause the UPS to switch from "dumb" to "smart" mode, and any pending data (=garbage) will be discarded. After this call, the device is ready for reading and writing via read(2) and write(2). Return a valid file descriptor on success, or else -1 with errno set. */ static int belkin_std_open_tty(const char *device) { int fd; struct termios tios; struct flock flock; char buf[128]; int r; /* open the device */ fd = open(device, O_RDWR | O_NONBLOCK); if (fd == -1) { return -1; } /* set communications parameters: 2400 baud, 8 bits, 1 stop bit, no parity, enable reading, hang up when done, ignore modem control lines. */ memset(&tios, 0, sizeof(tios)); tios.c_cflag = B2400 | CS8 | CREAD | HUPCL | CLOCAL; tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; r = tcsetattr(fd, TCSANOW, &tios); if (r == -1) { close(fd); return -1; } /* signal the UPS to enter "smart" mode. This is done by setting RTS and dropping DTR for at least 0.25 seconds. RTS and DTR refer to two specific pins in the 9-pin serial connector. Note: this must be done for at least 0.25 seconds for the UPS to react. Ignore any errors, as this probably means we are not on a "real" serial port. */ ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 1); /* flush both directions of serial port: throw away all data in transit */ r = ser_flush_io(fd); if (r == -1) { close(fd); return -1; } /* lock the port */ memset(&flock, 0, sizeof(flock)); flock.l_type = F_RDLCK; r = fcntl(fd, F_SETLK, &flock); if (r == -1) { close(fd); return -1; } /* sleep at least 0.25 seconds for the UPS to wake up. Belkin's own software sleeps 1 second, so that's what we do, too. */ usleep(1000000); /* flush incoming data again, and read any remaining garbage bytes. There should not be any. */ r = tcflush(fd, TCIFLUSH); if (r == -1) { close(fd); return -1; } r = read(fd, buf, 127); if (r == -1 && errno != EAGAIN) { close(fd); return -1; } /* leave port in non-blocking state */ return fd; } /* blocking read with 1-second timeout (use non-blocking i/o) */ static int belkin_std_upsread(int fd, unsigned char *buf, int n) { int count = 0; int r; int tries = 0; while (count < n) { r = read(fd, &buf[count], n-count); if (r==-1 && errno==EAGAIN) { /* non-blocking i/o, no data available */ usleep(100000); tries++; } else if (r == -1) { return -1; } else { count += r; } if (tries > 10) { return -1; } } return count; } /* blocking write with 1-second timeout (use non-blocking i/o) */ static int belkin_std_upswrite(int fd, unsigned char *buf, int n) { int count = 0; int r; int tries = 0; while (count < n) { r = write(fd, &buf[count], n-count); if (r==-1 && errno==EAGAIN) { /* non-blocking i/o, no data available */ usleep(100000); tries++; } else if (r == -1) { return -1; } else { count += r; } if (tries > 10) { return -1; } } return count; } /* receive Belkin message from UPS, check for well-formedness (leading byte, checksum). Return length of message, or -1 if not well-formed */ static int belkin_std_receive(int fd, unsigned char *buf, int bufsize) { int r; int n=0; int len; /* read 0x7e */ if (n+1 > bufsize) { return -1; } r = belkin_std_upsread(fd, &buf[0], 1); if (r==-1 || buf[0]!=0x7e) { return -1; } n+=r; /* read instruction, size, and register */ if (n+3 > bufsize) { return -1; } r = belkin_std_upsread(fd, &buf[1], 3); if (r!=3) { return -1; } n+=r; len = buf[2]; /* read data and checksum */ if (n+len > bufsize) { return -1; } r = belkin_std_upsread(fd, &buf[4], len); if (r!=len) { return -1; } n+=r; /* check checksum */ if (belkin_checksum(buf, len+3) != buf[len+3]) { return -1; } return n; } /* read the value of an integer register from UPS. Return -1 on failure. */ static int belkin_std_read_int(int fd, int reg) { unsigned char buf[MAXMSGSIZE]; int len, r; /* send the request */ buf[0] = 0x7e; buf[1] = 0x03; buf[2] = 0x02; buf[3] = reg; buf[4] = 0; buf[5] = belkin_checksum(buf, 5); r = belkin_std_upswrite(fd, buf, 6); if (r<0) { return -1; } /* receive the answer */ r = belkin_std_receive(fd, buf, MAXMSGSIZE); if (r<0) { return -1; } if ((buf[1]!=0x05 && buf[1]!=0x01) || buf[3] != reg) { return -1; } if (buf[1]==0x01) { return -1; } /* convert the answer to an integer */ len = buf[2]-1; if (len==1) { return buf[4]; } else if (len==2) { return buf[4] + 256*buf[5]; } else { return -1; } } /* write the value of an integer register to UPS. Return -1 on failure, else 0 */ static int belkin_std_write_int(int fd, int reg, int val) { unsigned char buf[MAXMSGSIZE]; int r; /* send the request */ buf[0] = 0x7e; buf[1] = 0x04; buf[2] = 0x03; buf[3] = reg; buf[4] = val & 0xff; buf[5] = (val>>8) & 0xff; buf[6] = belkin_checksum(buf, 6); r = belkin_std_upswrite(fd, buf, 7); if (r<0) { return -1; } /* receive the acknowledgement */ r = belkin_std_receive(fd, buf, MAXMSGSIZE); if (r<0) { return -1; } if ((buf[1]!=0x02 && buf[1]!=0x01) || buf[3] != reg) { return -1; } if (buf[1]==0x01) { return -1; } return 0; } /* ---------------------------------------------------------------------- */ /* "standalone" program executed when driver is called with the '-x wait' or '-x wait=' flag or option */ /* this function updates the status line, as specified by the smode parameter (0=silent, 1=normal, 2=dumbterminal). This is only done if the status has not changed from the previous call */ static void updatestatus(int smode, const char *fmt, ...) { char buf[1024]; /* static string limit is OK */ static char oldbuf[1024] = { 0 }; static int init = 1; va_list ap; if (smode==0) { return; } if (init) { init = 0; oldbuf[0] = 0; } /* read formatted argument string */ va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf)-1] = 0; va_end(ap); if (strcmp(oldbuf, buf)==0) { return; } strcpy(oldbuf, buf); if (smode==2) { /* "dumbterm" version just prints a new line each time */ printf("%s\n", buf); } else { /* "normal" version overwrites same line each time */ printf(COL0 "%s", buf); } fflush(stdout); } /* switch from status line display to normal output mode */ static void endstatus(int smode) { if (smode==1) { fprintf(stdout, "\n"); fflush(stdout); } } static int belkin_wait(void) { int level = 0; /* battery level to wait for */ int smode = 1; /* statusline mode: 0=silent, 1=normal, 2=dumbterm */ int nohang = 0; /* nohang flag */ int flash = 0; /* flash flag */ char *val; int failcount = 0; /* count consecutive failed connection attempts */ int failerrno = 0; int fd; int r; int bs, ov, bl, st; /* read command line '-x' options */ val = getval("wait"); if (val) { level = atoi(val); } if (dstate_getinfo("driver.flag.nohang")) { nohang = 1; } if (dstate_getinfo("driver.flag.flash")) { flash = 1; } if (dstate_getinfo("driver.flag.silent")) { smode = 0; } else if (dstate_getinfo("driver.flag.dumbterm")) { smode = 2; } updatestatus(smode, "Connecting to UPS..."); failcount = 0; fd = -1; while (1) { if (failcount >= 3 && nohang) { endstatus(smode); printf("UPS is not responding: %s\n", strerror(failerrno)); return 1; } else if (failcount >= 3) { updatestatus(smode, "UPS is not responding, will keep trying: %s", strerror(failerrno)); } if (fd == -1) { fd = belkin_std_open_tty(device_path); } if (fd == -1) { failcount++; failerrno = errno; sleep(1); continue; } /* wait until the UPS is online and the battery level is >= level */ bs = belkin_std_read_int(fd, REG_BATSTATUS); /* battery status */ if (bs==-1) { failcount++; failerrno = errno; close(fd); fd = -1; sleep(1); continue; } ov = belkin_std_read_int(fd, REG_OUTPUTVOLT); /* output voltage */ if (ov==-1) { failcount++; failerrno = errno; close(fd); fd = -1; sleep(1); continue; } bl = belkin_std_read_int(fd, REG_BATLEVEL); /* battery level */ if (bl==-1) { failcount++; failerrno = errno; close(fd); fd = -1; sleep(1); continue; } /* successfully got data from UPS */ failcount = 0; if (bs & BS_ONBATTERY) { st = ST_BATTERY; } else if (ov>0) { st = ST_ONLINE; } else { st = ST_OFF; } updatestatus(smode, "%s, battery level: %d%%", status[st], bl); if (st == ST_ONLINE && bl >= level) { break; } sleep(1); } /* termination condition reached */ endstatus(smode); if (flash) { printf("Interrupting UPS load for ca. 2 minutes.\n"); r = belkin_std_write_int(fd, REG_RESTARTTIMER, 2); if (r==0) { r = belkin_std_write_int(fd, REG_SHUTDOWNTIMER, 1); } if (r) { printf("Timed shutdown operation failed.\n"); close(fd); return 2; } } close(fd); return 0; } /* ---------------------------------------------------------------------- */ /* functions which interface with main.c */ /* read all hardcoded info about this UPS */ void upsdrv_initinfo(void) { char *str; int val; int i; /* read hard-wired values */ val = belkin_nut_read_int(REG_VOLTRATING); if (val!=-1) { dstate_setinfo("input.voltage.nominal", "%d", val); } val = belkin_nut_read_int(REG_FREQRATING); if (val!=-1) { dstate_setinfo("input.frequency.nominal", "%d", val); } val = belkin_nut_read_int(REG_POWERRATING); if (val!=-1) { dstate_setinfo("ups.power.nominal", "%d", val); } val = belkin_nut_read_int(REG_BATVOLTRATING); if (val!=-1) { dstate_setinfo("battery.voltage.nominal", "%d", val); } xfer_lo_max = belkin_nut_read_int(REG_XFER_LO_MAX); xfer_lo_min = belkin_nut_read_int(REG_XFER_LO_MIN); xfer_hi_max = belkin_nut_read_int(REG_XFER_HI_MAX); xfer_hi_min = belkin_nut_read_int(REG_XFER_HI_MIN); str = belkin_nut_read_str(REG_UPSMODEL); if (str) { dstate_setinfo("ups.model", "%s", str); free(str); } val = belkin_nut_read_int(REG_FIRMWARE); if (val!=-1) { dstate_setinfo("ups.firmware", "%d", (val>>4) & 0xf); dstate_setinfo("ups.type", "%s", upstype[(val & 0x0f) % 3]); } /* read writable values and declare them writable */ val = belkin_nut_read_int(REG_VOLTSENS); if (val!=-1) { dstate_setinfo("input.sensitivity", "%s", (val>=0 && val 0) { status_set("OL"); /* online */ } else { status_set("OFF"); /* off */ } if (us & US_ACFAILURE) { status_set("ACFAIL"); /* AC failure, self-invented */ /* Note: this is not the same as "on battery", because this flag makes sense even during a test, or when the load is off. It simply reflects the status of utility power. A "critical" situation should be OB && BL && ACFAIL. */ } if (us & US_OVERLOAD) { status_set("OVER"); /* overload */ } if (us & US_OVERHEAT) { status_set("OVERHEAT"); /* overheat, self-invented */ } if (us & US_UPSFAULT) { status_set("COMMFAULT"); /* UPS Fault */ } if (bs & BS_LOW) { status_set("LB"); /* low battery */ } if (bs & BS_CHARGING) { status_set("CHRG"); /* charging */ } if (bs & BS_DEPLETED) { status_set("DEPLETED"); /* battery depleted, self-invented */ } if (bs & BS_REPLACE) { status_set("RB"); /* replace battery */ } status_commit(); /* new read everything else */ val = belkin_nut_read_int(REG_XFER_LO); if (val!=-1) { dstate_setinfo("input.transfer.low", "%d", val); } val = belkin_nut_read_int(REG_XFER_HI); if (val!=-1) { dstate_setinfo("input.transfer.high", "%d", val); } val = belkin_nut_read_int(REG_VOLTSENS); if (val!=-1) { dstate_setinfo("input.sensitivity", "%s", (val>=0 && val=0 && val0 && (maxutil==-1 || val>maxutil)) { maxutil = val; } if (val>0 && (minutil==-1 || val' */ addvar(VAR_FLAG, "wait", "Wait for AC power "); addvar(VAR_VALUE, "wait", "Wait for AC power and battery level"); /* allow '-x nohang' */ addvar(VAR_FLAG, "nohang", "In wait mode: quit if UPS dead "); /* allow '-x flash' */ addvar(VAR_FLAG, "flash", "In wait mode: do brief shutdown "); /* allow '-x silent' */ addvar(VAR_FLAG, "silent", "In wait mode: suppress status line "); /* allow '-x dumbterm' */ addvar(VAR_FLAG, "dumbterm", "In wait mode: simpler status line "); } /* prep the serial port */ void upsdrv_initups(void) { /* If '-x wait' or '-x wait=' option given, branch into standalone behavior. */ if (getval("wait") || dstate_getinfo("driver.flag.wait")) { exit(belkin_wait()); } belkin_nut_open_tty(); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/openups-hid.c0000644000175000017500000003236112640473702013342 00000000000000/* openups-hid.c - subdriver to monitor Minibox openUPS USB/HID devices with NUT * * Copyright (C) * 2003 - 2012 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * 2012 Nicu Pavel * * 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 "usbhid-ups.h" #include "openups-hid.h" #include "main.h" /* for getval() */ #include "usb-common.h" #define OPENUPS_HID_VERSION "openUPS HID 0.4" /* Minibox */ #define OPENUPS_VENDORID 0x04d8 /* constants for converting HID read values to real values */ static const double vin_scale_d004 = 0.03545 * 100; static const double vout_scale_d004 = 0.02571 * 100; /* static const double vbat_scale = 0.00857 * 100; */ static const double ccharge_scale_d004 = 0.8274 / 10; static const double cdischarge_scale_d004 = 16.113 / 10; static double vin_scale = 1; static double vout_scale= 1; static double ccharge_scale = 1; static double cdischarge_scale = 1; static char openups_scratch_buf[20]; static void *get_voltage_multiplier(USBDevice_t *device) { switch(device->ProductID) { case 0xd004: vin_scale = vin_scale_d004; vout_scale= vout_scale_d004; ccharge_scale= ccharge_scale_d004; cdischarge_scale= cdischarge_scale_d004; break; case 0xd005: vin_scale = 0.1; vout_scale = 0.1; ccharge_scale = 0.1; /* unverified */ cdischarge_scale = 0.1; /* unverified */ break; } upsdebugx(1, "vin_scale = %g; vout_scale = %g\n", vin_scale, vout_scale); return NULL; } /* USB IDs device table */ static /* const */ usb_device_id_t openups_usb_device_table[] = { /* openUPS Intelligent UPS (minimum required firmware 1.4) */ {USB_DEVICE(OPENUPS_VENDORID, 0xd004), get_voltage_multiplier}, {USB_DEVICE(OPENUPS_VENDORID, 0xd005), get_voltage_multiplier}, /* Terminating entry */ {-1, -1, NULL} }; /* Thermistor table used for temperature lookups * taken from the windows monitoring application */ static const unsigned int therm_tbl[] = { (unsigned int)0x31, (unsigned int)0x40, (unsigned int)0x53, (unsigned int)0x68, (unsigned int)0x82, (unsigned int)0xA0, (unsigned int)0xC3, (unsigned int)0xE9, (unsigned int)0x113, (unsigned int)0x13F, (unsigned int)0x16E, (unsigned int)0x19F, (unsigned int)0x1CF, (unsigned int)0x200, (unsigned int)0x22F, (unsigned int)0x25C, (unsigned int)0x286, (unsigned int)0x2AE, (unsigned int)0x2D3, (unsigned int)0x2F4, (unsigned int)0x312, (unsigned int)0x32D, (unsigned int)0x345, (unsigned int)0x35A, (unsigned int)0x36D, (unsigned int)0x37E, (unsigned int)0x38C, (unsigned int)0x399, (unsigned int)0x3A5, (unsigned int)0x3AF, (unsigned int)0x3B7, (unsigned int)0x3BF, (unsigned int)0x3C6, (unsigned int)0x3CC }; static const unsigned int therm_tbl_size = sizeof(therm_tbl)/sizeof(therm_tbl[0]); static const char *openups_charging_fun(double value); static const char *openups_discharging_fun(double value); static const char *openups_online_fun(double value); static const char *openups_nobattery_fun(double value); static const char *openups_off_fun(double value); static const char *openups_scale_vin_fun(double value); static const char *openups_scale_vout_fun(double value); /* static const char *openups_scale_vbat_fun(double value); */ static const char *openups_scale_ccharge_fun(double value); static const char *openups_scale_cdischarge_fun(double value); static const char *openups_temperature_fun(double value); static info_lkp_t openups_charging_info[] = { {0, NULL, openups_charging_fun} }; static info_lkp_t openups_discharging_info[] = { {0, NULL, openups_discharging_fun} }; static info_lkp_t openups_online_info[] = { {0, NULL, openups_online_fun} }; static info_lkp_t openups_nobattery_info[] = { {0, NULL, openups_nobattery_fun} }; static info_lkp_t openups_off_info[] = { {0, NULL, openups_off_fun} }; static info_lkp_t openups_vin_info[] = { {0, NULL, openups_scale_vin_fun} }; static info_lkp_t openups_vout_info[] = { {0, NULL, openups_scale_vout_fun} }; /* static info_lkp_t openups_vbat_info[] = { {0, NULL, openups_scale_vbat_fun} };*/ static info_lkp_t openups_ccharge_info[] = { {0, NULL, openups_scale_ccharge_fun} }; static info_lkp_t openups_cdischarge_info[] = { {0, NULL, openups_scale_cdischarge_fun} }; static info_lkp_t openups_temperature_info[] = { {0, NULL, openups_temperature_fun} }; static const char *openups_charging_fun(double value) { return value ? "chrg" : "!chrg"; } static const char *openups_discharging_fun(double value) { return value ? "dischrg" : "!dischrg"; } static const char *openups_online_fun(double value) { return value ? "online" : "!online"; } static const char *openups_nobattery_fun(double value) { return value ? "nobattery" : "!nobattery"; } static const char *openups_off_fun(double value) { return value ? "!off" : "off"; } static const char *openups_scale_vin_fun(double value) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vin_scale); return openups_scratch_buf; } static const char *openups_scale_vout_fun(double value) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vout_scale); return openups_scratch_buf; } /* static const char *openups_scale_vbat_fun(double value) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vbat_scale); return openups_scratch_buf; }*/ static const char *openups_scale_ccharge_fun(double value) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.3f", value * ccharge_scale); return openups_scratch_buf; } static const char *openups_scale_cdischarge_fun(double value) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.3f", value * cdischarge_scale); return openups_scratch_buf; } static const char *openups_temperature_fun(double value) { int i; int pos = -1; unsigned int thermistor = value * 100; if (thermistor <= therm_tbl[0]) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", -40); } else { if (thermistor >= therm_tbl[therm_tbl_size - 1]) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", 125); } else { for (i = therm_tbl_size - 1; i >= 0; i--) { if (thermistor >= therm_tbl[i]) { pos = i; break; } } if (thermistor == therm_tbl[pos]) { snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", pos * 5 - 40); } else { int t1 = pos * 5 - 40; int t2 = (pos + 1) * 5 - 40; unsigned int d1 = therm_tbl[pos]; unsigned int d2 = therm_tbl[pos + 1]; double temp = (double) (thermistor - d1) * (t2 - t1) / (d2 - d1) + t1; snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", temp); } } } return openups_scratch_buf; } /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* OPENUPS usage table */ static usage_lkp_t openups_usage_lkp[] = { {"Cell1", 0x00000001}, /* Battery cell 1 on J6 pin 1 */ {"Cell2", 0x00000002}, /* Battery cell 2 on J6 pin 2 */ {"Cell3", 0x00000003}, /* Battery cell 3 on J6 pin 3 */ {"Cell4", 0x00000004}, /* Battery cell 4 on J6 pin 4 */ {"Cell5", 0x00000005}, /* Battery cell 5 on J6 pin 5 */ {"Cell6", 0x00000006}, /* Battery cell 6 on J4 pin 1 */ /* Usage table for windows monitoring app only updates when * certain request codes are written to USB endpoint */ /*{ "OpenUPSExtra", 0xff000001 }, */ {NULL, 0} }; static usage_tables_t openups_utab[] = { openups_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ static hid_info_t openups_hid2nut[] = { {"ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion}, /* Battery */ {"battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion}, {"battery.mfr.date", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", 0, stringid_conversion}, {"battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", HU_FLAG_QUICK_POLL, NULL}, /* { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info }, */ {"battery.current", 0, 0, "UPS.PowerSummary.Current", NULL, "%.3f", HU_FLAG_QUICK_POLL, NULL}, {"battery.capacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", HU_FLAG_STATIC, NULL}, {"battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, {"battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, {"battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL}, {"battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, {"battery.temperature", 0, 0, "UPS.PowerSummary.Temperature", NULL, NULL, HU_FLAG_QUICK_POLL, openups_temperature_info}, /* {"battery.cell1.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell1", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, {"battery.cell2.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell2", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, {"battery.cell3.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell3", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, {"battery.cell4.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell4", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, {"battery.cell5.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell5", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, {"battery.cell6.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell6", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, */ /* Output */ {"output.voltage", 0, 0, "UPS.PowerSummary.Output.Voltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vout_info}, {"output.current", 0, 0, "UPS.PowerSummary.Output.Current", NULL, NULL, HU_FLAG_QUICK_POLL, openups_cdischarge_info}, /* Input */ {"input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vin_info}, {"input.current", 0, 0, "UPS.PowerSummary.Input.Current", NULL, NULL, HU_FLAG_QUICK_POLL, openups_ccharge_info}, /* Status */ {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, HU_FLAG_QUICK_POLL, openups_off_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, HU_FLAG_QUICK_POLL, commfault_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, HU_FLAG_QUICK_POLL, overheat_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, HU_FLAG_QUICK_POLL, shutdownimm_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, HU_FLAG_QUICK_POLL, timelimitexpired_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, openups_charging_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, openups_discharging_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, openups_online_info}, {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, HU_FLAG_QUICK_POLL, openups_nobattery_info}, /* end of structure. */ {NULL, 0, 0, NULL, NULL, NULL, 0, NULL} }; static const char *openups_format_model(HIDDevice_t * hd) { return hd->Product; } static const char *openups_format_mfr(HIDDevice_t * hd) { return hd->Vendor ? hd->Vendor : "openUPS"; } static const char *openups_format_serial(HIDDevice_t * hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int openups_claim(HIDDevice_t * hd) { int status = is_usb_device_supported(openups_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("openUPS", hd); return 0; case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t openups_subdriver = { OPENUPS_HID_VERSION, openups_claim, openups_utab, openups_hid2nut, openups_format_model, openups_format_mfr, openups_format_serial, }; nut-2.7.4/drivers/main.h0000644000175000017500000000571412640473702012042 00000000000000#ifndef MAIN_H #define MAIN_H #include "common.h" #include "upsconf.h" #include "dstate.h" #include "extstate.h" /* public functions & variables from main.c */ extern const char *progname, *upsname, *device_name; extern char *device_path; extern int upsfd, extrafd, broken_driver, experimental_driver, do_lock_port, exit_flag; extern unsigned int poll_interval; /* functions & variables required in each driver */ void upsdrv_initups(void); /* open connection to UPS, fail if not found */ void upsdrv_initinfo(void); /* prep data, settings for UPS monitoring */ void upsdrv_updateinfo(void); /* update state data if possible */ void upsdrv_shutdown(void); /* make the UPS power off the load */ void upsdrv_help(void); /* tack on anything useful for the -h text */ void upsdrv_banner(void); /* print your version information */ void upsdrv_cleanup(void); /* free any resources before shutdown */ /* --- details for the variable/value sharing --- */ /* main calls this driver function - it needs to call addvar */ void upsdrv_makevartable(void); /* retrieve the value of variable if possible */ char *getval(const char *var); /* see if has been defined, even if no value has been given to it */ int testvar(const char *var); /* extended variable table - used for -x defines/flags */ typedef struct vartab_s { int vartype; /* VAR_* value, below */ char *var; /* left side of =, or whole word if none */ char *val; /* right side of = */ char *desc; /* 40 character description for -h text */ int found; /* set once encountered, for testvar() */ struct vartab_s *next; } vartab_t; /* flags to define types in the vartab */ #define VAR_FLAG 0x0001 /* argument is a flag (no value needed) */ #define VAR_VALUE 0x0002 /* argument requires a value setting */ #define VAR_SENSITIVE 0x0004 /* do not publish in driver.parameter */ /* callback from driver - create the table for future -x entries */ void addvar(int vartype, const char *name, const char *desc); /* subdriver description structure */ typedef struct upsdrv_info_s { const char *name; /* driver full name, for banner printing, ... */ const char *version; /* driver version */ const char *authors; /* authors name */ const int status; /* driver development status */ struct upsdrv_info_s *subdrv_info[2]; /* sub driver information */ } upsdrv_info_t; /* flags to define the driver development status */ #define DRV_BROKEN 0x0001 /* dito... */ #define DRV_EXPERIMENTAL 0x0002 /* dito... */ #define DRV_BETA 0x0004 /* more stable and complete, but still * not suitable for production systems */ #define DRV_STABLE 0x0008 /* suitable for production systems, but * not 100 % feature complete */ #define DRV_COMPLETE 0x0010 /* gold level: implies 100 % of the * protocol implemented and the full QA * pass */ /* FIXME: complete with mfr support, and other interesting info */ /* public driver information from the driver file */ extern upsdrv_info_t upsdrv_info; #endif /* MAIN_H */ nut-2.7.4/drivers/libusb.c0000644000175000017500000004225312640473702012370 00000000000000/*! * @file libusb.c * @brief HID Library - Generic USB communication sub driver (using libusb) * * @author Copyright (C) * 2003 - 2007 Arnaud Quette * 2005 - 2007 Peter Selinger * * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com * * The logic of this file is ripped from mge-shut driver (also from * Arnaud Quette), which is a "HID over serial link" UPS driver for * Network UPS Tools * * 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. * * -------------------------------------------------------------------------- */ #include "config.h" /* for HAVE_USB_DETACH_KERNEL_DRIVER_NP flag */ #include "common.h" /* for xmalloc, upsdebugx prototypes */ #include "usb-common.h" #include "libusb.h" #define USB_DRIVER_NAME "USB communication driver" #define USB_DRIVER_VERSION "0.33" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { USB_DRIVER_NAME, USB_DRIVER_VERSION, NULL, 0, { NULL } }; #define MAX_REPORT_SIZE 0x1800 static void libusb_close(usb_dev_handle *udev); /*! Add USB-related driver variables with addvar(). * This removes some code duplication across the USB drivers. */ void nut_usb_addvars(void) { /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string"); addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string"); addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number"); addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)"); addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)"); } /* From usbutils: workaround libusb API goofs: "byte" should never be sign extended; * using "char" is trouble. Likewise, sizes should never be negative. */ static inline int typesafe_control_msg(usb_dev_handle *dev, unsigned char requesttype, unsigned char request, int value, int index, unsigned char *bytes, unsigned size, int timeout) { return usb_control_msg(dev, requesttype, request, value, index, (char *) bytes, (int) size, timeout); } /* invoke matcher against device */ static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) { if (!matcher) { return 1; } return matcher->match_function(device, matcher->privdata); } /*! If needed, set the USB alternate interface. * * In NUT 2.7.2 and earlier, the following call was made unconditionally: * usb_set_altinterface(udev, 0); * * Although harmless on Linux and *BSD, this extra call prevents old Tripp Lite * devices from working on Mac OS X (presumably the OS is already setting * altinterface to 0). */ static int nut_usb_set_altinterface(usb_dev_handle *udev) { int altinterface = 0, ret = 0; char *alt_string, *endp = NULL; if(testvar("usb_set_altinterface")) { alt_string = getval("usb_set_altinterface"); if(alt_string) { altinterface = (int)strtol(alt_string, &endp, 10); if(endp && !(endp[0] == 0)) { upslogx(LOG_WARNING, "%s: '%s' is not a valid number", __func__, alt_string); } if(altinterface < 0 || altinterface > 255) { upslogx(LOG_WARNING, "%s: setting bAlternateInterface to %d will probably not work", __func__, altinterface); } } /* set default interface */ upsdebugx(2, "%s: calling usb_set_altinterface(udev, %d)", __func__, altinterface); ret = usb_set_altinterface(udev, altinterface); if(ret != 0) { upslogx(LOG_WARNING, "%s: usb_set_altinterface(udev, %d) returned %d (%s)", __func__, altinterface, ret, usb_strerror() ); } upslogx(LOG_NOTICE, "%s: usb_set_altinterface() should not be necessary - please email the nut-upsdev list with information about your UPS.", __func__); } else { upsdebugx(3, "%s: skipped usb_set_altinterface(udev, 0)", __func__); } return ret; } #define usb_control_msg typesafe_control_msg /* On success, fill in the curDevice structure and return the report * descriptor length. On failure, return -1. * Note: When callback is not NULL, the report descriptor will be * passed to this function together with the udev and USBDevice_t * information. This callback should return a value > 0 if the device * is accepted, or < 1 if not. If it isn't accepted, the next device * (if any) will be tried, until there are no more devices left. */ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, unsigned char *rdbuf, int rdlen)) { #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP int retries; #endif int rdlen1, rdlen2; /* report descriptor length, method 1+2 */ USBDeviceMatcher_t *m; struct usb_device *dev; struct usb_bus *bus; usb_dev_handle *udev; struct usb_interface_descriptor *iface; int ret, res; unsigned char buf[20]; unsigned char *p; char string[256]; int i; /* All devices use HID descriptor at index 0. However, some newer * Eaton units have a light HID descriptor at index 0, and the full * version is at index 1 (in which case, bcdDevice == 0x0202) */ int hid_desc_index = 0; /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; int rdlen; /* libusb base init */ usb_init(); usb_find_busses(); usb_find_devices(); #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ libusb_close(*udevp); #endif for (bus = usb_busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { upsdebugx(2, "Checking device (%04X/%04X) (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); /* supported vendors are now checked by the supplied matcher */ /* open the device */ *udevp = udev = usb_open(dev); if (!udev) { upsdebugx(2, "Failed to open device, skipping. (%s)", usb_strerror()); continue; } /* collect the identifying information of this device. Note that this is safe, because there's no need to claim an interface for this (and therefore we do not yet need to detach any kernel drivers). */ free(curDevice->Vendor); free(curDevice->Product); free(curDevice->Serial); free(curDevice->Bus); memset(curDevice, '\0', sizeof(*curDevice)); curDevice->VendorID = dev->descriptor.idVendor; curDevice->ProductID = dev->descriptor.idProduct; curDevice->Bus = strdup(bus->dirname); curDevice->bcdDevice = dev->descriptor.bcdDevice; if (dev->descriptor.iManufacturer) { ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, string, sizeof(string)); if (ret > 0) { curDevice->Vendor = strdup(string); } } if (dev->descriptor.iProduct) { ret = usb_get_string_simple(udev, dev->descriptor.iProduct, string, sizeof(string)); if (ret > 0) { curDevice->Product = strdup(string); } } if (dev->descriptor.iSerialNumber) { ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, string, sizeof(string)); if (ret > 0) { curDevice->Serial = strdup(string); } } upsdebugx(2, "- VendorID: %04x", curDevice->VendorID); upsdebugx(2, "- ProductID: %04x", curDevice->ProductID); upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { hid_desc_index = 1; } upsdebugx(2, "Trying to match device"); for (m = matcher; m; m=m->next) { ret = matches(m, curDevice); if (ret==0) { upsdebugx(2, "Device does not match - skipping"); goto next_device; } else if (ret==-1) { fatal_with_errno(EXIT_FAILURE, "matcher"); goto next_device; } else if (ret==-2) { upsdebugx(2, "matcher: unspecified error"); goto next_device; } } upsdebugx(2, "Device matches"); /* Now we have matched the device we wanted. Claim it. */ #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ retries = 3; while (usb_claim_interface(udev, 0) < 0) { upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); if (usb_detach_kernel_driver_np(udev, 0) < 0) { upsdebugx(2, "failed to detach kernel driver from USB device: %s", usb_strerror()); } else { upsdebugx(2, "detached kernel driver from USB device..."); } if (retries-- > 0) { continue; } fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #else if (usb_claim_interface(udev, 0) < 0) { fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #endif nut_usb_set_altinterface(udev); if (!callback) { return 1; } if (!dev->config) { /* ?? this should never happen */ upsdebugx(2, " Couldn't retrieve descriptors"); goto next_device; } rdlen1 = -1; rdlen2 = -1; /* Get HID descriptor */ /* FIRST METHOD: ask for HID descriptor directly. */ /* res = usb_get_descriptor(udev, USB_DT_HID, hid_desc_index, buf, 0x9); */ res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_HID << 8) + hid_desc_index, 0, buf, 0x9, USB_TIMEOUT); if (res < 0) { upsdebugx(2, "Unable to get HID descriptor (%s)", usb_strerror()); } else if (res < 9) { upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); } else { upsdebug_hex(3, "HID descriptor, method 1", buf, 9); rdlen1 = buf[7] | (buf[8] << 8); } if (rdlen1 < -1) { upsdebugx(2, "Warning: HID descriptor, method 1 failed"); } upsdebugx(3, "HID descriptor length (method 1) %d", rdlen1); /* SECOND METHOD: find HID descriptor among "extra" bytes of interface descriptor, i.e., bytes tucked onto the end of descriptor 2. */ /* Note: on some broken UPS's (e.g. Tripp Lite Smart1000LCD), only this second method gives the correct result */ /* for now, we always assume configuration 0, interface 0, altsetting 0, as above. */ iface = &dev->config[0].interface[0].altsetting[0]; for (i=0; iextralen; i+=iface->extra[i]) { upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, iface->extra[i], iface->extra[i+1]); if (i+9 <= iface->extralen && iface->extra[i] >= 9 && iface->extra[i+1] == 0x21) { p = &iface->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); rdlen2 = p[7] | (p[8] << 8); break; } } if (rdlen2 < -1) { upsdebugx(2, "Warning: HID descriptor, method 2 failed"); } upsdebugx(3, "HID descriptor length (method 2) %d", rdlen2); /* when available, always choose the second value, as it seems to be more reliable (it is the one reported e.g. by lsusb). Note: if the need arises, can change this to use the maximum of the two values instead. */ if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); rdlen = rdlen1; } else { rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; } if (rdlen < 0) { upsdebugx(2, "Unable to retrieve any HID descriptor"); goto next_device; } if (rdlen1 >= 0 && rdlen2 >= 0 && rdlen1 != rdlen2) { upsdebugx(2, "Warning: two different HID descriptors retrieved (Reportlen = %d vs. %d)", rdlen1, rdlen2); } upsdebugx(2, "HID descriptor length %d", rdlen); if (rdlen > (int)sizeof(rdbuf)) { upsdebugx(2, "HID descriptor too long %d (max %d)", rdlen, (int)sizeof(rdbuf)); goto next_device; } /* res = usb_get_descriptor(udev, USB_DT_REPORT, hid_desc_index, bigbuf, rdlen); */ res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_REPORT << 8) + hid_desc_index, 0, rdbuf, rdlen, USB_TIMEOUT); if (res < 0) { upsdebug_with_errno(2, "Unable to get Report descriptor"); goto next_device; } if (res < rdlen) { upsdebugx(2, "Warning: report descriptor too short (expected %d, got %d)", rdlen, res); rdlen = res; /* correct rdlen if necessary */ } res = callback(udev, curDevice, rdbuf, rdlen); if (res < 1) { upsdebugx(2, "Caller doesn't like this device"); goto next_device; } upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); upsdebugx(2, "Found HID device"); fflush(stdout); return rdlen; next_device: usb_close(udev); } } *udevp = NULL; upsdebugx(2, "No appropriate HID device found"); fflush(stdout); return -1; } /* * Error handler for usb_get/set_* functions. Return value > 0 success, * 0 unknown or temporary failure (ignored), < 0 permanent failure (reconnect) */ static int libusb_strerror(const int ret, const char *desc) { if (ret > 0) { return ret; } switch(ret) { case -EBUSY: /* Device or resource busy */ case -EPERM: /* Operation not permitted */ case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ case -EPIPE: /* Broken pipe */ case -ENOSYS: /* Function not implemented */ upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); return ret; case -ETIMEDOUT: /* Connection timed out */ upsdebugx(2, "%s: Connection timed out", desc); return 0; case -EOVERFLOW: /* Value too large for defined data type */ #ifdef EPROTO case -EPROTO: /* Protocol error */ #endif upsdebugx(2, "%s: %s", desc, usb_strerror()); return 0; default: /* Undetermined, log only */ upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); return 0; } } /* return the report of ID=type in report * return -1 on failure, report length on success */ static int libusb_get_report(usb_dev_handle *udev, int ReportId, unsigned char *raw_buf, int ReportSize ) { int ret; upsdebugx(4, "Entering libusb_get_report"); if (!udev) { return 0; } ret = usb_control_msg(udev, USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x01, /* HID_REPORT_GET */ ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, USB_TIMEOUT); /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { return 0; } return libusb_strerror(ret, __func__); } static int libusb_set_report(usb_dev_handle *udev, int ReportId, unsigned char *raw_buf, int ReportSize ) { int ret; if (!udev) { return 0; } ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, /* HID_REPORT_SET = 0x09*/ ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, USB_TIMEOUT); /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { return 0; } return libusb_strerror(ret, __func__); } static int libusb_get_string(usb_dev_handle *udev, int StringIdx, char *buf, size_t buflen) { int ret; if (!udev) { return -1; } ret = usb_get_string_simple(udev, StringIdx, buf, buflen); return libusb_strerror(ret, __func__); } static int libusb_get_interrupt(usb_dev_handle *udev, unsigned char *buf, int bufsize, int timeout) { int ret; if (!udev) { return -1; } /* FIXME: hardcoded interrupt EP => need to get EP descr for IF descr */ ret = usb_interrupt_read(udev, 0x81, (char *)buf, bufsize, timeout); /* Clear stall condition */ if (ret == -EPIPE) { ret = usb_clear_halt(udev, 0x81); } return libusb_strerror(ret, __func__); } static void libusb_close(usb_dev_handle *udev) { if (!udev) { return; } /* usb_release_interface() sometimes blocks and goes into uninterruptible sleep. So don't do it. */ /* usb_release_interface(udev, 0); */ usb_close(udev); } usb_communication_subdriver_t usb_subdriver = { USB_DRIVER_VERSION, USB_DRIVER_NAME, libusb_open, libusb_close, libusb_get_report, libusb_set_report, libusb_get_string, libusb_get_interrupt }; nut-2.7.4/drivers/eaton-ats-mib.c0000644000175000017500000003412212667775412013560 00000000000000/* eaton_ats-mib.c - subdriver to monitor eaton_ats SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2016 Arnaud Quette * * Note: this subdriver was initially generated as a "stub" by the * gen-snmp-subdriver script. It must be customized! * * 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 "eaton-ats-mib.h" #define EATON_ATS_MIB_VERSION "0.11" #define EATON_ATS_SYSOID ".1.3.6.1.4.1.534.10" #define EATON_ATS_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" static info_lkp_t ats_source_info[] = { { 1, "init" }, { 2, "diagnosis" }, { 3, "off" }, { 4, "1" }, { 5, "2" }, { 6, "safe" }, { 7, "fault" }, { 0, NULL } }; static info_lkp_t ats_sensitivity_info[] = { { 1, "normal" }, { 2, "high" }, { 3, "low" }, { 0, NULL } }; static info_lkp_t ats_input_frequency_status_info[] = { { 1, "good" }, /* No threshold triggered */ { 2, "out-of-range" }, /* Frequency out of range triggered */ { 0, NULL } }; static info_lkp_t ats_input_voltage_status_info[] = { { 1, "good" }, /* No threshold triggered */ { 2, "derated-range" }, /* Voltage derated */ { 3, "out-of-range" }, /* Voltage out of range triggered */ { 4, "unknown" }, /* "missing" */ { 0, NULL } }; static info_lkp_t ats_test_result_info[] = { { 1, "done and passed" }, { 2, "done and warning" }, { 3, "done and error" }, { 4, "aborted" }, { 5, "in progress" }, { 6, "no test initiated" }, { 0, NULL } }; static info_lkp_t ats_output_status_info[] = { { 1, "OFF" }, /* Output not powered */ { 2, "OL" }, /* Output powered */ { 0, NULL } }; /* EATON_ATS Snmp2NUT lookup table */ static snmp_info_t eaton_ats_mib[] = { /* Device collection */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, /* ats2IdentManufacturer.0 = STRING: EATON */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* ats2IdentModel.0 = STRING: Eaton ATS */ { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* FIXME: RFC for device.firmware! */ /* ats2IdentFWVersion.0 = STRING: 00.00.0009 */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* FIXME: RFC for device.firmware.aux! */ /* ats2IdentRelease.0 = STRING: JF */ { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.4.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* ats2IdentSerialNumber.0 = STRING: GA04F23009 */ { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* ats2IdentPartNumber.0 = STRING: EATS16N */ { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* ats2IdentAgentVersion.0 = STRING: 301F23C28 */ /* { "unmapped.ats2IdentAgentVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, */ /* ats2InputDephasing.0 = INTEGER: 2 degrees */ /* { "unmapped.ats2InputDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, */ /* Input collection */ /* ats2InputIndex.source1 = INTEGER: source1(1) */ { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* ats2InputIndex.source2 = INTEGER: source2(2) */ { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* ats2InputVoltage.source1 = INTEGER: 2292 0.1 V */ { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputVoltage.source2 = INTEGER: 2432 0.1 V */ { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusVoltage.source1 = INTEGER: normalRange(1) */ { "input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, ats_input_voltage_status_info, NULL }, /* ats2InputStatusVoltage.source2 = INTEGER: normalRange(1) */ { "input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, ats_input_voltage_status_info, NULL }, /* ats2InputFrequency.source1 = INTEGER: 500 0.1 Hz */ { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputFrequency.source2 = INTEGER: 500 0.1 Hz */ { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusFrequency.source1 = INTEGER: good(1) */ { "input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, ats_input_frequency_status_info, NULL }, /* ats2InputStatusFrequency.source2 = INTEGER: good(1) */ { "input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, ats_input_frequency_status_info, NULL }, /* ats2ConfigSensitivity.0 = INTEGER: normal(1) */ { "input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &ats_sensitivity_info[0], NULL }, /* ats2OperationMode.0 = INTEGER: source1(4) */ { "input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, ats_source_info, NULL }, /* ats2ConfigPreferred.0 = INTEGER: source1(1) */ { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL, NULL }, /* Output collection */ /* ats2OutputVoltage.0 = INTEGER: 2304 0.1 V */ { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigOutputVoltage.0 = INTEGER: 230 1 V */ { "output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2OutputCurrent.0 = INTEGER: 5 0.1 A */ { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* UPS collection */ /* FIXME: RFC for device.test.result! */ /* ats2ConfigTransferTest.0 = INTEGER: noTestInitiated(6) */ { "ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, ats_test_result_info, NULL }, /* FIXME: RFC for device.status! */ /* ats2StatusOutput.0 = INTEGER: outputPowered(2) */ { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, ats_output_status_info, NULL }, /* Ambient collection */ /* ats2EnvRemoteTemp.0 = INTEGER: 0 degrees Centigrade */ { "ambient.temperature", 0, 1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2EnvRemoteTempLowerLimit.0 = INTEGER: 5 degrees Centigrade */ { "ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2EnvRemoteTempUpperLimit.0 = INTEGER: 40 degrees Centigrade */ { "ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2EnvRemoteHumidity.0 = INTEGER: 0 percent */ { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2EnvRemoteHumidityLowerLimit.0 = INTEGER: 5 percent */ { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL, NULL }, #if 0 /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ { "unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusIndex.source1 = INTEGER: source1(1) */ { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusIndex.source2 = INTEGER: source2(2) */ { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusGood.source1 = INTEGER: voltageAndFreqNormalRange(2) */ { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusGood.source2 = INTEGER: voltageAndFreqNormalRange(2) */ { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusInternalFailure.source1 = INTEGER: good(1) */ { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusInternalFailure.source2 = INTEGER: good(1) */ { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusUsed.source1 = INTEGER: poweringLoad(2) */ { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2InputStatusUsed.source2 = INTEGER: notPoweringLoad(1) */ { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2StatusInternalFailure.0 = INTEGER: good(1) */ { "unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2StatusOverload.0 = INTEGER: noOverload(1) */ { "unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2StatusOverTemperature.0 = INTEGER: noOverTemperature(1) */ { "unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2StatusShortCircuit.0 = INTEGER: noShortCircuit(1) */ { "unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2StatusCommunicationLost.0 = INTEGER: good(1) */ { "unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2StatusConfigurationFailure.0 = INTEGER: good(1) */ { "unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigTimeRTC.0 = Wrong Type (should be Counter32): Gauge32: 19191036 */ { "unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigTimeTextDate.0 = STRING: 08/11/1970 */ { "unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigTimeTextTime.0 = STRING: 02/50/36 */ { "unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigInputVoltageRating.0 = INTEGER: 1 1 V */ { "unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigInputFrequencyRating.0 = INTEGER: 50 Hz */ { "unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigTransferMode.0 = INTEGER: standard(1) */ { "unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigBrownoutLow.0 = INTEGER: 202 1 V */ { "unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigBrownoutLowDerated.0 = INTEGER: 189 1 V */ { "unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigBrownoutHigh.0 = INTEGER: 258 1 V */ { "unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ConfigHysteresisVoltage.0 = INTEGER: 5 1 V */ { "unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL, NULL }, /* Ambient collection */ /* ats2EnvNumContacts.0 = INTEGER: 2 */ { "unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactIndex.1 = INTEGER: 1 */ { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactIndex.2 = INTEGER: 2 */ { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactType.1 = INTEGER: notUsed(4) */ { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactType.2 = INTEGER: notUsed(4) */ { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactState.1 = INTEGER: open(1) */ { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactState.2 = INTEGER: open(1) */ { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactDescr.1 = STRING: Input #1 */ { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL, NULL }, /* ats2ContactDescr.2 = STRING: Input #2 */ { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL, NULL }, #endif /* if 0 */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t eaton_ats = { "eaton_ats", EATON_ATS_MIB_VERSION, NULL, EATON_ATS_MODEL, eaton_ats_mib, ".1.3.6.1.4.1.705.1" }; /* FIXME: Eaton ATS need to be fixed for the sysOID (currently .1.3.6.1.4.1.705.1!) */ /* mib2nut_info_t eaton_ats = { "eaton_ats", EATON_ATS_MIB_VERSION, NULL, EATON_ATS_MODEL, eaton_ats_mib, EATON_ATS_SYSOID }; */ nut-2.7.4/drivers/asem.c0000644000175000017500000002647612640473702012046 00000000000000/* asem.c - driver for ASEM PB 1300 hardware, accessible through i2c. Copyright (C) 2014 Giuseppe Corbelli 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 ASEM SPA contributed with support and documentation. Copan Italia SPA funded the development. There are 2 versions of the charger. Older one is based on Max1667, newer one is a custom solution. Both are on address 0x09. To be compatible with both versions just read bit 15 of address 0x13 to have online/on battery status. Battery monitor is a BQ2060 at address 0x0B. Beware that the SystemIO memory used by the i2c controller is reserved by ACPI. On Linux, as of 3.5.x kernel only a native driver (i2c_i801) is available, so you need to boot with acpi_enforce_resources=lax option. */ /* Depends on i2c-dev.h, Linux only */ #include #include #include #include #include "main.h" #ifndef __STR__ # define __STR__(x) #x #endif #ifndef __XSTR__ # define __XSTR__(x) __STR__(x) #endif #define DRIVER_NAME "ASEM" #define DRIVER_VERSION "0.10" /* Valid on ASEM PB1300 UPS */ #define BQ2060_ADDRESS 0x0B #define CHARGER_ADDRESS 0x09 #define CMD_DEVICENAME 0x21 #define LOW_BATTERY_THRESHOLD 25 #define HIGH_BATTERY_THRESHOLD 75 #define ACCESS_DEVICE(fd, address) \ if (ioctl(fd, I2C_SLAVE, address) < 0) { \ fatal_with_errno(EXIT_FAILURE, "Failed to acquire bus access and/or talk to slave 0x%02X", address); \ } static unsigned long lb_threshold = LOW_BATTERY_THRESHOLD; static unsigned long hb_threshold = HIGH_BATTERY_THRESHOLD; static char *valid_devicename_data[] = { "ASEM SPA", NULL }; upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Giuseppe Corbelli ", DRV_EXPERIMENTAL, {NULL} }; void upsdrv_initinfo(void) { __s32 i2c_status; __u8 buffer[10]; unsigned short year, month, day; ACCESS_DEVICE(upsfd, BQ2060_ADDRESS); /* Set capacity mode in mA(h) */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x03); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not read BatteryMode word data"); } /* Clear 15th bit */ i2c_status = i2c_smbus_write_word_data(upsfd, 0x03, i2c_status & ~0x8000); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not set BatteryMode word data"); } /* Device name */ memset(buffer, 0, 10); i2c_status = i2c_smbus_read_block_data(upsfd, 0x21, buffer); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not read DeviceName block data"); } upsdebugx(1, "UPS model %s", (char *) buffer); dstate_setinfo("ups.model", "%s", (char *) buffer); /* Manufacturing date */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x1B); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not read ManufactureDate word data"); } /* (Year - 1980) * 512 */ year = (i2c_status >> 9) & 0x000000FF; /* Month * 32 */ month = (i2c_status >> 4) & 0x0000001F; day = i2c_status & 0x0000001F; upsdebugx(1, "UPS manufacturing date %d-%02d-%02d (%d)", year + 1980, month, day, i2c_status); dstate_setinfo("ups.mfr.date", "%d-%02d-%02d", year + 1980, month, day); /* Device chemistry */ memset(buffer, 0, 10); i2c_status = i2c_smbus_read_block_data(upsfd, 0x22, buffer); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not read DeviceChemistry block data"); } upsdebugx(1, "Battery chemistry %s", (char *) buffer); dstate_setinfo("battery.type", "%s", (char *) buffer); /* Serial number */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x1C); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not read SerialNumber block data"); } upsdebugx(1, "Serial Number %d", i2c_status); dstate_setinfo("ups.serial", "%d", i2c_status); } void upsdrv_updateinfo(void) { static char online; static char discharging; static char fully_charged; static unsigned short charge_percentage; static unsigned short voltage; static unsigned short capacity; static signed short current; static __s32 i2c_status; static __s32 temperature; static __s32 runtime_to_empty; ACCESS_DEVICE(upsfd, CHARGER_ADDRESS); /* Charger only supplies online/offline status */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x13); if (i2c_status == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read charger status word at address 0x13"); return; } online = (i2c_status & 0x8000) != 0; upsdebugx(3, "Charger status 0x%02X, online %d", i2c_status, online); ACCESS_DEVICE(upsfd, BQ2060_ADDRESS); i2c_status = i2c_smbus_read_word_data(upsfd, 0x16); if (i2c_status == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read bq2060 status word at address 0x16"); return; } upsdebugx(3, "bq2060 status 0x04%X", i2c_status); /* Busy, leave data as stale, try next time */ if (i2c_status & 0x0001) { dstate_datastale(); upslogx(LOG_NOTICE, "bq2060 is busy"); return; } /* Error, leave data as stale, try next time */ if (i2c_status & 0x000F) { dstate_datastale(); upslogx(LOG_WARNING, "bq2060 returned error code 0x%02X", i2c_status & 0x000F); return; } discharging = (i2c_status & 0x0040); fully_charged = (i2c_status & 0x0020); /* Charge percentage */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x0D); if (i2c_status == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read charge percentage from bq2060 at address 0x0D"); return; } charge_percentage = i2c_status & 0xFFFF; upsdebugx(3, "Charge percentage %03d", charge_percentage); /* Battery voltage in mV */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x09); if (i2c_status == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read voltage from bq2060 at address 0x09"); return; } voltage = i2c_status & 0x0000FFFF; upsdebugx(3, "Battery voltage %d mV", voltage); /* Temperature in °K */ temperature = i2c_smbus_read_word_data(upsfd, 0x08); if (temperature == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read temperature from bq2060 at address 0x08"); return; } upsdebugx(3, "Temperature %4.1f K", temperature / 10.0); /* Current load in mA, positive for charge, negative for discharge */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x0A); if (i2c_status == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read current from bq2060 at address 0x0A"); return; } current = i2c_status & 0x0000FFFF; upsdebugx(3, "Current %d mA", current); /* Current capacity */ i2c_status = i2c_smbus_read_word_data(upsfd, 0x0F); if (i2c_status == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read RemainingCapacity word data"); return; } capacity = i2c_status & 0x0000FFFF; upsdebugx(3, "Current capacity %d mAh", capacity); /* Expected runtime capacity, averaged by gauge */ runtime_to_empty = i2c_smbus_read_word_data(upsfd, 0x12); if (runtime_to_empty == -1) { dstate_datastale(); upslogx(LOG_ERR, "Could not read AverageTimeToEmpty word data"); return; } upsdebugx(3, "Expected run-time to empty %d m", runtime_to_empty); status_init(); status_set(online ? "OL" : "OB"); if (!discharging & !fully_charged) status_set("CHRG"); else if (discharging && current < 0) status_set("DISCHRG"); if (charge_percentage >= hb_threshold) status_set("HB"); else if (charge_percentage <= lb_threshold) status_set("LB"); /* In V */ dstate_setinfo("battery.voltage", "%2.3f", voltage / 1000.0); /* In mAh */ dstate_setinfo("battery.current", "%2.3f", current / 1000.0); dstate_setinfo("battery.charge", "%d", charge_percentage); /* In mAh */ dstate_setinfo("battery.capacity", "%2.3f", capacity / 1000.0); /* In °C */ dstate_setinfo("ups.temperature", "%4.1f", (temperature / 10.0) - 273.15); /* In seconds */ dstate_setinfo("battery.runtime", "%d", runtime_to_empty * 60); status_commit(); dstate_dataok(); } void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ fatalx(EXIT_FAILURE, "shutdown not supported"); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ } void upsdrv_help(void) { /* Redundant */ printf("\nASEM options\n"); printf(" HIGH/low battery thresholds\n"); printf(" lb = " __XSTR__(LOW_BATTERY_THRESHOLD) " (battery is low under this level)\n"); printf(" hb = " __XSTR__(HIGH_BATTERY_THRESHOLD) " (battery is high above this level)\n"); } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "lb", "Low battery threshold, default " __XSTR__(LOW_BATTERY_THRESHOLD)); addvar(VAR_VALUE, "hb", "High battery threshold, default " __XSTR__(HIGH_BATTERY_THRESHOLD)); } void upsdrv_initups(void) { __s32 i2c_status; __u8 DeviceName_buffer[10]; unsigned int i; unsigned long x; char *DeviceName; char *option; upsfd = open(device_path, O_RDWR); if (upsfd < 0) { fatal_with_errno(EXIT_FAILURE, "Could not open device port '%s'", device_path); } ACCESS_DEVICE(upsfd, BQ2060_ADDRESS); /* Get ManufacturerName */ memset(DeviceName_buffer, 0, 10); i2c_status = i2c_smbus_read_block_data(upsfd, 0x20, DeviceName_buffer); if (i2c_status == -1) { fatal_with_errno(EXIT_FAILURE, "Could not read DeviceName block data"); } i = 0; while ( (DeviceName = valid_devicename_data[i++]) ) { if (0 == memcmp(DeviceName, DeviceName_buffer, i2c_status)) break; } if (!DeviceName) { fatal_with_errno(EXIT_FAILURE, "Device '%s' unknown", (char *) DeviceName_buffer); } upsdebugx(1, "Found device '%s' on port '%s'", (char *) DeviceName, device_path); dstate_setinfo("ups.mfr", "%s", (char *) DeviceName); option = getval("lb"); if (option) { x = strtoul(option, NULL, 0); if ((x == 0) && (errno != 0)) { upslogx(LOG_WARNING, "Invalid value specified for low battery threshold: '%s'", option); } else { lb_threshold = x; } } option = getval("hb"); if (option) { x = strtoul(option, NULL, 0); if ((x == 0) && (errno != 0)) { upslogx(LOG_WARNING, "Invalid value specified for high battery threshold: '%s'", option); } else if ((x < 1) || (x > 100)) { upslogx(LOG_WARNING, "Invalid value specified for high battery threshold: '%s' (must be 1 < hb <= 100)", option); } else { hb_threshold = x; } } /* Invalid values specified */ if (lb_threshold > hb_threshold) { upslogx(LOG_WARNING, "lb > hb specified in options. Returning to defaults."); lb_threshold = LOW_BATTERY_THRESHOLD; hb_threshold = HIGH_BATTERY_THRESHOLD; } upslogx(LOG_NOTICE, "High battery threshold is %lu, low battery threshold is %lu", lb_threshold, hb_threshold); } void upsdrv_cleanup(void) { close(upsfd); } nut-2.7.4/drivers/tripplite.h0000644000175000017500000000471012640443572013127 00000000000000/* tripplite.h - model specific routines for Tripp Lite SmartUPS models (tested with: "SMART 700" [on back -- "SmartPro UPS" on front], "SMART700SER") tripplite.c was derived from Russell Kroll's bestups.c by Rik Faith. Copyright (C) 1999 Russell Kroll Copyright (C) 2001 Rickard E. (Rik) Faith Copyright (C) 2004,2008 Nicholas J. Kain This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define ENDCHAR '\n' /* replies end with CR LF -- use LF to end */ #define IGNCHAR '\r' /* ignore CR */ #define MAXTRIES 3 /* max number of times we try to detect ups */ #define SER_WAIT_SEC 1 /* allow 1.0 sec for ser_get calls */ #define SER_WAIT_USEC 0 #define DEFAULT_OFFDELAY 64 /* seconds (max 0xFF) */ #define DEFAULT_STARTDELAY 60 /* seconds (max 0xFFFFFF) */ #define DEFAULT_BOOTDELAY 64 /* seconds (max 0xFF) */ /* Sometimes the UPS seems to return bad data, so we range check it. */ #define INVOLT_MAX 270 #define INVOLT_MIN 0 #define FREQ_MAX 80.0 #define FREQ_MIN 30.0 #define TEMP_MAX 100 #define TEMP_MIN 0 #define LOAD_MAX 100 #define LOAD_MIN 0 /* We calculate battery charge (q) as a function of voltage (V). * It seems that this function probably varies by firmware revision or * UPS model - the Windows monitoring software gives different q for a * given V than the old open source Tripp Lite monitoring software. * * The discharge curve should be the same for any given battery chemistry, * so it should only be necessary to specify the minimum and maximum * voltages likely to be seen in operation. */ /* Interval notation for Q% = 10% <= [minV, maxV] <= 100% */ #define MAX_VOLT 13.4 /* Max battery voltage (100%) */ #define MIN_VOLT 11.0 /* Min battery voltage (10%) */ nut-2.7.4/drivers/nutdrv_qx_voltronic.c0000644000175000017500000036271412640473702015250 00000000000000/* nutdrv_qx_voltronic.c - Subdriver for Voltronic Power UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_voltronic.h" #define VOLTRONIC_VERSION "Voltronic 0.06" /* Support functions */ static int voltronic_claim(void); static void voltronic_makevartable(void); static void voltronic_massive_unskip(const int protocol); /* Range/enum functions */ static int voltronic_batt_low(char *value, const size_t len); static int voltronic_bypass_volt_max(char *value, const size_t len); static int voltronic_bypass_volt_min(char *value, const size_t len); static int voltronic_bypass_freq_max(char *value, const size_t len); static int voltronic_bypass_freq_min(char *value, const size_t len); static int voltronic_eco_freq_min(char *value, const size_t len); static int voltronic_eco_freq_max(char *value, const size_t len); /* Preprocess functions */ static int voltronic_process_setvar(item_t *item, char *value, const size_t valuelen); static int voltronic_process_command(item_t *item, char *value, const size_t valuelen); static int voltronic_capability(item_t *item, char *value, const size_t valuelen); static int voltronic_capability_set(item_t *item, char *value, const size_t valuelen); static int voltronic_capability_set_nonut(item_t *item, char *value, const size_t valuelen); static int voltronic_capability_reset(item_t *item, char *value, const size_t valuelen); static int voltronic_eco_volt(item_t *item, char *value, const size_t valuelen); static int voltronic_eco_volt_range(item_t *item, char *value, const size_t valuelen); static int voltronic_eco_freq(item_t *item, char *value, const size_t valuelen); static int voltronic_bypass(item_t *item, char *value, const size_t valuelen); static int voltronic_batt_numb(item_t *item, char *value, const size_t valuelen); static int voltronic_batt_runtime(item_t *item, char *value, const size_t valuelen); static int voltronic_protocol(item_t *item, char *value, const size_t valuelen); static int voltronic_fault(item_t *item, char *value, const size_t valuelen); static int voltronic_warning(item_t *item, char *value, const size_t valuelen); static int voltronic_mode(item_t *item, char *value, const size_t valuelen); static int voltronic_status(item_t *item, char *value, const size_t valuelen); static int voltronic_output_powerfactor(item_t *item, char *value, const size_t valuelen); static int voltronic_serial_numb(item_t *item, char *value, const size_t valuelen); static int voltronic_outlet(item_t *item, char *value, const size_t valuelen); static int voltronic_outlet_delay(item_t *item, char *value, const size_t valuelen); static int voltronic_outlet_delay_set(item_t *item, char *value, const size_t valuelen); static int voltronic_p31b(item_t *item, char *value, const size_t valuelen); static int voltronic_p31b_set(item_t *item, char *value, const size_t valuelen); static int voltronic_p31g(item_t *item, char *value, const size_t valuelen); static int voltronic_p31g_set(item_t *item, char *value, const size_t valuelen); static int voltronic_phase(item_t *item, char *value, const size_t valuelen); static int voltronic_phase_set(item_t *item, char *value, const size_t valuelen); static int voltronic_parallel(item_t *item, char *value, const size_t valuelen); /* Capability vars */ static char *bypass_alarm, *battery_alarm, *bypass_when_off, *alarm_control, *converter_mode, *eco_mode, *battery_open_status_check, *bypass_forbidding, *site_fault_detection, *advanced_eco_mode, *constant_phase_angle, *limited_runtime_on_battery; /* ups.conf settings */ static int max_bypass_volt, min_bypass_volt, battery_number, output_phase_angle, work_range_type; static double max_bypass_freq, min_bypass_freq; /* == Ranges/enums == */ /* Range for ups.delay.start */ static info_rw_t voltronic_r_ondelay[] = { { "0", 0 }, { "599940", 0 }, { "", 0 } }; /* Range for ups.delay.shutdown */ static info_rw_t voltronic_r_offdelay[] = { { "12", 0 }, { "5940", 0 }, { "", 0 } }; /* Enumlist for output phase angle */ static info_rw_t voltronic_e_phase[] = { { "000", 0 }, { "120", 0 }, { "180", 0 }, { "240", 0 }, { "", 0 } }; /* Range for battery low voltage */ static info_rw_t voltronic_r_batt_low[] = { { "20", 0 }, { "24", voltronic_batt_low }, { "28", voltronic_batt_low }, { "", 0 } }; /* Preprocess range value for battery low voltage */ static int voltronic_batt_low(char *value, const size_t len) { int val = strtol(value, NULL, 10); const char *ovn = dstate_getinfo("output.voltage.nominal"), *ocn = dstate_getinfo("output.current.nominal"); if (!ovn || !ocn) { upsdebugx(2, "%s: unable to get the value of output voltage nominal/output current nominal", __func__); return -1; } if ((strtol(ovn, NULL, 10) * strtol(ocn, NULL, 10)) < 1000) { if (val == 24) return 0; else return -1; } else { if (val == 28) return 0; else return -1; } } /* Range for outlet.n.delay.shutdown */ static info_rw_t voltronic_r_outlet_delay[] = { { "0", 0 }, { "59940", 0 }, { "", 0 } }; /* Enumlist for device grid working range type */ static info_rw_t voltronic_e_work_range[] = { { "Appliance", 0 }, /* 00 */ { "UPS", 0 }, /* 01 */ { "", 0 } }; /* Enumlist for battery type */ static info_rw_t voltronic_e_batt_type[] = { { "Li", 0 }, /* 00 */ { "Flooded", 0 }, /* 01 */ { "AGM", 0 }, /* 02 */ { "", 0 } }; /* Range for number of battery packs */ static info_rw_t voltronic_r_batt_packs[] = { { "1", 0 }, { "99", 0 }, { "", 0 } }; /* Range for number of batteries */ static info_rw_t voltronic_r_batt_numb[] = { { "1", 0 }, { "9", 0 }, { "", 0 } }; /* Range for Bypass Mode maximum voltage */ static info_rw_t voltronic_r_bypass_volt_max[] = { { "60", voltronic_bypass_volt_max }, /* P09 */ { "115", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14/P99 ivn<200 */ { "120", voltronic_bypass_volt_max }, /* P01 ivn<200 */ { "132", voltronic_bypass_volt_max }, /* P99 ivn<200 */ { "138", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14 ivn<200 */ { "140", voltronic_bypass_volt_max }, /* P01 ivn<200, P09 */ { "230", voltronic_bypass_volt_max }, /* P01 ivn>=200 */ { "231", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14/P99 ivn>=200 */ { "261", voltronic_bypass_volt_max }, /* P99 ivn>=200 */ { "264", voltronic_bypass_volt_max }, /* P01 ivn>=200 */ { "276", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14 ivn>=200 */ { "", 0 } }; /* Preprocess range value for Bypass Mode maximum voltage */ static int voltronic_bypass_volt_max(char *value, const size_t len) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), val = strtol(value, NULL, 10), ivn; const char *involtnom = dstate_getinfo("input.voltage.nominal"); if (!involtnom) { upsdebugx(2, "%s: unable to get input.voltage.nominal", __func__); return -1; } ivn = strtol(involtnom, NULL, 10); switch (val) { case 60: /* P09 */ if (protocol == 9) return 0; break; case 115: /* P02/P03/P10/P13/P14/P99 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 120: /* P01 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 1) return 0; break; case 132: /* P99 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 99) return 0; break; case 138: /* P02/P03/P10/P13/P14 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 2 || protocol == 2 || protocol == 10 || protocol == 13 || protocol == 14) return 0; break; case 140: /* P01 ivn<200, P09 */ if (protocol == 9) return 0; if (ivn >= 200) return -1; if (protocol == 1) return 0; break; case 230: /* P01 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 1) return 0; break; case 231: /* P02/P03/P10/P13/P14/P99 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 2 || protocol == 2 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 261: /* P99 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 99) return 0; break; case 264: /* P01 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 1) return 0; break; case 276: /* P02/P03/P10/P13/P14 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) return 0; break; default: upsdebugx(2, "%s: unknown value (%s)", __func__, value); break; } return -1; } /* Range for Bypass Mode minimum voltage */ static info_rw_t voltronic_r_bypass_volt_min[] = { { "50", voltronic_bypass_volt_min }, /* P99 ivn<200 */ { "55", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14 ivn<200 */ { "60", voltronic_bypass_volt_min }, /* P09 */ { "85", voltronic_bypass_volt_min }, /* P01/P99 ivn<200 */ { "104", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14 ivn<200 */ { "110", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14 ivn>=200 */ { "115", voltronic_bypass_volt_min }, /* P01 ivn<200 */ { "140", voltronic_bypass_volt_min }, /* P09 */ { "149", voltronic_bypass_volt_min }, /* P99 ivn>=200 */ { "170", voltronic_bypass_volt_min }, /* P01 ivn>=200 */ { "209", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14/P99 ivn>=200 */ { "220", voltronic_bypass_volt_min }, /* P01 ivn>=200 */ { "", 0 } }; /* Preprocess range value for Bypass Mode minimum voltage */ static int voltronic_bypass_volt_min(char *value, const size_t len) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), val = strtol(value, NULL, 10), ivn; const char *involtnom = dstate_getinfo("input.voltage.nominal"); if (!involtnom) { upsdebugx(2, "%s: unable to get input.voltage.nominal", __func__); return -1; } ivn = strtol(involtnom, NULL, 10); switch (val) { case 50: /* P99 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 99) return 0; break; case 55: /* P02/P03/P10/P13/P14 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 10 || protocol == 13 || protocol == 14) return 0; break; case 60: /* P09 */ case 140: /* P09 */ if (protocol == 9) return 0; break; case 85: /* P01/P99 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 1 || protocol == 99) return 0; break; case 104: /* P02/P03/P10/P13/P14 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) return 0; break; case 110: /* P02/P03/P10/P13/P14 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) return 0; break; case 115: /* P01 ivn<200 */ if (ivn >= 200) return -1; if (protocol == 1) return 0; break; case 149: /* P99 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 99) return 0; break; case 170: /* P01 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 1) return 0; break; case 209: /* P02/P03/P10/P13/P14/P99 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 220: /* P01 ivn>=200 */ if (ivn < 200) return -1; if (protocol == 1) return 0; break; default: upsdebugx(2, "%s: unknown value (%s)", __func__, value); break; } return -1; } /* Range for Bypass Mode maximum frequency */ static info_rw_t voltronic_r_bypass_freq_max[] = { { "51.0", voltronic_bypass_freq_max }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "54.0", voltronic_bypass_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "60.0", voltronic_bypass_freq_max }, /* P01/P09 ofn==50.0 */ { "61.0", voltronic_bypass_freq_max }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "64.0", voltronic_bypass_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "70.0", voltronic_bypass_freq_max }, /* P01/P09 ofn==60.0 */ { "", 0 } }; /* Preprocess range value for Bypass Mode maximum frequency */ static int voltronic_bypass_freq_max(char *value, const size_t len) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), val = strtol(value, NULL, 10); double ofn; const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); if (!outfreqnom) { upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); return -1; } ofn = strtod(outfreqnom, NULL); switch (val) { case 51: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 54: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 60: /* P01/P09 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 61: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 64: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 70: /* P01/P09 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; default: upsdebugx(2, "%s: unknown value (%s)", __func__, value); break; } return -1; } /* Range for Bypass Mode minimum frequency */ static info_rw_t voltronic_r_bypass_freq_min[] = { { "40.0", voltronic_bypass_freq_min }, /* P01/P09 ofn==50.0 */ { "46.0", voltronic_bypass_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "49.0", voltronic_bypass_freq_min }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "50.0", voltronic_bypass_freq_min }, /* P01/P09 ofn==60.0 */ { "56.0", voltronic_bypass_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "59.0", voltronic_bypass_freq_min }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "", 0 } }; /* Preprocess range value for Bypass Mode minimum frequency */ static int voltronic_bypass_freq_min(char *value, const size_t len) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), val = strtol(value, NULL, 10); double ofn; const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); if (!outfreqnom) { upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); return -1; } ofn = strtod(outfreqnom, NULL); switch (val) { case 40: /* P01/P09 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 46: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 49: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 50: /* P01/P09 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 56: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 59: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; default: upsdebugx(2, "%s: unknown value (%s)", __func__, value); break; } return -1; } /* Range for Eco Mode maximum voltage: filled at runtime by voltronic_eco_volt */ static info_rw_t voltronic_r_eco_volt_max[] = { { "", 0 }, { "", 0 }, { "", 0 } }; /* Range for ECO Mode minimum voltage: filled at runtime by voltronic_eco_volt */ static info_rw_t voltronic_r_eco_volt_min[] = { { "", 0 }, { "", 0 }, { "", 0 } }; /* Range for ECO Mode minimum frequency */ static info_rw_t voltronic_r_eco_freq_min[] = { { "40.0", voltronic_eco_freq_min }, /* P01/P09 ofn==50.0 */ { "46.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "47.0", voltronic_eco_freq_min }, /* P01/P09 ofn==50.0 */ { "48.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "50.0", voltronic_eco_freq_min }, /* P01/P09 ofn==60.0 */ { "56.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "57.0", voltronic_eco_freq_min }, /* P01/P09 ofn==60.0 */ { "58.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "", 0 } }; /* Preprocess range value for ECO Mode minimum frequency */ static int voltronic_eco_freq_min(char *value, const size_t len) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), val = strtol(value, NULL, 10); double ofn; const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); if (!outfreqnom) { upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); return -1; } ofn = strtod(outfreqnom, NULL); switch (val) { case 40: /* P01/P09 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 46: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 47: /* P01/P09 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 48: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 50: /* P01/P09 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 56: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 57: /* P01/P09 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 58: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; default: upsdebugx(2, "%s: unknown value (%s)", __func__, value); break; } return -1; } /* Range for ECO Mode maximum frequency */ static info_rw_t voltronic_r_eco_freq_max[] = { { "52.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "53.0", voltronic_eco_freq_max }, /* P01/P09 ofn==50.0 */ { "54.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ { "60.0", voltronic_eco_freq_max }, /* P01/P09 ofn==50.0 */ { "62.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "63.0", voltronic_eco_freq_max }, /* P01/P09 ofn==60.0 */ { "64.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ { "70.0", voltronic_eco_freq_max }, /* P01/P09 ofn==60.0 */ { "", 0 } }; /* Preprocess range value for ECO Mode maximum frequency */ static int voltronic_eco_freq_max(char *value, const size_t len) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), val = strtol(value, NULL, 10); double ofn; const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); if (!outfreqnom) { upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); return -1; } ofn = strtod(outfreqnom, NULL); switch (val) { case 52: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 53: /* P01/P09 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 54: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 60: /* P01/P09 ofn==50.0 */ if (ofn != 50.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 62: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 63: /* P01/P09 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; case 64: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) return 0; break; case 70: /* P01/P09 ofn==60.0 */ if (ofn != 60.0) return -1; if (protocol == 1 || protocol == 9) return 0; break; default: upsdebugx(2, "%s: unknown value (%s)", __func__, value); break; } return -1; } /* Enumlist for UPS capabilities that have a NUT var */ static info_rw_t voltronic_e_cap[] = { { "no", 0 }, { "yes", 0 }, { "", 0 } }; /* Enumlist for NONUT capabilities */ static info_rw_t voltronic_e_cap_nonut[] = { { "enabled", 0 }, { "disabled", 0 }, { "", 0 } }; /* == qx2nut lookup table == */ static item_t voltronic_qx2nut[] = { /* Query UPS for protocol * > [QPI\r] * < [(PI00\r] * 012345 * 0 */ { "ups.firmware.aux", 0, NULL, "QPI\r", "", 6, '(', "", 1, 4, "%s", QX_FLAG_STATIC, NULL, NULL, voltronic_protocol }, /* Query UPS for ratings * > [QRI\r] * < [(230.0 004 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "output.voltage.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 1, 5, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "output.current.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 7, 9, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 11, 15, "%.1f", QX_FLAG_SEMI_STATIC, NULL, NULL, NULL }, /* as *per battery pack*: the value will change when the number of batteries is changed (battery_number through BATNn) */ { "output.frequency.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 17, 20, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* Query UPS for ratings * > [QMD\r] * < [(#######OLHVT1K0 ###1000 80 1/1 230 230 02 12.0\r] <- Some UPS may reply with spaces instead of hashes * 012345678901234567890123456789012345678901234567 * 0 1 2 3 4 */ { "device.model", 0, NULL, "QMD\r", "", 48, '(', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.power.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 17, 23, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "output.powerfactor", 0, NULL, "QMD\r", "", 48, '(', "", 25, 26, "%.1f", QX_FLAG_STATIC, NULL, NULL, voltronic_output_powerfactor }, { "input.phases", 0, NULL, "QMD\r", "", 48, '(', "", 28, 28, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "output.phases", 0, NULL, "QMD\r", "", 48, '(', "", 30, 30, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.voltage.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 32, 34, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "output.voltage.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 36, 38, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* redundant with value from QRI */ /* { "battery_number", ST_FLAG_RW, voltronic_r_batt_numb, "QMD\r", "", 48, '(', "", 40, 41, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_batt_numb }, *//* redundant with value from QBV */ /* { "battery.voltage.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 43, 46, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, *//* as *per battery* vs *per pack* reported by QRI */ /* Query UPS for ratings * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* Query UPS for manufacturer * > [QMF\r] * < [(#######BOH\r] <- I don't know if it has a fixed length (-> so min length = ( + \r = 2). Hashes may be replaced by spaces * 012345678901 * 0 1 */ { "device.mfr", 0, NULL, "QMF\r", "", 2, '(', "", 1, 0, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Query UPS for firmware version * > [QVFW\r] * < [(VERFW:00322.02\r] * 0123456789012345 * 0 1 */ { "ups.firmware", 0, NULL, "QVFW\r", "", 16, '(', "", 7, 14, "%s", QX_FLAG_STATIC, NULL, NULL, NULL }, /* Query UPS for serial number * > [QID\r] * < [(12345679012345\r] <- As far as I know it hasn't a fixed length -> min length = ( + \r = 2 * 0123456789012345 * 0 1 */ { "device.serial", 0, NULL, "QID\r", "", 2, '(', "", 1, 0, "%s", QX_FLAG_STATIC, NULL, NULL, voltronic_serial_numb }, /* Query UPS for vendor infos * > [I\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Query UPS for status * > [QGS\r] * < [(234.9 50.0 229.8 50.0 000.0 000 369.1 ---.- 026.5 ---.- 018.8 100000000001\r] * 0123456789012345678901234567890123456789012345678901234567890123456789012345 * 0 1 2 3 4 5 6 7 */ { "input.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "QGS\r", "", 76, '(', "", 7, 10, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 12, 16, "%.1f", 0, NULL, NULL, NULL }, { "output.frequency", 0, NULL, "QGS\r", "", 76, '(', "", 18, 21, "%.1f", 0, NULL, NULL, NULL }, { "output.current", 0, NULL, "QGS\r", "", 76, '(', "", 23, 27, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "QGS\r", "", 76, '(', "", 29, 31, "%.0f", 0, NULL, NULL, NULL }, /* { "unknown.1", 0, NULL, "QGS\r", "", 76, '(', "", 33, 37, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ /* { "unknown.2", 0, NULL, "QGS\r", "", 76, '(', "", 39, 43, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ { "battery.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 45, 49, "%.2f", 0, NULL, NULL, NULL }, /* { "unknown.3", 0, NULL, "QGS\r", "", 76, '(', "", 51, 55, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ { "ups.temperature", 0, NULL, "QGS\r", "", 76, '(', "", 57, 61, "%.1f", 0, NULL, NULL, NULL }, { "ups.type", 0, NULL, "QGS\r", "", 76, '(', "", 63, 64, "%s", QX_FLAG_SEMI_STATIC, NULL, NULL, voltronic_status }, { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 65, 65, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 66, 66, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Battery Low */ { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 67, 67, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "QGS\r", "", 76, '(', "", 67, 67, "%s", 0, NULL, NULL, voltronic_status }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "QGS\r", "", 76, '(', "", 68, 68, "%s", 0, NULL, NULL, voltronic_status }, /* UPS Fault */ /* { "unknown.4", 0, NULL, "QGS\r", "", 76, '(', "", 69, 69, "%s", 0, NULL, NULL, voltronic_status }, *//* Unknown */ { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 70, 70, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Test in Progress */ { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 71, 71, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "QGS\r", "", 76, '(', "", 72, 72, "%s", 0, NULL, NULL, voltronic_status }, /* Beeper status - ups.beeper.status */ /* { "unknown.5", 0, NULL, "QGS\r", "", 76, '(', "", 73, 73, "%s", 0, NULL, NULL, voltronic_status }, *//* Unknown */ /* { "unknown.6", 0, NULL, "QGS\r", "", 76, '(', "", 74, 74, "%s", 0, NULL, NULL, voltronic_status }, *//* Unknown */ /* Query UPS for actual working mode * > [QMOD\r] * < [(S\r] * 012 * 0 */ { "ups.alarm", 0, NULL, "QMOD\r", "", 3, '(', "", 1, 1, "%s", 0, NULL, NULL, voltronic_mode }, { "ups.status", 0, NULL, "QMOD\r", "", 3, '(', "", 1, 1, "%s", 0, NULL, NULL, voltronic_mode }, /* Query UPS for faults and their type. Unskipped when a fault is found in 12bit flag of QGS, otherwise you'll get a fake reply. * > [QFS\r] * < [(OK\r] <- No fault * 0123 * 0 * < [(14 212.1 50.0 005.6 49.9 006 010.6 343.8 ---.- 026.2 021.8 01101100\r] <- Fault type + Short status * 012345678901234567890123456789012345678901234567890123456789012345678 * 0 1 2 3 4 5 6 */ { "ups.alarm", 0, NULL, "QFS\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SKIP, NULL, NULL, voltronic_fault }, /* Query UPS for warnings and their type * > [QWS\r] * < [(0000000100000000000000000000000000000000000000000000000000000000\r] * 012345678901234567890123456789012345678901234567890123456789012345 * 0 1 2 3 4 5 6 */ { "ups.alarm", 0, NULL, "QWS\r", "", 66, '(', "", 1, 64, "%s", 0, NULL, NULL, voltronic_warning }, /* Query UPS for actual infos about battery * > [QBV\r] * < [(026.5 02 01 068 255\r] * 012345678901234567890 * 0 1 2 */ { "battery.voltage", 0, NULL, "QBV\r", "", 21, '(', "", 1, 5, "%.2f", 0, NULL, NULL, NULL }, { "battery_number", ST_FLAG_RW, voltronic_r_batt_numb, "QBV\r", "", 21, '(', "", 7, 9, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_batt_numb }, /* Number of batteries that make a pack */ { "battery.packs", ST_FLAG_RW, voltronic_r_batt_packs, "QBV\r", "", 21, '(', "", 10, 11, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, NULL, NULL, NULL }, /* Number of battery packs in parallel */ { "battery.charge", 0, NULL, "QBV\r", "", 21, '(', "", 13, 15, "%.0f", 0, NULL, NULL, NULL }, { "battery.runtime", 0, NULL, "QBV\r", "", 21, '(', "", 17, 19, "%.0f", 0, NULL, NULL, voltronic_batt_runtime }, /* Query UPS for last seen min/max load level * > [QLDL\r] * < [(021 023\r] <- minimum load level - maximum load level * 012345678 * 0 */ { "output.power.minimum.percent", 0, NULL, "QLDL\r", "", 9, '(', "", 1, 3, "%.0f", 0, NULL, NULL, NULL }, { "output.power.maximum.percent", 0, NULL, "QLDL\r", "", 9, '(', "", 5, 7, "%.0f", 0, NULL, NULL, NULL }, /* Query UPS for multi-phase voltages/frequencies * > [Q3**\r] * < [(123.4 123.4 123.4 123.4 123.4 123.4\r] <- Q3PV * < [(123.4 123.4 123.4 123.4 123.4 123.4\r] <- Q3OV * < [(123 123 123\r] <- Q3PC * < [(123 123 123\r] <- Q3OC * < [(123 123 123\r] <- Q3LD * < [(123.4 123.4 123.4\r] <- Q3YV - P09 protocol * < [(123.4 123.4 123.4 123.4 123.4 123.4\r] <- Q3YV - P10/P03 protocols * 0123456789012345678901234567890123456 * 0 1 2 3 * * P09 = 2-phase input/2-phase output * Q3PV (Input Voltage L1 | Input Voltage L2 | Input Voltage L3 | Input Voltage L1-L2 | Input Voltage L1-L3 | Input Voltage L2-L3 * Q3OV (Output Voltage L1 | Output Voltage L2 | Output Voltage L3 | Output Voltage L1-L2 | Output Voltage L1-L3 | Output Voltage L2-L3 * Q3PC (Input Current L1 | Input Current L2 | Input Current L3 * Q3OC (Output Current L1 | Output Current L2 | Output Current L3 * Q3LD (Output Load Level L1 | Output Load Level L2 | Output Load Level L3 * Q3YV (Output Bypass Voltage L1 | Output Bypass Voltage L2 | Output Bypass Voltage L3 * * P10 = 3-phase input/3-phase output / P03 = 3-phase input/ 1-phase output * Q3PV (Input Voltage L1 | Input Voltage L2 | Input Voltage L3 | Input Voltage L1-L2 | Input Voltage L2-L3 | Input Voltage L1-L3 * Q3OV (Output Voltage L1 | Output Voltage L2 | Output Voltage L3 | Output Voltage L1-L2 | Output Voltage L2-L3 | Output Voltage L1-L3 * Q3PC (Input Current L1 | Input Current L2 | Input Current L3 * Q3OC (Output Current L1 | Output Current L2 | Output Current L3 * Q3LD (Output Load Level L1 | Output Load Level L2 | Output Load Level L3 * Q3YV (Output Bypass Voltage L1 | Output Bypass Voltage L2 | Output Bypass Voltage L3 | Output Bypass Voltage L1-L2 | Output Bypass Voltage L2-L3 | Output Bypass Voltage L1-L3 * */ /* From Q3PV */ { "input.L1-N.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L2-N.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L3-N.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L1-L2.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 19, 23, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L2-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L1-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* { "input.L1-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ /* { "input.L2-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ /* From Q3PC */ { "input.L1.current", 0, NULL, "Q3PC\r", "", 13, '(', "", 1, 3, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L2.current", 0, NULL, "Q3PC\r", "", 13, '(', "", 5, 7, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "input.L3.current", 0, NULL, "Q3PC\r", "", 13, '(', "", 9, 11, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* From Q3OV */ { "output.L1-N.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L2-N.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L3-N.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L1-L2.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 19, 23, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L2-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L1-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* { "output.L1-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ /* { "output.L2-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ /* From Q3OC */ { "output.L1.current", 0, NULL, "Q3OC\r", "", 13, '(', "", 1, 3, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L2.current", 0, NULL, "Q3OC\r", "", 13, '(', "", 5, 7, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L3.current", 0, NULL, "Q3OC\r", "", 13, '(', "", 9, 11, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* From Q3LD */ { "output.L1.power.percent", 0, NULL, "Q3LD\r", "", 13, '(', "", 1, 3, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L2.power.percent", 0, NULL, "Q3LD\r", "", 13, '(', "", 5, 7, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.L3.power.percent", 0, NULL, "Q3LD\r", "", 13, '(', "", 9, 11, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* From Q3YV */ { "output.bypass.L1-N.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.bypass.L2-N.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.bypass.L3-N.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.bypass.L1-N.voltage", 0, NULL, "Q3YV\r", "", 19, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* P09 */ { "output.bypass.L2-N.voltage", 0, NULL, "Q3YV\r", "", 19, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* P09 */ /* { "output.bypass.L3-N.voltage", 0, NULL, "Q3YV\r", "", 19, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ { "output.bypass.L1-L2.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 19, 23, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.bypass.L2-L3.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, { "output.bypass.L1-L3.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* Query UPS for capability - total options available: 23; only those whom the UPS is capable of are reported as Enabled or Disabled * > [QFLAG\r] * < [(EpashcDbroegfl\r] * 0123456789012345 * 0 1 * min length = ( + E + D + \r = 4 */ { "ups.start.auto", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, { "battery.protection", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, { "battery.energysave", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, { "ups.start.battery", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, { "outlet.0.switchable", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, /* Not available in NUT */ { "bypass_alarm", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "battery_alarm", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "bypass_when_off", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "alarm_control", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "converter_mode", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "eco_mode", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "battery_open_status_check", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "bypass_forbidding", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "site_fault_detection", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "advanced_eco_mode", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "constant_phase_angle", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, { "limited_runtime_on_battery", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, /* Enable or Disable or Reset to safe default values capability options * > [PEX\r] > [PDX\r] > [PF\r] * < [(ACK\r] < [(ACK\r] < [(ACK\r] * 01234 01234 01234 * 0 0 0 */ { "ups.start.auto", 0, voltronic_e_cap, "P%sR\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, { "battery.protection", 0, voltronic_e_cap, "P%sS\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, { "battery.energysave", 0, voltronic_e_cap, "P%sG\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, { "ups.start.battery", 0, voltronic_e_cap, "P%sC\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, { "outlet.0.switchable", 0, voltronic_e_cap, "P%sJ\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, /* Not available in NUT */ { "reset_to_default", 0, NULL, "PF\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_reset }, { "bypass_alarm", 0, voltronic_e_cap_nonut, "P%sP\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "battery_alarm", 0, voltronic_e_cap_nonut, "P%sB\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "bypass_when_off", 0, voltronic_e_cap_nonut, "P%sO\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "alarm_control", 0, voltronic_e_cap_nonut, "P%sA\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "converter_mode", 0, voltronic_e_cap_nonut, "P%sV\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "eco_mode", 0, voltronic_e_cap_nonut, "P%sE\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "battery_open_status_check", 0, voltronic_e_cap_nonut, "P%sD\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "bypass_forbidding", 0, voltronic_e_cap_nonut, "P%sF\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "site_fault_detection", 0, voltronic_e_cap_nonut, "P%sL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "advanced_eco_mode", 0, voltronic_e_cap_nonut, "P%sN\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "constant_phase_angle", 0, voltronic_e_cap_nonut, "P%sQ\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, { "limited_runtime_on_battery", 0, voltronic_e_cap_nonut, "P%sW\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, /* Query UPS for programmable outlet (1-4) status * > [QSK1\r] * < [(1\r] <- if outlet is on -> (1 , if off -> (0 ; (NAK -> outlet is not programmable * 012 * 0 */ { "outlet.1.switchable", 0, NULL, "QSK1\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.1.status", 0, NULL, "QSK1\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.2.switchable", 0, NULL, "QSK2\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.2.status", 0, NULL, "QSK2\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.3.switchable", 0, NULL, "QSK3\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.3.status", 0, NULL, "QSK3\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.4.switchable", 0, NULL, "QSK4\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, { "outlet.4.status", 0, NULL, "QSK4\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, /* Query UPS for programmable outlet n (1-4) delay time before it shuts down the load when on battery mode * > [QSKT1\r] * < [(008\r] <- if delay time is set (by PSK[1-4]n) it'll report n (minutes) otherwise it'll report (NAK (also if outlet is not programmable) * 01234 * 0 */ { "outlet.1.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT1\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, { "outlet.2.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT2\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, { "outlet.3.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT3\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, { "outlet.4.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT4\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, /* Set delay time for programmable outlets * > [PSK1nnn\r] n = 0..9 * < [(ACK\r] * 01234 * 0 */ { "outlet.1.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK1%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, { "outlet.2.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK2%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, { "outlet.3.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK3%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, { "outlet.4.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK4%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, /* Query UPS for ECO Mode voltage limits * > [QHE\r] * < [(242 218\r] * 012345678 * 0 */ { "input.transfer.high", ST_FLAG_RW, voltronic_r_eco_volt_max, "QHE\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt }, { "input.transfer.low", ST_FLAG_RW, voltronic_r_eco_volt_min, "QHE\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt }, { "input.transfer.low.min", 0, NULL, "QHE\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, { "input.transfer.low.max", 0, NULL, "QHE\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, { "input.transfer.high.min", 0, NULL, "QHE\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, { "input.transfer.high.max", 0, NULL, "QHE\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, /* Set ECO Mode voltage limits * > [HEHnnn\r] > [HELnnn\r] n = 0..9 * < [(ACK\r] < [(ACK\r] * 01234 01234 * 0 0 */ { "input.transfer.high", 0, voltronic_r_eco_volt_max, "HEH%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, { "input.transfer.low", 0, voltronic_r_eco_volt_min, "HEL%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, /* Query UPS for ECO Mode frequency limits * > [QFRE\r] * < [(53.0 47.0\r] * 01234567890 * 0 1 */ { "input.frequency.high", ST_FLAG_RW, voltronic_r_eco_freq_max, "QFRE\r", "", 11, '(', "", 1, 4, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_freq }, { "input.frequency.low", ST_FLAG_RW, voltronic_r_eco_freq_min, "QFRE\r", "", 11, '(', "", 6, 9, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_freq }, /* Set ECO Mode frequency limits * > [FREHnn.n\r] > [FRELnn.n\r] n = 0..9 * < [(ACK\r] < [(ACK\r] * 01234 01234 * 0 0 */ { "input.frequency.high", 0, voltronic_r_eco_freq_max, "FREH%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, { "input.frequency.low", 0, voltronic_r_eco_freq_min, "FREL%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, /* Query UPS for Bypass Mode voltage limits * > [QBYV\r] * < [(264 170\r] * 012345678 * 0 */ { "max_bypass_volt", ST_FLAG_RW, voltronic_r_bypass_volt_max, "QBYV\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, { "min_bypass_volt", ST_FLAG_RW, voltronic_r_bypass_volt_min, "QBYV\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, /* Set Bypass Mode voltage limits * > [PHVnnn\r] > [PLVnnn\r] n = 0..9 * < [(ACK\r] < [(ACK\r] * 01234 01234 * 0 0 */ { "max_bypass_volt", 0, voltronic_r_bypass_volt_max, "PHV%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, { "min_bypass_volt", 0, voltronic_r_bypass_volt_min, "PLV%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, /* Query UPS for Bypass Mode frequency limits * > [QBYF\r] * < [(53.0 47.0\r] * 01234567890 * 0 1 */ { "max_bypass_freq", ST_FLAG_RW, voltronic_r_bypass_freq_max, "QBYF\r", "", 11, '(', "", 1, 4, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, { "min_bypass_freq", ST_FLAG_RW, voltronic_r_bypass_freq_min, "QBYF\r", "", 11, '(', "", 6, 9, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, /* Set Bypass Mode frequency limits * > [PGFnn.n\r] > [PSFnn.n\r] n = 0..9 * < [(ACK\r] < [(ACK\r] * 01234 01234 * 0 0 */ { "max_bypass_freq", 0, voltronic_r_bypass_freq_max, "PGF%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, { "min_bypass_freq", 0, voltronic_r_bypass_freq_min, "PSF%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, /* Set number of batteries that make a pack to n (integer, 1-9). NOTE: changing the number of batteries will change the UPS's estimation on battery charge/runtime * > [BATNn\r] * < [(ACK\r] * 01234 * 0 */ { "battery_number", 0, voltronic_r_batt_numb, "BATN%1.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_process_setvar }, /* Set number of battery packs in parallel to n (integer, 01-99). NOTE: changing the number of battery packs will change the UPS's estimation on battery charge/runtime * > [BATGNn\r] * < [(ACK\r] * 01234 * 0 */ { "battery.packs", 0, voltronic_r_batt_packs, "BATGN%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, /* Query UPS for battery type (Only P31) * > [QBT\r] * < [(01\r] <- 00="Li", 01="Flooded" or 02="AGM" * 0123 * 0 */ { "battery.type", ST_FLAG_RW, voltronic_e_batt_type, "QBT\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_p31b }, /* Set battery type (Only P31) * > [PBTnn\r] nn = 00/01/02 * < [(ACK\r] * 01234 * 0 */ { "battery.type", 0, voltronic_e_batt_type, "PBT%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_p31b_set }, /* Query UPS for device grid working range (Only P31) * > [QGR\r] * < [(01\r] <- 00=Appliance, 01=UPS * 0123 * 0 */ { "work_range_type", ST_FLAG_RW, voltronic_e_work_range, "QGR\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_p31g }, /* Set device grid working range type (Only P31) * > [PBTnn\r] nn = 00/01 * < [(ACK\r] * 01234 * 0 */ { "work_range_type", 0, voltronic_e_work_range, "PGR%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_p31g_set }, /* Query UPS for battery low voltage * > [RE0\r] * < [#20\r] * 012 * 0 */ { "battery.voltage.low", ST_FLAG_RW, voltronic_r_batt_low, "RE0\r", "", 3, '#', "", 1, 2, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, NULL, NULL, NULL }, /* Set voltage for battery low to n (integer, 20..24/20..28). NOTE: changing the battery low voltage will change the UPS's estimation on battery charge/runtime * > [W0En\r] * < [(ACK\r] * 01234 * 0 */ { "battery.voltage.low", 0, voltronic_r_batt_low, "W0E%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, /* Query UPS for Phase Angle * > [QPD\r] * < [(000 120\r] <- Input Phase Angle - Output Phase Angle * 012345678 * 0 */ { "input_phase_angle", 0, NULL, "QPD\r", "", 9, '(', "", 1, 3, "%03d", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_phase }, { "output_phase_angle", ST_FLAG_RW, voltronic_e_phase, "QPD\r", "", 9, '(', "", 5, 7, "%03d", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_NONUT, NULL, NULL, voltronic_phase }, /* Set output phase angle * > [PPDn\r] n = (000, 120, 180 or 240) * < [(ACK\r] * 01234 * 0 */ { "output_phase_angle", 0, voltronic_e_phase, "PPD%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_phase_set }, /* Query UPS for master/slave for a system of UPSes in parallel * > [QPAR\r] * < [(001\r] <- 001 for master UPS, 002 and 003 for slave UPSes * 01234 * 0 */ { "voltronic_parallel", 0, NULL, "QPAR\r", "", 5, '(', "", 1, 3, "%s", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_parallel }, /* Query UPS for ?? * > [QBDR\r] * < [(1234\r] <- unknown reply - My UPS (NAK at me * 012345 * 0 */ { "unknown.7", 0, NULL, "QBDR\r", "", 5, '(', "", 1, 0, "%s", QX_FLAG_STATIC | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, NULL }, /* Instant commands */ { "load.off", 0, NULL, "SOFF\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "SON\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, { "shutdown.stop", 0, NULL, "CS\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "beeper.toggle", 0, NULL, "BZ%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, /* Enable/disable beeper: unskipped if the UPS can control alarm (capability) */ { "beeper.enable", 0, NULL, "PEA\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "beeper.disable", 0, NULL, "PDA\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, /* Outlet control: unskipped if the outlets are manageable */ { "outlet.1.load.off", 0, NULL, "SKOFF1\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.1.load.on", 0, NULL, "SKON1\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.2.load.off", 0, NULL, "SKOFF2\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.2.load.on", 0, NULL, "SKON2\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.3.load.off", 0, NULL, "SKOFF3\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.3.load.on", 0, NULL, "SKON3\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.4.load.off", 0, NULL, "SKOFF4\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "outlet.4.load.on", 0, NULL, "SKON4\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, /* Bypass: unskipped if the UPS is capable of ECO Mode */ { "bypass.start", 0, NULL, "PEE\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, { "bypass.stop", 0, NULL, "PDE\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, voltronic_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, voltronic_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* == Testing table == */ #ifdef TESTING static testing_t voltronic_testing[] = { { "QGS\r", "(234.9 50.0 229.8 50.0 000.0 00A 369.1 ---.- 026.5 ---.- 018.8 100000000001\r", -1 }, { "QPI\r", "(PI01\r", -1 }, { "QRI\r", "(230.0 004 024.0 50.0\r", -1 }, { "QMF\r", "(#####VOLTRONIC\r", -1 }, { "I\r", "#------------- ------ VT12046Q \r", -1 }, { "F\r", "#220.0 000 024.0 50.0\r", -1 }, { "QMD\r", "(#######OLHVT1K0 ###1000 80 2/2 230 230 02 12.0\r", -1 }, { "QFS\r", "(14 212.1 50.0 005.6 49.9 006 010.6 343.8 ---.- 026.2 021.8 01101100\r", -1 }, { "QMOD\r", "(S\r", -1 }, { "QVFW\r", "(VERFW:00322.02\r", -1 }, { "QID\r", "(685653211455\r", -1 }, { "QBV\r", "(026.5 02 01 068 255\r", -1 }, { "QFLAG\r", "(EpashcjDbroegfl\r", -1 }, { "QWS\r", "(0000000000000000000000000000000000000000000000000000000001000001\r", -1 }, { "QHE\r", "(242 218\r", -1 }, { "QBYV\r", "(264 170\r", -1 }, { "QBYF\r", "(53.0 47.0\r", -1 }, { "QSK1\r", "(1\r", -1 }, { "QSK2\r", "(0\r", -1 }, { "QSK3\r", "(1\r", -1 }, { "QSK4\r", "(NAK\r", -1 }, { "QSKT1\r", "(008\r", -1 }, { "QSKT2\r", "(012\r", -1 }, { "QSKT3\r", "(NAK\r", -1 }, { "QSKT4\r", "(007\r", -1 }, { "RE0\r", "#20\r", -1 }, { "W0E24\r", "(ACK\r", -1 }, { "PF\r", "(ACK\r", -1 }, { "PEA\r", "(ACK\r", -1 }, { "PDR\r", "(NAK\r", -1 }, { "HEH250\r", "(ACK\r", -1 }, { "HEL210\r", "(ACK\r", -1 }, { "PHV260\r", "(NAK\r", -1 }, { "PLV190\r", "(ACK\r", -1 }, { "PGF51.0\r", "(NAK\r", -1 }, { "PSF47.5\r", "(ACK\r", -1 }, { "BATN2\r", "(ACK\r", -1 }, { "BATGN04\r", "(ACK\r", -1 }, { "QBT\r", "(01\r", -1 }, { "PBT02\r", "(ACK\r", -1 }, { "QGR\r", "(00\r", -1 }, { "PGR01\r", "(ACK\r", -1 }, { "PSK1008\r", "(ACK\r", -1 }, { "PSK3987\r", "(ACK\r", -1 }, { "PSK2009\r", "(ACK\r", -1 }, { "PSK4012\r", "(ACK\r", -1 }, { "Q3PV\r", "(123.4 456.4 789.4 012.4 323.4 223.4\r", -1 }, { "Q3OV\r", "(253.4 163.4 023.4 143.4 103.4 523.4\r", -1 }, { "Q3OC\r", "(109 069 023\r", -1 }, { "Q3LD\r", "(005 033 089\r", -1 }, { "Q3YV\r", "(303.4 245.4 126.4 222.4 293.4 321.4\r", -1 }, { "Q3PC\r", "(002 023 051\r", -1 }, { "SOFF\r", "(NAK\r", -1 }, { "SON\r", "(ACK\r", -1 }, { "T\r", "(NAK\r", -1 }, { "TL\r", "(ACK\r", -1 }, { "CS\r", "(ACK\r", -1 }, { "CT\r", "(NAK\r", -1 }, { "BZOFF\r", "(ACK\r", -1 }, { "BZON\r", "(ACK\r", -1 }, { "S.3R0002\r", "(ACK\r", -1 }, { "S02R0024\r", "(NAK\r", -1 }, { "S.5\r", "(ACK\r", -1 }, { "T.3\r", "(ACK\r", -1 }, { "T02\r", "(NAK\r", -1 }, { "SKON1\r", "(ACK\r", -1 }, { "SKOFF1\r", "(NAK\r", -1 }, { "SKON2\r", "(ACK\r", -1 }, { "SKOFF2\r", "(ACK\r", -1 }, { "SKON3\r", "(NAK\r", -1 }, { "SKOFF3\r", "(ACK\r", -1 }, { "SKON4\r", "(NAK\r", -1 }, { "SKOFF4\r", "(NAK\r", -1 }, { "QPAR\r", "(003\r", -1 }, { "QPD\r", "(000 240\r", -1 }, { "PPD120\r", "(ACK\r", -1 }, { "QLDL\r", "(005 080\r", -1 }, { "QBDR\r", "(1234\r", -1 }, { "QFRE\r", "(50.0 00.0\r", -1 }, { "FREH54.0\r", "(ACK\r", -1 }, { "FREL47.0\r", "(ACK\r", -1 }, { "PEP\r", "(ACK\r", -1 }, { "PDP\r", "(ACK\r", -1 }, { "PEB\r", "(ACK\r", -1 }, { "PDB\r", "(ACK\r", -1 }, { "PER\r", "(NAK\r", -1 }, { "PDR\r", "(NAK\r", -1 }, { "PEO\r", "(ACK\r", -1 }, { "PDO\r", "(ACK\r", -1 }, { "PEA\r", "(ACK\r", -1 }, { "PDA\r", "(ACK\r", -1 }, { "PES\r", "(ACK\r", -1 }, { "PDS\r", "(ACK\r", -1 }, { "PEV\r", "(ACK\r", -1 }, { "PDV\r", "(ACK\r", -1 }, { "PEE\r", "(ACK\r", -1 }, { "PDE\r", "(ACK\r", -1 }, { "PEG\r", "(ACK\r", -1 }, { "PDG\r", "(NAK\r", -1 }, { "PED\r", "(ACK\r", -1 }, { "PDD\r", "(ACK\r", -1 }, { "PEC\r", "(ACK\r", -1 }, { "PDC\r", "(NAK\r", -1 }, { "PEF\r", "(NAK\r", -1 }, { "PDF\r", "(ACK\r", -1 }, { "PEJ\r", "(NAK\r", -1 }, { "PDJ\r", "(ACK\r", -1 }, { "PEL\r", "(ACK\r", -1 }, { "PDL\r", "(ACK\r", -1 }, { "PEN\r", "(ACK\r", -1 }, { "PDN\r", "(ACK\r", -1 }, { "PEQ\r", "(ACK\r", -1 }, { "PDQ\r", "(ACK\r", -1 }, { "PEW\r", "(NAK\r", -1 }, { "PDW\r", "(ACK\r", -1 }, { NULL } }; #endif /* TESTING */ /* == Support functions == */ /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ static int voltronic_claim(void) { /* We need at least QGS and QPI to run this subdriver */ item_t *item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value */ if (ups_infoval_set(item) != 1) return 0; /* UPS Protocol */ item = find_nut_info("ups.firmware.aux", 0, 0); /* Don't know what happened */ if (!item) { dstate_delinfo("input.voltage"); return 0; } /* No reply/Unable to get value */ if (qx_process(item, NULL)) { dstate_delinfo("input.voltage"); return 0; } /* Unable to process value/Protocol out of range */ if (ups_infoval_set(item) != 1) { dstate_delinfo("input.voltage"); return 0; } return 1; } /* Subdriver-specific flags/vars */ static void voltronic_makevartable(void) { /* Capability vars */ addvar(VAR_FLAG, "reset_to_default", "Reset capability options and their limits to safe default values"); addvar(VAR_VALUE, "bypass_alarm", "Alarm (BEEP!) at Bypass Mode [enabled/disabled]"); addvar(VAR_VALUE, "battery_alarm", "Alarm (BEEP!) at Battery Mode [enabled/disabled]"); addvar(VAR_VALUE, "bypass_when_off", "Bypass when the UPS is Off [enabled/disabled]"); addvar(VAR_VALUE, "alarm_control", "Alarm (BEEP!) Control [enabled/disabled]"); addvar(VAR_VALUE, "converter_mode", "Converter Mode [enabled/disabled]"); addvar(VAR_VALUE, "eco_mode", "ECO Mode [enabled/disabled]"); addvar(VAR_VALUE, "battery_open_status_check", "Battery Open Status Check [enabled/disabled]"); addvar(VAR_VALUE, "bypass_forbidding", "Bypass not allowed (Bypass Forbidding) [enabled/disabled]"); addvar(VAR_VALUE, "site_fault_detection", "Site fault detection [enabled/disabled]"); addvar(VAR_VALUE, "advanced_eco_mode", "Advanced ECO Mode [enabled/disabled]"); addvar(VAR_VALUE, "constant_phase_angle", "Constant Phase Angle Function (Output and input phase angles are not equal) [enabled/disabled]"); addvar(VAR_VALUE, "limited_runtime_on_battery", "Limited runtime on battery mode [enabled/disabled]"); /* Bypass Mode frequency/voltage limits */ addvar(VAR_VALUE, "max_bypass_volt", "Maximum voltage for Bypass Mode"); addvar(VAR_VALUE, "min_bypass_volt", "Minimum voltage for Bypass Mode"); addvar(VAR_VALUE, "max_bypass_freq", "Maximum frequency for Bypass Mode"); addvar(VAR_VALUE, "min_bypass_freq", "Minimum frequency for Bypass Mode"); /* Device grid working range type for P31 UPSes */ addvar(VAR_VALUE, "work_range_type", "Device grid working range for P31 UPSes [Appliance/UPS]"); /* Output phase angle */ addvar(VAR_VALUE, "output_phase_angle", "Change output phase angle to the provided value [000, 120, 180, 240]°"); /* Number of batteries */ addvar(VAR_VALUE, "battery_number", "Set number of batteries that make a pack to n (integer, 1-9)"); /* For testing purposes */ addvar(VAR_FLAG, "testing", "If invoked the driver will exec also commands that still need testing"); } /* Unskip vars according to protocol used by the UPS */ static void voltronic_massive_unskip(const int protocol) { item_t *item; for (item = voltronic_qx2nut; item->info_type != NULL; item++) { if (!item->command) continue; if ( /* == Multiphase UPSes == */ /* P09 */ (protocol == 9 && ( /* (!strcasecmp(item->info_type, "input.L1-L3.voltage") && item->from == 25) || *//* Not unskipped because P09 should be 2-phase input/output */ /* (!strcasecmp(item->info_type, "input.L2-L3.voltage") && item->from == 31) || *//* Not unskipped because P09 should be 2-phase input/output */ /* (!strcasecmp(item->info_type, "output.L1-L3.voltage") && item->from == 25) || *//* Not unskipped because P09 should be 2-phase input/output */ /* (!strcasecmp(item->info_type, "output.L2-L3.voltage") && item->from == 31) || *//* Not unskipped because P09 should be 2-phase input/output */ (!strcasecmp(item->info_type, "output.bypass.L1-N.voltage") && item->answer_len == 19) || (!strcasecmp(item->info_type, "output.bypass.L2-N.voltage") && item->answer_len == 19)/* || (!strcasecmp(item->info_type, "output.bypass.L3-N.voltage") && item->answer_len == 19) *//* Not unskipped because P09 should be 2-phase input/output */ )) || /* P10 */ (protocol == 10 && ( !strcasecmp(item->info_type, "output.L3-N.voltage") || (!strcasecmp(item->info_type, "output.L2-L3.voltage") && item->from == 25) || (!strcasecmp(item->info_type, "output.L1-L3.voltage") && item->from == 31) || (!strcasecmp(item->info_type, "output.bypass.L1-N.voltage") && item->answer_len == 37) || (!strcasecmp(item->info_type, "output.bypass.L2-N.voltage") && item->answer_len == 37) || (!strcasecmp(item->info_type, "output.bypass.L3-N.voltage") && item->answer_len == 37) || !strcasecmp(item->info_type, "output.bypass.L1-L2.voltage") || !strcasecmp(item->info_type, "output.bypass.L2-L3.voltage") || !strcasecmp(item->info_type, "output.bypass.L1-L3.voltage") || !strcasecmp(item->info_type, "output.L3.current") || !strcasecmp(item->info_type, "output.L3.power.percent") )) || /* P09-P10 */ ((protocol == 9 || protocol == 10) && ( !strcasecmp(item->info_type, "output.L1-N.voltage") || !strcasecmp(item->info_type, "output.L2-N.voltage") ||/* !strcasecmp(item->info_type, "output.L3-N.voltage") || *//* Not unskipped because P09 should be 2-phase input/output */ !strcasecmp(item->info_type, "output.L1-L2.voltage") || !strcasecmp(item->info_type, "output.L1.current") || !strcasecmp(item->info_type, "output.L2.current") ||/* !strcasecmp(item->info_type, "output.L3.current") || *//* Not unskipped because P09 should be 2-phase input/output */ !strcasecmp(item->info_type, "output.L1.power.percent") || !strcasecmp(item->info_type, "output.L2.power.percent")/* || !strcasecmp(item->info_type, "output.L3.power.percent") *//* Not unskipped because P09 should be 2-phase input/output */ )) || /* P03-P09-P10 */ ((protocol == 3 || protocol == 9 || protocol == 10) && ( !strcasecmp(item->info_type, "input.L1-N.voltage") || !strcasecmp(item->info_type, "input.L2-N.voltage") ||/* !strcasecmp(item->info_type, "input.L3-N.voltage") ||*//* Not unskipped because P09 should be 2-phase input/output */ !strcasecmp(item->info_type, "input.L1-L2.voltage") || !strcasecmp(item->info_type, "input.L1.current") || !strcasecmp(item->info_type, "input.L2.current")/* || !strcasecmp(item->info_type, "input.L3.current")*//* Not unskipped because P09 should be 2-phase input/output */ )) || /* P03-P10 */ ((protocol == 3 || protocol == 10) && ( !strcasecmp(item->info_type, "input.L3-N.voltage") || (!strcasecmp(item->info_type, "input.L2-L3.voltage") && item->from == 25) || (!strcasecmp(item->info_type, "input.L1-L3.voltage") && item->from == 31) || !strcasecmp(item->info_type, "input.L3.current") )) || /* == P31 battery type/device grid working range == */ (protocol == 31 && ( !strcasecmp(item->info_type, "battery.type") || (!strcasecmp(item->info_type, "work_range_type") && !(item->qxflags & QX_FLAG_SETVAR)) || (!strcasecmp(item->info_type, "work_range_type") && (item->qxflags & QX_FLAG_SETVAR) && getval(item->info_type)) )) || /* == ByPass limits: all but P00/P08/P31 == */ (protocol != 0 && protocol != 8 && protocol != 31 && ( (!strcasecmp(item->info_type, "max_bypass_volt") && !(item->qxflags & QX_FLAG_SETVAR)) || (!strcasecmp(item->info_type, "min_bypass_volt") && !(item->qxflags & QX_FLAG_SETVAR)) || (!strcasecmp(item->info_type, "max_bypass_freq") && !(item->qxflags & QX_FLAG_SETVAR)) || (!strcasecmp(item->info_type, "min_bypass_freq") && !(item->qxflags & QX_FLAG_SETVAR)) )) || /* == Reset capabilities options to safe default values == */ (!strcasecmp(item->info_type, "reset_to_default") && testvar("reset_to_default")) || /* == QBDR (unknown) == */ (!strcasecmp(item->info_type, "unknown.7") && testvar("testing")) ) { item->qxflags &= ~QX_FLAG_SKIP; } } } /* == Preprocess functions == */ /* *SETVAR(/NONUT)* Preprocess setvars */ static int voltronic_process_setvar(item_t *item, char *value, const size_t valuelen) { double val; if (!strlen(value)) { upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); return -1; } val = strtod(value, NULL); if (!strcasecmp(item->info_type, "ups.delay.start")) { /* Truncate to minute */ val -= ((int)val % 60); snprintf(value, valuelen, "%.0f", val); return 0; } else if (!strcasecmp(item->info_type, "ups.delay.shutdown")) { /* Truncate to nearest setable value */ if (val < 60) { val -= ((int)val % 6); } else { val -= ((int)val % 60); } snprintf(value, valuelen, "%.0f", val); return 0; } else if (!strcasecmp(item->info_type, "max_bypass_freq")) { if (val == max_bypass_freq) { upslogx(LOG_INFO, "%s is already set to %.1f", item->info_type, val); return -1; } } else if (!strcasecmp(item->info_type, "min_bypass_freq")) { if (val == min_bypass_freq) { upslogx(LOG_INFO, "%s is already set to %.1f", item->info_type, val); return -1; } } else if (!strcasecmp(item->info_type, "max_bypass_volt")) { if (val == max_bypass_volt) { upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); return -1; } } else if (!strcasecmp(item->info_type, "min_bypass_volt")) { if (val == min_bypass_volt) { upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); return -1; } } else if (!strcasecmp(item->info_type, "battery_number")) { if (val == battery_number) { upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); return -1; } } snprintf(value, valuelen, item->command, val); return 0; } /* *CMD* Preprocess instant commands */ static int voltronic_process_command(item_t *item, char *value, const size_t valuelen) { char buf[SMALLBUF] = ""; if (!strcasecmp(item->info_type, "shutdown.return")) { /* Sn: Shutdown after n minutes and then turn on when mains is back * SnRm: Shutdown after n minutes and then turn on after m minutes * Accepted values for n: .2 -> .9 , 01 -> 99 * Accepted values for m: 0001 -> 9999 */ int offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10), ondelay = strtol(dstate_getinfo("ups.delay.start"), NULL, 10) / 60; if (ondelay == 0) { if (offdelay < 60) { snprintf(buf, sizeof(buf), ".%d", offdelay / 6); } else { snprintf(buf, sizeof(buf), "%02d", offdelay / 60); } } else if (offdelay < 60) { snprintf(buf, sizeof(buf), ".%dR%04d", offdelay / 6, ondelay); } else { snprintf(buf, sizeof(buf), "%02dR%04d", offdelay / 60, ondelay); } } else if (!strcasecmp(item->info_type, "shutdown.stayoff")) { /* SnR0000 * Shutdown after n minutes and stay off * Accepted values for n: .2 -> .9 , 01 -> 99 */ int offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10); if (offdelay < 60) { snprintf(buf, sizeof(buf), ".%d", offdelay / 6); } else { snprintf(buf, sizeof(buf), "%02d", offdelay / 60); } } else if (!strcasecmp(item->info_type, "test.battery.start")) { /* Accepted values for test time: .2 -> .9 (.2=12sec ..), 01 -> 99 (minutes) * -> you have to invoke test.battery.start + number of seconds [12..5940] */ int delay; if (strlen(value) != strspn(value, "0123456789")) { upslogx(LOG_ERR, "%s: non numerical value [%s]", item->info_type, value); return -1; } delay = strlen(value) > 0 ? strtol(value, NULL, 10) : 600; if ((delay < 12) || (delay > 5940)) { upslogx(LOG_ERR, "%s: battery test time '%d' out of range [12..5940] seconds", item->info_type, delay); return -1; } /* test time < 1 minute */ if (delay < 60) { delay = delay / 6; snprintf(buf, sizeof(buf), ".%d", delay); /* test time > 1 minute */ } else { delay = delay / 60; snprintf(buf, sizeof(buf), "%02d", delay); } } else if (!strcasecmp(item->info_type, "beeper.toggle")) { const char *beeper_status = dstate_getinfo("ups.beeper.status"); /* If the UPS is beeping then we can call BZOFF; if we previously set BZOFF we can call BZON, provided that the beeper is not disabled */ /* The UPS can disable/enable alarm (from UPS capability) */ if (alarm_control) { if (!strcmp(beeper_status, "enabled")) { snprintf(buf, sizeof(buf), "OFF"); } else if (!strcmp(beeper_status, "muted")) { snprintf(buf, sizeof(buf), "ON"); /* Beeper disabled */ } else { upslogx(LOG_INFO, "The beeper is already disabled"); return -1; } /* The UPS can't disable/enable alarm (from UPS capability) */ } else { if (!strcmp(beeper_status, "enabled")) { snprintf(buf, sizeof(buf), "OFF"); } else if (!strcmp(beeper_status, "disabled")) { snprintf(buf, sizeof(buf), "ON"); } } } else { /* Don't know what happened */ return -1; } snprintf(value, valuelen, item->command, buf); return 0; } /* UPS capabilities */ static int voltronic_capability(item_t *item, char *value, const size_t valuelen) { char rawval[SMALLBUF], *enabled, *disabled, *val = NULL, *saveptr = NULL; item_t *unskip; snprintf(rawval, sizeof(rawval), "%s", item->value); enabled = strtok_r(rawval+1, "D", &saveptr); disabled = strtok_r(NULL, "\0", &saveptr); if (!enabled && !disabled) { upsdebugx(2, "%s: parsing failed", __func__); return -1; } enabled = enabled ? enabled : ""; disabled = disabled ? disabled : ""; /* NONUT items */ if (!strcasecmp(item->info_type, "bypass_alarm")) { if (strchr(enabled, 'p')) { val = bypass_alarm = "enabled"; } else if (strchr(disabled, 'p')) { val = bypass_alarm = "disabled"; } } else if (!strcasecmp(item->info_type, "battery_alarm")) { if (strchr(enabled, 'b')) { val = battery_alarm = "enabled"; } else if (strchr(disabled, 'b')) { val = battery_alarm = "disabled"; } } else if (!strcasecmp(item->info_type, "bypass_when_off")) { if (strchr(enabled, 'o')) { val = bypass_when_off = "enabled"; } else if (strchr(disabled, 'o')) { val = bypass_when_off = "disabled"; } } else if (!strcasecmp(item->info_type, "alarm_control")) { if (strchr(item->value, 'a')) { if (strchr(enabled, 'a')) { const char *beeper = dstate_getinfo("ups.beeper.status"); val = alarm_control = "enabled"; if (!beeper || strcmp(beeper, "muted")) { dstate_setinfo("ups.beeper.status", "enabled"); } } else if (strchr(disabled, 'a')) { val = alarm_control = "disabled"; dstate_setinfo("ups.beeper.status", "disabled"); } /* Unskip beeper.{enable,disable} instcmds */ unskip = find_nut_info("beeper.enable", QX_FLAG_CMD, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; unskip = find_nut_info("beeper.disable", QX_FLAG_CMD, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } } else if (!strcasecmp(item->info_type, "converter_mode")) { if (strchr(enabled, 'v')) { val = converter_mode = "enabled"; } else if (strchr(disabled, 'v')) { val = converter_mode = "disabled"; } } else if (!strcasecmp(item->info_type, "eco_mode")) { if (strchr(item->value, 'e')) { if (strchr(enabled, 'e')) { val = eco_mode = "enabled"; } else if (strchr(disabled, 'e')) { val = eco_mode = "disabled"; } /* Unskip bypass.{start,stop} instcmds */ unskip = find_nut_info("bypass.start", QX_FLAG_CMD, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; unskip = find_nut_info("bypass.stop", QX_FLAG_CMD, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; /* Unskip input.transfer.{high,low} */ unskip = find_nut_info("input.transfer.high", QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; unskip = find_nut_info("input.transfer.low", QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; /* Unskip input.frequency.{high,low} */ unskip = find_nut_info("input.frequency.high", QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; unskip = find_nut_info("input.frequency.low", QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } } else if (!strcasecmp(item->info_type, "battery_open_status_check")) { if (strchr(enabled, 'd')) { val = battery_open_status_check = "enabled"; } else if (strchr(disabled, 'd')) { val = battery_open_status_check = "disabled"; } } else if (!strcasecmp(item->info_type, "bypass_forbidding")) { if (strchr(enabled, 'f')) { val = bypass_forbidding = "enabled"; } else if (strchr(disabled, 'f')) { val = bypass_forbidding = "disabled"; } } else if (!strcasecmp(item->info_type, "site_fault_detection")) { if (strchr(enabled, 'l')) { val = site_fault_detection = "enabled"; } else if (strchr(disabled, 'l')) { val = site_fault_detection = "disabled"; } } else if (!strcasecmp(item->info_type, "advanced_eco_mode")) { if (strchr(enabled, 'n')) { val = advanced_eco_mode = "enabled"; } else if (strchr(disabled, 'n')) { val = advanced_eco_mode = "disabled"; } } else if (!strcasecmp(item->info_type, "constant_phase_angle")) { if (strchr(enabled, 'q')) { val = constant_phase_angle = "enabled"; } else if (strchr(disabled, 'q')) { val = constant_phase_angle = "disabled"; } } else if (!strcasecmp(item->info_type, "limited_runtime_on_battery")) { if (strchr(enabled, 'w')) { val = limited_runtime_on_battery = "enabled"; } else if (strchr(disabled, 'w')) { val = limited_runtime_on_battery = "disabled"; } /* } else if (!strcasecmp(item->info_type, "")) { if (strchr(enabled, 'h')) { unknown/unused } else if (strchr(disabled, 'h')) { } } else if (!strcasecmp(item->info_type, "")) { if (strchr(enabled, 't')) { unknown/unused } else if (strchr(disabled, 't')) { } } else if (!strcasecmp(item->info_type, "")) { if (strchr(enabled, 'k')) { unknown/unused } else if (strchr(disabled, 'k')) { } } else if (!strcasecmp(item->info_type, "")) { if (strchr(enabled, 'i')) { unknown/unused } else if (strchr(disabled, 'i')) { } } else if (!strcasecmp(item->info_type, "")) { if (strchr(enabled, 'm')) { unknown/unused } else if (strchr(disabled, 'm')) { } } else if (!strcasecmp(item->info_type, "")) { if (strchr(enabled, 'z')) { unknown/unused } else if (strchr(disabled, 'z')) { } */ /* Items with a NUT variable */ } else if (!strcasecmp(item->info_type, "ups.start.auto")) { if (strchr(enabled, 'r')) { val = "yes"; } else if (strchr(disabled, 'r')) { val = "no"; } } else if (!strcasecmp(item->info_type, "battery.protection")) { if (strchr(enabled, 's')) { val = "yes"; } else if (strchr(disabled, 's')) { val = "no"; } } else if (!strcasecmp(item->info_type, "battery.energysave")) { if (strchr(enabled, 'g')) { val = "yes"; } else if (strchr(disabled, 'g')) { val = "no"; } } else if (!strcasecmp(item->info_type, "ups.start.battery")) { if (strchr(enabled, 'c')) { val = "yes"; } else if (strchr(disabled, 'c')) { val = "no"; } } else if (!strcasecmp(item->info_type, "outlet.0.switchable")) { if (strchr(enabled, 'j')) { int i; char buf[SMALLBUF]; val = "yes"; /* Unskip outlet.n.{switchable,status} */ for (i = 1; i <= 4; i++) { snprintf(buf, sizeof(buf), "outlet.%d.switchable", i); unskip = find_nut_info(buf, 0, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; snprintf(buf, sizeof(buf), "outlet.%d.status", i); unskip = find_nut_info(buf, 0, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } } else if (strchr(disabled, 'j')) { val = "no"; } } /* UPS doesn't have that capability */ if (!val) return -1; snprintf(value, valuelen, item->dfl, val); /* This item doesn't have a NUT var and we were not asked by the user to change its value */ if ((item->qxflags & QX_FLAG_NONUT) && !getval(item->info_type)) return 0; /* Unskip setvar */ unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* *SETVAR* Set UPS capability options */ static int voltronic_capability_set(item_t *item, char *value, const size_t valuelen) { if (!strcasecmp(value, "yes")) { snprintf(value, valuelen, item->command, "E"); return 0; } if (!strcasecmp(value, "no")) { snprintf(value, valuelen, item->command, "D"); return 0; } /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ upslogx(LOG_ERR, "%s: given value [%s] is not acceptable. Enter either 'yes' or 'no'.", item->info_type, value); return -1; } /* *SETVAR/NONUT* Change UPS capability according to user configuration in ups.conf */ static int voltronic_capability_set_nonut(item_t *item, char *value, const size_t valuelen) { const char *match = NULL; int i; const struct { const char *type; /* Name of the option */ const char *match; /* Value reported by the UPS */ } capability[] = { { "bypass_alarm", bypass_alarm }, { "battery_alarm", battery_alarm }, { "bypass_when_off", bypass_when_off }, { "alarm_control", alarm_control }, { "converter_mode", converter_mode }, { "eco_mode", eco_mode }, { "battery_open_status_check", battery_open_status_check }, { "bypass_forbidding", bypass_forbidding }, { "site_fault_detection", site_fault_detection }, { "advanced_eco_mode", advanced_eco_mode }, { "constant_phase_angle", constant_phase_angle }, { "limited_runtime_on_battery", limited_runtime_on_battery }, { NULL } }; for (i = 0; capability[i].type; i++) { if (strcasecmp(item->info_type, capability[i].type)) continue; match = capability[i].match; break; } /* UPS doesn't have that capability */ if (!match) return -1; /* At this point value should have been already checked by nutdrv_qx's own setvar so this shouldn't happen.. however.. */ if (!strcasecmp(value, match)) { upslogx(LOG_INFO, "%s is already %s", item->info_type, match); return -1; } if (!strcasecmp(value, "disabled")) { snprintf(value, valuelen, item->command, "D"); } else if (!strcasecmp(value, "enabled")) { snprintf(value, valuelen, item->command, "E"); } else { /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ upslogx(LOG_ERR, "%s: [%s] is not within acceptable values [enabled/disabled]", item->info_type, value); return -1; } return 0; } /* *SETVAR/NONUT* Reset capability options and their limits to safe default values */ static int voltronic_capability_reset(item_t *item, char *value, const size_t valuelen) { /* Nothing to do */ if (!testvar("reset_to_default")) return -1; /* UPS capability options can be reset only when the UPS is in 'Standby Mode' (=OFF) (from QMOD) */ if (!(qx_status() & STATUS(OFF))) { upslogx(LOG_ERR, "%s: UPS capability options can be reset only when the UPS is in Standby Mode (i.e. ups.status = 'OFF').", item->info_type); return -1; } snprintf(value, valuelen, "%s", item->command); return 0; } /* Voltage limits for ECO Mode */ static int voltronic_eco_volt(item_t *item, char *value, const size_t valuelen) { const int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10); int ovn; const char *outvoltnom; char buf[SMALLBUF]; item_t *unskip; /* Range of accepted values for maximum voltage for ECO Mode */ struct { int lower; /* Lower limit */ int upper; /* Upper limit */ } max; /* Range of accepted values for minimum voltage for ECO Mode */ struct { int lower; /* Lower limit */ int upper; /* Upper limit */ } min; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } snprintf(value, valuelen, item->dfl, strtod(item->value, NULL)); outvoltnom = dstate_getinfo("output.voltage.nominal"); /* Since query for ratings (QRI) is not mandatory to run this driver, skip next steps if we can't get the value of output voltage nominal */ if (!outvoltnom) { upsdebugx(2, "%s: unable to get output voltage nominal", __func__); /* We return 0 since we have the value and all's ok: simply we can't set its range so we won't unskip SETVAR item and .{min,max} */ return 0; } ovn = strtol(outvoltnom, NULL, 10); /* For P01/P09 */ if (protocol == 1 || protocol == 9) { if (ovn >= 200) { min.lower = ovn - 24; min.upper = ovn - 7; max.lower = ovn + 7; max.upper = ovn + 24; } else { min.lower = ovn - 12; min.upper = ovn - 3; max.lower = ovn + 3; max.upper = ovn + 12; } /* For P02/P03/P10/P13/P14/P99 */ } else if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) { if (ovn >= 200) { min.lower = ovn - 24; min.upper = ovn - 11; max.lower = ovn + 11; max.upper = ovn + 24; } else { min.lower = ovn - 12; min.upper = ovn - 5; max.lower = ovn + 5; max.upper = ovn + 12; } /* ECO mode not supported */ } else { upsdebugx(2, "%s: the UPS doesn't seem to support ECO Mode", __func__); /* We return 0 since we have the value and all's ok: simply we can't set its range so we won't unskip SETVAR item and .{min,max} */ return 0; } if (!strcasecmp(item->info_type, "input.transfer.high")) { /* Fill voltronic_r_eco_volt_max */ snprintf(item->info_rw[0].value, sizeof(item->info_rw[0].value), "%d", max.lower); snprintf(item->info_rw[1].value, sizeof(item->info_rw[1].value), "%d", max.upper); } else if (!strcasecmp(item->info_type, "input.transfer.low")) { /* Fill voltronic_r_eco_volt_min */ snprintf(item->info_rw[0].value, sizeof(item->info_rw[0].value), "%d", min.lower); snprintf(item->info_rw[1].value, sizeof(item->info_rw[1].value), "%d", min.upper); } /* Unskip input.transfer.{high,low}.{min,max} */ snprintf(buf, sizeof(buf), "%s.min", item->info_type); unskip = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; snprintf(buf, sizeof(buf), "%s.max", item->info_type); unskip = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; /* Unskip input.transfer.{high,low} setvar */ unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* Voltage limits for ECO Mode (max, min) */ static int voltronic_eco_volt_range(item_t *item, char *value, const size_t valuelen) { char *buf; int i; item_t *from; if (!strcasecmp(item->info_type, "input.transfer.low.min")) { buf = "input.transfer.low"; i = 0; } else if (!strcasecmp(item->info_type, "input.transfer.low.max")) { buf = "input.transfer.low"; i = 1; } else if (!strcasecmp(item->info_type, "input.transfer.high.min")) { buf = "input.transfer.high"; i = 0; } else if (!strcasecmp(item->info_type, "input.transfer.high.max")) { buf = "input.transfer.high"; i = 1; } else { /* Don't know what happened */ return -1; } from = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened */ if (!from) return -1; /* Value is set at runtime by voltronic_eco_volt, so if it's still unset something went wrong */ if (!strlen(from->info_rw[i].value)) return -1; snprintf(value, valuelen, "%s", from->info_rw[i].value); return 0; } /* Frequency limits for ECO Mode */ static int voltronic_eco_freq(item_t *item, char *value, const size_t valuelen) { item_t *unskip; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } snprintf(value, valuelen, item->dfl, strtod(item->value, NULL)); /* Unskip input.transfer.{high,low} setvar */ unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* *NONUT* Voltage/frequency limits for Bypass Mode */ static int voltronic_bypass(item_t *item, char *value, const size_t valuelen) { item_t *unskip; double val; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } if (!strcasecmp(item->info_type, "max_bypass_volt")) { val = max_bypass_volt = strtol(item->value, NULL, 10); } else if (!strcasecmp(item->info_type, "min_bypass_volt")) { val = min_bypass_volt = strtol(item->value, NULL, 10); } else if (!strcasecmp(item->info_type, "max_bypass_freq")) { val = max_bypass_freq = strtod(item->value, NULL); } else if (!strcasecmp(item->info_type, "min_bypass_freq")) { val = min_bypass_freq = strtod(item->value, NULL); } else { /* Don't know what happened */ return -1; } snprintf(value, valuelen, item->dfl, val); /* No user-provided value to change.. */ if (!getval(item->info_type)) return 0; /* Unskip {min,max}_bypass_volt setvar */ unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* *NONUT* Number of batteries */ static int voltronic_batt_numb(item_t *item, char *value, const size_t valuelen) { item_t *unskip; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } battery_number = strtol(item->value, NULL, 10); snprintf(value, valuelen, item->dfl, battery_number); /* No user-provided value to change.. */ if (!getval(item->info_type)) return 0; /* Unskip battery_number setvar */ unskip = find_nut_info("battery_number", QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* Battery runtime */ static int voltronic_batt_runtime(item_t *item, char *value, const size_t valuelen) { double runtime; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } /* Battery runtime is reported by the UPS in minutes, NUT expects seconds */ runtime = strtod(item->value, NULL) * 60; snprintf(value, valuelen, item->dfl, runtime); return 0; } /* Protocol used by the UPS */ static int voltronic_protocol(item_t *item, char *value, const size_t valuelen) { int protocol; if (strncasecmp(item->value, "PI", 2)) { upsdebugx(2, "%s: invalid start characters [%.2s]", __func__, item->value); return -1; } /* Here we exclude non numerical value and other non accepted protocols (hence the restricted comparison target) */ if (strspn(item->value+2, "0123489") != strlen(item->value+2)) { upslogx(LOG_ERR, "Protocol [%s] is not supported by this driver", item->value); return -1; } protocol = strtol(item->value+2, NULL, 10); switch (protocol) { case 0: case 1: case 2: case 3: case 8: case 9: case 10: case 13: case 14: case 31: case 99: break; default: upslogx(LOG_ERR, "Protocol [PI%02d] is not supported by this driver", protocol); return -1; } snprintf(value, valuelen, "P%02d", protocol); /* Unskip vars according to protocol */ voltronic_massive_unskip(protocol); return 0; } /* Fault reported by the UPS: * When the UPS is queried for status (QGS), if it reports a fault (6th bit of 12bit flag of the reply to QGS set to 1), the driver unskips the QFS item in qx2nut array: this function processes the reply to QFS query */ static int voltronic_fault(item_t *item, char *value, const size_t valuelen) { int protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10); char alarm[SMALLBUF]; upslogx(LOG_INFO, "Checking for faults.."); if (!strcasecmp(item->value, "OK")) { snprintf(value, valuelen, item->dfl, "No fault found"); upslogx(LOG_INFO, "%s", value); item->qxflags |= QX_FLAG_SKIP; return 0; } if ((strspn(item->value, "0123456789ABC") != 2) || ((item->value[0] != '1') && (strspn(item->value+1, "0123456789") != 1))) { snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); /* P31 UPSes */ } else if (protocol == 31) { if (strpbrk(item->value+1, "ABC")) { snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); } else { switch (strtol(item->value, NULL, 10)) { case 1: strcpy(alarm, "Fan failure."); break; case 2: strcpy(alarm, "Over temperature fault."); break; case 3: strcpy(alarm, "Battery voltage is too high."); break; case 4: strcpy(alarm, "Battery voltage too low."); break; case 5: strcpy(alarm, "Inverter relay short-circuited."); break; case 6: strcpy(alarm, "Inverter voltage over maximum value."); break; case 7: strcpy(alarm, "Overload fault."); update_status("OVER"); break; case 8: strcpy(alarm, "Bus voltage exceeds its upper limit."); break; case 9: strcpy(alarm, "Bus soft start fail."); break; case 10: strcpy(alarm, "Unknown fault [Fault code: 10]"); break; case 51: strcpy(alarm, "Over current fault."); break; case 52: strcpy(alarm, "Bus voltage below its under limit."); break; case 53: strcpy(alarm, "Inverter soft start fail."); break; case 54: strcpy(alarm, "Self test fail."); break; case 55: strcpy(alarm, "Output DC voltage exceeds its upper limit."); break; case 56: strcpy(alarm, "Battery open fault."); break; case 57: strcpy(alarm, "Current sensor fault."); break; case 58: strcpy(alarm, "Battery short."); break; case 59: strcpy(alarm, "Inverter voltage below its lower limit."); break; default: snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); break; } } /* All other UPSes */ } else { switch (strtol(item->value, NULL, 10)) { case 1: switch (item->value[1]) { case 'A': strcpy(alarm, "L1 inverter negative power out of acceptable range."); break; case 'B': strcpy(alarm, "L2 inverter negative power out of acceptable range."); break; case 'C': strcpy(alarm, "L3 inverter negative power out of acceptable range."); break; default: strcpy(alarm, "Bus voltage not within default setting."); break; } break; case 2: strcpy(alarm, "Bus voltage over maximum value."); break; case 3: strcpy(alarm, "Bus voltage below minimum value."); break; case 4: strcpy(alarm, "Bus voltage differences out of acceptable range."); break; case 5: strcpy(alarm, "Bus voltage of slope rate drops too fast."); break; case 6: strcpy(alarm, "Over current in PFC input inductor."); break; case 11: strcpy(alarm, "Inverter voltage not within default setting."); break; case 12: strcpy(alarm, "Inverter voltage over maximum value."); break; case 13: strcpy(alarm, "Inverter voltage below minimum value."); break; case 14: strcpy(alarm, "Inverter short-circuited."); break; case 15: strcpy(alarm, "L2 phase inverter short-circuited."); break; case 16: strcpy(alarm, "L3 phase inverter short-circuited."); break; case 17: strcpy(alarm, "L1L2 inverter short-circuited."); break; case 18: strcpy(alarm, "L2L3 inverter short-circuited."); break; case 19: strcpy(alarm, "L3L1 inverter short-circuited."); break; case 21: strcpy(alarm, "Battery SCR short-circuited."); break; case 22: strcpy(alarm, "Line SCR short-circuited."); break; case 23: strcpy(alarm, "Inverter relay open fault."); break; case 24: strcpy(alarm, "Inverter relay short-circuited."); break; case 25: strcpy(alarm, "Input and output wires oppositely connected."); break; case 26: strcpy(alarm, "Battery oppositely connected."); break; case 27: strcpy(alarm, "Battery voltage is too high."); break; case 28: strcpy(alarm, "Battery voltage too low."); break; case 29: strcpy(alarm, "Failure for battery fuse being open-circuited."); break; case 31: strcpy(alarm, "CAN-bus communication fault."); break; case 32: strcpy(alarm, "Host signal circuit fault."); break; case 33: strcpy(alarm, "Synchronous signal circuit fault."); break; case 34: strcpy(alarm, "Synchronous pulse signal circuit fault."); break; case 35: strcpy(alarm, "Parallel cable disconnected."); break; case 36: strcpy(alarm, "Load unbalanced."); break; case 41: strcpy(alarm, "Over temperature fault."); break; case 42: strcpy(alarm, "Communication failure between CPUs in control board."); break; case 43: strcpy(alarm, "Overload fault."); update_status("OVER"); break; case 44: strcpy(alarm, "Fan failure."); break; case 45: strcpy(alarm, "Charger failure."); break; case 46: strcpy(alarm, "Model fault."); break; case 47: strcpy(alarm, "MCU communication fault."); break; default: snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); break; } } snprintf(value, valuelen, item->dfl, alarm); upslogx(LOG_INFO, "Fault found: %s", alarm); item->qxflags |= QX_FLAG_SKIP; return 0; } /* Warnings reported by the UPS */ static int voltronic_warning(item_t *item, char *value, const size_t valuelen) { char warn[SMALLBUF] = "", unk[SMALLBUF] = "", bitwarns[SMALLBUF] = "", warns[4096] = ""; int i; if (strspn(item->value, "01") != strlen(item->value)) { upsdebugx(2, "%s: invalid reply from the UPS [%s]", __func__, item->value); return -1; } /* No warnings */ if (strspn(item->value, "0") == strlen(item->value)) { return 0; } snprintf(value, valuelen, "UPS warnings:"); for (i = 0; i < (int)strlen(item->value); i++) { int u = 0; if (item->value[i] == '1') { switch (i) { case 0: strcpy(warn, "Battery disconnected."); break; case 1: strcpy(warn, "Neutral not connected."); break; case 2: strcpy(warn, "Site fault."); break; case 3: strcpy(warn, "Phase sequence incorrect."); break; case 4: strcpy(warn, "Phase sequence incorrect in bypass."); break; case 5: strcpy(warn, "Input frequency unstable in bypass."); break; case 6: strcpy(warn, "Battery overcharged."); break; case 7: strcpy(warn, "Low battery."); update_status("LB"); break; case 8: strcpy(warn, "Overload alarm."); update_status("OVER"); break; case 9: strcpy(warn, "Fan alarm."); break; case 10: strcpy(warn, "EPO enabled."); break; case 11: strcpy(warn, "Unable to turn on UPS."); break; case 12: strcpy(warn, "Over temperature alarm."); break; case 13: strcpy(warn, "Charger alarm."); break; case 14: strcpy(warn, "Remote auto shutdown."); break; case 15: strcpy(warn, "L1 input fuse not working."); break; case 16: strcpy(warn, "L2 input fuse not working."); break; case 17: strcpy(warn, "L3 input fuse not working."); break; case 18: strcpy(warn, "Positive PFC abnormal in L1."); break; case 19: strcpy(warn, "Negative PFC abnormal in L1."); break; case 20: strcpy(warn, "Positive PFC abnormal in L2."); break; case 21: strcpy(warn, "Negative PFC abnormal in L2."); break; case 22: strcpy(warn, "Positive PFC abnormal in L3."); break; case 23: strcpy(warn, "Negative PFC abnormal in L3."); break; case 24: strcpy(warn, "Abnormal in CAN-bus communication."); break; case 25: strcpy(warn, "Abnormal in synchronous signal circuit."); break; case 26: strcpy(warn, "Abnormal in synchronous pulse signal circuit."); break; case 27: strcpy(warn, "Abnormal in host signal circuit."); break; case 28: strcpy(warn, "Male connector of parallel cable not connected well."); break; case 29: strcpy(warn, "Female connector of parallel cable not connected well."); break; case 30: strcpy(warn, "Parallel cable not connected well."); break; case 31: strcpy(warn, "Battery connection not consistent in parallel systems."); break; case 32: strcpy(warn, "AC connection not consistent in parallel systems."); break; case 33: strcpy(warn, "Bypass connection not consistent in parallel systems."); break; case 34: strcpy(warn, "UPS model types not consistent in parallel systems."); break; case 35: strcpy(warn, "Capacity of UPSes not consistent in parallel systems."); break; case 36: strcpy(warn, "Auto restart setting not consistent in parallel systems."); break; case 37: strcpy(warn, "Battery cell over charge."); break; case 38: strcpy(warn, "Battery protection setting not consistent in parallel systems."); break; case 39: strcpy(warn, "Battery detection setting not consistent in parallel systems."); break; case 40: strcpy(warn, "Bypass not allowed setting not consistent in parallel systems."); break; case 41: strcpy(warn, "Converter setting not consistent in parallel systems."); break; case 42: strcpy(warn, "High loss point for frequency in bypass mode not consistent in parallel systems."); break; case 43: strcpy(warn, "Low loss point for frequency in bypass mode not consistent in parallel systems."); break; case 44: strcpy(warn, "High loss point for voltage in bypass mode not consistent in parallel systems."); break; case 45: strcpy(warn, "Low loss point for voltage in bypass mode not consistent in parallel systems."); break; case 46: strcpy(warn, "High loss point for frequency in AC mode not consistent in parallel systems."); break; case 47: strcpy(warn, "Low loss point for frequency in AC mode not consistent in parallel systems."); break; case 48: strcpy(warn, "High loss point for voltage in AC mode not consistent in parallel systems."); break; case 49: strcpy(warn, "Low loss point for voltage in AC mode not consistent in parallel systems."); break; case 50: strcpy(warn, "Warning for locking in bypass mode after 3 consecutive overloads within 30 min."); break; case 51: strcpy(warn, "Warning for three-phase AC input current unbalance."); break; case 52: strcpy(warn, "Warning for a three-phase input current unbalance detected in battery mode."); break; case 53: strcpy(warn, "Warning for Inverter inter-current unbalance."); break; case 54: strcpy(warn, "Programmable outlets cut off pre-alarm."); break; case 55: strcpy(warn, "Warning for Battery replace."); update_status("RB"); break; case 56: strcpy(warn, "Abnormal warning on input phase angle."); break; case 57: strcpy(warn, "Warning!! Cover of maintain switch is open."); break; case 61: strcpy(warn, "EEPROM operation error."); break; default: snprintf(warn, sizeof(warn), "Unknown warning from UPS [bit: #%02d]", i + 1); u++; break; } upslogx(LOG_INFO, "Warning from UPS: %s", warn); if (u) { /* Unknown warnings */ snprintfcat(unk, sizeof(unk), ", #%02d", i + 1); } else { /* Known warnings */ if (strlen(warns) > 0) { /* For too long warnings (total) */ snprintfcat(bitwarns, sizeof(bitwarns), ", #%02d", i + 1); /* For warnings (total) not too long */ snprintfcat(warns, sizeof(warns), " %s", warn); } else { snprintf(bitwarns, sizeof(bitwarns), "Known (see log or manual) [bit: #%02d", i + 1); snprintf(warns, sizeof(warns), "%s", warn); } } } } /* There's some known warning, at least */ if (strlen(warns) > 0) { /* We have both known and unknown warnings */ if (strlen(unk) > 0) { /* Appending unknown ones to known ones; removing leading comma from unk - 'explicit' */ snprintfcat(warns, sizeof(warns), " Unknown warnings [bit:%s]", unk+1); /* Appending unknown ones to known ones; removing leading comma from unk - 'cryptic' */ snprintfcat(bitwarns, sizeof(bitwarns), "]; Unknown warnings [bit:%s]", unk+1); /* We have only known warnings */ } else { snprintfcat(bitwarns, sizeof(bitwarns), "]"); } /* We have only unknown warnings */ } else if (strlen(unk) > 0) { /* Removing leading comma from unk */ snprintf(warns, sizeof(warns), "Unknown warnings [bit:%s]", unk+1); strcpy(bitwarns, warns); } else { /* Don't know what happened */ upsdebugx(2, "%s: failed to process warnings", __func__); return -1; } /* If grand total of warnings doesn't exceed value of alarm (=ST_MAX_VALUE_LEN) minus some space (32) for other alarms.. */ if ((ST_MAX_VALUE_LEN - 32) > strlen(warns)) { /* ..then be explicit.. */ snprintfcat(value, valuelen, " %s", warns); /* ..otherwise.. */ } else { /* ..be cryptic */ snprintfcat(value, valuelen, " %s", bitwarns); } return 0; } /* Working mode reported by the UPS */ static int voltronic_mode(item_t *item, char *value, const size_t valuelen) { char *status = NULL, *alarm = NULL; switch (item->value[0]) { case 'P': alarm = "UPS is going ON"; break; case 'S': status = "OFF"; break; case 'Y': status = "BYPASS"; break; case 'L': status = "OL"; break; case 'B': status = "!OL"; break; case 'T': status = "CAL"; break; case 'F': alarm = "Fault reported by UPS."; break; case 'E': alarm = "UPS is in ECO Mode."; break; case 'C': alarm = "UPS is in Converter Mode."; break; case 'D': alarm = "UPS is shutting down!"; status = "FSD"; break; default: upsdebugx(2, "%s: invalid reply from the UPS [%s]", __func__, item->value); return -1; } if (alarm && !strcasecmp(item->info_type, "ups.alarm")) { snprintf(value, valuelen, item->dfl, alarm); } else if (status && !strcasecmp(item->info_type, "ups.status")) { snprintf(value, valuelen, item->dfl, status); } return 0; } /* Process status bits */ static int voltronic_status(item_t *item, char *value, const size_t valuelen) { char *val = ""; if (strspn(item->value, "01") != strlen(item->value)) { upsdebugx(3, "%s: unexpected value %s@%d->%s", __func__, item->value, item->from, item->value); return -1; } switch (item->from) { case 63: /* UPS Type - ups.type */ { int type = strtol(item->value, NULL, 10); if (!type) /* 00 -> Offline */ val = "offline"; else if (type == 1) /* 01 -> Line-interactive */ val = "line-interactive"; else if (type == 10) /* 10 -> Online */ val = "online"; else { upsdebugx(2, "%s: invalid type [%s: %s]", __func__, item->info_type, item->value); return -1; } } break; case 65: /* Utility Fail (Immediate) - ups.status */ if (item->value[0] == '1') val = "!OL"; else val = "OL"; break; case 66: /* Battery Low - ups.status */ if (item->value[0] == '1') val = "LB"; else val = "!LB"; break; case 67: /* Bypass/Boost or Buck Active - ups.{status,alarm} */ if (item->value[0] == '1') { double vi, vo; vi = strtod(dstate_getinfo("input.voltage"), NULL); vo = strtod(dstate_getinfo("output.voltage"), NULL); if (vo < 0.5 * vi) { upsdebugx(2, "%s: output voltage too low", __func__); return -1; } if (vo < 0.95 * vi) { val = "TRIM"; } else if (vo < 1.05 * vi) { int prot = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10); if (!prot || prot == 8) { /* ups.alarm */ if (!strcasecmp(item->info_type, "ups.alarm")) val = "UPS is in AVR Mode."; } else { /* ups.status */ if (!strcasecmp(item->info_type, "ups.status")) val = "BYPASS"; } } else if (vo < 1.5 * vi) { val = "BOOST"; } else { upsdebugx(2, "%s: output voltage too high", __func__); return -1; } } break; case 68: /* UPS Fault - ups.alarm */ if (item->value[0] == 1) { item_t *faultitem; for (faultitem = voltronic_qx2nut; faultitem->info_type != NULL; faultitem++) { if (!faultitem->command) continue; if (!strcasecmp(faultitem->command, "QFS\r")) { faultitem->qxflags &= ~QX_FLAG_SKIP; break; } } val = "UPS Fault!"; } break; /* case 69: *//* unknown */ /* break;*/ case 70: /* Test in Progress - ups.status */ if (item->value[0] == '1') val = "CAL"; else val = "!CAL"; break; case 71: /* Shutdown Active - ups.status */ if (item->value[0] == '1') val = "FSD"; else val = "!FSD"; break; case 72: /* Beeper status - ups.beeper.status */ /* The UPS has the ability to enable/disable the alarm (from UPS capability) */ if (alarm_control) { const char *beeper = dstate_getinfo("ups.beeper.status"); if (!beeper || strcasecmp(beeper, "disabled")) { if (item->value[0] == '0') /* Beeper On */ val = "enabled"; else val = "muted"; } /* The UPS lacks the ability to enable/disable the alarm (from UPS capability) */ } else { if (item->value[0] == '0') /* Beeper On */ val = "enabled"; else val = "disabled"; } break; /* case 73: *//* unknown */ /* break;*/ /* case 74: *//* unknown */ /* break;*/ default: /* Don't know what happened */ return -1; } snprintf(value, valuelen, "%s", val); return 0; } /* Output power factor */ static int voltronic_output_powerfactor(item_t *item, char *value, const size_t valuelen) { double opf; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } /* UPS report a value expressed in % so -> output.powerfactor*100 e.g. opf = 0,8 -> ups = 80 */ opf = strtod(item->value, NULL) * 0.01; snprintf(value, valuelen, item->dfl, opf); return 0; } /* UPS serial number */ static int voltronic_serial_numb(item_t *item, char *value, const size_t valuelen) { /* If the UPS report a 00..0 serial we'll log it but we won't store it in device.serial */ if (strspn(item->value, "0") == strlen(item->value)) { upslogx(LOG_INFO, "%s: UPS reported a non-significant serial [%s]", item->info_type, item->value); return -1; } snprintf(value, valuelen, item->dfl, item->value); return 0; } /* Outlet status */ static int voltronic_outlet(item_t *item, char *value, const size_t valuelen) { const char *status, *switchable; char number = item->info_type[7], buf[SMALLBUF]; item_t *outlet_item; switch (item->value[0]) { case '1': switchable = "yes"; status = "on"; break; case '0': switchable = "yes"; status = "off"; break; default: upsdebugx(2, "%s: invalid reply from the UPS [%s: %s]", __func__, item->info_type, item->value); return -1; } if (strstr(item->info_type, "switchable")) { snprintf(value, valuelen, item->dfl, switchable); } else if (strstr(item->info_type, "status")) { snprintf(value, valuelen, item->dfl, status); } else { /* Don't know what happened */ return -1; } /* Unskip outlet.n.delay.shutdown */ snprintf(buf, sizeof(buf), "outlet.%c.delay.shutdown", number); outlet_item = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); /* Don't know what happened*/ if (!outlet_item) return -1; outlet_item->qxflags &= ~QX_FLAG_SKIP; /* Unskip outlet.n.load.on */ snprintf(buf, sizeof(buf), "outlet.%c.load.on", number); outlet_item = find_nut_info(buf, QX_FLAG_CMD, 0); /* Don't know what happened*/ if (!outlet_item) return -1; outlet_item->qxflags &= ~QX_FLAG_SKIP; /* Unskip outlet.n.load.off */ snprintf(buf, sizeof(buf), "outlet.%c.load.off", number); outlet_item = find_nut_info(buf, QX_FLAG_CMD, 0); /* Don't know what happened*/ if (!outlet_item) return -1; outlet_item->qxflags &= ~QX_FLAG_SKIP; return 0; } /* Outlet delay time */ static int voltronic_outlet_delay(item_t *item, char *value, const size_t valuelen) { char number = item->info_type[7], buf[SMALLBUF]; double val; item_t *setvar_item; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } /* UPS reports minutes, NUT expects seconds */ val = strtod(item->value, NULL) * 60; snprintf(value, valuelen, item->dfl, val); /* Unskip outlet.n.delay.shutdown setvar */ snprintf(buf, sizeof(buf), "outlet.%c.delay.shutdown", number); setvar_item = find_nut_info(buf, QX_FLAG_SETVAR, 0); /* Don't know what happened*/ if (!setvar_item) return -1; setvar_item->qxflags &= ~QX_FLAG_SKIP; return 0; } /* *SETVAR* Outlet delay time */ static int voltronic_outlet_delay_set(item_t *item, char *value, const size_t valuelen) { int delay = strtol(value, NULL, 10); /* From seconds to minute */ delay = delay / 60; snprintf(value, valuelen, item->command, delay); return 0; } /* Type of battery */ static int voltronic_p31b(item_t *item, char *value, const size_t valuelen) { int val; if ((item->value[0] != '0') || (strspn(item->value+1, "012") != 1)) { upsdebugx(2, "%s: invalid battery type reported by the UPS [%s]", __func__, item->value); return -1; } val = strtol(item->value, NULL, 10); snprintf(value, valuelen, item->dfl, item->info_rw[val].value); return 0; } /* *SETVAR* Type of battery */ static int voltronic_p31b_set(item_t *item, char *value, const size_t valuelen) { int i; for (i = 0; strlen(item->info_rw[i].value) > 0; i++) { if (!strcasecmp(item->info_rw[i].value, value)) break; } /* At this point value should already be checked against enum so this shouldn't happen.. however.. */ if (i >= (int)(sizeof(item->info_rw) / sizeof(item->info_rw[0]))) { upslogx(LOG_ERR, "%s: value [%s] out of range", item->info_type, value); return -1; } snprintf(value, valuelen, "%d", i); return voltronic_process_setvar(item, value, valuelen); } /* *NONUT* Actual device grid working range type for P31 UPSes */ static int voltronic_p31g(item_t *item, char *value, const size_t valuelen) { int val; if ((item->value[0] != '0') || (strspn(item->value+1, "01") != 1)) { upsdebugx(2, "%s: invalid device grid working range reported by the UPS [%s]", __func__, item->value); return -1; } val = strtol(item->value, NULL, 10); snprintf(value, valuelen, item->dfl, item->info_rw[val].value); work_range_type = val; return 0; } /* *SETVAR/NONUT* Device grid working range type for P31 UPSes */ static int voltronic_p31g_set(item_t *item, char *value, const size_t valuelen) { int i; for (i = 0; strlen(item->info_rw[i].value) > 0; i++) { if (!strcasecmp(item->info_rw[i].value, value)) break; } /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ if (i >= (int)(sizeof(item->info_rw) / sizeof(item->info_rw[0]))) { upslogx(LOG_ERR, "%s: value [%s] out of range", item->info_type, value); return -1; } if (i == work_range_type) { upslogx(LOG_INFO, "%s is already set to %s", item->info_type, item->info_rw[i].value); return -1; } snprintf(value, valuelen, "%d", i); return voltronic_process_setvar(item, value, valuelen); } /* *NONUT* UPS actual input/output phase angles */ static int voltronic_phase(item_t *item, char *value, const size_t valuelen) { int angle; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } angle = strtol(item->value, NULL, 10); if (!strcasecmp(item->info_type, "output_phase_angle")) { output_phase_angle = angle; /* User-provided value to change.. */ if (getval(item->info_type)) { item_t *unskip; /* Unskip output_phase_angle setvar */ unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } } snprintf(value, valuelen, item->dfl, angle); return 0; } /* *SETVAR/NONUT* Output phase angle */ static int voltronic_phase_set(item_t *item, char *value, const size_t valuelen) { int i; for (i = 0; strlen(item->info_rw[i].value) > 0; i++) { if (!strcasecmp(item->info_rw[i].value, value)) break; } /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ if (i >= (int)(sizeof(item->info_rw) / sizeof(item->info_rw[0]))) { upslogx(LOG_ERR, "%s: value [%s] out of range", item->info_type, value); return -1; } if (strtol(item->info_rw[i].value, NULL, 10) == output_phase_angle) { upslogx(LOG_INFO, "%s is already set to %s", item->info_type, item->info_rw[i].value); return -1; } snprintf(value, valuelen, "%d", i); return voltronic_process_setvar(item, value, valuelen); } /* *NONUT* UPS is master/slave in a system of UPSes in parallel */ static int voltronic_parallel(item_t *item, char *value, const size_t valuelen) { char *type; if (strlen(item->value) != strspn(item->value, "0123456789")) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } /* 001 for master UPS, 002 and 003 for slave UPSes */ switch (strtol(item->value, NULL, 10)) { case 1: type = "master"; break; case 2: case 3: type = "slave"; break; default: upsdebugx(2, "%s: invalid reply from the UPS [%s]", __func__, item->value); return -1; } snprintf(value, valuelen, "This UPS is *%s* in a system of UPSes in parallel", type); return 0; } /* == Subdriver interface == */ subdriver_t voltronic_subdriver = { VOLTRONIC_VERSION, voltronic_claim, voltronic_qx2nut, NULL, NULL, voltronic_makevartable, "ACK", "(NAK\r", #ifdef TESTING voltronic_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/safenet.c0000644000175000017500000003062712640443572012541 00000000000000/* * safenet.c - model specific routines for following units: * * - Fairstone L525/-625/-750 * - Fenton P400/-600/-800 * - Gemini UPS625/-1000 * - Powerwell PM525A/-625A/-800A/-1000A/-1250A * - Repotec RPF525/-625/-800/-1000 * - Soltec Winmate 525/625/800/1000 * - Sweex 500/1000 * - others using SafeNet software and serial interface * * Status: * 20081102/Revision 1.41 - Arjen de Korte * - allow more time for reading reply to command * 20081106/Revision 1.5 - Arjen de Korte * - changed communication with UPS * - improved handling of battery & system test * 20081228/Revision 1.6 - Arjen de Korte * - add ondelay and offdelay * * Copyright (C) 2003-2008 Arjen de Korte * * 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 "main.h" #include "serial.h" #include "safenet.h" #define DRIVER_NAME "Generic SafeNet UPS driver" #define DRIVER_VERSION "1.6" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte ", DRV_STABLE, { NULL } }; /* * Here we keep the last known status of the UPS */ static union { char reply[10]; struct safenet status; } ups; static int ondelay = 1; /* minutes */ static int offdelay = 30; /* seconds */ static int safenet_command(const char *command) { char reply[32]; int i, ret; /* * Get rid of whatever is in the in- and output buffers. */ ser_flush_io(upsfd); /* * Send the command and read back the status line. When we just send * a status polling command, it will return the actual status. */ ret = ser_send(upsfd, "%s", command); if (ret < 0) { upsdebug_with_errno(3, "send"); return -1; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); return -1; } upsdebugx(3, "send: %.*s", (int)strcspn(command, "\r"), command); /* * Read the reply from the UPS. */ ret = ser_get_buf(upsfd, reply, sizeof(reply), 1, 0); if (ret < 0) { upsdebug_with_errno(3, "read"); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); return -1; } upsdebugx(3, "read: %.*s", (int)strcspn(reply, "\r"), reply); /* * We check if the reply looks like a valid status. */ if ((ret != 12) || (reply[0] != '$') || (strspn(reply+1, "AB") != 10)) { return -1; } for (i = 0; i < 10; i++) { ups.reply[i] = ((reply[i+1] == 'B') ? 1 : 0); } return 0; } static void safenet_update(void) { status_init(); if (ups.status.onbattery) { status_set("OB"); } else { status_set("OL"); } if (ups.status.batterylow) { status_set("LB"); } if (ups.status.overload) { status_set("OVER"); } if (ups.status.batteryfail) { status_set("RB"); } if (ups.status.systemtest) { status_set("CAL"); } alarm_init(); if (ups.status.systemfail) { alarm_set("System selftest fail!"); } alarm_commit(); status_commit(); } static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.mute' for this driver"); return instcmd("beeper.mute", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } /* * Start the UPS selftest */ if (!strcasecmp(cmdname, "test.battery.start")) { if (safenet_command(COM_BATT_TEST)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * Stop the UPS selftest */ if (!strcasecmp(cmdname, "test.battery.stop")) { if (safenet_command(COM_STOP_TEST)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * Start simulated mains failure */ if (!strcasecmp (cmdname, "test.failure.start")) { if (safenet_command(COM_MAINS_TEST)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * Stop simulated mains failure */ if (!strcasecmp (cmdname, "test.failure.stop")) { if (safenet_command(COM_STOP_TEST)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * If beeper is off, toggle beeper state (so it should be ON after this) */ if (!strcasecmp(cmdname, "beeper.enable")) { if (ups.status.silenced && safenet_command(COM_TOGGLE_BEEP)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * If beeper is not off, toggle beeper state (so it should be OFF after this) * Unfortunately, this only mutes the beeper, it turns back on for the next * event automatically (no way to stop this, besides side cutters) */ if (!strcasecmp(cmdname, "beeper.mute")) { if (!ups.status.silenced && safenet_command(COM_TOGGLE_BEEP)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * Toggle beeper state unconditionally */ if (!strcasecmp(cmdname, "beeper.toggle")) { if (safenet_command(COM_TOGGLE_BEEP)) { return STAT_INSTCMD_FAILED; } else { return STAT_INSTCMD_HANDLED; } } /* * Shutdown and wait for the power to return */ if (!strcasecmp(cmdname, "shutdown.return")) { char command[] = SHUTDOWN_RETURN; command[4] += ((offdelay % 1000) / 100); command[5] += ((offdelay % 100) / 10); command[6] += (offdelay % 10); safenet_command(command); return STAT_INSTCMD_HANDLED; } /* * Shutdown and reboot */ if (!strcasecmp(cmdname, "shutdown.reboot")) { char command[] = SHUTDOWN_REBOOT; command[3] += ((offdelay % 1000) / 100); command[4] += ((offdelay % 100) / 10); command[5] += (offdelay % 10); command[7] += ((ondelay % 10000) / 1000); command[8] += ((ondelay % 1000) / 100); command[9] += ((ondelay % 100) / 10); command[10] += (ondelay % 10); safenet_command(command); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_initinfo(void) { int retry = 3; char *v; usleep(100000); /* * Very crude hardware detection. If an UPS is attached, it will set DSR * to 1. Bail out if it isn't. */ if (!ser_get_dsr(upsfd)) { fatalx(EXIT_FAILURE, "Serial cable problem or nothing attached to %s", device_path); } /* * Initialize the serial interface of the UPS by sending the magic * string. If it does not respond with a valid status reply, * display an error message and give up. */ while (safenet_command(COM_INITIALIZE)) { if (--retry) { continue; } fatalx(EXIT_FAILURE, "SafeNet protocol compatible UPS not found on %s", device_path); } /* * Read the commandline settings for the following parameters, since we can't * autodetect them. */ dstate_setinfo("ups.mfr", "%s", ((v = getval("manufacturer")) != NULL) ? v : "unknown"); dstate_setinfo("ups.model", "%s", ((v = getval("modelname")) != NULL) ? v : "unknown"); dstate_setinfo("ups.serial", "%s", ((v = getval("serialnumber")) != NULL) ? v : "unknown"); dstate_setinfo("ups.delay.start", "%d", 60 * ondelay); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); /* * These are the instant commands we support. */ dstate_addcmd ("test.battery.start"); dstate_addcmd ("test.battery.stop"); dstate_addcmd ("test.failure.start"); dstate_addcmd ("test.failure.stop"); dstate_addcmd ("beeper.on"); dstate_addcmd ("beeper.off"); dstate_addcmd ("beeper.enable"); dstate_addcmd ("beeper.mute"); dstate_addcmd ("beeper.toggle"); dstate_addcmd ("shutdown.return"); dstate_addcmd ("shutdown.reboot"); upsh.instcmd = instcmd; } /* * The status polling commands are *almost* random. Whatever the reason * is, there is a certain pattern in them. The first character after the * start character 'Z' determines how many positions there are between * that character and the single 'L' character that's in each command (A=0, * B=1,...,J=9). The rest is filled with random (?) data [A...J]. But why? * No idea. The UPS *does* check if the polling commands match this format. * And as the SafeNet software uses "random" polling commands, so do we. * * Note: if you don't use ASCII, the characters will be different! */ void upsdrv_updateinfo(void) { char command[] = COM_POLL_STAT; int i; static int retry = 0; /* * Fill the command portion with random characters from the range * [A...J]. */ for (i = 1; i < 12; i++) { command[i] = (random() % 10) + 'A'; } /* * Find which character must be an 'L' and put it there. */ command[command[1]-'A'+2] = 'L'; /* * Do a status poll. */ if (safenet_command(command)) { ser_comm_fail("Status read failed"); if (retry < 2) { retry++; } else { dstate_datastale(); } return; } ser_comm_good(); retry = 0; if (ups.status.systemtest && ups.status.batterylow) { /* * Don't update status after stopping battery test, to * allow UPS to update the status flags (OB+LB glitch) */ if (safenet_command(COM_STOP_TEST)) { upslogx(LOG_WARNING, "Can't terminate battery test!"); } else { upslogx(LOG_INFO, "Battery test finished"); return; } } safenet_update(); dstate_dataok(); } void upsdrv_shutdown(void) { int retry = 3; /* * Since we may have arrived here before the hardware is initialized, * try to initialize it here. * * Initialize the serial interface of the UPS by sending the magic * string. If it does not respond with a valid status reply, * display an error message and give up. */ while (safenet_command(COM_INITIALIZE)) { if (--retry) { continue; } fatalx(EXIT_FAILURE, "SafeNet protocol compatible UPS not found on %s", device_path); } /* * Since the UPS will happily restart on battery, we must use a * different shutdown command depending on the line status, so * we need to check the status of the UPS here. */ if (ups.status.onbattery) { instcmd("shutdown.return", NULL); } else { instcmd("shutdown.reboot", NULL); } } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "manufacturer", "manufacturer [unknown]"); addvar(VAR_VALUE, "modelname", "modelname [unknown]"); addvar(VAR_VALUE, "serialnumber", "serialnumber [unknown]"); addvar(VAR_VALUE, "ondelay", "Delay before UPS startup (minutes)"); addvar(VAR_VALUE, "offdelay", "Delay before UPS shutdown (seconds)"); } void upsdrv_initups(void) { struct termios tio; const char *val; /* * Open and lock the serial port and set the speed to 1200 baud. */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* * Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = '\r'; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } /* * Set DTR and clear RTS to provide power for the serial interface. */ ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); val = getval("ondelay"); if (val) { ondelay = strtol(val, NULL, 10); } if ((ondelay < 0) || (ondelay > 9999)) { fatalx(EXIT_FAILURE, "Start delay '%d' out of range [0..9999]", ondelay); } val = getval("offdelay"); if (val) { offdelay = strtol(val, NULL, 10); } if ((offdelay < 0) || (offdelay > 999)) { fatalx(EXIT_FAILURE, "Shutdown delay '%d' out of range [0..999]", offdelay); } } void upsdrv_cleanup(void) { ser_set_dtr(upsfd, 0); ser_close(upsfd, device_path); } nut-2.7.4/drivers/metasys.c0000644000175000017500000007675212640443572012612 00000000000000/* metasys.c - driver for Meta System UPS Copyright (C) 2004 Fabio Di Niro 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 */ /* Uncomment if you want to read additional Meta System UPS data */ /* #define EXTRADATA */ #include "main.h" #include "serial.h" #define DRIVER_NAME "Metasystem UPS driver" #define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Fabio Di Niro ", DRV_STABLE, { NULL } }; /* Autorestart flag */ int autorestart = 0; int nominal_power = 0; /* ups commands */ #define UPS_INFO 0x00 #define UPS_OUTPUT_DATA 0x01 #define UPS_INPUT_DATA 0x02 #define UPS_STATUS 0x03 #define UPS_BATTERY_DATA 0x04 #define UPS_HISTORY_DATA 0x05 #define UPS_GET_SCHEDULING 0x06 #define UPS_EVENT_LIST 0x07 #define UPS_GET_TIMES_ON_BATTERY 0x08 #define UPS_GET_NEUTRAL_SENSE 0x09 #define UPS_SET_SCHEDULING 0x0a #define UPS_SET_NEUTRAL_SENSE 0x0b #define UPS_SET_TIMES_ON_BATTERY 0x0c #define UPS_SET_BUZZER_MUTE 0x0d #define UPS_SET_BATTERY_TEST 0x0e static int instcmd(const char *cmdname, const char *extra); /* Metasystem UPS data transfer are made with packet of the format: STX DATA_LENGTH DATA CHECKSUM where: STX is 0x02 and is the start of transmission byte DATA_LENGTH is number of data bytes + the checksum byte DATA ...... CHECKSUM is the sum modulus 256 of all DATA bytes + DATA_LENGTH The answer from the UPS have the same packet format and the first data byte is equal to the command that the ups is answering to */ int get_word(unsigned char *buffer) { /* return an integer reading a word in the supplied buffer */ unsigned char a, b; int result; a = buffer[0]; b = buffer[1]; result = b*256 + a; return result; } long int get_long(unsigned char *buffer) { /* return a long integer reading 4 bytes in the supplied buffer */ unsigned char a, b, c, d; long int result; a=buffer[0]; b=buffer[1]; c=buffer[2]; d=buffer[3]; result = (256*256*256*d) + (256*256*c) + (256*b) + a; return result; } void send_zeros(void) { /* send 100 times the value 0x00.....it seems to be used for resetting */ unsigned char buf[100]; /* the ups serial port */ memset(buf, '\0', sizeof(buf)); ser_send_buf(upsfd, buf, sizeof(buf)); return; } /* was used just for the debug process */ void dump_buffer(unsigned char *buffer, int buf_len) { int i; for (i = 0; i < buf_len; i++) { printf("byte %d: %x\n", i, buffer[i]); } return; } /* send a read command to the UPS, it retries 5 times before give up it's a 4 byte request (STX, LENGTH, COMMAND and CHECKSUM) */ void send_read_command(char command) { int retry, sent; unsigned char buf[4]; retry = 0; sent = 0; while ((sent != 4) && (retry < 5)) { buf[0]=0x02; /* STX Start of Transmission */ buf[1]=0x02; /* data length(data + checksum byte) */ buf[2]=command; /* command to send */ buf[3]=buf[1] + buf[2]; /* checksum (sum modulus 256 of data bytes + length) */ if (retry == 4) send_zeros(); /* last retry is preceded by a serial reset...*/ sent = ser_send_buf(upsfd, buf, 4); retry += 1; } } /* send a write command to the UPS, the write command and the value to be written are passed with a char* buffer it retries 5 times before give up */ void send_write_command(unsigned char *command, int command_length) { int i, retry, sent, checksum; unsigned char raw_buf[255]; /* prepares the raw data */ raw_buf[0] = 0x02; /* STX byte */ raw_buf[1] = (unsigned char)(command_length + 1); /* data length + checksum */ memcpy(raw_buf+2, command, command_length); command_length += 2; /* calculate checksum */ checksum = 0; for (i = 1; i < command_length; i++) checksum += raw_buf[i]; checksum = checksum % 256; raw_buf[command_length] = (unsigned char)checksum; command_length +=1; retry = 0; sent = 0; while ((sent != (command_length)) && (retry < 5)) { if (retry == 4) send_zeros(); /* last retry is preceded by a serial reset... */ sent = ser_send_buf(upsfd, raw_buf, (command_length)); if (sent != (command_length)) printf("Error sending command %d\n", raw_buf[2]); retry += 1; } } /* get the answer of a command from the ups */ int get_answer(unsigned char *data) { unsigned char my_buf[255]; /* packet has a maximum length of 256 bytes */ int packet_length, checksum, i, res; /* Read STX byte */ res = ser_get_char(upsfd, my_buf, 1, 0); if (res < 1) { ser_comm_fail("Receive error (STX): %d!!!\n", res); return -1; } if (my_buf[0] != 0x02) { ser_comm_fail("Receive error (STX): packet not on start!!\n"); return -1; } /* Read data length byte */ res = ser_get_char(upsfd, my_buf, 1, 0); if (res < 1) { ser_comm_fail("Receive error (length): %d!!!\n", res); return -1; } packet_length = my_buf[0]; if (packet_length < 2) { ser_comm_fail("Receive error (length): packet length %d!!!\n", packet_length); return -1; } /* Try to read all the remainig bytes (packet_length) */ res = ser_get_buf_len(upsfd, my_buf, packet_length, 1, 0); if (res != packet_length) { ser_comm_fail("Receive error (data): got %d bytes instead of %d!!!\n", res, packet_length); return -1; } /* now we have the whole answer from the ups, we can checksum it checksum byte is equal to the sum modulus 256 of all the data bytes + packet_length (no STX no checksum byte itself) */ checksum = packet_length; for (i = 0; i < (packet_length - 1); i++) checksum += my_buf[i]; checksum = checksum % 256; if (my_buf[packet_length-1] != checksum) { ser_comm_fail("checksum error! got %x instad of %x, received %d bytes \n", my_buf[packet_length - 1], checksum, packet_length); dump_buffer(my_buf, packet_length); return -1; } packet_length-=1; /* get rid of the checksum byte */ memcpy(data, my_buf, packet_length); return packet_length; } /* send a read command and try get the answer, if something fails, it retries (5 times max) if it is on the 4th or 5th retry, it will flush the serial before sending commands it returns the length of the received answer or -1 in case of failure */ int command_read_sequence(unsigned char command, unsigned char *data) { int bytes_read = 0; int retry = 0; while ((bytes_read < 1) && (retry < 5)) { send_read_command(command); bytes_read = get_answer(data); if (retry > 2) ser_flush_in(upsfd, "", 0); retry += 1; } if ((data[0] != command) || (retry == 5)) { ser_comm_fail("Error executing command %d\n", command); dstate_datastale(); return -1; } ser_comm_good(); return bytes_read; } /* send a write command and try get the answer, if something fails, it retries (5 times max) if it is on the 4th or 5th retry, it will flush the serial before sending commands it returns the length of the received answer or -1 in case of failure */ int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer) { int bytes_read = 0; int retry = 0; while ((bytes_read < 1) && (retry < 5)) { send_write_command(command, command_length); bytes_read = get_answer(answer); if (retry > 2) ser_flush_in(upsfd, "", 0); retry += 1; } if ((answer[0] != command[0]) || (retry == 5)) { ser_comm_fail("Error executing command N.%d\n", command[0]); dstate_datastale(); return -1; } ser_comm_good(); return bytes_read; } void upsdrv_initinfo(void) { unsigned char my_answer[255]; char serial[13]; int res, i; /* Initial setup of variables */ #ifdef EXTRADATA dstate_setinfo("output.power", "%d", -1); dstate_setflags("output.power", ST_FLAG_RW); #endif dstate_setinfo("output.voltage", "%d", -1); dstate_setflags("output.voltage", ST_FLAG_RW); dstate_setinfo("output.current", "%d", -1); dstate_setflags("output.current", ST_FLAG_RW); #ifdef EXTRADATA dstate_setinfo("output.current.peak", "%2.2f", -1); dstate_setflags("output.current.peak", ST_FLAG_RW); dstate_setinfo("input.power", "%d", -1); dstate_setflags("input.power", ST_FLAG_RW); #endif dstate_setinfo("input.voltage", "%d", -1); dstate_setflags("input.voltage", ST_FLAG_RW); #ifdef EXTRADATA dstate_setinfo("input.current", "%2.2f", -1); dstate_setflags("input.current", ST_FLAG_RW); dstate_setinfo("input.current.peak", "%2.2f", -1); dstate_setflags("input.current.peak", ST_FLAG_RW); #endif dstate_setinfo("battery.voltage", "%d", -1); dstate_setflags("battery.voltage", ST_FLAG_RW); #ifdef EXTRADATA dstate_setinfo("battery.voltage.low", "%2.2f", -1); dstate_setflags("battery.voltage.low", ST_FLAG_RW); dstate_setinfo("battery.voltage.exhaust", "%2.2f", -1); dstate_setflags("battery.voltage.exhaust", ST_FLAG_RW); dstate_setinfo("ups.total.runtime", "retrieving..."); dstate_setflags("ups.total.runtime", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.total.runtime", 20); dstate_setinfo("ups.inverter.runtime", "retrieving..."); dstate_setflags("ups.inverter.runtime", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.inverter.runtime", 20); dstate_setinfo("ups.inverter.interventions", "%d", -1); dstate_setflags("ups.inverter.interventions", ST_FLAG_RW); dstate_setinfo("battery.full.discharges", "%d", -1); dstate_setflags("battery.full.discharges", ST_FLAG_RW); dstate_setinfo("ups.bypass.interventions", "%d", -1); dstate_setflags("ups.bypass.interventions", ST_FLAG_RW); dstate_setinfo("ups.overheatings", "%d", -1); dstate_setflags("ups.overheatings", ST_FLAG_RW); #endif dstate_setinfo("ups.load", "%d", -1); dstate_setflags("ups.load", ST_FLAG_RW); dstate_setinfo("ups.delay.shutdown", "%d", -1); dstate_setflags("ups.delay.shutdown", ST_FLAG_RW); dstate_setinfo("ups.delay.start", "%d", -1); dstate_setflags("ups.delay.start", ST_FLAG_RW); dstate_setinfo("ups.temperature", "%d", -1); dstate_setflags("ups.temperature", ST_FLAG_RW); dstate_setinfo("ups.test.result", "not yet done..."); dstate_setflags("ups.test.result", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.test.result", 20); /* UPS INFO READ */ res = command_read_sequence(UPS_INFO, my_answer); if (res < 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); /* the manufacturer is hard coded into the driver, the model type is in the second byte of the answer, the third byte identifies the model version */ dstate_setinfo("ups.mfr", "Meta System"); i = my_answer[1] * 10 + my_answer[2]; switch (i) { case 11: dstate_setinfo("ups.model", "%s", "HF Line (1 board)"); nominal_power = 630; break; case 12: dstate_setinfo("ups.model", "%s", "HF Line (2 boards)"); nominal_power = 1260; break; case 13: dstate_setinfo("ups.model", "%s", "HF Line (3 boards)"); nominal_power = 1890; break; case 14: dstate_setinfo("ups.model", "%s", "HF Line (4 boards)"); nominal_power = 2520; break; case 21: dstate_setinfo("ups.model", "%s", "ECO Network 750/1000"); nominal_power = 500; break; case 22: dstate_setinfo("ups.model", "%s", "ECO Network 1050/1500"); nominal_power = 700; break; case 23: dstate_setinfo("ups.model", "%s", "ECO Network 1500/2000"); nominal_power = 1000; break; case 24: dstate_setinfo("ups.model", "%s", "ECO Network 1800/2500"); nominal_power = 1200; break; case 25: dstate_setinfo("ups.model", "%s", "ECO Network 2100/3000"); nominal_power = 1400; break; case 31: dstate_setinfo("ups.model", "%s", "ECO 308"); nominal_power = 500; break; case 32: dstate_setinfo("ups.model", "%s", "ECO 311"); nominal_power = 700; break; case 44: dstate_setinfo("ups.model", "%s", "HF Line (4 boards)/2"); nominal_power = 2520; break; case 45: dstate_setinfo("ups.model", "%s", "HF Line (5 boards)/2"); nominal_power = 3150; break; case 46: dstate_setinfo("ups.model", "%s", "HF Line (6 boards)/2"); nominal_power = 3780; break; case 47: dstate_setinfo("ups.model", "%s", "HF Line (7 boards)/2"); nominal_power = 4410; break; case 48: dstate_setinfo("ups.model", "%s", "HF Line (8 boards)/2"); nominal_power = 5040; break; case 51: dstate_setinfo("ups.model", "%s", "HF Millennium 810"); nominal_power = 700; break; case 52: dstate_setinfo("ups.model", "%s", "HF Millennium 820"); nominal_power = 1400; break; case 61: dstate_setinfo("ups.model", "%s", "HF TOP Line 910"); nominal_power = 700; break; case 62: dstate_setinfo("ups.model", "%s", "HF TOP Line 920"); nominal_power = 1400; break; case 63: dstate_setinfo("ups.model", "%s", "HF TOP Line 930"); nominal_power = 2100; break; case 64: dstate_setinfo("ups.model", "%s", "HF TOP Line 940"); nominal_power = 2800; break; case 74: dstate_setinfo("ups.model", "%s", "HF TOP Line 940/2"); nominal_power = 2800; break; case 75: dstate_setinfo("ups.model", "%s", "HF TOP Line 950/2"); nominal_power = 3500; break; case 76: dstate_setinfo("ups.model", "%s", "HF TOP Line 960/2"); nominal_power = 4200; break; case 77: dstate_setinfo("ups.model", "%s", "HF TOP Line 970/2"); nominal_power = 4900; break; case 78: dstate_setinfo("ups.model", "%s", "HF TOP Line 980/2"); nominal_power = 5600; break; case 81: dstate_setinfo("ups.model", "%s", "ECO 508"); nominal_power = 500; break; case 82: dstate_setinfo("ups.model", "%s", "ECO 511"); nominal_power = 700; break; case 83: dstate_setinfo("ups.model", "%s", "ECO 516"); nominal_power = 1000; break; case 84: dstate_setinfo("ups.model", "%s", "ECO 519"); nominal_power = 1200; break; case 85: dstate_setinfo("ups.model", "%s", "ECO 522"); nominal_power = 1400; break; case 91: dstate_setinfo("ups.model", "%s", "ECO 305 / Harviot 530 SX"); nominal_power = 330; break; case 92: dstate_setinfo("ups.model", "%s", "ORDINATORE 2"); nominal_power = 330; break; case 93: dstate_setinfo("ups.model", "%s", "Harviot 730 SX"); nominal_power = 430; break; case 101: dstate_setinfo("ups.model", "%s", "ECO 308 SX / SX Interactive / Ordinatore"); nominal_power = 500; break; case 102: dstate_setinfo("ups.model", "%s", "ECO 311 SX / SX Interactive"); nominal_power = 700; break; case 111: dstate_setinfo("ups.model", "%s", "ally HF 800 / BI-TWICE 800"); nominal_power = 560; break; case 112: dstate_setinfo("ups.model", "%s", "ally HF 1600"); nominal_power = 1120; break; case 121: dstate_setinfo("ups.model", "%s", "ally HF 1000 / BI-TWICE 1000"); nominal_power = 700; break; case 122: dstate_setinfo("ups.model", "%s", "ally HF 2000"); nominal_power = 1400; break; case 131: dstate_setinfo("ups.model", "%s", "ally HF 1250 / BI-TWICE 1250"); nominal_power = 875; break; case 132: dstate_setinfo("ups.model", "%s", "ally HF 2500"); nominal_power = 1750; break; case 141: dstate_setinfo("ups.model", "%s", "Megaline 1250"); nominal_power = 875; break; case 142: dstate_setinfo("ups.model", "%s", "Megaline 2500"); nominal_power = 1750; break; case 143: dstate_setinfo("ups.model", "%s", "Megaline 3750"); nominal_power = 2625; break; case 144: dstate_setinfo("ups.model", "%s", "Megaline 5000"); nominal_power = 3500; break; case 154: dstate_setinfo("ups.model", "%s", "Megaline 5000 / 2"); nominal_power = 3500; break; case 155: dstate_setinfo("ups.model", "%s", "Megaline 6250 / 2"); nominal_power = 4375; break; case 156: dstate_setinfo("ups.model", "%s", "Megaline 7500 / 2"); nominal_power = 5250; break; case 157: dstate_setinfo("ups.model", "%s", "Megaline 8750 / 2"); nominal_power = 6125; break; case 158: dstate_setinfo("ups.model", "%s", "Megaline 10000 / 2"); nominal_power = 7000; break; default: fatal_with_errno(EXIT_FAILURE, "Unknown UPS"); break; } /* Get the serial number */ memcpy(serial, my_answer + 7, res - 7); /* serial number start from the 8th byte */ serial[12]=0; /* terminate string */ dstate_setinfo("ups.serial", "%s", serial); /* get the ups firmware. The major number is in the 5th byte, the minor is in the 6th */ dstate_setinfo("ups.firmware", "%u.%u", my_answer[5], my_answer[6]); printf("Detected %s [%s] v.%s on %s\n", dstate_getinfo("ups.model"), dstate_getinfo("ups.serial"), dstate_getinfo("ups.firmware"), device_path); /* Add instant commands */ dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); dstate_addcmd("shutdown.stop"); dstate_addcmd("test.failure.start"); dstate_addcmd("test.failure.stop"); dstate_addcmd("test.battery.start"); dstate_addcmd("beeper.enable"); dstate_addcmd("beeper.mute"); dstate_addcmd("beeper.on"); dstate_addcmd("beeper.off"); upsh.instcmd = instcmd; return; } void upsdrv_updateinfo(void) { int res, int_num; #ifdef EXTRADATA int day, hour, minute; #endif float float_num; long int long_num; unsigned char my_answer[255]; /* GET Output data */ res = command_read_sequence(UPS_OUTPUT_DATA, my_answer); if (res < 0) { printf("Could not communicate with the ups"); dstate_datastale(); } else { /* Active power */ int_num = get_word(&my_answer[1]); if (nominal_power != 0) { float_num = (float)((int_num * 100)/nominal_power); dstate_setinfo("ups.load", "%2.1f", float_num); } else { dstate_setinfo("ups.load", "%s", "not available"); } #ifdef EXTRADATA dstate_setinfo("output.power", "%d", int_num); #endif /* voltage */ int_num = get_word(&my_answer[3]); if (int_num > 0) dstate_setinfo("output.voltage", "%d", int_num); if (int_num == -1) dstate_setinfo("output.voltage", "%s", "overrange"); if (int_num == -2) dstate_setinfo("output.voltage", "%s", "not available"); /* current */ float_num = get_word(&my_answer[5]); if (float_num == -1) dstate_setinfo("output.current", "%s", "overrange"); if (float_num == -2) dstate_setinfo("output.current", "%s", "not available"); if (float_num > 0) { float_num = (float)(float_num/10); dstate_setinfo("output.current", "%2.2f", float_num); } #ifdef EXTRADATA /* peak current */ float_num = get_word(&my_answer[7]); if (float_num == -1) dstate_setinfo("output.current.peak", "%s", "overrange"); if (float_num == -2) dstate_setinfo("output.current.peak", "%s", "not available"); if (float_num > 0) { float_num = (float)(float_num/10); dstate_setinfo("output.current.peak", "%2.2f", float_num); } #endif } /* GET Input data */ res = command_read_sequence(UPS_INPUT_DATA, my_answer); if (res < 0){ printf("Could not communicate with the ups"); dstate_datastale(); } else { #ifdef EXTRADATA /* Active power */ int_num = get_word(&my_answer[1]); if (int_num > 0) dstate_setinfo("input.power", "%d", int_num); if (int_num == -1) dstate_setinfo("input.power", "%s", "overrange"); if (int_num == -2) dstate_setinfo("input.power", "%s", "not available"); #endif /* voltage */ int_num = get_word(&my_answer[3]); if (int_num > 0) dstate_setinfo("input.voltage", "%d", int_num); if (int_num == -1) dstate_setinfo("input.voltage", "%s", "overrange"); if (int_num == -2) dstate_setinfo("input.voltage", "%s", "not available"); #ifdef EXTRADATA /* current */ float_num = get_word(&my_answer[5]); if (float_num == -1) dstate_setinfo("input.current", "%s", "overrange"); if (float_num == -2) dstate_setinfo("input.current", "%s", "not available"); if (float_num > 0) { float_num = (float)(float_num/10); dstate_setinfo("input.current", "%2.2f", float_num); } /* peak current */ float_num = get_word(&my_answer[7]); if (float_num == -1) dstate_setinfo("input.current.peak", "%s", "overrange"); if (float_num == -2) dstate_setinfo("input.current.peak", "%s", "not available"); if (float_num > 0) { float_num = (float)(float_num/10); dstate_setinfo("input.current.peak", "%2.2f", float_num); } #endif } /* GET Battery data */ res = command_read_sequence(UPS_BATTERY_DATA, my_answer); if (res < 0) { printf("Could not communicate with the ups"); dstate_datastale(); } else { /* Actual value */ float_num = get_word(&my_answer[1]); float_num = (float)(float_num/10); dstate_setinfo("battery.voltage", "%2.2f", float_num); #ifdef EXTRADATA /* reserve threshold */ float_num = get_word(&my_answer[3]); float_num = (float)(float_num/10); dstate_setinfo("battery.voltage.low", "%2.2f", float_num); /* exhaust threshold */ float_num = get_word(&my_answer[5]); float_num = (float)(float_num/10); dstate_setinfo("battery.voltage.exhaust", "%2.2f", float_num); #endif } #ifdef EXTRADATA /* GET history data */ res = command_read_sequence(UPS_HISTORY_DATA, my_answer); if (res < 0) { printf("Could not communicate with the ups"); dstate_datastale(); } else { /* ups total runtime */ long_num = get_long(&my_answer[1]); day = (int)(long_num / 86400); long_num -= (long)(day*86400); hour = (int)(long_num / 3600); long_num -= (long)(hour*3600); minute = (int)(long_num / 60); long_num -= (minute*60); dstate_setinfo("ups.total.runtime", "%d days %dh %dm %lds", day, hour, minute, long_num); /* ups inverter runtime */ long_num = get_long(&my_answer[5]); day = (int)(long_num / 86400); long_num -= (long)(day*86400); hour = (int)(long_num / 3600); long_num -= (long)(hour*3600); minute = (int)(long_num / 60); long_num -= (minute*60); dstate_setinfo("ups.inverter.runtime", "%d days %dh %dm %lds", day, hour, minute, long_num); /* ups inverter interventions */ dstate_setinfo("ups.inverter.interventions", "%d", get_word(&my_answer[9])); /* battery full discharges */ dstate_setinfo("battery.full.discharges", "%d", get_word(&my_answer[11])); /* ups bypass / stabilizer interventions */ int_num = get_word(&my_answer[13]); if (int_num == -2) dstate_setinfo("ups.bypass.interventions", "%s", "not avaliable"); if (int_num >= 0) dstate_setinfo("ups.bypass.interventions", "%d", int_num); /* ups overheatings */ int_num = get_word(&my_answer[15]); if (int_num == -2) dstate_setinfo("ups.overheatings", "%s", "not avalilable"); if (int_num >= 0) dstate_setinfo("ups.overheatings", "%d", int_num); } #endif /* GET times on battery */ res = command_read_sequence(UPS_GET_TIMES_ON_BATTERY, my_answer); if (res < 0) { printf("Could not communicate with the ups"); dstate_datastale(); } else { autorestart = my_answer[5]; } /* GET schedule */ res = command_read_sequence(UPS_GET_SCHEDULING, my_answer); if (res < 0) { printf("Could not communicate with the ups"); dstate_datastale(); } else { /* time remaining to shutdown */ long_num = get_long(&my_answer[1]); if (long_num == -1) { dstate_setinfo("ups.delay.shutdown", "%d", 120); } else { dstate_setinfo("ups.delay.shutdown", "%ld", long_num); } /* time remaining to restart */ long_num = get_long(&my_answer[5]); if (long_num == -1) { dstate_setinfo("ups.delay.start", "%d", 0); } else { dstate_setinfo("ups.delay.start", "%ld", long_num); } } /* GET ups status */ res = command_read_sequence(UPS_STATUS, my_answer); if (res < 0) { printf("Could not communicate with the ups"); dstate_datastale(); } else { /* ups temperature */ my_answer[3] -=128; if (my_answer[3] > 0) { dstate_setinfo("ups.temperature", "%d", my_answer[3]); } else { dstate_setinfo("ups.temperature", "%s", "not available"); } /* Status */ status_init(); switch (my_answer[1]) { /* byte 1 = STATUS */ case 0x00: status_set("OL"); /* running on mains power */ break; case 0x01: status_set("OB"); /* running on battery power */ break; case 0x02: status_set("LB"); /* battery reserve */ break; case 0x03: /* bypass engaged */ case 0x04: /* manual bypass engaged */ status_set("BY"); break; default: printf("status unknown \n"); break; } switch (my_answer[2]) { /* byte 2 = FAULTS */ case 0x00: /* all right */ break; case 0x01: /* overload */ status_set("OVER"); break; case 0x02: /* overheat */ break; case 0x03: /* hardware fault */ break; case 0x04: /* battery charger failure (overcharging) */ break; case 0x05: /* replace batteries */ status_set("RB"); break; default: printf("status unknown \n"); break; } status_commit(); dstate_dataok(); } return; } void upsdrv_shutdown(void) { unsigned char command[10], answer[10]; /* Ensure that the ups is configured for automatically restart after a complete battery discharge and when the power comes back after a shutdown */ if (! autorestart) { command[0]=UPS_SET_TIMES_ON_BATTERY; command[1]=0x00; /* max time on */ command[2]=0x00; /* battery */ command[3]=0x00; /* max time after */ command[4]=0x00; /* battery reserve */ command[5]=0x01; /* autorestart after battery depleted enabled */ command_write_sequence(command, 6, answer); } /* shedule a shutdown in 120 seconds */ command[0]=UPS_SET_SCHEDULING; command[1]=0x96; /* remaining */ command[2]=0x00; /* time */ command[3]=0x00; /* to */ command[4]=0x00; /* shutdown 150 secs */ /* restart time has been set to 1 instead of 0 for avoiding a bug in some ups firmware */ command[5]=0x01; /* programmed */ command[6]=0x00; /* time */ command[7]=0x00; /* to */ command[8]=0x00; /* restart 1 sec */ command_write_sequence(command, 9, answer); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ } static int instcmd(const char *cmdname, const char *extra) { unsigned char command[10], answer[10]; int res; if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.mute' for this driver"); return instcmd("beeper.mute", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } if (!strcasecmp(cmdname, "shutdown.return")) { /* Same stuff as upsdrv_shutdown() */ if (! autorestart) { command[0]=UPS_SET_TIMES_ON_BATTERY; command[1]=0x00; /* max time on */ command[2]=0x00; /* battery */ command[3]=0x00; /* max time after */ command[4]=0x00; /* battery reserve */ command[5]=0x01; /* autorestart after battery depleted enabled */ command_write_sequence(command, 6, answer); } /* shedule a shutdown in 30 seconds */ command[0]=UPS_SET_SCHEDULING; command[1]=0x1e; /* remaining */ command[2]=0x00; /* time */ command[3]=0x00; /* to */ command[4]=0x00; /* shutdown 30 secs */ command[5]=0x01; /* programmed */ command[6]=0x00; /* time */ command[7]=0x00; /* to */ command[8]=0x00; /* restart 1 sec */ command_write_sequence(command, 9, answer); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stayoff")) { /* shedule a shutdown in 30 seconds with no restart (-1) */ command[0]=UPS_SET_SCHEDULING; command[1]=0x1e; /* remaining */ command[2]=0x00; /* time */ command[3]=0x00; /* to */ command[4]=0x00; /* shutdown 150 secs */ command[5]=0xff; /* programmed */ command[6]=0xff; /* time */ command[7]=0xff; /* to */ command[8]=0xff; /* restart -1 no restart*/ command_write_sequence(command, 9, answer); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stop")) { /* set shutdown and restart time to -1 (no shutdown, no restart) */ command[0]=UPS_SET_SCHEDULING; command[1]=0xff; /* remaining */ command[2]=0xff; /* time */ command[3]=0xff; /* to */ command[4]=0xff; /* shutdown -1 (no shutdown) */ command[5]=0xff; /* programmed */ command[6]=0xff; /* time */ command[7]=0xff; /* to */ command[8]=0xff; /* restart -1 no restart */ command_write_sequence(command, 9, answer); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.failure.start")) { /* force ups on battery power */ command[0]=UPS_SET_BATTERY_TEST; command[1]=0x01; /* 0 = perform battery test 1 = force UPS on battery power 2 = restore standard mode (mains power) */ command_write_sequence(command, 2, answer); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.failure.stop")) { /* restore standard mode (mains power) */ command[0]=UPS_SET_BATTERY_TEST; command[1]=0x02; /* 0 = perform battery test 1 = force UPS on battery power 2 = restore standard mode (mains power) */ command_write_sequence(command, 2, answer); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { /* launch battery test */ command[0]=UPS_SET_BATTERY_TEST; command[1]=0x00; /* 0 = perform battery test 1 = force UPS on battery power 2 = restore standard mode (mains power) */ send_write_command(command, 2); sleep(15); res = get_answer(answer); switch (answer[1]) { /* byte 1 = Test result */ case 0x00: /* all right */ dstate_setinfo("ups.test.result", "OK"); break; case 0x01: dstate_setinfo("ups.test.result", "Battery charge: 20%%"); break; case 0x02: dstate_setinfo("ups.test.result", "Battery charge: 40%%"); break; case 0x03: dstate_setinfo("ups.test.result", "Battery charge: 60%%"); break; case 0x04: dstate_setinfo("ups.test.result", "Battery charge: 80%%"); break; case 0x05: dstate_setinfo("ups.test.result", "Battery charge: 100%%"); break; case 0xfe: dstate_setinfo("ups.test.result", "Bad battery pack: replace"); break; default: dstate_setinfo("ups.test.result", "Impossible to test"); break; } dstate_dataok(); upslogx(LOG_NOTICE, "instcmd: test battery returned with %d bytes", res); upslogx(LOG_NOTICE, "test battery byte 1 = %x", answer[1]); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "beeper.enable")) { /* set buzzer to not muted */ command[0]=UPS_SET_BUZZER_MUTE; command[1]=0x00; /* 0 = not muted 1 = muted 2 = read current status */ command_write_sequence(command, 2, answer); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "beeper.mute")) { /* set buzzer to muted */ command[0]=UPS_SET_BUZZER_MUTE; command[1]=0x01; /* 0 = not muted 1 = muted 2 = read current status */ command_write_sequence(command, 2, answer); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* allow '-x xyzzy' */ /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ /* allow '-x foo=' */ /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); send_zeros(); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/huawei-mib.h0000644000175000017500000000021512640473702013134 00000000000000#ifndef HUAWEI_MIB_H #define HUAWEI_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t huawei; #endif /* HUAWEI_MIB_H */ nut-2.7.4/drivers/blazer.c0000644000175000017500000005041712640473702012370 00000000000000/* * blazer.c: driver core for Megatec/Q1 protocol based UPSes * * A document describing the protocol implemented by this driver can be * found online at http://www.networkupstools.org/ups-protocols/megatec.html * * Copyright (C) * 2008,2009 - Arjen de Korte * 2012 - Arnaud Quette * * 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 "main.h" #include "blazer.h" #include static int ondelay = 3; /* minutes */ static int offdelay = 30; /* seconds */ static int proto; static int online = 1; static struct { double packs; /* battery voltage multiplier */ struct { double nom; /* nominal runtime on battery (full load) */ double est; /* estimated runtime remaining (full load) */ double exp; /* load exponent */ } runt; struct { double act; /* actual battery voltage */ double high; /* battery float voltage */ double nom; /* nominal battery voltage */ double low; /* battery low voltage */ } volt; struct { double act; /* actual battery charge */ long time; /* recharge time from empty to full */ } chrg; } batt = { 1, { -1, 0, 0 }, { -1, -1, -1, -1 }, { -1, 43200 } }; static struct { double act; /* actual load (reported by UPS) */ double low; /* idle load */ double eff; /* effective load */ } load = { 0, 0.1, 1 }; static time_t lastpoll = 0; /* * This little structure defines the various flavors of the Megatec protocol. * Only the .name and .status are mandatory, .rating and .vendor elements are * optional. If only some models support the last two, fill them in anyway * and tell people to use the 'norating' and 'novendor' options to bypass * getting them. */ static const struct { const char *name; const char *status; const char *rating; const char *vendor; } command[] = { { "megatec", "Q1\r", "F\r", "I\r" }, { "mustek", "QS\r", "F\r", "I\r" }, { "megatec/old", "D\r", "F\r", "I\r" }, { "zinto", "Q1\r", "F\r", "FW?\r" }, { NULL } }; /* * Do whatever we think is needed when we read a battery voltage from the UPS. * Basically all it does now, is guestimating the battery charge, but this * could be extended. */ static double blazer_battery(const char *ptr, char **endptr) { batt.volt.act = batt.packs * strtod(ptr, endptr); if ((!getval("runtimecal") || !dstate_getinfo("battery.charge")) && (batt.volt.low > 0) && (batt.volt.high > batt.volt.low)) { batt.chrg.act = 100 * (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low); if (batt.chrg.act < 0) { batt.chrg.act = 0; } if (batt.chrg.act > 100) { batt.chrg.act = 100; } dstate_setinfo("battery.charge", "%.0f", batt.chrg.act); } return batt.volt.act; } /* * Do whatever we think is needed when we read the load from the UPS. */ static double blazer_load(const char *ptr, char **endptr) { load.act = strtod(ptr, endptr); load.eff = pow(load.act / 100, batt.runt.exp); if (load.eff < load.low) { load.eff = load.low; } return load.act; } /* * The battery voltage will quickly return to at least the nominal value after * discharging them. For overlapping battery.voltage.low/high ranges therefor * choose the one with the highest multiplier. */ static double blazer_packs(const char *ptr, char **endptr) { const double packs[] = { 120, 100, 80, 60, 48, 36, 30, 24, 18, 12, 8, 6, 4, 3, 2, 1, 0.5, -1 }; const char *val; int i; val = dstate_getinfo("battery.voltage.nominal"); batt.volt.nom = strtod(val ? val : ptr, endptr); for (i = 0; packs[i] > 0; i++) { if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) { continue; } if (packs[i] * batt.volt.act < 0.8 * batt.volt.nom) { upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act); break; } batt.packs = packs[i]; break; } return batt.volt.nom; } static int blazer_status(const char *cmd) { const struct { const char *var; const char *fmt; double (*conv)(const char *, char **); } status[] = { { "input.voltage", "%.1f", strtod }, { "input.voltage.fault", "%.1f", strtod }, { "output.voltage", "%.1f", strtod }, { "ups.load", "%.0f", blazer_load }, { "input.frequency", "%.1f", strtod }, { "battery.voltage", "%.2f", blazer_battery }, { "ups.temperature", "%.1f", strtod }, { NULL } }; char buf[SMALLBUF], *val, *last = NULL; int i; /* * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ if (blazer_command(cmd, buf, sizeof(buf)) < 46) { upsdebugx(2, "%s: short reply", __func__); return -1; } if (buf[0] != '(') { upsdebugx(2, "%s: invalid start character [%02x]", __func__, buf[0]); return -1; } for (i = 0, val = strtok_r(buf+1, " ", &last); status[i].var; i++, val = strtok_r(NULL, " \r\n", &last)) { if (!val) { upsdebugx(2, "%s: parsing failed", __func__); return -1; } if (strspn(val, "0123456789.") != strlen(val)) { upsdebugx(2, "%s: non numerical value [%s]", __func__, val); continue; } dstate_setinfo(status[i].var, status[i].fmt, status[i].conv(val, NULL)); } if (!val) { upsdebugx(2, "%s: parsing failed", __func__); return -1; } if (strspn(val, "01") != 8) { upsdebugx(2, "Invalid status [%s]", val); return -1; } if (val[7] == '1') { /* Beeper On */ dstate_setinfo("ups.beeper.status", "enabled"); } else { dstate_setinfo("ups.beeper.status", "disabled"); } if (val[4] == '1') { /* UPS Type is Standby (0 is On_line) */ dstate_setinfo("ups.type", "offline / line interactive"); } else { dstate_setinfo("ups.type", "online"); } status_init(); if (val[0] == '1') { /* Utility Fail (Immediate) */ status_set("OB"); online = 0; } else { status_set("OL"); online = 1; } if (val[1] == '1') { /* Battery Low */ status_set("LB"); } if (val[2] == '1') { /* Bypass/Boost or Buck Active */ double vi, vo; vi = strtod(dstate_getinfo("input.voltage"), NULL); vo = strtod(dstate_getinfo("output.voltage"), NULL); if (vo < 0.5 * vi) { upsdebugx(2, "%s: output voltage too low", __func__); } else if (vo < 0.95 * vi) { status_set("TRIM"); } else if (vo < 1.05 * vi) { status_set("BYPASS"); } else if (vo < 1.5 * vi) { status_set("BOOST"); } else { upsdebugx(2, "%s: output voltage too high", __func__); } } if (val[5] == '1') { /* Test in Progress */ status_set("CAL"); } alarm_init(); if (val[3] == '1') { /* UPS Failed */ alarm_set("UPS selftest failed!"); } if (val[6] == '1') { /* Shutdown Active */ alarm_set("Shutdown imminent!"); status_set("FSD"); } alarm_commit(); status_commit(); return 0; } static int blazer_rating(const char *cmd) { const struct { const char *var; const char *fmt; double (*conv)(const char *, char **); } rating[] = { { "input.voltage.nominal", "%.0f", strtod }, { "input.current.nominal", "%.1f", strtod }, { "battery.voltage.nominal", "%.1f", blazer_packs }, { "input.frequency.nominal", "%.0f", strtod }, { NULL } }; char buf[SMALLBUF], *val, *last = NULL; int i; /* * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ if (blazer_command(cmd, buf, sizeof(buf)) < 22) { upsdebugx(2, "%s: short reply", __func__); return -1; } if (buf[0] != '#') { upsdebugx(2, "%s: invalid start character [%02x]", __func__, buf[0]); return -1; } for (i = 0, val = strtok_r(buf+1, " ", &last); rating[i].var; i++, val = strtok_r(NULL, " \r\n", &last)) { if (!val) { upsdebugx(2, "%s: parsing failed", __func__); return -1; } if (strspn(val, "0123456789.") != strlen(val)) { upsdebugx(2, "%s: non numerical value [%s]", __func__, val); continue; } dstate_setinfo(rating[i].var, rating[i].fmt, rating[i].conv(val, NULL)); } return 0; } static int blazer_vendor(const char *cmd) { const struct { const char *var; const int len; } information[] = { { "ups.mfr", 15 }, { "ups.model", 10 }, { "ups.firmware", 10 }, { NULL } }; char buf[SMALLBUF]; int i, index; /* * > [I\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ if (blazer_command(cmd, buf, sizeof(buf)) < 39) { upsdebugx(2, "%s: short reply", __func__); return -1; } if (buf[0] != '#') { upsdebugx(2, "%s: invalid start character [%02x]", __func__, buf[0]); return -1; } for (i = 0, index = 1; information[i].var; index += information[i++].len+1) { char val[SMALLBUF]; snprintf(val, sizeof(val), "%.*s", information[i].len, &buf[index]); dstate_setinfo(information[i].var, "%s", str_rtrim(val, ' ')); } return 0; } static int blazer_instcmd(const char *cmdname, const char *extra) { const struct { const char *cmd; const char *ups; } instcmd[] = { { "beeper.toggle", "Q\r" }, { "load.off", "S00R0000\r" }, { "load.on", "C\r" }, { "shutdown.stop", "C\r" }, { "test.battery.start.deep", "TL\r" }, { "test.battery.start.quick", "T\r" }, { "test.battery.stop", "CT\r" }, { NULL } }; char buf[SMALLBUF] = ""; int i; upslogx(LOG_INFO, "instcmd(%s, %s)", cmdname, extra ? extra : "[NULL]"); for (i = 0; instcmd[i].cmd; i++) { if (strcasecmp(cmdname, instcmd[i].cmd)) { continue; } snprintf(buf, sizeof(buf), "%s", instcmd[i].ups); /* * If a command is invalid, it will be echoed back * As an exception, Best UPS units will report "ACK" in case of success! * Other UPSes will reply "(ACK" in case of success. */ if (blazer_command(buf, buf, sizeof(buf)) > 0) { if (strncmp(buf, "ACK", 3) && strncmp(buf, "(ACK", 4)) { upslogx(LOG_ERR, "instcmd: command [%s] failed", cmdname); return STAT_INSTCMD_FAILED; } } upslogx(LOG_INFO, "instcmd: command [%s] handled", cmdname); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { /* * Sn: Shutdown after n minutes and then turn on when mains is back * SnRm: Shutdown after n minutes and then turn on after m minutes * Accepted values for n: .2 -> .9 , 01 -> 10 * Accepted values for m: 0001 -> 9999 * Note: "S01R0001" and "S01R0002" may not work on early (GE) * firmware versions. The failure mode is that the UPS turns * off and never returns. The fix is to push the return value * up by 2, i.e. S01R0003, and it will return online properly. * (thus the default of ondelay=3 mins) */ if (ondelay == 0) { if (offdelay < 60) { snprintf(buf, sizeof(buf), "S.%d\r", offdelay / 6); } else { snprintf(buf, sizeof(buf), "S%02d\r", offdelay / 60); } } else if (offdelay < 60) { snprintf(buf, sizeof(buf), "S.%dR%04d\r", offdelay / 6, ondelay); } else { snprintf(buf, sizeof(buf), "S%02dR%04d\r", offdelay / 60, ondelay); } } else if (!strcasecmp(cmdname, "shutdown.stayoff")) { /* * SnR0000 * Shutdown after n minutes and stay off * Accepted values for n: .2 -> .9 , 01 -> 10 */ if (offdelay < 60) { snprintf(buf, sizeof(buf), "S.%dR0000\r", offdelay / 6); } else { snprintf(buf, sizeof(buf), "S%02dR0000\r", offdelay / 60); } } else if (!strcasecmp(cmdname, "test.battery.start")) { int delay = extra ? strtol(extra, NULL, 10) : 10; if ((delay < 1) || (delay > 99)) { upslogx(LOG_ERR, "instcmd: command [%s] failed, delay [%s] out of range", cmdname, extra); return STAT_INSTCMD_FAILED; } snprintf(buf, sizeof(buf), "T%02d\r", delay); } else { upslogx(LOG_ERR, "instcmd: command [%s] not found", cmdname); return STAT_INSTCMD_UNKNOWN; } /* * If a command is invalid, it will be echoed back. * As an exception, Best UPS units will report "ACK" in case of success! * Other UPSes will reply "(ACK" in case of success. */ if (blazer_command(buf, buf, sizeof(buf)) > 0) { if (strncmp(buf, "ACK", 3) && strncmp(buf, "(ACK", 4)) { upslogx(LOG_ERR, "instcmd: command [%s] failed", cmdname); return STAT_INSTCMD_FAILED; } } upslogx(LOG_INFO, "instcmd: command [%s] handled", cmdname); return STAT_INSTCMD_HANDLED; } void blazer_makevartable(void) { addvar(VAR_VALUE, "ondelay", "Delay before UPS startup (minutes)"); addvar(VAR_VALUE, "offdelay", "Delay before UPS shutdown (seconds)"); addvar(VAR_VALUE, "runtimecal", "Parameters used for runtime calculation"); addvar(VAR_VALUE, "chargetime", "Nominal charge time for UPS battery"); addvar(VAR_VALUE, "idleload", "Minimum load to be used for runtime calculation"); addvar(VAR_FLAG, "norating", "Skip reading rating information from UPS"); addvar(VAR_FLAG, "novendor", "Skip reading vendor information from UPS"); addvar(VAR_FLAG, "protocol", "Preselect communication protocol (skip autodetection)"); } void blazer_initups(void) { const char *val; val = getval("ondelay"); if (val) { ondelay = strtol(val, NULL, 10); } if ((ondelay < 0) || (ondelay > 9999)) { fatalx(EXIT_FAILURE, "Start delay '%d' out of range [0..9999]", ondelay); } val = getval("offdelay"); if (val) { offdelay = strtol(val, NULL, 10); } if ((offdelay < 12) || (offdelay > 600)) { fatalx(EXIT_FAILURE, "Shutdown delay '%d' out of range [12..600]", offdelay); } /* Truncate to nearest setable value */ if (offdelay < 60) { offdelay -= (offdelay % 6); } else { offdelay -= (offdelay % 60); } val = dstate_getinfo("battery.voltage.high"); if (val) { batt.volt.high = strtod(val, NULL); } val = dstate_getinfo("battery.voltage.low"); if (val) { batt.volt.low = strtod(val, NULL); } } static void blazer_initbattery(void) { const char *val; /* If no values were provided by the user in ups.conf, try to guesstimate * battery.charge, but announce it! */ if ((batt.volt.nom != 1) && ((batt.volt.high == -1) || (batt.volt.low == -1))) { upslogx(LOG_INFO, "No values provided for battery high/low voltages in ups.conf\n"); /* Basic formula, which should cover most cases */ batt.volt.low = 104 * batt.volt.nom / 120; batt.volt.high = 130 * batt.volt.nom / 120; /* Publish these data too */ dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); upslogx(LOG_INFO, "Using 'guestimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high); } val = getval("runtimecal"); if (val) { double rh, lh, rl, ll; time(&lastpoll); if (sscanf(val, "%lf,%lf,%lf,%lf", &rh, &lh, &rl, &ll) < 4) { fatalx(EXIT_FAILURE, "Insufficient parameters for runtimecal"); } if ((rl < rh) || (rh <= 0)) { fatalx(EXIT_FAILURE, "Parameter out of range (runtime)"); } if ((lh > 100) || (ll > lh) || (ll <= 0)) { fatalx(EXIT_FAILURE, "Parameter out of range (load)"); } batt.runt.exp = log(rl / rh) / log(lh / ll); upsdebugx(2, "battery runtime exponent : %.3f", batt.runt.exp); batt.runt.nom = rh * pow(lh / 100, batt.runt.exp); upsdebugx(2, "battery runtime nominal : %.1f", batt.runt.nom); } else { upslogx(LOG_INFO, "Battery runtime will not be calculated (runtimecal not set)"); return; } if (batt.chrg.act < 0) { batt.volt.low = batt.volt.nom; batt.volt.high = 1.15 * batt.volt.nom; blazer_battery(dstate_getinfo("battery.voltage"), NULL); } val = dstate_getinfo("battery.charge"); if (val) { batt.runt.est = batt.runt.nom * strtod(val, NULL) / 100; upsdebugx(2, "battery runtime estimate : %.1f", batt.runt.est); } else { fatalx(EXIT_FAILURE, "Initial battery charge undetermined"); } val = getval("chargetime"); if (val) { batt.chrg.time = strtol(val, NULL, 10); if (batt.chrg.time <= 0) { fatalx(EXIT_FAILURE, "Charge time out of range [1..s]"); } upsdebugx(2, "battery charge time : %ld", batt.chrg.time); } else { upslogx(LOG_INFO, "No charge time specified, using built in default [%ld seconds]", batt.chrg.time); } val = getval("idleload"); if (val) { load.low = strtod(val, NULL) / 100; if ((load.low <= 0) || (load.low > 1)) { fatalx(EXIT_FAILURE, "Idle load out of range [0..100]"); } upsdebugx(2, "minimum load used (idle) : %.3f", load.low); } else { upslogx(LOG_INFO, "No idle load specified, using built in default [%.1f %%]", 100 * load.low); } } void blazer_initinfo(void) { const char *protocol = getval("protocol"); int retry; for (proto = 0; command[proto].status; proto++) { int ret; if (protocol && strcasecmp(protocol, command[proto].name)) { upsdebugx(2, "Skipping %s protocol...", command[proto].name); continue; } upsdebugx(2, "Trying %s protocol...", command[proto].name); for (retry = 1; retry <= MAXTRIES; retry++) { ret = blazer_status(command[proto].status); if (ret < 0) { upsdebugx(2, "Status read %d failed", retry); continue; } upsdebugx(2, "Status read in %d tries", retry); break; } if (!ret) { upslogx(LOG_INFO, "Supported UPS detected with %s protocol", command[proto].name); break; } } if (!command[proto].status) { fatalx(EXIT_FAILURE, "No supported UPS detected"); } if (command[proto].rating && !testvar("norating")) { int ret; for (retry = 1; retry <= MAXTRIES; retry++) { ret = blazer_rating(command[proto].rating); if (ret < 0) { upsdebugx(1, "Rating read %d failed", retry); continue; } upsdebugx(2, "Ratings read in %d tries", retry); break; } if (ret) { upslogx(LOG_DEBUG, "Rating information unavailable"); } } if (command[proto].vendor && !testvar("novendor")) { int ret; for (retry = 1; retry <= MAXTRIES; retry++) { ret = blazer_vendor(command[proto].vendor); if (ret < 0) { upsdebugx(1, "Vendor information read %d failed", retry); continue; } upslogx(LOG_INFO, "Vendor information read in %d tries", retry); break; } if (ret) { upslogx(LOG_DEBUG, "Vendor information unavailable"); } } blazer_initbattery(); dstate_setinfo("ups.delay.start", "%d", 60 * ondelay); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); dstate_addcmd("beeper.toggle"); dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); dstate_addcmd("shutdown.stop"); dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.start.deep"); dstate_addcmd("test.battery.start.quick"); dstate_addcmd("test.battery.stop"); upsh.instcmd = blazer_instcmd; } void upsdrv_updateinfo(void) { static int retry = 0; if (blazer_status(command[proto].status)) { if (retry < MAXTRIES) { upsdebugx(1, "Communications with UPS lost: status read failed!"); retry++; } else if (retry == MAXTRIES) { upslogx(LOG_WARNING, "Communications with UPS lost: status read failed!"); retry++; } else { dstate_datastale(); } return; } if (getval("runtimecal")) { time_t now; time(&now); if (online) { /* OL */ batt.runt.est += batt.runt.nom * difftime(now, lastpoll) / batt.chrg.time; if (batt.runt.est > batt.runt.nom) { batt.runt.est = batt.runt.nom; } } else { /* OB */ batt.runt.est -= load.eff * difftime(now, lastpoll); if (batt.runt.est < 0) { batt.runt.est = 0; } } dstate_setinfo("battery.charge", "%.0f", 100 * batt.runt.est / batt.runt.nom); dstate_setinfo("battery.runtime", "%.0f", batt.runt.est / load.eff); lastpoll = now; } if (retry > MAXTRIES) { upslogx(LOG_NOTICE, "Communications with UPS re-established"); } retry = 0; dstate_dataok(); } void upsdrv_shutdown(void) { int retry; /* Stop pending shutdowns */ for (retry = 1; retry <= MAXTRIES; retry++) { if (blazer_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } break; } if (retry > MAXTRIES) { upslogx(LOG_NOTICE, "No shutdown pending"); } /* Shutdown */ for (retry = 1; retry <= MAXTRIES; retry++) { if (blazer_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } fatalx(EXIT_SUCCESS, "Shutting down in %d seconds", offdelay); } fatalx(EXIT_FAILURE, "Shutdown failed!"); } nut-2.7.4/drivers/dummy-ups.h0000644000175000017500000002217212640473702013053 00000000000000/* dummy-ups.h - NUT testing driver and repeater Copyright (C) 2005 - 2012 Arnaud Quette 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 */ /* This file lists all valid data with their type and info. * * These are then enabled through a definition file, specified * as the "port" parameter (only the file name, not the path). * * The format of this file is the same as an upsc dump: * * : * * FIXME: use cmdvartab for conformance checking * ... * Once the driver is loaded: * - change the value by using upsrw * - ?you can add new variables and change variable values by * editing the definition file? */ /* from usbhid-ups.h */ /* --------------------------------------------------------------- */ /* Struct & data for ups.status processing */ /* --------------------------------------------------------------- */ #if 0 /* XXX status lookup table not currently used???? */ /* * Status lookup table type definition */ typedef struct { const char *status_str; /* ups.status string */ int status_value; /* ups.status flag bit */ } status_lkp_t; #define STATUS_CAL (1 << 0) /* calibration */ #define STATUS_TRIM (1 << 1) /* SmartTrim */ #define STATUS_BOOST (1 << 2) /* SmartBoost */ #define STATUS_OL (1 << 3) /* on line */ #define STATUS_OB (1 << 4) /* on battery */ #define STATUS_OVER (1 << 5) /* overload */ #define STATUS_LB (1 << 6) /* low battery */ #define STATUS_RB (1 << 7) /* replace battery */ #define STATUS_BYPASS (1 << 8) /* on bypass */ #define STATUS_OFF (1 << 9) /* ups is off */ #define STATUS_CHRG (1 << 10) /* charging */ #define STATUS_DISCHRG (1 << 11) /* discharging */ /* * Status lookup table */ status_lkp_t status_info[] = { { "CAL", STATUS_CAL }, { "TRIM", STATUS_TRIM }, { "BOOST", STATUS_BOOST }, { "OL", STATUS_OL }, { "OB", STATUS_OB }, { "OVER", STATUS_OVER }, { "LB", STATUS_LB }, { "RB", STATUS_RB }, { "BYPASS", STATUS_BYPASS }, { "OFF", STATUS_OFF }, { "CHRG", STATUS_CHRG }, { "DISCHRG", STATUS_DISCHRG }, { "NULL", 0 }, }; #endif /* 0 -- not currently used??? */ typedef struct { char hid_value; /* HID value */ char *nut_value; /* NUT value */ } info_enum_t; /* --------------------------------------------------------------- */ /* Structure containing information about how to get/set data */ /* from/to the UPS and convert these to/from NUT standard */ /* --------------------------------------------------------------- */ typedef struct { const char *info_type; /* NUT variable name */ int info_flags; /* NUT flags (to set in addinfo) */ float info_len; /* if ST_FLAG_STRING: length of the string */ /* if HU_TYPE_CMD: command value ; multiplier (or max len) otherwise */ const char *default_value; /* if HU_FLAG_ABSENT: default value ; format otherwise */ int drv_flags; /* */ char **var_values; /* all possible values for this variable (allows to check data...) */ /* FIXME: "void *" so we can have bound or enum */ /* interpreter interpret; */ /* FFE: interpreter fct, NULL if not needed */ } dummy_info_t; /* data flags */ #define DU_FLAG_NONE 0 #define DU_FLAG_INIT 1 /* intialy show element to upsd */ /* --------------------------------------------------------------- */ /* Data table (all possible info from NUT, then enable upon cong */ /* --------------------------------------------------------------- */ /* FIXME: need to enforce value check with enum or bounds */ dummy_info_t nut_data[] = { /* Essential variables, loaded before parsing the definition file */ { "ups.mfr", ST_FLAG_STRING | ST_FLAG_RW, 32, "Dummy Manufacturer", DU_FLAG_INIT, NULL }, { "ups.model", ST_FLAG_STRING | ST_FLAG_RW, 32, "Dummy UPS", DU_FLAG_INIT, NULL }, { "ups.status", ST_FLAG_STRING | ST_FLAG_RW, 32, "OL", DU_FLAG_INIT, NULL }, /* Other known variables */ { "ups.serial", ST_FLAG_STRING | ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, { "ups.load", ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, { "ups.alarm", ST_FLAG_STRING | ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, { "ups.time", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.date", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.mfr.date", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.serial", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.firmware", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.firmware.aux", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.temperature", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "ups.delay.start", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ups.delay.reboot", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ups.delay.shutdown", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ups.test.interval", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ups.test.result", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, /* ups.display.language ups.contacts ups.power ups.power.nominal */ { "input.voltage", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.voltage.maximum", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.voltage.minimum", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.voltage.nominal", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.reason", ST_FLAG_STRING | ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.quality", ST_FLAG_STRING | ST_FLAG_RW, 6, NULL, DU_FLAG_NONE, NULL }, { "input.frequency", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.boost.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.boost.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.trim.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "input.transfer.trim.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "output.voltage", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "output.frequency", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "output.current", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, /* output.voltage.nominal */ { "battery.charge", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.charge.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.voltage", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.current", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.runtime", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.runtime.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.charge.restart", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.temperature", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "battery.voltage.nominal", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, /* battery.alarm.threshold battery.date battery.packs battery.packs.bad */ { "ambient.temperature", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.temperature.alarm", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.temperature.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.temperature.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.humidity", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.humidity.alarm", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.humidity.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "ambient.humidity.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, /* FIXME: how to manage these? (i.e. index ) outlet.n.id outlet.n.desc outlet.n.switch outlet.n.status outlet.n.switchable outlet.n.autoswitch.charge.low outlet.n.delay.shutdown outlet.n.delay.start ... driver.name driver.version driver.version.internal | Internal driver version | 1.23.45 | driver.parameter.xxx driver.flag.xxx No need for these! server.info server.version Cmds load.off load.on shutdown.return shutdown.stayoff shutdown.stop shutdown.reboot shutdown.reboot.graceful test.panel.start test.panel.stop test.failure.start test.failure.stop test.battery.start test.battery.stop calibrate.start calibrate.stop bypass.start bypass.stop reset.input.minmax reset.watchdog beeper.enable beeper.disable */ /* end of structure. */ { NULL, 0, 0, NULL, DU_FLAG_NONE, NULL } }; nut-2.7.4/drivers/nutdrv_qx_megatec.h0000644000175000017500000000176112640473702014633 00000000000000/* nutdrv_qx_megatec.h - Subdriver for Megatec protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_MEGATEC_H #define NUTDRV_QX_MEGATEC_H #include "nutdrv_qx.h" extern subdriver_t megatec_subdriver; #endif /* NUTDRV_QX_MEGATECH_H */ nut-2.7.4/drivers/netxml-ups.h0000644000175000017500000000512012640443572013223 00000000000000/* netxml-ups.h Driver data/defines for network XML UPS units Copyright (C) 2008-2009 Arjen de Korte This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NETXML_UPS_H #define NETXML_UPS_H #include "nut_stdint.h" typedef struct { const char *version; /* name of this subdriver */ const char *initups; const char *initinfo; char *configure; /* central configuration */ char *subscribe; /* alarm subscriptions */ char *summary; /* used for quick updates */ char *getobject; char *setobject; int (*startelm_cb)(void *userdata, int parent, const char *nspace, const char *name, const char **atts); int (*cdata_cb)(void *userdata, int state, const char *cdata, size_t len); int (*endelm_cb)(void *userdata, int state, const char *nspace, const char *name); } subdriver_t; /* ---------------------------------------------------------------------- */ /* data for processing boolean values from UPS */ #define STATUS_BIT(x) (ups_status & (uint32_t)1< based on: apcsmart.c - model specific routines for APC smart protocol units Copyright (C) 1999 Russell Kroll 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 "main.h" #include "serial.h" #include "belkin.h" #define DRIVER_NAME "Belkin Smart protocol driver" #define DRIVER_VERSION "0.24" static int init_communication(void); static int get_belkin_reply(char *buf); /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Marcus Müller ", DRV_STABLE, { NULL } }; static void send_belkin_command(char cmd, const char *subcmd, const char *data) { ser_flush_io(upsfd); ser_send(upsfd, "~00%c%03d%s%s", cmd, (int)strlen(data) + 3, subcmd, data); upsdebugx(3, "Send Command: %s, %s", subcmd, data); } static int init_communication(void) { int i, res; char temp[SMALLBUF]; for (i = 0; i < 10; i++) { send_belkin_command(STATUS, MANUFACTURER, ""); res = get_belkin_reply(temp); if (res > 0) { /* return the number of retries needed before a valid reply is read (discard contents) */ return i; } } /* no valid reply read */ return -1; } static char *get_belkin_field(const char *in, char *out, size_t outlen, size_t num) { size_t i, c = 1; char *ptr; /* special case */ if (num == 1) { snprintf(out, outlen, "%s", in); ptr = strchr(out, ';'); if (ptr) { *ptr = '\0'; } return out; } for (i = 0; i < strlen(in); i++) { if (in[i] == ';') { c++; } if (c == num) { snprintf(out, outlen, "%s", &in[i + 1]); ptr = strchr(out, ';'); if (ptr) { *ptr = '\0'; } return out; } } return NULL; } static int get_belkin_reply(char *buf) { int ret; long cnt; char tmp[8]; usleep(25000); /* pull first 7 bytes to get data length - like ~00D004 */ ret = ser_get_buf_len(upsfd, (unsigned char *)tmp, 7, 2, 0); if (ret != 7) { ser_comm_fail("Initial read returned %d bytes", ret); return -1; } tmp[7] = 0; cnt = strtol(tmp + 4, NULL, 10); upsdebugx(3, "Received: %s", tmp); if (cnt == 0) { /* possible to have ~00R000, return empty response */ buf[0] = 0; return 0; } if ((cnt < 0) || (cnt > 255)) { return -1; } /* give it time to respond to us */ usleep(5000 * cnt); ret = ser_get_buf_len(upsfd, (unsigned char *)buf, cnt, 2, 0); buf[cnt] = 0; upsdebugx(3, "Received: %s", buf); if (ret != cnt) { ser_comm_fail("Second read returned %d bytes, expected %ld", ret, cnt); return -1; } ser_comm_good(); return ret; } static int do_broken_rat(char *buf) { int ret; long cnt; char tmp[8]; usleep(25000); /* pull first 7 bytes to get data length - like ~00D004 */ ret = ser_get_buf_len(upsfd, (unsigned char *)tmp, 7, 2, 0); if (ret != 7) { ser_comm_fail("Initial read returned %d bytes", ret); return -1; } tmp[7] = 0; cnt = strtol(tmp + 4, NULL, 10); upsdebugx(3, "Received: %s", tmp); if (cnt == 0) { /* possible to have ~00R000, return empty response */ buf[0] = 0; return 0; } if ((cnt < 0) || (cnt > 255)) { return -1; } /* give it time to respond to us */ usleep(5000 * cnt); /* firmware 001 only sends 50 bytes instead of the proper 53 */ if (cnt == 53) { cnt = 50; } ret = ser_get_buf_len(upsfd, (unsigned char *)buf, cnt, 2, 0); buf[cnt] = 0; upsdebugx(3, "Received: %s", buf); if (ret != cnt) { ser_comm_fail("Second read returned %d bytes, expected %ld", ret, cnt); return -1; } ser_comm_good(); return ret; } /* normal idle loop - keep up with the current state of the UPS */ void upsdrv_updateinfo(void) { static int retry = 0; int res; char temp[SMALLBUF], st[SMALLBUF]; send_belkin_command(STATUS, STAT_STATUS, ""); res = get_belkin_reply(temp); if (res < 0) { if (retry < MAXTRIES) { upsdebugx(1, "Communications with UPS lost: status read failed!"); retry++; } else { /* too many retries */ upslogx(LOG_WARNING, "Communications with UPS lost: status read failed!"); dstate_datastale(); } return; } if (retry) { /* previous attempt had failed */ upslogx(LOG_WARNING, "Communications with UPS re-established"); retry = 0; } if (res == 0) { upsdebugx(1, "Ignoring empty return value after status query"); return; } status_init(); get_belkin_field(temp, st, sizeof(st), 6); if (*st == '1') { status_set("OFF"); } get_belkin_field(temp, st, sizeof(st), 2); if (*st == '1') { status_set("OB"); /* on battery */ } else { status_set("OL"); /* on line */ } get_belkin_field(temp, st, sizeof(st), 16); dstate_setinfo("ups.beeper.status", "%s", (*st == '0' ? "disabled" : "enabled")); send_belkin_command(STATUS, STAT_BATTERY, ""); res = get_belkin_reply(temp); if (res > 0) { /* report the compiled in battery charge where the driver assumes the battery is low */ dstate_setinfo("battery.charge.low", "%d", LOW_BAT); get_belkin_field(temp, st, sizeof(st), 10); res = atoi(st); get_belkin_field(temp, st, sizeof(st), 2); if (*st == '1' || res < LOW_BAT) { status_set("LB"); /* low battery */ } get_belkin_field(temp, st, sizeof(st), 10); dstate_setinfo("battery.charge", "%.0f", strtod(st, NULL)); get_belkin_field(temp, st, sizeof(st), 9); dstate_setinfo("battery.temperature", "%.0f", strtod(st, NULL)); get_belkin_field(temp, st, sizeof(st), 7); dstate_setinfo("battery.voltage", "%.1f", strtod(st, NULL) / 10); get_belkin_field(temp, st, sizeof(st), 9); dstate_setinfo("ups.temperature", "%.0f", strtod(st, NULL)); } send_belkin_command(STATUS, STAT_INPUT, ""); res = get_belkin_reply(temp); if (res > 0) { get_belkin_field(temp, st, sizeof(st), 3); dstate_setinfo("input.voltage", "%.1f", strtod(st, NULL) / 10); get_belkin_field(temp, st, sizeof(st), 2); dstate_setinfo("input.frequency", "%.1f", strtod(st, NULL) / 10); } send_belkin_command(STATUS, STAT_OUTPUT, ""); res = get_belkin_reply(temp); if (res > 0) { get_belkin_field(temp, st, sizeof(st), 2); dstate_setinfo("output.frequency", "%.1f", strtod(st, NULL) / 10); get_belkin_field(temp, st, sizeof(st), 4); dstate_setinfo("output.voltage", "%.1f", strtod(st, NULL) / 10); get_belkin_field(temp, st, sizeof(st), 7); dstate_setinfo("ups.load", "%.0f", strtod(st, NULL)); } send_belkin_command(STATUS, TEST_RESULT, ""); res = get_belkin_reply(temp); if (res > 0) { get_belkin_field(temp, st, sizeof(st), 1); switch (*st) { case '0': dstate_setinfo("ups.test.result", "%s", "No test performed"); break; case '1': dstate_setinfo("ups.test.result", "%s", "Passed"); break; case '2': dstate_setinfo("ups.test.result", "%s", "In progress"); break; case '3': case '4': dstate_setinfo("ups.test.result", "%s", "10s test failed"); break; case '5': dstate_setinfo("ups.test.result", "%s", "deep test failed"); break; case '6': dstate_setinfo("ups.test.result", "%s", "Aborted"); break; default: upsdebugx(3, "Unhandled test status '%c'", *st); break; } } status_commit(); dstate_dataok(); } /* power down the attached load immediately */ void upsdrv_shutdown(void) { int res; res = init_communication(); if (res < 0) { printf("Detection failed. Trying a shutdown command anyway.\n"); } /* tested on a F6C525-SER: this works when OL and OB */ /* shutdown type 2 (UPS system) */ send_belkin_command(CONTROL, "SDT", "2"); /* SDR means "do SDT and SDA, then reboot after n minutes" */ send_belkin_command(CONTROL, "SDR", "1"); printf("UPS should power off load in 5 seconds\n"); /* shutdown in 5 seconds */ send_belkin_command(CONTROL, "SDA", "5"); } /* handle "beeper.disable" */ static void do_beeper_off(void) { int res; char temp[SMALLBUF]; const char *arg; /* Compare the model name, as the BUZZER_OFF argument depends on it */ send_belkin_command(STATUS, MODEL, ""); res = get_belkin_reply(temp); if (res == -1) return; if (!strcmp(temp, "F6C1400-EUR")) { arg = BUZZER_OFF2; } else { arg = BUZZER_OFF0; } send_belkin_command(CONTROL,BUZZER,arg); } /* handle the "load.off" with some paranoia */ static void do_off(void) { static time_t lastcmd = 0; time_t now, elapsed; #ifdef CONFIRM_DANGEROUS_COMMANDS time(&now); elapsed = now - lastcmd; /* reset the timer every call - this means if you call it too * * early, then you have to wait MINCMDTIME again before sending #2 */ lastcmd = now; if ((elapsed < MINCMDTIME) || (elapsed > MAXCMDTIME)) { /* FUTURE: tell the user (via upsd) to try it again */ return; } #endif upslogx(LOG_INFO, "Sending powerdown command to UPS\n"); send_belkin_command(CONTROL,POWER_OFF,"1;1"); usleep(1500000); send_belkin_command(CONTROL,POWER_OFF,"1;1"); } static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "beeper.disable")) { do_beeper_off(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "beeper.enable")) { send_belkin_command(CONTROL,BUZZER,BUZZER_ON); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.off")) { do_off(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { send_belkin_command(CONTROL,POWER_ON,"1;1"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start.quick")) { send_belkin_command(CONTROL,TEST,TEST_10SEC); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start.deep")) { send_belkin_command(CONTROL,TEST,TEST_DEEP); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.stop")) { send_belkin_command(CONTROL,TEST,TEST_CANCEL); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } /* prep the serial port */ void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); /* set DTR to low and RTS to high */ ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 1); sleep(1); ser_flush_io(upsfd); } void upsdrv_initinfo(void) { int res; char temp[SMALLBUF], st[SMALLBUF]; res = init_communication(); if (res < 0) { fatalx(EXIT_FAILURE, "Unable to detect an Belkin Smart protocol UPS on port %s\n" "Check the cabling, port name or model name and try again", device_path ); } dstate_setinfo("ups.mfr", "BELKIN"); send_belkin_command(STATUS, MODEL, ""); res = get_belkin_reply(temp); if (res > 0) { dstate_setinfo("ups.model", "%s", temp); } send_belkin_command(STATUS, VERSION_CMD, ""); res = get_belkin_reply(temp); if (res > 0) { dstate_setinfo("ups.firmware", "%s", temp); } /* deal with stupid firmware that breaks RAT */ send_belkin_command(STATUS, RATING, ""); if (!strcmp(temp, "001")) { res = do_broken_rat(temp); } else { res = get_belkin_reply(temp); } if (res > 0) { get_belkin_field(temp, st, sizeof(st), 8); dstate_setinfo("input.transfer.low", "%.0f", strtod(st, NULL) / 0.88); get_belkin_field(temp, st, sizeof(st), 9); dstate_setinfo("input.transfer.high", "%.0f", strtod(st, NULL) * 0.88); } dstate_addcmd("beeper.disable"); dstate_addcmd("beeper.enable"); dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("test.battery.start.quick"); dstate_addcmd("test.battery.start.deep"); dstate_addcmd("test.battery.stop"); upsh.instcmd = instcmd; } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/liebert-hid.c0000644000175000017500000001263412640473702013300 00000000000000/* liebert-hid.c - subdriver to monitor Liebert USB/HID devices with NUT * * Copyright (C) * 2003 - 2008 Arnaud Quette * 2005 - 2006 Peter Selinger * 2007 Charles Lepple * * 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 "main.h" /* for getval() */ #include "usbhid-ups.h" #include "liebert-hid.h" #include "usb-common.h" #define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.3" /* FIXME: experimental flag to be put in upsdrv_info */ /* Phoenixtec Power Co., Ltd */ #define LIEBERT_VENDORID 0x06da /*! USB IDs device table. * * Note that this subdriver was named before the USB VendorID was determined to * actually belong to Phoenixtec. The belkin-hid.c file covers the other * Liebert units which share some of the same incorrect exponents as the * Belkin HID firmware. */ static usb_device_id_t liebert_usb_device_table[] = { /* various models */ { USB_DEVICE(LIEBERT_VENDORID, 0xffff), NULL }, /* Terminating entry */ { -1, -1, NULL } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* LIEBERT usage table */ static usage_lkp_t liebert_usage_lkp[] = { { NULL, 0 } }; static usage_tables_t liebert_utab[] = { liebert_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ static hid_info_t liebert_hid2nut[] = { #if 0 { "unmapped.ups.powersummary.flowid", 0, 0, "UPS.PowerSummary.FlowID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, #endif { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.0f", 0, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, "%.0f", HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, "%.0f", HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, "%.0f", HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, "%.0f", HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, "%.0f", 0, overload_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, "%.0f", 0, shutdownimm_info }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *liebert_format_model(HIDDevice_t *hd) { return hd->Product; } static const char *liebert_format_mfr(HIDDevice_t *hd) { return hd->Vendor ? hd->Vendor : "Liebert"; } static const char *liebert_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int liebert_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(liebert_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("Liebert", hd); return 0; case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t liebert_subdriver = { LIEBERT_HID_VERSION, liebert_claim, liebert_utab, liebert_hid2nut, liebert_format_model, liebert_format_mfr, liebert_format_serial, }; nut-2.7.4/drivers/mge-utalk.c0000644000175000017500000006201312640473702012772 00000000000000/* mge-utalk.c - monitor MGE UPS for NUT with UTalk protocol * * Copyright (C) 2002 - 2005 * Arnaud Quette * Hans Ekkehard Plesser * Martin Loyer * Patrick Agrain * Nicholas Reilly * Dave Abbott * Marek Kralewski * * This driver is a collaborative effort by the above people, * Sponsored by MGE UPS SYSTEMS * * 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 * */ /* * IMPLEMENTATION DETAILS * * Not all UTalk models provide all possible information, settings and commands. * mge-utalk checks on startup which variables and commands are available from * the UPS, and re-reads these regularly. Thus, startup is a bit slow, but this * should not matter much. * * mge-utalk.h defines a struct array that tells the driver how to read * variables from the UPS and publish them as NUT data. * * "ups.status" variable is not included in this array, since it contains * information that requires several calls to the UPS and more advanced analysis * of the reponses. The function get_ups_status does this job. * * Note that MGE enumerates the status "bits" from right to left, * i.e., if buf[] contains the reponse to command "Ss" (read system status), * then buf[0] contains "bit" Ss.1.7 (General alarm), while buf[7] contains * "bit" Ss.1.0 (Load unprotected). * * enable_ups_comm() is called before each attempt to read/write data * from/to the UPS to re synchronise the communication. */ #include #include #include "timehead.h" #include "main.h" #include "serial.h" #include "mge-utalk.h" /* --------------------------------------------------------------- */ /* Define "technical" constants */ /* --------------------------------------------------------------- */ #define DRIVER_NAME "MGE UPS SYSTEMS/U-Talk driver" #define DRIVER_VERSION "0.93" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette \n" \ "Hans Ekkehard Plesser \n" \ "Martin Loyer \n" \ "Patrick Agrain \n" \ "Nicholas Reilly \n" \ "Dave Abbott \n" \ "Marek Kralewski ", DRV_STABLE, { NULL } }; /* delay after sending each char to UPS (in MICROSECONDS) */ #define MGE_CHAR_DELAY 0 /* delay after command, before reading UPS reply (in MICROSECONDS) */ #define MGE_REPLY_DELAY 1000 /* delay after enable_ups_comm */ #define MGE_CONNECT_DELAY 500000 #define MGE_COMMAND_ENDCHAR "\r\n" /* some UPS need \r and \n */ #define MGE_REPLY_ENDCHAR '\r' #define MGE_REPLY_IGNCHAR "\r\n" #define MAXTRIES 10 /* max number of connect tries */ #define BUFFLEN 256 #define SD_RETURN 0 #define SD_STAYOFF 1 int sdtype = SD_RETURN; static time_t lastpoll; /* Timestamp the last polling */ /* --------------------------------------------------------------- */ /* Structure with information about UPS */ /* --------------------------------------------------------------- */ static struct { int MultTab; int LowBatt; /* percent */ int OnDelay; /* minutes */ int OffDelay; /* seconds */ } mge_ups = { 0, DEFAULT_LOWBATT, DEFAULT_ONDELAY, DEFAULT_OFFDELAY }; /* --------------------------------------------------------------- */ /* Declaration of internal functions */ /* --------------------------------------------------------------- */ static int instcmd(const char *cmdname, const char *extra); static int setvar(const char *varname, const char *val); static void enable_ups_comm(void); static void disable_ups_comm(void); static void extract_info(const char *buf, const mge_info_item_t *mge, char *infostr, int infolen); static const char *info_variable_cmd(const char *type); static bool_t info_variable_ok(const char *type); static int get_ups_status(void); static int mge_command(char *reply, int replylen, const char *fmt, ...); /* --------------------------------------------------------------- */ /* UPS Driver Functions */ /* --------------------------------------------------------------- */ void upsdrv_makevartable(void) { char temp[BUFFLEN]; snprintf(temp, sizeof(temp), "Low battery level, in %% (default = %3d)", DEFAULT_LOWBATT); addvar (VAR_VALUE, "LowBatt", temp); snprintf(temp, sizeof(temp), "Delay before startup, in minutes (default = %3d)", DEFAULT_ONDELAY); addvar (VAR_VALUE, "OnDelay", temp); snprintf(temp, sizeof(temp), "Delay before shutdown, in seconds (default = %3d)", DEFAULT_OFFDELAY); addvar (VAR_VALUE, "OffDelay", temp); addvar(VAR_FLAG, "oldmac", "Enable Oldworld Apple Macintosh support"); } /* --------------------------------------------------------------- */ void upsdrv_initups(void) { char buf[BUFFLEN]; int RTS = TIOCM_RTS; upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); /* read command line/conf variable that affect comm. */ if (testvar ("oldmac")) RTS = ~TIOCM_RTS; /* Init serial line */ ioctl(upsfd, TIOCMBIC, &RTS); enable_ups_comm(); /* Try to set "Low Battery Level" (if supported and given) */ if (getval ("lowbatt")) { mge_ups.LowBatt = atoi (getval ("lowbatt")); /* Set the value in the UPS */ mge_command(buf, sizeof(buf), "Bl %d", mge_ups.LowBatt); if(!strcmp(buf, "OK")) upsdebugx(1, "Low Battery Level set to %d%%", mge_ups.LowBatt); else upsdebugx(1, "initups: Low Battery Level cannot be set"); } /* Try to set "ON delay" (if supported and given) */ if (getval ("ondelay")) { mge_ups.OnDelay = atoi (getval ("ondelay")); /* Set the value in the UPS */ mge_command(buf, sizeof(buf), "Sm %d", mge_ups.OnDelay); if(!strcmp(buf, "OK")) upsdebugx(1, "ON delay set to %d min", mge_ups.OnDelay); else upsdebugx(1, "initups: OnDelay unavailable"); } /* Try to set "OFF delay" (if supported and given) */ if (getval ("offdelay")) { mge_ups.OffDelay = atoi (getval ("offdelay")); /* Set the value in the UPS */ mge_command(buf, sizeof(buf), "Sn %d", mge_ups.OffDelay); if(!strcmp(buf, "OK")) upsdebugx(1, "OFF delay set to %d sec", mge_ups.OffDelay); else upsdebugx(1, "initups: OffDelay unavailable"); } } /* --------------------------------------------------------------- */ void upsdrv_initinfo(void) { char buf[BUFFLEN]; const char *model = NULL; char *firmware = NULL; char *p; char *v = NULL; /* for parsing Si output, get Version ID */ int table; int tries; int status_ok = 0; int bytes_rcvd; int si_data1 = 0; int si_data2 = 0; mge_info_item_t *item; models_name_t *model_info; mge_model_info_t *legacy_model; char infostr[32]; int chars_rcvd; /* manufacturer -------------------------------------------- */ dstate_setinfo("ups.mfr", "MGE UPS SYSTEMS"); /* loop until we have at status */ tries = 0; do { printf("."); /* get model information in ASCII string form: */ bytes_rcvd = mge_command(buf, sizeof(buf), "Si 1"); if(bytes_rcvd > 0 && buf[0] != '?') { dstate_setinfo("ups.id", "%s", buf); /* raw id */ model = buf; p = strrchr(buf, ' '); if ( p != NULL ) { *p = '\0'; firmware = p+1; } if( firmware && strlen(firmware) < 1 ) firmware = NULL; /* no firmware information */ /* Parsing model names table */ for ( model_info = Si1_models_names ; model_info->basename != NULL ; model_info++ ) { if(!strcasecmp(model_info->basename, model)) { model = model_info->finalname; upsdebugx(1, "initinfo: UPS model == >%s<", model); break; } } } else { upsdebugx(1, "initinfo: 'Si 1' unavailable, switching to 'Si' command"); /* get model information, numbered form, : */ bytes_rcvd = mge_command(buf, sizeof(buf), "Si"); if(bytes_rcvd > 0 && buf[0] != '?') { upsdebugx(1, "initinfo: Si == >%s<", buf); printf("\nCAUTION : This is an older model. It may not support too much polling.\nPlease read man mge-utalk and use pollinterval\n"); p = strchr(buf, ' '); if ( p != NULL ) { *p = '\0'; si_data1 = atoi(buf); v = p+1; p = strchr(v, ' '); } if ( p != NULL ) { *p = '\0'; si_data2 = atoi(v); } /* Parsing legacy model table in order to find it */ for ( legacy_model = mge_model ; legacy_model->name != NULL ; legacy_model++ ) { if(legacy_model->Data1 == si_data1 && legacy_model->Data2 == si_data2){ model = legacy_model->name; upsdebugx(1, "initinfo: UPS model == >%s<", model); break; } } if( model == NULL ) printf("No model found by that model and version ID\nPlease contact us with UPS model, name and reminder info\nReminder info : Data1=%i , Data2=%i\n", si_data1, si_data2); } } if ( model ) { upsdebugx(2, "Got model name: %s", model); /* deal with truncated model names */ if (!strncmp(model, "Evolutio", 8)) { dstate_setinfo("ups.model", "Evolution %i", atoi(strchr(model, ' '))); } else { dstate_setinfo("ups.model", "%s", model); } } if ( firmware && strcmp(firmware, "")) dstate_setinfo("ups.firmware", "%s", firmware); else dstate_setinfo("ups.firmware", "unknown"); /* multiplier table */ /* */ bytes_rcvd = mge_command(buf, sizeof(buf), "Ai"); if (bytes_rcvd > 0 && buf[0] != '?') { p = strchr(buf, ' '); if ( p != NULL ) { table = atoi(p + 1); if ( 0 < table && table < 4 ) mge_ups.MultTab = table; } } /* status --- try only system status, to get the really important * information (OL, OB, LB); all else is added later by updateinfo */ status_ok = get_ups_status(); } while ( (!status_ok) && (tries++ < MAXTRIES) && (exit_flag != 1) ); if ( tries == MAXTRIES && !status_ok ) fatalx(EXIT_FAILURE, "Could not get status from UPS."); if ( mge_ups.MultTab == 0 ) upslogx(LOG_WARNING, "Could not get multiplier table: using raw readings."); /* all other variables ------------------------------------ */ for ( item = mge_info ; item->type != NULL ; item++ ) { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) return; /* send request, read answer */ chars_rcvd = mge_command(buf, sizeof(buf), item->cmd); if ( chars_rcvd < 1 || buf[0] == '?' ) { item->ok = FALSE; upsdebugx(1, "initinfo: %s unavailable", item->type); } else { item->ok = TRUE; extract_info(buf, item, infostr, sizeof(infostr)); dstate_setinfo(item->type, "%s", infostr); dstate_setflags(item->type, item->flags); upsdebugx(1, "initinfo: %s == >%s<", item->type, infostr); /* Set max length for strings */ if (item->flags & ST_FLAG_STRING) dstate_setaux(item->type, item->length); } } /* for item */ /* store timestamp */ lastpoll = time(NULL); /* commands ----------------------------------------------- */ /* FIXME: check if available before adding! */ dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); dstate_addcmd("test.panel.start"); dstate_addcmd("test.battery.start"); dstate_addcmd("bypass.start"); dstate_addcmd("bypass.stop"); /* install handlers */ upsh.setvar = setvar; upsh.instcmd = instcmd; printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path); } /* --------------------------------------------------------------- */ void upsdrv_updateinfo(void) { char buf[BUFFLEN]; char infostr[32]; int status_ok; int bytes_rcvd; mge_info_item_t *item; /* make sure that communication is enabled */ enable_ups_comm(); /* update status */ status_ok = get_ups_status(); /* only sys status is critical */ if ( !status_ok ) { upslogx(LOG_NOTICE, "updateinfo: Cannot update system status"); /* try to re enable communication */ disable_ups_comm(); enable_ups_comm(); } else { dstate_dataok(); } /* Don't overload old units (at startup) */ if ( (unsigned int)time(NULL) <= (unsigned int)(lastpoll + poll_interval) ) return; /* update all other ok variables */ for ( item = mge_info ; item->type != NULL ; item++ ) { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) return; if ( item->ok ) { /* send request, read answer */ bytes_rcvd = mge_command(buf, sizeof(buf), item->cmd); if ( bytes_rcvd > 0 && buf[0] != '?' ) { extract_info(buf, item, infostr, sizeof(infostr)); dstate_setinfo(item->type, "%s", infostr); upsdebugx(2, "updateinfo: %s == >%s<", item->type, infostr); dstate_dataok(); } else { upslogx(LOG_NOTICE, "updateinfo: Cannot update %s", item->type); /* try to re enable communication */ disable_ups_comm(); enable_ups_comm(); } } /* if item->ok */ } /* store timestamp */ lastpoll = time(NULL); } /* --------------------------------------------------------------- */ void upsdrv_shutdown(void) { char buf[BUFFLEN]; /* static time_t lastcmd = 0; */ memset(buf, 0, sizeof(buf)); if (sdtype == SD_RETURN) { /* enable automatic restart */ mge_command(buf, sizeof(buf), "Sx 5"); upslogx(LOG_INFO, "UPS response to Automatic Restart was %s", buf); } /* Only call the effective shutoff if restart is ok */ /* or if we need only a stayoff... */ if (!strcmp(buf, "OK") || (sdtype == SD_STAYOFF)) { /* shutdown UPS */ mge_command(buf, sizeof(buf), "Sx 0"); upslogx(LOG_INFO, "UPS response to Shutdown was %s", buf); } /* if(strcmp(buf, "OK")) */ /* call the cleanup to disable/close the comm link */ upsdrv_cleanup(); } /* --------------------------------------------------------------- */ void upsdrv_help(void) { } /* --------------------------------------------------------------- */ /* Internal Functions */ /* --------------------------------------------------------------- */ /* handler for commands to be sent to UPS */ int instcmd(const char *cmdname, const char *extra) { char temp[BUFFLEN]; /* Start battery test */ if (!strcasecmp(cmdname, "test.battery.start")) { mge_command(temp, sizeof(temp), "Bx 1"); upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else return STAT_INSTCMD_HANDLED; } /* Start front panel test */ if (!strcasecmp(cmdname, "test.panel.start")) { mge_command(temp, sizeof(temp), "Sx 129"); upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else return STAT_INSTCMD_HANDLED; } /* Shutdown UPS */ if (!strcasecmp(cmdname, "shutdown.stayoff")) { sdtype = SD_STAYOFF; upsdrv_shutdown(); } if (!strcasecmp(cmdname, "shutdown.return")) { sdtype = SD_RETURN; upsdrv_shutdown(); } /* Power Off [all] plugs */ if (!strcasecmp(cmdname, "load.off")) { /* TODO: Powershare (per plug) control */ mge_command(temp, sizeof(temp), "Wy 65535"); upsdebugx(2, "UPS response to Select All Plugs was %s", temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else { mge_command(temp, sizeof(temp), "Wx 0"); upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else return STAT_INSTCMD_HANDLED; } } /* Power On all plugs */ if (!strcasecmp(cmdname, "load.on")) { /* TODO: add per plug control */ mge_command(temp, sizeof(temp), "Wy 65535"); upsdebugx(2, "UPS response to Select All Plugs was %s", temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else { mge_command(temp, sizeof(temp), "Wx 1"); upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else return STAT_INSTCMD_HANDLED; } } /* Switch on/off Maintenance Bypass */ if ((!strcasecmp(cmdname, "bypass.start")) || (!strcasecmp(cmdname, "bypass.stop"))) { /* TODO: add control on bypass value */ /* read maintenance bypass status */ if(mge_command(temp, sizeof(temp), "Ps") > 0) { if (temp[0] == '1') { /* Disable Maintenance Bypass */ mge_command(temp, sizeof(temp), "Px 2"); upsdebugx(2, "UPS response to Select All Plugs was %s", temp); } else { /* Enable Maintenance Bypass */ mge_command(temp, sizeof(temp), "Px 3"); } upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else return STAT_INSTCMD_HANDLED; } } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } /* --------------------------------------------------------------- */ /* handler for settable variables in UPS*/ int setvar(const char *varname, const char *val) { char temp[BUFFLEN]; char cmd[15]; /* TODO : add some controls */ if(info_variable_ok(varname)) { /* format command */ snprintf(cmd, sizeof(cmd), "%s", info_variable_cmd(varname)); sprintf(strchr(cmd, '?'), "%s", val); /* Execute command */ mge_command(temp, sizeof(temp), cmd); upslogx(LOG_INFO, "setvar: UPS response to Set %s to %s was %s", varname, val, temp); } else upsdebugx(1, "setvar: Variable %s not supported by UPS", varname); return STAT_SET_UNKNOWN; } /* --------------------------------------------------------------- */ /* disable communication with UPS to avoid interference with * kernel serial init at boot time (ie with V24 init) */ static void disable_ups_comm(void) { upsdebugx(1, "disable_ups_comm()"); ser_flush_in(upsfd, "?\r\n", 0); usleep(MGE_CONNECT_DELAY); mge_command(NULL, 0, "Ax 0"); } /* enable communication with UPS */ static void enable_ups_comm(void) { char buf[8]; /* send Z twice --- speeds up re-connect */ mge_command(NULL, 0, "Z"); mge_command(NULL, 0, "Z"); /* only enable communication if needed! */ if ( mge_command(buf, 8, "Si") <= 0) { mge_command(NULL, 0, "Ax 1"); usleep(MGE_CONNECT_DELAY); } ser_flush_in(upsfd, "?\r\n", nut_debug_level); } /* --------------------------------------------------------------- */ /* extract information from buffer in: buf : reply from UPS item : INFO item queried out: infostr: to be placed in INFO_ variable NOTE: buf="?" must be handled before calling extract_info buf is changed inspite of const !!!!! */ static void extract_info(const char *buf, const mge_info_item_t *item, char *infostr, int infolen) { /* initialize info string */ infostr[0] = '\0'; /* write into infostr with proper formatting */ if ( strpbrk(item->fmt, "feEgG") ) { /* float */ snprintf(infostr, infolen, item->fmt, multiplier[mge_ups.MultTab][item->unit] * atof(buf)); } else if ( strpbrk(item->fmt, "dioxXuc") ) { /* int */ snprintf(infostr, infolen, item->fmt, (int) (multiplier[mge_ups.MultTab][item->unit] * atof(buf))); } else { snprintf(infostr, infolen, item->fmt, buf); } } /* --------------------------------------------------------------- */ /* get system status, at least: OB, OL, LB calls set_status appropriately tries MAXTRIES times returns non-nil if successful NOTE: MGE counts bytes/chars the opposite way as C, see mge-utalk manpage. If status commands send two data items, these are separated by a space, so the elements of the second item are in buf[16..9]. */ static int get_ups_status(void) { char buf[BUFFLEN]; int rb_set= FALSE; /* has RB flag been set ? */ int over_set= FALSE; /* has OVER flag been set ? */ int tries = 0; int ok = FALSE; int bytes_rcvd = 0; do { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) return FALSE; /* must clear status buffer before each round */ status_init(); /* system status */ /* FIXME: some old units sometimes return "Syst Stat >1<" resulting in an temporary OB status */ bytes_rcvd = mge_command(buf, sizeof(buf), "Ss"); upsdebugx(1, "Syst Stat >%s<", buf); if ( bytes_rcvd > 0 && strlen(buf) > 7 ) { ok = TRUE; if (buf[6] == '1') { over_set = TRUE; status_set("OVER"); } if (buf[5] == '1') status_set("OB"); else status_set("OL"); if (buf[4] == '1') status_set("LB"); if (buf[3] == '1') { rb_set = TRUE; status_set("RB"); } /* buf[2] not used */ if (buf[1] == '1') status_set("COMMFAULT"); /* self-invented */ /* FIXME: better to call datastale()?! */ if (buf[0] == '1') status_set("ALARM"); /* self-invented */ /* FIXME: better to use ups.alarm */ } /* if strlen */ /* battery status */ mge_command(buf, sizeof(buf), "Bs"); upsdebugx(1, "Batt Stat >%s<", buf); if ( strlen(buf) > 7 ) { if ( !rb_set && ( buf[7] == '1' || buf[3] == '1' ) ) status_set("RB"); if (buf[1] == '1') status_set("CHRG"); if (buf[0] == '1') status_set("DISCHRG"); } /* if strlen */ /* load status */ mge_command(buf, sizeof(buf), "Ls"); upsdebugx(1, "Load Stat >%s<", buf); if ( strlen(buf) > 7 ) { if (buf[4] == '1') status_set("BOOST"); if ( !over_set && ( buf[3] == '1' ) ) status_set("OVER"); if (buf[2] == '1') status_set("TRIM"); } /* if strlen */ if ( strlen(buf) > 15 ) { /* second "byte", skip */ if (buf[16] == '1') { status_set("OB"); status_set("LB"); } /* FIXME: to be checked (MUST be buf[8]) !! */ /* if ( !(buf[9] == '1') ) */ /* This is not the OFF status! if ( !(buf[8] == '1') ) status_set("OFF"); */ } /* if strlen */ /* Bypass status */ mge_command(buf, sizeof(buf), "Ps"); upsdebugx(1, "Bypass Stat >%s<", buf); if ( strlen(buf) > 7 ) { /* FIXME: extend ups.status for BYPASS: */ /* Manual Bypass */ if (buf[7] == '1') status_set("BYPASS"); /* Automatic Bypass */ if (buf[6] == '1') status_set("BYPASS"); } /* if strlen */ } while ( !ok && tries++ < MAXTRIES ); status_commit(); return ok; } /* --------------------------------------------------------------- */ /* return proper variable "ok" given INFO_ type */ static bool_t info_variable_ok(const char *type) { mge_info_item_t *item = mge_info ; while ( strcasecmp(item->type, type )) item++; return item->ok; } /* --------------------------------------------------------------- */ /* return proper variable "cmd" given INFO_ type */ static const char *info_variable_cmd(const char *type) { mge_info_item_t *item = mge_info ; while ( strcasecmp(item->type, type )) item++; return item->cmd; } /* --------------------------------------------------------------- */ /* send command to UPS and read reply if requested reply : buffer for reply, NULL if no reply expected replylen: length of buffer reply fmt : format string, followed by optional data for command returns : no of chars received, -1 if error */ static int mge_command(char *reply, int replylen, const char *fmt, ...) { const char *p; char command[BUFFLEN]; int bytes_sent = 0; int bytes_rcvd = 0; int ret; va_list ap; /* build command string */ va_start(ap, fmt); ret = vsnprintf(command, sizeof(command), fmt, ap); if ((ret < 1) || (ret >= (int) sizeof(command))) upsdebugx(4, "mge_command: command truncated"); va_end(ap); /* Delay a bit to avoid overlap of a previous answer (500 ms), as per * http://old.networkupstools.org/protocols/mge/9261zwfa.pdf § 6.1. Timings */ usleep(500000); /* flush received, unread data */ tcflush(upsfd, TCIFLUSH); /* send command */ for (p = command; *p; p++) { if ( isprint(*p & 0xFF) ) upsdebugx(4, "mge_command: sending [%c]", *p); else upsdebugx(4, "mge_command: sending [%02X]", *p); if (write(upsfd, p, 1) != 1) return -1; bytes_sent++; usleep(MGE_CHAR_DELAY); } /* send terminating string */ if (MGE_COMMAND_ENDCHAR) { for (p = MGE_COMMAND_ENDCHAR; *p; p++) { if ( isprint(*p & 0xFF) ) upsdebugx(4, "mge_command: sending [%c]", *p); else upsdebugx(4, "mge_command: sending [%02X]", *p); if (write(upsfd, p, 1) != 1) return -1; bytes_sent++; usleep(MGE_CHAR_DELAY); } } if ( !reply ) return bytes_rcvd; else usleep(MGE_REPLY_DELAY); bytes_rcvd = ser_get_line(upsfd, reply, replylen, MGE_REPLY_ENDCHAR, MGE_REPLY_IGNCHAR, 3, 0); upsdebugx(4, "mge_command: received %d byte(s)", bytes_rcvd); return bytes_rcvd; } void upsdrv_cleanup(void) { upsdebugx(1, "cleaning up"); disable_ups_comm(); ser_close(upsfd, device_path); } nut-2.7.4/drivers/richcomm_usb.c0000644000175000017500000003104212640473702013554 00000000000000/* * richcomm_usb.c - driver for UPS with Richcomm dry-contact to USB * solution, such as 'Sweex Manageable UPS 1000VA' * * May also work on 'Kebo UPS-650D', not tested as of 05/23/2007 * * Copyright (C) 2007 Peter van Valderen * Dirk Teurlings * * 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 "main.h" #include "usb-common.h" /* driver version */ #define DRIVER_NAME "Richcomm dry-contact to USB driver" #define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Peter van Valderen \n" "Dirk Teurlings ", DRV_EXPERIMENTAL, { NULL } }; #define STATUS_REQUESTTYPE 0x21 #define REPLY_REQUESTTYPE 0x81 #define QUERY_PACKETSIZE 4 #define REPLY_PACKETSIZE 6 #define REQUEST_VALUE 0x09 #define MESSAGE_VALUE 0x200 #define INDEX_VALUE 0 /* limit the amount of spew that goes in the syslog when we lose the UPS (from nut_usb.h) */ #define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define USB_ERR_RATE 10 /* then only print every 10th error */ static usb_device_id_t richcomm_usb_id[] = { /* Sweex 1000VA */ { USB_DEVICE(0x0925, 0x1234), NULL }, /* end of list */ {-1, -1, NULL} }; static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static unsigned int comm_failures = 0; static int device_match_func(USBDevice_t *device, void *privdata) { switch (is_usb_device_supported(richcomm_usb_id, device)) { case SUPPORTED: return 1; case POSSIBLY_SUPPORTED: case NOT_SUPPORTED: default: return 0; } } static USBDeviceMatcher_t device_matcher = { &device_match_func, NULL, NULL }; static int execute_and_retrieve_query(char *query, char *reply) { int ret; ret = usb_control_msg(udev, STATUS_REQUESTTYPE, REQUEST_VALUE, MESSAGE_VALUE, INDEX_VALUE, query, QUERY_PACKETSIZE, 1000); if (ret <= 0) { upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout"); return ret; } upsdebug_hex(3, "send", query, ret); ret = usb_interrupt_read(udev, REPLY_REQUESTTYPE, reply, REPLY_PACKETSIZE, 1000); if (ret <= 0) { upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); return ret; } upsdebug_hex(3, "read", reply, ret); return ret; } static int query_ups(char *reply) { /* * This packet is a status request to the UPS */ char query[QUERY_PACKETSIZE] = { 0x01, 0x00, 0x00, 0x30 }; return execute_and_retrieve_query(query, reply); } static void usb_comm_fail(const char *fmt, ...) { int ret; char why[SMALLBUF]; va_list ap; /* this means we're probably here because select was interrupted */ if (exit_flag != 0) { return; /* ignored, since we're about to exit anyway */ } comm_failures++; if ((comm_failures == USB_ERR_LIMIT) || ((comm_failures % USB_ERR_RATE) == 0)) { upslogx(LOG_WARNING, "Warning: excessive comm failures, limiting error reporting"); } /* once it's past the limit, only log once every USB_ERR_LIMIT calls */ if ((comm_failures > USB_ERR_LIMIT) && ((comm_failures % USB_ERR_LIMIT) != 0)) { return; } /* generic message if the caller hasn't elaborated */ if (!fmt) { upslogx(LOG_WARNING, "Communications with UPS lost - check cabling"); return; } va_start(ap, fmt); ret = vsnprintf(why, sizeof(why), fmt, ap); va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(why))) { upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed more than %d bytes", (int)sizeof(why)); } upslogx(LOG_WARNING, "Communications with UPS lost: %s", why); } static void usb_comm_good(void) { if (comm_failures == 0) { return; } upslogx(LOG_NOTICE, "Communications with UPS re-established"); comm_failures = 0; } /* * Callback that is called by usb_device_open() that handles USB device * settings prior to accepting the devide. At the very least claim the * device here. Detaching the kernel driver will be handled by the * caller, don't do this here. Return < 0 on error, 0 or higher on * success. */ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) { if (usb_set_configuration(handle, 1) < 0) { upsdebugx(5, "Can't set USB configuration"); return -1; } if (usb_claim_interface(handle, 0) < 0) { upsdebugx(5, "Can't claim USB interface"); return -1; } if (usb_set_altinterface(handle, 0) < 0) { upsdebugx(5, "Can't set USB alternate interface"); return -1; } if (usb_clear_halt(handle, 0x81) < 0) { upsdebugx(5, "Can't reset USB endpoint"); return -1; } return 1; } static int usb_device_close(usb_dev_handle *handle) { if (!handle) { return 0; } /* usb_release_interface() sometimes blocks and goes into uninterruptible sleep. So don't do it. */ /* usb_release_interface(handle, 0); */ return usb_close(handle); } static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *handle, USBDevice_t *device)) { struct usb_bus *bus; /* libusb base init */ usb_init(); usb_find_busses(); usb_find_devices(); #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ usb_device_close(*handlep); #endif for (bus = usb_busses; bus; bus = bus->next) { struct usb_device *dev; usb_dev_handle *handle; for (dev = bus->devices; dev; dev = dev->next) { int i, ret; USBDeviceMatcher_t *m; upsdebugx(4, "Checking USB device [%04x:%04x] (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); /* supported vendors are now checked by the supplied matcher */ /* open the device */ *handlep = handle = usb_open(dev); if (!handle) { upsdebugx(4, "Failed to open USB device, skipping: %s", usb_strerror()); continue; } /* collect the identifying information of this device. Note that this is safe, because there's no need to claim an interface for this (and therefore we do not yet need to detach any kernel drivers). */ free(device->Vendor); free(device->Product); free(device->Serial); free(device->Bus); memset(device, 0, sizeof(*device)); device->VendorID = dev->descriptor.idVendor; device->ProductID = dev->descriptor.idProduct; device->Bus = strdup(bus->dirname); if (dev->descriptor.iManufacturer) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iManufacturer, buf, sizeof(buf)); if (ret > 0) { device->Vendor = strdup(buf); } } if (dev->descriptor.iProduct) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iProduct, buf, sizeof(buf)); if (ret > 0) { device->Product = strdup(buf); } } if (dev->descriptor.iSerialNumber) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iSerialNumber, buf, sizeof(buf)); if (ret > 0) { device->Serial = strdup(buf); } } upsdebugx(4, "- VendorID : %04x", device->VendorID); upsdebugx(4, "- ProductID : %04x", device->ProductID); upsdebugx(4, "- Manufacturer : %s", device->Vendor ? device->Vendor : "unknown"); upsdebugx(4, "- Product : %s", device->Product ? device->Product : "unknown"); upsdebugx(4, "- Serial Number: %s", device->Serial ? device->Serial : "unknown"); upsdebugx(4, "- Bus : %s", device->Bus ? device->Bus : "unknown"); for (m = matcher; m; m = m->next) { switch (m->match_function(device, m->privdata)) { case 0: upsdebugx(4, "Device does not match - skipping"); goto next_device; case -1: fatal_with_errno(EXIT_FAILURE, "matcher"); goto next_device; case -2: upsdebugx(4, "matcher: unspecified error"); goto next_device; } } for (i = 0; i < 3; i++) { ret = callback(handle, device); if (ret >= 0) { upsdebugx(4, "USB device [%04x:%04x] opened", device->VendorID, device->ProductID); return ret; } #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ if (usb_detach_kernel_driver_np(handle, 0) < 0) { upsdebugx(4, "failed to detach kernel driver from USB device: %s", usb_strerror()); } else { upsdebugx(4, "detached kernel driver from USB device..."); } #endif } fatalx(EXIT_FAILURE, "USB device [%04x:%04x] matches, but driver callback failed: %s", device->VendorID, device->ProductID, usb_strerror()); next_device: usb_close(handle); } } *handlep = NULL; upsdebugx(4, "No matching USB device found"); return -1; } /* * Initialise the UPS */ void upsdrv_initups(void) { char reply[REPLY_PACKETSIZE]; int i; for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { if ((i < 32) && (sleep(5) == 0)) { usb_comm_fail("Can't open USB device, retrying ..."); continue; } fatalx(EXIT_FAILURE, "Unable to find Richcomm dry-contact to USB solution\n\n" "Things to try:\n" " - Connect UPS device to USB bus\n" " - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n" " See upsdrvctl(8) and ups.conf(5).\n\n" "Fatal error: unusable configuration"); } /* * Read rubbish data a few times; the UPS doesn't seem to respond properly * the first few times after connecting */ for (i = 0; i < 5; i++) { query_ups(reply); sleep(1); } } void upsdrv_cleanup(void) { usb_device_close(udev); free(usbdevice.Vendor); free(usbdevice.Product); free(usbdevice.Serial); free(usbdevice.Bus); } void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "%s", "Richcomm dry-contact to USB solution"); dstate_setinfo("ups.model", "%s", usbdevice.Product ? usbdevice.Product : "unknown"); dstate_setinfo("ups.serial", "%s", usbdevice.Serial ? usbdevice.Serial : "unknown"); dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); } void upsdrv_updateinfo(void) { char reply[REPLY_PACKETSIZE]; int ret, online, battery_normal; if (!udev) { ret = usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback); if (ret < 0) { return; } } ret = query_ups(reply); if (ret < 4) { usb_comm_fail("Query to UPS failed"); dstate_datastale(); usb_device_close(udev); udev = NULL; return; } usb_comm_good(); dstate_dataok(); /* * 3rd bit of 4th byte indicates whether the UPS is on line (1) * or on battery (0) */ online = (reply[3]&4)>>2; /* * 2nd bit of 4th byte indicates battery status; normal (1) * or low (0) */ battery_normal = (reply[3]&2)>>1; status_init(); if (online) { status_set("OL"); } else { status_set("OB"); } if (!battery_normal) { status_set("LB"); } status_commit(); } /* * The shutdown feature is a bit strange on this UPS IMHO, it * switches the polarity of the 'Shutdown UPS' signal, at which * point it will automatically power down once it loses power. * * It will still, however, be possible to poll the UPS and * reverse the polarity _again_, at which point it will * start back up once power comes back. * * Maybe this is the normal way, it just seems a bit strange. * * Please note, this function doesn't power the UPS off if * line power is connected. */ void upsdrv_shutdown(void) { /* * This packet shuts down the UPS, that is, * if it is not currently on line power */ char prepare[QUERY_PACKETSIZE] = { 0x02, 0x00, 0x00, 0x00 }; /* * This should make the UPS turn itself back on once the * power comes back on; which is probably what we want */ char restart[QUERY_PACKETSIZE] = { 0x02, 0x01, 0x00, 0x00 }; char reply[REPLY_PACKETSIZE]; execute_and_retrieve_query(prepare, reply); /* * have to, the previous command seems to be * ignored if the second command comes right * behind it */ sleep(1); execute_and_retrieve_query(restart, reply); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } nut-2.7.4/drivers/apcsmart_tabs.c0000644000175000017500000001502412640444140013721 00000000000000/* apcsmart_tabs.c - common tables for APC smart protocol units * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham * (C) 2011+ Michal Soltys * * 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 "apcsmart_tabs.h" /* APC_MULTI variables *must* be listed in order of preference */ apc_vartab_t apc_vartab[] = { { "ups.temperature", 'C', APC_POLL|APC_F_CELSIUS }, { "ups.load", 'P', APC_POLL|APC_F_PERCENT }, { "ups.test.interval", 'E', APC_F_HOURS }, { "ups.test.result", 'X', APC_POLL }, { "ups.delay.start", 'r', APC_F_SECONDS }, { "ups.delay.shutdown", 'p', APC_F_SECONDS }, { "ups.id", 'c', APC_STRING }, { "ups.contacts", 'i', APC_POLL|APC_F_HEX }, { "ups.display.language", '\014' }, { "input.voltage", 'L', APC_POLL|APC_F_VOLT }, { "input.frequency", 'F', APC_POLL|APC_F_DEC }, { "input.sensitivity", 's', }, { "input.quality", '9', APC_POLL|APC_F_HEX }, { "input.transfer.low", 'l', APC_F_VOLT }, { "input.transfer.high", 'u', APC_F_VOLT }, { "input.transfer.reason", 'G', APC_POLL|APC_F_REASON }, { "input.voltage.maximum", 'M', APC_POLL|APC_F_VOLT }, { "input.voltage.minimum", 'N', APC_POLL|APC_F_VOLT }, { "output.current", '/', APC_POLL|APC_F_AMP }, { "output.voltage", 'O', APC_POLL|APC_F_VOLT }, { "output.voltage.nominal", 'o', APC_F_VOLT }, { "ambient.humidity", 'h', APC_POLL|APC_F_PERCENT }, { "ambient.0.humidity", 'H', APC_POLL|APC_PACK|APC_F_PERCENT }, { "ambient.0.humidity.high", '{', APC_POLL|APC_PACK|APC_F_PERCENT }, { "ambient.0.humidity.low", '}', APC_POLL|APC_PACK|APC_F_PERCENT }, { "ambient.temperature", 't', APC_POLL|APC_F_CELSIUS }, { "ambient.0.temperature", 'T', APC_MULTI|APC_POLL|APC_PACK|APC_F_CELSIUS, "^[0-9]{2}\\.[0-9]{2}$" }, { "ambient.0.temperature.high", '[', APC_POLL|APC_PACK|APC_F_CELSIUS }, { "ambient.0.temperature.low", ']', APC_POLL|APC_PACK|APC_F_CELSIUS }, { "battery.date", 'x', APC_STRING }, { "battery.charge", 'f', APC_POLL|APC_F_PERCENT }, { "battery.charge.restart", 'e', APC_F_PERCENT }, { "battery.voltage", 'B', APC_POLL|APC_F_VOLT }, { "battery.voltage.nominal", 'g', }, { "battery.runtime", 'j', APC_POLL|APC_F_MINUTES }, { "battery.runtime.low", 'q', APC_F_MINUTES }, { "battery.packs", '>', APC_F_DEC }, { "battery.packs.bad", '<', APC_F_DEC }, { "battery.alarm.threshold", 'k', }, { "device.uptime", 'T', APC_MULTI|APC_POLL|APC_F_HOURS, "^[0-9]{3}\\.[0-9]{1}$" }, { "ups.serial", 'n', }, { "ups.mfr.date", 'm', }, { "ups.model", '\001' }, { "ups.firmware.aux", 'v', }, { "ups.firmware", 'b', APC_MULTI, "^[[:alnum:]]+\\.[[:alnum:]]+\\.[[:alnum:]]+$" }, { "ups.firmware", 'V', APC_MULTI }, { NULL } /* todo: I = alarm enable (hex field) - split into alarm.n.enable J = alarm status (hex field) - split into alarm.n.status 0x15 = output voltage selection (APC_F_VOLT) 0x5C = load power (APC_POLL|APC_F_PERCENT) */ }; /* * APC commands mapped to NUT's instant commands * the format of extra values is matched by extended posix regex * APC_CMD_CUSTOM means that the instant command is handled by separate * function, thus the actual APC cmd in the table is ignored */ apc_cmdtab_t apc_cmdtab[] = { { "shutdown.return", "^[Aa][Tt]:[0-9]{1,3}$", APC_CMD_GRACEDOWN, APC_NASTY }, { "shutdown.return", "^([Cc][Ss]|)$", APC_CMD_SOFTDOWN, APC_NASTY }, { "shutdown.stayoff", 0, APC_CMD_SHUTDOWN, APC_NASTY|APC_REPEAT }, { "load.off", 0, APC_CMD_OFF, APC_NASTY|APC_REPEAT }, { "load.on", 0, APC_CMD_ON, APC_REPEAT }, { "calibrate.start", 0, APC_CMD_CALTOGGLE, 0 }, { "calibrate.stop", 0, APC_CMD_CALTOGGLE, 0 }, { "test.panel.start", 0, APC_CMD_FPTEST, 0 }, { "test.failure.start", 0, APC_CMD_SIMPWF, 0 }, { "test.battery.start", 0, APC_CMD_BTESTTOGGLE, 0 }, { "test.battery.stop", 0, APC_CMD_BTESTTOGGLE, 0 }, { "bypass.start", 0, APC_CMD_BYPTOGGLE, 0 }, { "bypass.stop", 0, APC_CMD_BYPTOGGLE, 0 }, { NULL } }; /* compatibility with hardware that doesn't do APC_CMDSET ('a') */ apc_compattab_t apc_compattab[] = { /* APC Matrix */ { "0XI", "@789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 }, { "0XM", "@789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 }, { "0ZI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, { "5UI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, { "5ZM", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, /* APC600 */ { "6QD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "6QI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "6TD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "6TI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* SmartUPS 900 */ { "7QD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7QI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* SmartUPS 600I */ { "6JI", "@789ABCFGKLMNOPQSTUVWXYZfg", 0 }, /* SmartUPS 900I */ { "7II", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, /* SmartUPS 2000I */ { "9II", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, { "9GI", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, /* SmartUPS 1250I */ { "8II", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, { "8GI", "@79ABCEFGKLMNOPQRSUVWXYZfg", 0 }, /* SmartUPS 1250 */ { "8QD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8QI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8TD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8TI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* CS 350 */ { "5.4.D", "@\1ABPQRSUYbdfgjmnx9", 0 }, /* * certain set of UPSes returning voltage > 255V through 'b'; "set\1" * is matched explicitly (fake key); among the UPS models - some old * APC 600 ones */ { "set\1", "@789ABCFGKLMNOPQRSUVWXYZ", 0 }, { NULL } }; upsdrv_info_t apc_tab_info = { "APC command table", APC_TABLE_VERSION }; nut-2.7.4/drivers/belkin-hid.h0000644000175000017500000000220312640443572013114 00000000000000/* belkin-hid.h - data to monitor Belkin UPS Systems USB/HID devices with NUT * * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 Peter Selinger * * Sponsored by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef BELKIN_HID_H #define BELKIN_HID_H #include "usbhid-ups.h" extern subdriver_t belkin_subdriver; #endif /* BELKIN_HID_H */ nut-2.7.4/drivers/tripplite.c0000644000175000017500000003624212640443572013127 00000000000000/* tripplite.c - model specific routines for Tripp Lite SmartUPS models (tested with: "SMART 700" [on back -- "SmartPro UPS" on front], "SMART700SER") tripplite.c was derived from Russell Kroll's bestups.c by Rik Faith. Copyright (C) 1999 Russell Kroll Copyright (C) 2001 Rickard E. (Rik) Faith Copyright (C) 2004,2008 Nicholas J. Kain 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 */ /* REFERENCE 1 A few magic numbers were derived from the GPL'd file opensrc_server/upscmd.cpp, formerly (but not longer) available from Tripp Lite at http://www.tripplite.com/linux/. */ /* REFERENCE 2 Other hints on commands were found on these web pages: http://www.kursknet.ru/~boa/ups/stinfo_command.html http://www.kursknet.ru/~boa/ups/rtinfo_command.html These pages confirm the information in the Tripp Lite source code referenced above and add more details. The first page tells how to derive the VA rating from w_value and l_value. It's a confusing explanation because shifts are used to mask out bits. Here is an example starting with the formula on the web page and proceeding to a formula that uses C syntax. I have a SMART 700 (700VA, 450W) w_value = 0x84 [available from upsc as REG1] l_value=- 0x60 [available from upsc as REG2] Unit Type: bit 6 of w_value is 0 so I have a Smart (vs. a Unison) VA Rating: ((([V:W Result]<<2)*8)+([V:L Result]>>3))*5 = (((w_value<<2)*8)+([l_value]>>3)) * 5 = ((w_value & 0x3f)*32 + (l_value >> 3)) * 5 = (4 * 32 + 12) * 5 = 700 */ /* Known UPS Commands: * * :N%02X -- delay the UPS for provided time (hex seconds) * :H%06X -- reboot the UPS. UPS will restart after provided time (hex s) * :A -- begins a self-test * :C -- fetches result of a self-test * :K1 -- turns on power receptacles * :K0 -- turns off power receptacles * :G -- unconfirmed: shuts down UPS until power returns * :Q1 -- enable "Remote Reboot" * :Q0 -- disable "Remote Reboot" * :W -- returns 'W' data * :L -- returns 'L' data * :V -- returns 'V' data (firmware revision) * :X -- returns 'X' data (firmware revision) * :D -- returns general status data * :B -- returns battery voltage (hexadecimal decivolts) * :I -- returns minimum input voltage (hexadecimal hertz) * :M -- returns maximum input voltage (hexadecimal hertz) * :P -- returns power rating * :Z -- unknown * :U -- unknown * :O -- unknown * :E -- unknown * :Y -- returns mains frequency (':D' is preferred) * :T -- returns ups temperature (':D' is preferred) * :R -- returns input voltage (':D' is preferred) * :F -- returns load percentage (':D' is preferred) * :S -- enables remote reboot/remote power on */ /* Returned value from ':D' looks like: * * 0123456789abcdef01234 * ABCCDEFFGGGGHHIIIIJJJ * A 0=LB 1=OK * B 0=OVER 1=OK * CC INFO_UTILITY * D 0=normal 1=TRIM 2=BOOST 3="EXTRA BOOST" * E 0=OFF 1=OB 2=OL 3=OB (1 and 3 are the same?) * FF f(INFO_UPSTEMP) * GG ? INFO_BATTPCT (00 when OB, values don't match table we use) * HH ? (always 00) * II INFO_LOADPCT * JJJJ ? (e.g., 5B82 5B82 5982 037B 0082) * KKK INFO_ACFREQ * 10 */ #include "main.h" #include "serial.h" #include "tripplite.h" #include #include #define DRIVER_NAME "Tripp-Lite SmartUPS driver" #define DRIVER_VERSION "0.91" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" \ "Rickard E. (Rik) Faith \n" \ "Nicholas J. Kain ", DRV_STABLE, { NULL } }; /* Time in seconds to delay before shutting down. */ static unsigned int offdelay = DEFAULT_OFFDELAY; static unsigned int startdelay = DEFAULT_STARTDELAY; static unsigned int bootdelay = DEFAULT_BOOTDELAY; static int hex2d(char *start, unsigned int len) { char buf[32]; snprintf(buf, sizeof buf, "%.*s", len, start); return strtol(buf, NULL, 16); } /* The UPS that I'm using (SMART700SER) has the bizarre characteristic * of innately echoing back commands. Therefore, we cannot use * ser_get_line and must manually discard our echoed command. * * All UPS commands are challenge-response, so this function makes things * very clean. * * return: # of chars in buf, excluding terminating \0 */ static int send_cmd(const char *str, char *buf, size_t len) { unsigned char c; int ret = 0; size_t i = 0; ser_flush_io(upsfd); ser_send(upsfd, "%s", str); if (!len || !buf) return -1; for (;;) { ret = ser_get_char(upsfd, &c, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) return -1; if (c == ENDCHAR) break; } do { ret = ser_get_char(upsfd, &c, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) return -1; if (c == IGNCHAR || c == ENDCHAR) continue; buf[i++] = c; } while (c != ENDCHAR && i < len); buf[i] = '\0'; return i; } static void get_letter_cmd(const char *str, char *buf, size_t len) { int tries, ret; for (tries = 0; tries < MAXTRIES; ++tries) { ret = send_cmd(str, buf, len); if ((ret > 0) && isalnum((unsigned char)buf[0])) return; } fatalx(EXIT_FAILURE, "\nFailed to find UPS - giving up..."); } static int do_reboot_now(void) { char buf[256], cmd[16]; snprintf(cmd, sizeof cmd, ":H%06X\r", startdelay); return send_cmd(cmd, buf, sizeof buf); } static void do_reboot(void) { char buf[256], cmd[16]; snprintf(cmd, sizeof cmd, ":N%02X\r", bootdelay); send_cmd(cmd, buf, sizeof buf); do_reboot_now(); } static int soft_shutdown(void) { char buf[256], cmd[16]; snprintf(cmd, sizeof cmd, ":N%02X\r", offdelay); send_cmd(cmd, buf, sizeof buf); return send_cmd(":G\r", buf, sizeof buf); } static int hard_shutdown(void) { char buf[256], cmd[16]; snprintf(cmd, sizeof cmd, ":N%02X\r", offdelay); send_cmd(cmd, buf, sizeof buf); return send_cmd(":K0\r", buf, sizeof buf); } static int instcmd(const char *cmdname, const char *extra) { char buf[256]; if (!strcasecmp(cmdname, "test.battery.start")) { send_cmd(":A\r", buf, sizeof buf); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.off")) { send_cmd(":K0\r", buf, sizeof buf); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { send_cmd(":K1\r", buf, sizeof buf); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.reboot")) { do_reboot_now(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) { do_reboot(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { soft_shutdown(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stayoff")) { hard_shutdown(); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.delay.shutdown")) { offdelay = atoi(val); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "ups.delay.start")) { startdelay = atoi(val); dstate_setinfo("ups.delay.start", "%d", startdelay); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "ups.delay.reboot")) { bootdelay = atoi(val); dstate_setinfo("ups.delay.reboot", "%d", bootdelay); return STAT_SET_HANDLED; } return STAT_SET_UNKNOWN; } void upsdrv_initinfo(void) { const char *model; char w_value[16], l_value[16], v_value[16], x_value[16]; int va; long w, l; get_letter_cmd(":W\r", w_value, sizeof w_value); get_letter_cmd(":L\r", l_value, sizeof l_value); get_letter_cmd(":V\r", v_value, sizeof v_value); get_letter_cmd(":X\r", x_value, sizeof x_value); dstate_setinfo("ups.mfr", "%s", "Tripp Lite"); w = hex2d(w_value, 2); l = hex2d(l_value, 2); model = "Smart %d"; if (w & 0x40) model = "Unison %d"; va = ((w & 0x3f) * 32 + (l >> 3)) * 5; /* New formula */ if (!(w & 0x80)) va = l / 2; /* Old formula */ dstate_setinfo("ups.model", model, va); dstate_setinfo("ups.firmware", "%c%c", 'A'+v_value[0]-'0', 'A'+v_value[1]-'0'); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.shutdown", 3); dstate_setinfo("ups.delay.start", "%d", startdelay); dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.start", 8); dstate_setinfo("ups.delay.reboot", "%d", bootdelay); dstate_setflags("ups.delay.reboot", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.reboot", 3); dstate_addcmd("test.battery.start"); /* Turns off automatically */ dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("shutdown.reboot"); dstate_addcmd("shutdown.reboot.graceful"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); upsh.instcmd = instcmd; upsh.setvar = setvar; printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model"), device_path); } void upsdrv_shutdown(void) { soft_shutdown(); } void upsdrv_updateinfo(void) { static int numfails; char buf[256]; int bp, volt, temp, load, vmax, vmin, stest, len; int bcond, lstate, tstate, mode; float bv, freq; len = send_cmd(":D\r", buf, sizeof buf); if (len != 21) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("Data command failed: [%d] bytes != 21 bytes.", len); dstate_datastale(); } return; } volt = hex2d(buf + 2, 2); temp = (int)(hex2d(buf + 6, 2)*0.3636 - 21.0); load = hex2d(buf + 12, 2); freq = hex2d(buf + 18, 3) / 10.0; bcond = buf[0]; lstate = buf[1]; tstate = buf[4]; mode = buf[5]; if (volt > INVOLT_MAX || volt < INVOLT_MIN || temp > TEMP_MAX || temp < TEMP_MIN || load > LOAD_MAX || load < LOAD_MIN || freq > FREQ_MAX || freq < FREQ_MIN) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("Data out of bounds: [%0d,%3d,%3d,%02.2f]", volt, temp, load, freq); dstate_datastale(); } return; } send_cmd(":B\r", buf, sizeof buf); bv = (float)hex2d(buf, 2) / 10.0; if (bv > 50.0 || bv < 0.0) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("Battery voltage out of bounds: [%02.1f]", bv); dstate_datastale(); } return; } send_cmd(":M\r", buf, sizeof buf); vmax = hex2d(buf, 2); if (vmax > INVOLT_MAX || vmax < INVOLT_MIN) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("InVoltMax out of bounds: [%d]", vmax); dstate_datastale(); } return; } send_cmd(":I\r", buf, sizeof buf); vmin = hex2d(buf, 2); if (vmin > INVOLT_MAX || vmin < INVOLT_MIN) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("InVoltMin out of bounds: [%d]", vmin); dstate_datastale(); } return; } send_cmd(":C\r", buf, sizeof buf); errno = 0; stest = strtol(buf, 0, 10); if (errno == ERANGE) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("Self test is out of range: [%d]", stest); dstate_datastale(); } return; } if (errno == EINVAL) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("Self test returned non-numeric data."); dstate_datastale(); } return; } if (stest > 3 || stest < 0) { ++numfails; if (numfails > MAXTRIES) { ser_comm_fail("Self test out of bounds: [%d]", stest); dstate_datastale(); } return; } /* We've successfully gathered all the data for an update. */ numfails = 0; dstate_setinfo("input.voltage", "%0d", volt); dstate_setinfo("ups.temperature", "%3d", temp); dstate_setinfo("ups.load", "%3d", load); dstate_setinfo("input.frequency", "%02.2f", freq); status_init(); /* Battery Voltage Condition */ switch (bcond) { case '0': /* Low Battery */ status_set("LB"); break; case '1': /* Normal */ break; default: /* Unknown */ upslogx(LOG_ERR, "Unknown battery state: %c", bcond); break; } /* Load State */ switch (lstate) { case '0': /* Overload */ status_set("OVER"); break; case '1': /* Normal */ break; default: /* Unknown */ upslogx(LOG_ERR, "Unknown load state: %c", lstate); break; } /* Tap State */ switch (tstate) { case '0': /* Normal */ break; case '1': /* Reducing */ status_set("TRIM"); break; case '2': /* Boost */ case '3': /* Extra Boost */ status_set("BOOST"); break; default: /* Unknown */ upslogx(LOG_ERR, "Unknown tap state: %c", tstate); break; } /* Mode */ switch (mode) { case '0': /* Off */ status_set("OFF"); break; case '1': /* On Battery */ status_set("OB"); break; case '2': /* On Line */ status_set("OL"); break; case '3': /* On Battery */ status_set("OB"); break; default: /* Unknown */ upslogx(LOG_ERR, "Unknown mode state: %c", mode); break; } status_commit(); /* dq ~= sqrt(dV) is a reasonable approximation * Results fit well against the discrete function used in the Tripp Lite * source, but give a continuous result. */ if (bv >= MAX_VOLT) bp = 100; else if (bv <= MIN_VOLT) bp = 10; else bp = (int)(100*sqrt((bv - MIN_VOLT) / (MAX_VOLT - MIN_VOLT))); dstate_setinfo("battery.voltage", "%.1f", bv); dstate_setinfo("battery.charge", "%3d", bp); dstate_setinfo("input.voltage.maximum", "%d", vmax); dstate_setinfo("input.voltage.minimum", "%d", vmin); switch (stest) { case 0: dstate_setinfo("ups.test.result", "%s", "OK"); break; case 1: dstate_setinfo("ups.test.result", "%s", "Battery Bad (Replace)"); break; case 2: dstate_setinfo("ups.test.result", "%s", "In Progress"); break; case 3: dstate_setinfo("ups.test.result", "%s", "Bad Inverter"); break; default: dstate_setinfo("ups.test.result", "Unknown (%s)", buf); break; } dstate_dataok(); ser_comm_good(); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { char msg[256]; snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d).", DEFAULT_OFFDELAY); addvar(VAR_VALUE, "offdelay", msg); snprintf(msg, sizeof msg, "Set start delay, in seconds (default=%d).", DEFAULT_STARTDELAY); addvar(VAR_VALUE, "startdelay", msg); snprintf(msg, sizeof msg, "Set reboot delay, in seconds (default=%d).", DEFAULT_BOOTDELAY); addvar(VAR_VALUE, "rebootdelay", msg); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); if (getval("offdelay")) offdelay = atoi(getval("offdelay")); if (getval("startdelay")) startdelay = atoi(getval("startdelay")); if (getval("rebootdelay")) bootdelay = atoi(getval("rebootdelay")); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/cps-hid.h0000644000175000017500000000204112640443572012435 00000000000000/* cps-hid.h - subdriver to monitor CPS USB/HID devices with NUT * * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 - 2006 Peter Selinger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef CPS_HID_H #define CPS_HID_H #include "usbhid-ups.h" extern subdriver_t cps_subdriver; #endif /* CPS_HID_H */ nut-2.7.4/drivers/nutdrv_qx.h0000644000175000017500000003031412640473702013142 00000000000000/* nutdrv_qx.h - Driver for USB and serial UPS units with Q* protocols * * Copyright (C) * 2013 Daniele Pezzini * Based on usbhid-ups.h - Copyright (C) * 2003-2009 Arnaud Quette * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_H #define NUTDRV_QX_H #include #include #include #include #include "config.h" /* For testing purposes */ /*#define TESTING*/ /* Driver's parameters */ #define QX_VAR_ONDELAY "ondelay" #define QX_VAR_OFFDELAY "offdelay" #define QX_VAR_POLLFREQ "pollfreq" /* Parameters default values */ #define DEFAULT_ONDELAY "180" /* Delay between return of utility power and powering up of load, in seconds */ #define DEFAULT_OFFDELAY "30" /* Delay before power off, in seconds */ #define DEFAULT_POLLFREQ 30 /* Polling interval between full updates, in seconds; the driver will do quick polls in the meantime */ #ifndef TRUE typedef enum { FALSE, TRUE } bool_t; #else typedef int bool_t; #endif /* Structure for rw vars */ typedef struct { char value[SMALLBUF]; /* Value for enum/range, or length for ST_FLAG_STRING */ int (*preprocess)(char *value, const size_t len); /* Optional function to preprocess range/enum value. * This function will be given value and its size_t and must return either 0 if value is supported or -1 if not supported. */ } info_rw_t; /* Structure containing information about how to get/set data from/to the UPS and convert these to/from NUT standard */ typedef struct item_t { const char *info_type; /* NUT variable name * If QX_FLAG_NONUT is set, name to print to the logs * If both QX_FLAG_NONUT and QX_FLAG_SETVAR are set, name of the var to retrieve from ups.conf */ const int info_flags; /* NUT flags (ST_FLAG_* values to set in dstate_addinfo) */ info_rw_t *info_rw; /* An array of info_rw_t to handle r/w variables: * If ST_FLAG_STRING is set: length of the string (dstate_setaux) * If QX_FLAG_ENUM is set: enumerated values (dstate_addenum) * If QX_FLAG_RANGE is set: range boundaries (dstate_addrange) * If QX_FLAG_SETVAR is set the value given by the user will be checked against these infos. */ const char *command; /* Command sent to the UPS to get answer/to execute an instant command/to set a variable */ char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime. * If you expect a nonvalid C string (e.g.: inner '\0's) or need to perform actions before the answer is used (and treated as a null-terminated string), you should set a preprocess_answer() function */ const int answer_len; /* Expected min length of the answer. Set it to 0 if there’s no minimum length to look after. */ const char leading; /* Expected leading character of the answer (optional) */ char value[SMALLBUF]; /* Value from the answer, filled at runtime (i.e. answer between from and to) */ const int from; /* Position of the starting character of the info (i.e. 'value') we're after in the answer */ const int to; /* Position of the ending character of the info (i.e. 'value') we're after in the answer: use 0 if all the remaining of the line is needed */ const char *dfl; /* Format to store value from the UPS in NUT variables. Not used by the driver for QX_FLAG_{CMD,SETVAR} items. * If there's no preprocess function, set it either to %s for strings or to a floating point specifier (e.g. %.1f) for numbers. * Otherwise: * If QX_FLAG_ABSENT: default value * If QX_FLAG_CMD: default command value */ unsigned long qxflags; /* Driver's own flags */ int (*preprocess_command)(struct item_t *item, char *command, const size_t commandlen); /* Last chance to preprocess the command to be sent to the UPS (e.g. to add CRC, ...). * This function is given the currently processed item (item), the command to be sent to the UPS (command) and its size_t (commandlen). * Return -1 in case of errors, else 0. * command must be filled with the actual command to be sent to the UPS. */ int (*preprocess_answer)(struct item_t *item, const int len); /* Function to preprocess the answer we got from the UPS before we do anything else (e.g. for CRC, decoding, ...). * This function is given the currently processed item (item) with the answer we got from the UPS unmolested and already stored in item->answer and the length of that answer (len). * Return -1 in case of errors, else the length of the newly allocated item->answer (from now on, treated as a null-terminated string). */ int (*preprocess)(struct item_t *item, char *value, const size_t valuelen); /* Function to preprocess the data from/to the UPS * This function is given the currently processed item (item), a char array (value) and its size_t (valuelen). * Return -1 in case of errors, else 0. * If QX_FLAG_SETVAR/QX_FLAG_CMD -> process command before it is sent: value must be filled with the command to be sent to the UPS. * Otherwise -> process value we got from answer before it gets stored in a NUT variable: value must be filled with the processed value already compliant to NUT standards. */ } item_t; /* Driver's own flags */ #define QX_FLAG_STATIC 2 /* Retrieve info only once. */ #define QX_FLAG_SEMI_STATIC 4 /* Retrieve info smartly, i.e. only when a command/setvar is executed and we expect that data could have been changed. */ #define QX_FLAG_ABSENT 8 /* Data is absent in the device, use default value. */ #define QX_FLAG_QUICK_POLL 16 /* Mandatory vars, polled also in QX_WALKMODE_QUICK_UPDATE. * If there’s a problem with a var not flagged as QX_FLAG_QUICK_POLL in QX_WALKMODE_INIT, the driver will automagically set QX_FLAG_SKIP on it and then it’ll skip that item in QX_WALKMODE_{QUICK,FULL}_UPDATE. * Otherwise, if the item has the flag QX_FLAG_QUICK_POLL set, in case of errors in QX_WALKMODE_INIT the driver will set datastale. */ #define QX_FLAG_CMD 32 /* Instant command. */ #define QX_FLAG_SETVAR 64 /* The var is settable and the actual item stores info on how to set it. */ #define QX_FLAG_TRIM 128 /* This var's value need to be trimmed of leading/trailing spaces/hashes. */ #define QX_FLAG_ENUM 256 /* Enum values exist and are stored in info_rw. */ #define QX_FLAG_RANGE 512 /* Ranges for this var available and are stored in info_rw. */ #define QX_FLAG_NONUT 1024 /* This var doesn't have a corresponding var in NUT. */ #define QX_FLAG_SKIP 2048 /* Skip this var: this item won’t be processed. */ #define MAXTRIES 3 /* Max number of retries */ #ifdef TESTING /* Testing struct */ typedef struct { const char *cmd; /* Command to match */ const char answer[SMALLBUF]; /* Answer for that command. * Note: if 'answer' contains inner '\0's, in order to preserve them, 'answer_len' as well as an item_t->preprocess_answer() function must be set */ const int answer_len; /* Answer length: * - if set to -1 -> auto calculate answer length (treat 'answer' as a null-terminated string) * - otherwise -> use the provided length (if reasonable) and preserve inner '\0's (treat 'answer' as a sequence of bytes till the item_t->preprocess_answer() function gets called) */ } testing_t; #endif /* TESTING */ /* Subdriver interface */ typedef struct { const char *name; /* Name of this subdriver, i.e. name (must be equal to the protocol name) + space + version */ int (*claim)(void); /* Function that allows the subdriver to "claim" a device: return 1 if device is covered by this subdriver, else 0 */ item_t *qx2nut; /* Main table of vars and instcmds */ void (*initups)(void); /* Subdriver specific upsdrv_initups. Called at the end of nutdrv_qx's own upsdrv_initups */ void (*initinfo)(void); /* Subdriver specific upsdrv_initinfo. Called at the end of nutdrv_qx's own upsdrv_initinfo */ void (*makevartable)(void); /* Subdriver specific ups.conf flags/vars */ const char *accepted; /* String to match if the driver is expecting a reply from the UPS on instcmd/setvar. * This comparison is done after the answer we got back from the UPS has been processed to get the value we are searching: * - you don’t have to include the trailing carriage return (\r) * - you can decide at which index of the answer the value should start or end setting the appropriate from and to in the item_t */ const char *rejected; /* String to match if the driver is expecting a reply from the UPS in case of error. * This comparison is done on the answer we got back from the UPS before it has been processed: * - include also the trailing carriage return (\r) and whatever character is expected */ #ifdef TESTING testing_t *testing; /* Testing table: commands and the replies used for testing the subdriver */ #endif /* TESTING */ } subdriver_t; /* The following functions are exported for the benefit of subdrivers */ /* Execute an instant command. In detail: * - look up the given 'cmdname' in the qx2nut data structure (if not found, try to fallback to commonly known commands); * - if 'cmdname' is found, call its preprocess function, passing to it 'extradata', if any, otherwise its dfl value, if any; * - send the command to the device and check the reply. * Return STAT_INSTCMD_INVALID if the command is invalid, STAT_INSTCMD_FAILED if it failed, STAT_INSTCMD_HANDLED on success. */ int instcmd(const char *cmdname, const char *extradata); /* Set r/w variable to a value after it has been checked against its info_rw structure. Return STAT_SET_HANDLED on success, otherwise STAT_SET_UNKNOWN. */ int setvar(const char *varname, const char *val); /* Find an item of item_t type in qx2nut data structure by its info_type, optionally filtered by its qxflags, and return it if found, otherwise return NULL. * - 'flag': flags that have to be set in the item, i.e. if one of the flags is absent in the item it won't be returned * - 'noflag': flags that have to be absent in the item, i.e. if at least one of the flags is set in the item it won't be returned */ item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag); /* Send 'command' (a null-terminated byte string) or, if it is NULL, send the command stored in the item to the UPS and process the reply, saving it in item->answer. Return -1 on errors, 0 on success. */ int qx_process(item_t *item, const char *command); /* Process the value we got back from the UPS (set status bits and set the value of other parameters), calling the item-specific preprocess function, if any, otherwise executing the standard preprocessing (including trimming if QX_FLAG_TRIM is set). * Return -1 on failure, 0 for a status update and 1 in all other cases. */ int ups_infoval_set(item_t *item); /* Return the currently processed status so that it can be checked with one of the status_bit_t passed to the STATUS() macro. */ int qx_status(void); /* Edit the current status: it takes one of the NUT status (all but OB are supported, simply set it as not OL), eventually preceded with an exclamation mark to clear it from the status (e.g. !OL). */ void update_status(const char *nutvalue); /* Data for processing status values */ #define STATUS(x) ((unsigned)1< ID config option by Jason White 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 "main.h" #include "serial.h" #define DRIVER_NAME "Best UPS driver" #define DRIVER_VERSION "1.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" \ "Jason White ", DRV_STABLE, { NULL } }; #define ENDCHAR 13 /* replies end with CR */ #define MAXTRIES 5 #define UPSDELAY 50000 /* 50 ms delay required for reliable operation */ #define SER_WAIT_SEC 3 /* allow 3.0 sec for ser_get calls */ #define SER_WAIT_USEC 0 static float lowvolt = 0, highvolt = 0; static int battvoltmult = 1; static int inverted_bypass_bit = 0; static void model_set(const char *abbr, const char *rating) { if (!strcmp(abbr, "FOR")) { dstate_setinfo("ups.mfr", "%s", "Best Power"); dstate_setinfo("ups.model", "Fortress %s", rating); return; } if (!strcmp(abbr, "FTC")) { dstate_setinfo("ups.mfr", "%s", "Best Power"); dstate_setinfo("ups.model", "Fortress Telecom %s", rating); return; } if (!strcmp(abbr, "PRO")) { dstate_setinfo("ups.mfr", "%s", "Best Power"); dstate_setinfo("ups.model", "Patriot Pro %s", rating); inverted_bypass_bit = 1; return; } if (!strcmp(abbr, "PR2")) { dstate_setinfo("ups.mfr", "%s", "Best Power"); dstate_setinfo("ups.model", "Patriot Pro II %s", rating); inverted_bypass_bit = 1; return; } if (!strcmp(abbr, "325")) { dstate_setinfo("ups.mfr", "%s", "Sola Australia"); dstate_setinfo("ups.model", "Sola 325 %s", rating); return; } if (!strcmp(abbr, "520")) { dstate_setinfo("ups.mfr", "%s", "Sola Australia"); dstate_setinfo("ups.model", "Sola 520 %s", rating); return; } if (!strcmp(abbr, "610")) { dstate_setinfo("ups.mfr", "%s", "Best Power"); dstate_setinfo("ups.model", "610 %s", rating); return; } if (!strcmp(abbr, "620")) { dstate_setinfo("ups.mfr", "%s", "Sola Australia"); dstate_setinfo("ups.model", "Sola 620 %s", rating); return; } if (!strcmp(abbr, "AX1")) { dstate_setinfo("ups.mfr", "%s", "Best Power"); dstate_setinfo("ups.model", "Axxium Rackmount %s", rating); return; } dstate_setinfo("ups.mfr", "%s", "Unknown"); dstate_setinfo("ups.model", "Unknown %s (%s)", abbr, rating); printf("Unknown model detected - please report this ID: '%s'\n", abbr); } static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_pace(upsfd, UPSDELAY, "CT\r"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { ser_send_pace(upsfd, UPSDELAY, "T\r"); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int get_ident(char *buf, size_t bufsize) { int i, ret; char *ID; ID = getval("ID"); /* user-supplied override from ups.conf */ if (ID) { upsdebugx(2, "NOTE: using user-supplied ID response"); snprintf(buf, bufsize, "%s", ID); return 1; } for (i = 0; i < MAXTRIES; i++) { ser_send_pace(upsfd, UPSDELAY, "\rID\r"); ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); if (ret > 0) upsdebugx(2, "get_ident: got [%s]", buf); /* buf must start with ( and be in the range [25-27] */ if ((ret > 0) && (buf[0] != '(') && (strlen(buf) >= 25) && (strlen(buf) <= 27)) return 1; sleep(1); } upslogx(LOG_INFO, "Giving up on hardware detection after %d tries", MAXTRIES); return 0; } static void ups_ident(void) { int i; char buf[256], *ptr; char *model = NULL, *rating = NULL; if (!get_ident(buf, sizeof(buf))) { fatalx(EXIT_FAILURE, "Unable to detect a Best/SOLA or Phoenix protocol UPS"); } /* FOR,750,120,120,20.0,27.6 */ ptr = strtok(buf, ","); for (i = 0; ptr; i++) { switch (i) { case 0: model = ptr; break; case 1: rating = ptr; break; case 2: dstate_setinfo("input.voltage.nominal", "%d", atoi(ptr)); break; case 3: dstate_setinfo("output.voltage.nominal", "%d", atoi(ptr)); break; case 4: lowvolt = atof(ptr); break; case 5: highvolt = atof(ptr); break; } ptr = strtok(NULL, ","); } if ((!model) || (!rating)) { fatalx(EXIT_FAILURE, "Didn't get a valid ident string"); } model_set(model, rating); /* Battery voltage multiplier */ ptr = getval("battvoltmult"); if (ptr) { battvoltmult = atoi(ptr); } /* Lookup the nominal battery voltage (should be between lowvolt and highvolt */ for (i = 0; i < 8; i++) { const int nominal[] = { 2, 6, 12, 24, 36, 48, 72, 96 }; if ((lowvolt < nominal[i]) && (highvolt > nominal[i])) { dstate_setinfo("battery.voltage.nominal", "%d", battvoltmult * nominal[i]); break; } } ptr = getval("nombattvolt"); if (ptr) { highvolt = atof(ptr); } } static void ups_sync(void) { char buf[256]; int i, ret; for (i = 0; i < MAXTRIES; i++) { ser_send_pace(upsfd, UPSDELAY, "\rQ1\r"); ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); /* return once we get something that looks usable */ if ((ret > 0) && (buf[0] == '(')) return; usleep(250000); } fatalx(EXIT_FAILURE, "Unable to detect a Best/SOLA or Phoenix protocol UPS"); } void upsdrv_initinfo(void) { ups_sync(); ups_ident(); printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model"), device_path); /* paranoia - cancel any shutdown that might already be running */ ser_send_pace(upsfd, UPSDELAY, "C\r"); upsh.instcmd = instcmd; dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); } static int ups_on_line(void) { int i, ret; char temp[256], pstat[32]; for (i = 0; i < MAXTRIES; i++) { ser_send_pace(upsfd, UPSDELAY, "\rQ1\r"); ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); /* Q1 must return 46 bytes starting with a ( */ if ((ret > 0) && (temp[0] == '(') && (strlen(temp) == 46)) { sscanf(temp, "%*s %*s %*s %*s %*s %*s %*s %s", pstat); if (pstat[0] == '0') return 1; /* on line */ return 0; /* on battery */ } sleep(1); } upslogx(LOG_ERR, "Status read failed: assuming on battery"); return 0; /* on battery */ } void upsdrv_shutdown(void) { printf("The UPS will shut down in approximately one minute.\n"); if (ups_on_line()) printf("The UPS will restart in about one minute.\n"); else printf("The UPS will restart when power returns.\n"); ser_send_pace(upsfd, UPSDELAY, "S01R0001\r"); } void upsdrv_updateinfo(void) { char involt[16], outvolt[16], loadpct[16], acfreq[16], battvolt[16], upstemp[16], pstat[16], buf[256]; float bvoltp; int ret; ret = ser_send_pace(upsfd, UPSDELAY, "\rQ1\r"); if (ret < 1) { ser_comm_fail("ser_send_pace failed"); dstate_datastale(); return; } /* these things need a long time to respond completely */ usleep(200000); ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) { ser_comm_fail("Poll failed: %s", ret ? strerror(errno) : "timeout"); dstate_datastale(); return; } if (ret < 46) { ser_comm_fail("Poll failed: short read (got %d bytes)", ret); dstate_datastale(); return; } if (ret > 46) { ser_comm_fail("Poll failed: response too long (got %d bytes)", ret); dstate_datastale(); return; } if (buf[0] != '(') { ser_comm_fail("Poll failed: invalid start character (got %02x)", buf[0]); dstate_datastale(); return; } ser_comm_good(); sscanf(buf, "%*c%s %*s %s %s %s %s %s %s", involt, outvolt, loadpct, acfreq, battvolt, upstemp, pstat); /* Guesstimation of battery charge left (inaccurate) */ bvoltp = 100 * (atof(battvolt) - lowvolt) / (highvolt - lowvolt); if (bvoltp > 100) { bvoltp = 100; } dstate_setinfo("battery.voltage", "%.1f", battvoltmult * atof(battvolt)); dstate_setinfo("input.voltage", "%s", involt); dstate_setinfo("output.voltage", "%s", outvolt); dstate_setinfo("ups.load", "%s", loadpct); dstate_setinfo("input.frequency", "%s", acfreq); if(upstemp[0] != 'X') { dstate_setinfo("ups.temperature", "%s", upstemp); } dstate_setinfo("battery.charge", "%02.1f", bvoltp); status_init(); if (pstat[0] == '0') { status_set("OL"); /* on line */ /* only allow these when OL since they're bogus when OB */ if (pstat[2] == (inverted_bypass_bit ? '0' : '1')) { /* boost or trim in effect */ if (atof(involt) < atof(outvolt)) status_set("BOOST"); if (atof(involt) > atof(outvolt)) status_set("TRIM"); } } else { status_set("OB"); /* on battery */ } if (pstat[1] == '1') status_set("LB"); /* low battery */ status_commit(); dstate_dataok(); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "nombattvolt", "Override nominal battery voltage"); addvar(VAR_VALUE, "battvoltmult", "Battery voltage multiplier"); addvar(VAR_VALUE, "ID", "Force UPS ID response string"); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/gamatronic.c0000644000175000017500000002271512640443572013237 00000000000000/* gamatronic.c * * SEC UPS Driver ported to the new NUT API for Gamatronic UPS Usage. * * Copyright (C) * 2001 John Marley * 2002 Jules Taplin * 2002 Eric Lawson * 2005 Arnaud Quette * 2005 Nadav Moskovitch * * 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 "main.h" #include "serial.h" #include "gamatronic.h" #define DRIVER_NAME "Gamatronic UPS driver" #define DRIVER_VERSION "0.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "John Marley \n" \ "Jules Taplin \n" \ "Eric Lawson \n" \ "Arnaud Quette \n" \ "Nadav Moskovitch ", DRV_STABLE, { NULL } }; #define ENDCHAR '\r' #define IGNCHARS "" #define SER_WAIT_SEC 1 /* allow 3.0 sec for ser_get calls */ #define SER_WAIT_USEC 0 int sec_upsrecv (char *buf) { char lenbuf[4]; int ret; ser_get_line(upsfd, buf, 140, ENDCHAR, IGNCHARS,SER_WAIT_SEC, SER_WAIT_USEC); if (buf[0] == SEC_MSG_STARTCHAR){ switch (buf[1]){ case SEC_NAK: return(-1); case SEC_ACK: return(0); case SEC_DATAMSG: strncpy(lenbuf,buf+2,3); ret = atoi(lenbuf); if (ret > 0){ strcpy(buf,buf+5); return(ret);} else return (-2); default: return(-2); } } else { return (-2); } } int sec_cmd(const char mode, const char *command, char *msgbuf, int *buflen) { char msg[140]; int ret; memset(msg, 0, sizeof(msg)); /* create the message string */ if (*buflen > 0) { snprintf(msg, sizeof(msg), "%c%c%03d%s%s", SEC_MSG_STARTCHAR, mode, (*buflen)+3, command, msgbuf); } else { snprintf(msg, sizeof(msg), "%c%c003%s", SEC_MSG_STARTCHAR, mode, command); } upsdebugx(1, "PC-->UPS: \"%s\"",msg); ret = ser_send(upsfd, "%s", msg); upsdebugx(1, " send returned: %d",ret); if (ret == -1) return -1; ret = sec_upsrecv(msg); if (ret < 0) return -1; if (ret >= 0) { strncpy(msgbuf, msg, ret); upsdebugx(1, "UPS<--PC: \"%s\"",msg); } /* *(msgbuf+ret) = '\0';*/ *buflen = ret; return ret; } void addquery(const char *cmd, int field, int varnum, int pollflag) { int q; for (q=0; q 0) { if (strcmp(sec_varlist[sqv(q,f)].value, r) != 0 ) { snprintf(sec_varlist[sqv(q,f)].value, sizeof(sec_varlist[sqv(q,f)].value), "%s", r); sec_setinfo(sqv(q,f), r); } /* If SEC VAR is alarm and its on, add it to the alarm property */ if (sec_varlist[sqv(q,f)].flags & FLAG_ALARM && strcmp(r,"1")== 0) { alarm_set(sec_varlist[sqv(q,f)].name); } } if (n == NULL) break; r = n+1; } } } void upsdrv_initinfo(void) { int msglen, v; char *a,*p,avail_list[300]; /* find out which variables/commands this UPS supports */ msglen = 0; sec_cmd(SEC_POLLCMD, SEC_AVAILP1, avail_list, &msglen); p = avail_list + msglen; if (p != avail_list) *p++ = ','; msglen = 0; sec_cmd(SEC_POLLCMD, SEC_AVAILP2, p, &msglen); *(p+msglen) = '\0'; if (strlen(avail_list) == 0){ fatalx(EXIT_FAILURE, "No available variables found!");} a = avail_list; while ((p = strtok(a, ",")) != NULL) { a = NULL; v = atoi(p); /* don't bother adding a write-only variable */ if (sec_varlist[v].flags == FLAG_WONLY) continue; addquery(sec_varlist[v].cmd, sec_varlist[v].field, v, sec_varlist[v].poll); } /* poll one time values */ sec_poll(FLAG_POLLONCE); printf("UPS: %s %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model")); } void upsdrv_updateinfo(void) { alarm_init(); /* poll status values values */ sec_poll(FLAG_POLL); alarm_commit(); update_pseudovars(); dstate_dataok(); } void upsdrv_shutdown(void) { int msg_len; char msgbuf[SMALLBUF]; msg_len = snprintf(msgbuf, sizeof(msgbuf), "-1"); sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msg_len); msg_len = snprintf(msgbuf, sizeof(msgbuf), "1"); sec_cmd(SEC_SETCMD, SEC_AUTORESTART, msgbuf, &msg_len); msg_len = snprintf(msgbuf, sizeof(msgbuf), "2"); sec_cmd(SEC_SETCMD, SEC_SHUTTYPE,msgbuf, &msg_len); msg_len = snprintf(msgbuf, sizeof(msgbuf), "5"); sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msg_len); } /* static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } */ void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* allow '-x xyzzy' */ /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ /* allow '-x foo=' */ /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ } void setup_serial(const char *port) { char temp[140]; int i,ret; /* Detect the ups baudrate */ for (i=0; i<5; i++) { ser_set_speed(upsfd, device_path,baud_rates[i].rate); ret = ser_send(upsfd, "^P003MAN"); ret = sec_upsrecv(temp); if (ret >= -1) break; } if (i == 5) { printf("Can't talk to UPS on port %s!\n",port); printf("Check the cabling and portname and try again\n"); printf("Please note that this driver only support UPS Models with SEC Protorol\n"); ser_close(upsfd, device_path); exit (1); } printf("Connected to UPS on %s baudrate: %d\n",port, baud_rates[i].name); } void upsdrv_initups(void) { upsfd = ser_open(device_path); setup_serial(device_path); /* upsfd = ser_open(device_path); */ /* ser_set_speed(upsfd, device_path, B1200); */ /* probe ups type */ /* to get variables and flags from the command line, use this: * * first populate with upsdrv_buildvartable above, then... * * set flag foo : /bin/driver -x foo * set variable 'cable' to '1234' : /bin/driver -x cable=1234 * * to test flag foo in your code: * * if (testvar("foo")) * do_something(); * * to show the value of cable: * * if ((cable == getval("cable"))) * printf("cable is set to %s\n", cable); * else * printf("cable is not set!\n"); * * don't use NULL pointers - test the return result first! */ /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ } void upsdrv_cleanup(void) { /* free(dynamic_mem); */ ser_close(upsfd, device_path); } nut-2.7.4/drivers/netxml-ups.c0000644000175000017500000012076212640473702013226 00000000000000/* netxml-ups.c Driver routines for network XML UPS units Copyright (C) 2008-2009 Arjen de Korte 2013 Vaclav Krpec 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 "main.h" #include "netxml-ups.h" #include "mge-xml.h" #include "dstate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "network XML UPS" #define DRIVER_VERSION "0.40" /** *_OBJECT query multi-part body boundary */ #define FORM_POST_BOUNDARY "NUT-NETXML-UPS-OBJECTS" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte " \ "Vaclav Krpec ", DRV_EXPERIMENTAL, { NULL } }; /** *_OBJECT query status */ typedef enum { OBJECT_OK = 0, /**< OK */ OBJECT_PARSE_ERROR, /**< Parse error */ OBJECT_ERROR, /**< Generic error */ } object_query_status_t; /* end of typedef enum */ /** *_OBJECT entry type */ typedef enum { SET_OBJECT_REQUEST, /**< SET_OBJECT request */ SET_OBJECT_RESPONSE, /**< SET_OBJECT response */ } object_query_type_t; /* end of typedef enum */ /** *_OBJECT POST request mode */ typedef enum { RAW_POST, /**< RAW POST mode */ FORM_POST, /**< FORM POST mode */ } object_post_mode_t; /* end of typedef enum */ typedef struct set_object_req set_object_req_t; /**< SET_OBJECT request carrier */ typedef struct set_object_resp set_object_resp_t; /**< SET_OBJECT response carrier */ typedef struct object_entry object_entry_t; /**< *_OBJECT entry carrier */ typedef struct object_query object_query_t; /**< *_OBJECT query handle */ /** SET_OBJECT request carrier */ struct set_object_req { char *name; /**< OBJECT name */ char *value; /**< OBJECT value */ }; /* end of struct set_object_req */ /** SET_OBJECT response carrier */ struct set_object_resp { char *name; /**< OBJECT name */ char *unit; /**< OBJECT unit */ char *access; /**< OBJECT access */ char *value; /**< OBJECT value */ }; /* end of struct set_object_resp */ /** *_OBJECT query entry */ struct object_entry { /** Payload */ union { set_object_req_t req; /**< Request entry */ set_object_resp_t resp; /**< Response entry */ } payld; /* Metadata */ object_entry_t *next; /**< Next entry */ object_entry_t *prev; /**< Previous entry */ }; /* end of struct object_entry */ /** *_OBJECT query handle */ struct object_query { object_query_status_t status; /**< Query status */ object_query_type_t type; /**< List entries type */ object_post_mode_t mode; /**< POST request mode */ size_t cnt; /**< Count of entries */ object_entry_t *head; /**< List head */ object_entry_t *tail; /**< List tail */ }; /* end of struct object_query */ /** * \brief *_OBJECT query constructor * * \param type Query type * \param mode Query mode * * \return *_OBJECT query handle or \c NULL in case of memory error */ static object_query_t *object_query_create( object_query_type_t type, object_post_mode_t mode); /** * \brief Number of *_OBJECT query entries * * \param handle Query handle * * \return NUmber of entries */ static size_t object_query_size(object_query_t *handle); /** * \brief *_OBJECT query destructor * * \param handle Query handle */ static void object_query_destroy(object_query_t *handle); /** * \brief SET_OBJECT: add request query entry * * \param handle Request query handle * \param name OBJECT name * \param value OBJECT value * * \return Query entry or \c NULL in case of memory error */ static object_entry_t *set_object_add( object_query_t *handle, const char *name, const char *value); /** * \brief SET_OBJECT: RAW POST mode implementation * * \brief req SET_OBJECT request * * \return Response to the request */ static object_query_t *set_object_raw(object_query_t *req); /** * \brief SET_OBJECT: FORM POST mode implementation * * \brief req SET_OBJECT request * * \return \c NULL (FORM POST mode resp. is ignored by specification) */ static object_query_t *set_object_form(object_query_t *req); /** * \brief SET_OBJECT: implementation * * \brief req SET_OBJECT request * * \return Response to the request */ static object_query_t *set_object(object_query_t *req); /** * \brief SET_OBJECT: RAW POST mode request serialisation * * \param handle Request query handle * * \return POST request body */ static ne_buffer *set_object_serialise_raw(object_query_t *handle); /** * \brief SET_OBJECT: FORM POST mode request serialisation * * \param handle Request query handle * * \return POST request body */ static ne_buffer *set_object_serialise_form(object_query_t *handle); /* FIXME: * "built with neon library %s" LIBNEON_VERSION * subdrivers (limited to MGE only ATM) */ /* Global vars */ uint32_t ups_status = 0; static int timeout = 5; int shutdown_duration = 120; static int shutdown_timer = 0; static time_t lastheard = 0; static subdriver_t *subdriver = &mge_xml_subdriver; static ne_session *session = NULL; static ne_socket *sock = NULL; static ne_uri uri; /* Support functions */ static void netxml_alarm_set(void); static void netxml_status_set(void); static int netxml_authenticate(void *userdata, const char *realm, int attempt, char *username, char *password); static int netxml_dispatch_request(ne_request *request, ne_xml_parser *parser); static int netxml_get_page(const char *page); static int instcmd(const char *cmdname, const char *extra); static int setvar(const char *varname, const char *val); static int netxml_alarm_subscribe(const char *page); #if HAVE_NE_SET_CONNECT_TIMEOUT && HAVE_NE_SOCK_CONNECT_TIMEOUT /* we don't need to use alarm() */ #else static void netxml_alarm_handler(int sig) { /* don't do anything here, just return */ } #endif void upsdrv_initinfo(void) { char *page, *last = NULL; char buf[SMALLBUF]; snprintf(buf, sizeof(buf), "%s", subdriver->initinfo); for (page = strtok_r(buf, " ", &last); page != NULL; page = strtok_r(NULL, " ", &last)) { if (netxml_get_page(page) != NE_OK) { continue; } dstate_setinfo("driver.version.data", "%s", subdriver->version); if (testvar("subscribe") && (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK)) { extrafd = ne_sock_fd(sock); time(&lastheard); } /* Register r/w variables */ vname_register_rw(); /* Set UPS driver handler callbacks */ upsh.setvar = &setvar; upsh.instcmd = &instcmd; return; } fatalx(EXIT_FAILURE, "%s: communication failure [%s]", __func__, ne_get_error(session)); } void upsdrv_updateinfo(void) { int ret, errors = 0; /* We really should be dealing with alarms through a separate callback, so that we can keep the * processing of alarms and polling for data separated. Currently, this isn't supported by the * driver main body, so we'll have to revert to polling each time we're called, unless the * socket indicates we're no longer connected. */ if (testvar("subscribe")) { char buf[LARGEBUF]; ret = ne_sock_read(sock, buf, sizeof(buf)); if (ret > 0) { /* alarm message received */ ne_xml_parser *parser = ne_xml_create(); upsdebugx(2, "%s: ne_sock_read(%d bytes) => %s", __func__, ret, buf); ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL); ne_xml_parse(parser, buf, strlen(buf)); ne_xml_destroy(parser); time(&lastheard); } else if ((ret == NE_SOCK_TIMEOUT) && (difftime(time(NULL), lastheard) < 180)) { /* timed out */ upsdebugx(2, "%s: ne_sock_read(timeout)", __func__); } else { /* connection closed or unknown error */ upslogx(LOG_ERR, "NSM connection with '%s' lost", uri.host); upsdebugx(2, "%s: ne_sock_read(%d) => %s", __func__, ret, ne_sock_error(sock)); ne_sock_close(sock); if (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK) { extrafd = ne_sock_fd(sock); time(&lastheard); return; } dstate_datastale(); extrafd = -1; return; } } /* get additional data */ ret = netxml_get_page(subdriver->getobject); if (ret != NE_OK) { errors++; } ret = netxml_get_page(subdriver->summary); if (ret != NE_OK) { errors++; } if (errors > 1) { dstate_datastale(); return; } status_init(); alarm_init(); netxml_alarm_set(); alarm_commit(); netxml_status_set(); status_commit(); dstate_dataok(); } void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ /* fatalx(EXIT_FAILURE, "shutdown not supported"); */ /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ int status = STAT_SET_FAILED; /* pessimistic assumption */ object_query_t *resp = NULL; object_query_t *req = NULL; /* Pragmatic do { ... } while (0) loop allowing break to cleanup */ do { /* Create SET_OBJECT request */ req = object_query_create(SET_OBJECT_REQUEST, FORM_POST); if (NULL == req) break; if (NULL == set_object_add(req, "battery.runtime.low", "999999999")) break; /* Send SET_OBJECT request */ resp = set_object(req); #if (0) /* FORM_POST method response is ignored, we can only hope it worked... */ if (NULL == resp) break; /* Check if setting was done */ if (1 > object_query_size(resp)) { status = STAT_SET_UNKNOWN; break; } #endif /* end of code removal */ status = STAT_SET_HANDLED; /* success */ } while (0); /* end of pragmatic loop, break target */ /* Cleanup */ if (NULL != req) object_query_destroy(req); if (NULL != resp) object_query_destroy(resp); if (STAT_SET_HANDLED != status) fatalx(EXIT_FAILURE, "Shutdown failed: %d", status); } static int instcmd(const char *cmdname, const char *extra) { /* if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } */ upslogx(LOG_NOTICE, "%s: unknown command [%s]", __func__, cmdname); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { int status = STAT_SET_FAILED; /* pessimistic assumption */ object_query_t *resp = NULL; object_query_t *req = NULL; /* Pragmatic do { ... } while (0) loop allowing break to cleanup */ do { /* Create SET_OBJECT request */ req = object_query_create(SET_OBJECT_REQUEST, FORM_POST); if (NULL == req) break; if (NULL == set_object_add(req, varname, val)) break; /* Send SET_OBJECT request */ resp = set_object(req); if (NULL == resp) break; /* Check if setting was done */ if (1 > object_query_size(resp)) { status = STAT_SET_UNKNOWN; break; } status = STAT_SET_HANDLED; /* success */ } while (0); /* end of pragmatic loop, break target */ /* Cleanup */ if (NULL != req) object_query_destroy(req); if (NULL != resp) object_query_destroy(resp); return status; } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { char buf[SMALLBUF]; snprintf(buf, sizeof(buf), "network timeout (default: %d seconds)", timeout); addvar(VAR_VALUE, "timeout", buf); addvar(VAR_FLAG, "subscribe", "authenticated subscription on NMC"); addvar(VAR_VALUE | VAR_SENSITIVE, "login", "login value for authenticated mode"); addvar(VAR_VALUE | VAR_SENSITIVE, "password", "password value for authenticated mode"); snprintf(buf, sizeof(buf), "shutdown duration in second (default: %d seconds)", shutdown_duration); addvar(VAR_VALUE, "shutdown_duration", buf); if( shutdown_timer > 0 ) { snprintf(buf, sizeof(buf), "shutdown timer in second (default: %d seconds)", shutdown_timer); } else { snprintf(buf, sizeof(buf), "shutdown timer in second (default: none)"); } addvar(VAR_VALUE, "shutdown_timer", buf); } void upsdrv_initups(void) { int ret; char *val; FILE *fp; #if HAVE_NE_SET_CONNECT_TIMEOUT && HAVE_NE_SOCK_CONNECT_TIMEOUT /* we don't need to use alarm() */ #else struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = netxml_alarm_handler; sigaction(SIGALRM, &sa, NULL); #endif /* allow override of default network timeout value */ val = getval("timeout"); if (val) { timeout = atoi(val); if (timeout < 1) { fatalx(EXIT_FAILURE, "timeout must be greater than 0"); } } val = getval("shutdown_duration"); if (val) { shutdown_duration = atoi(val); if (shutdown_duration < 0) { fatalx(EXIT_FAILURE, "shutdown duration must be greater than or equal to 0"); } } val = getval("shutdown_timer"); if (val) { shutdown_timer = atoi(val); if (shutdown_timer < 0) { fatalx(EXIT_FAILURE, "shutdwon timer must be greater than or equal to 0"); } } if (nut_debug_level > 5) { ne_debug_init(stderr, NE_DBG_HTTP | NE_DBG_HTTPBODY); } if (ne_sock_init()) { fatalx(EXIT_FAILURE, "%s: failed to initialize socket libraries", progname); } if (ne_uri_parse(device_path, &uri) || uri.host == NULL) { fatalx(EXIT_FAILURE, "%s: invalid hostname '%s'", progname, device_path); } /* if (uri.scheme == NULL) { uri.scheme = strdup("http"); } if (uri.host == NULL) { uri.host = strdup(device_path); } */ if (uri.port == 0) { uri.port = ne_uri_defaultport(uri.scheme); } upsdebugx(1, "using %s://%s port %d", uri.scheme, uri.host, uri.port); session = ne_session_create(uri.scheme, uri.host, uri.port); /* timeout if we can't (re)connect to the UPS */ #ifdef HAVE_NE_SET_CONNECT_TIMEOUT ne_set_connect_timeout(session, timeout); #endif /* just wait for a couple of seconds */ ne_set_read_timeout(session, timeout); ne_set_useragent(session, subdriver->version); if (strcasecmp(uri.scheme, "https") == 0) { ne_ssl_trust_default_ca(session); } ne_set_server_auth(session, netxml_authenticate, NULL); /* if debug level is set, direct output to stderr */ if (!nut_debug_level) { fp = fopen("/dev/null", "w"); } else { fp = stderr; } if (!fp) { fatal_with_errno(EXIT_FAILURE, "Connectivity test failed"); } /* see if we have a connection */ ret = ne_get(session, subdriver->initups, fileno(fp)); if (!nut_debug_level) { fclose(fp); } else { fprintf(fp, "\n"); } if (ret != NE_OK) { fatalx(EXIT_FAILURE, "Connectivity test: %s", ne_get_error(session)); } upslogx(LOG_INFO, "Connectivity test: %s", ne_get_error(session)); } void upsdrv_cleanup(void) { free(subdriver->configure); free(subdriver->subscribe); free(subdriver->summary); free(subdriver->getobject); free(subdriver->setobject); if (sock) { ne_sock_close(sock); } if (session) { ne_session_destroy(session); } ne_uri_free(&uri); } /********************************************************************** * Support functions *********************************************************************/ static int netxml_get_page(const char *page) { int ret; ne_request *request; ne_xml_parser *parser; upsdebugx(2, "%s: %s", __func__, page); request = ne_request_create(session, "GET", page); parser = ne_xml_create(); ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL); ret = netxml_dispatch_request(request, parser); if (ret) { upsdebugx(2, "%s: %s", __func__, ne_get_error(session)); } ne_xml_destroy(parser); ne_request_destroy(request); return ret; } static int netxml_alarm_subscribe(const char *page) { int ret, port = -1, secret = -1; char buf[LARGEBUF], *s; ne_request *request; ne_sock_addr *addr; const ne_inet_addr *ai; char resp_buf[LARGEBUF]; /* Clear response buffer */ memset(resp_buf, 0, sizeof(resp_buf)); upsdebugx(2, "%s: %s", __func__, page); sock = ne_sock_create(); if (gethostname(buf, sizeof(buf)) == 0) { dstate_setinfo("driver.hostname", "%s", buf); } else { dstate_setinfo("driver.hostname", ""); } #ifdef HAVE_NE_SOCK_CONNECT_TIMEOUT ne_sock_connect_timeout(sock, timeout); #endif ne_sock_read_timeout(sock, 1); netxml_get_page(subdriver->configure); snprintf(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "%s v%s\n", progname, DRIVER_VERSION); snprintfcat(buf, sizeof(buf), "connected socket\n"); snprintfcat(buf, sizeof(buf), "%s\n", dstate_getinfo("driver.hostname")); snprintfcat(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "%d\n", shutdown_duration); if( shutdown_timer > 0 ) { snprintfcat(buf, sizeof(buf), "%d\r\n", shutdown_timer); } else { snprintfcat(buf, sizeof(buf), "NONE\n"); } snprintfcat(buf, sizeof(buf), "LOCAL\n"); snprintfcat(buf, sizeof(buf), "1\n"); snprintfcat(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "\n"); /* now send subscription message setting all the proper flags */ request = ne_request_create(session, "POST", page); ne_set_request_body_buffer(request, buf, strlen(buf)); /* as the NMC reply is not xml standard compliant let's parse it this way */ do { #ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT alarm(timeout+1); #endif ret = ne_begin_request(request); #ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT alarm(0); #endif if (ret != NE_OK) { break; } ret = ne_read_response_block(request, resp_buf, sizeof(resp_buf)); if (ret == NE_OK) { ret = ne_end_request(request); } } while (ret == NE_RETRY); ne_request_destroy(request); /* due to different formats used by the various NMCs, we need to\ break up the reply in lines and parse each one separately */ for (s = strtok(resp_buf, "\r\n"); s != NULL; s = strtok(NULL, "\r\n")) { upsdebugx(2, "%s: parsing %s", __func__, s); if (!strncasecmp(s, "", 6) && (sscanf(s+6, "%u", &port) != 1)) { return NE_RETRY; } if (!strncasecmp(s, "", 8) && (sscanf(s+8, "%u", &secret) != 1)) { return NE_RETRY; } } if ((port == -1) || (secret == -1)) { upsdebugx(2, "%s: parsing initial subcription failed", __func__); return NE_RETRY; } /* Resolve the given hostname. 'flags' must be zero. Hex * string IPv6 addresses (e.g. `::1') may be enclosed in brackets * (e.g. `[::1]'). */ addr = ne_addr_resolve(uri.host, 0); /* Returns zero if name resolution was successful, non-zero on * error. */ if (ne_addr_result(addr) != 0) { upsdebugx(2, "%s: name resolution failure on %s: %s", __func__, uri.host, ne_addr_error(addr, buf, sizeof(buf))); ne_addr_destroy(addr); return NE_RETRY; } for (ai = ne_addr_first(addr); ai != NULL; ai = ne_addr_next(addr)) { upsdebugx(2, "%s: connecting to host %s port %d", __func__, ne_iaddr_print(ai, buf, sizeof(buf)), port); #ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT alarm(timeout+1); #endif ret = ne_sock_connect(sock, ai, port); #ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT alarm(0); #endif if (ret == NE_OK) { upsdebugx(2, "%s: connection to %s open on fd %d", __func__, uri.host, ne_sock_fd(sock)); break; } } ne_addr_destroy(addr); if (ai == NULL) { upsdebugx(2, "%s: failed to create listening socket", __func__); return NE_RETRY; } snprintf(buf, sizeof(buf), "", secret); ret = ne_sock_fullwrite(sock, buf, strlen(buf) + 1); if (ret != NE_OK) { upsdebugx(2, "%s: send failed: %s", __func__, ne_sock_error(sock)); return NE_RETRY; } ret = ne_sock_read(sock, buf, sizeof(buf)); if (ret < 1) { upsdebugx(2, "%s: read failed: %s", __func__, ne_sock_error(sock)); return NE_RETRY; } if (strcasecmp(buf, "")) { upsdebugx(2, "%s: subscription rejected", __func__); return NE_RETRY; } upslogx(LOG_INFO, "NSM connection to '%s' established", uri.host); return NE_OK; } static int netxml_dispatch_request(ne_request *request, ne_xml_parser *parser) { int ret; /* * Starting with neon-0.27.0 the ne_xml_dispatch_request() function will check * for a valid XML content-type (following RFC 3023 rules) in the header. * Unfortunately, (at least) the Transverse NMC doesn't follow this RFC, so * we can't use this anymore and we'll have to roll our own here. */ do { #ifndef HAVE_NE_SET_CONNECT_TIMEOUT alarm(timeout+1); #endif ret = ne_begin_request(request); #ifndef HAVE_NE_SET_CONNECT_TIMEOUT alarm(0); #endif if (ret != NE_OK) { break; } ret = ne_xml_parse_response(request, parser); if (ret == NE_OK) { ret = ne_end_request(request); } } while (ret == NE_RETRY); return ret; } /* Supply the 'login' and 'password' when authentication is required */ static int netxml_authenticate(void *userdata, const char *realm, int attempt, char *username, char *password) { char *val; upsdebugx(2, "%s: realm = [%s], attempt = %d", __func__, realm, attempt); val = getval("login"); snprintf(username, NE_ABUFSIZ, "%s", val ? val : ""); val = getval("password"); snprintf(password, NE_ABUFSIZ, "%s", val ? val : ""); return attempt; } /* Convert the local status information to NUT format and set NUT alarms. */ static void netxml_alarm_set(void) { if (STATUS_BIT(REPLACEBATT)) { alarm_set("Replace battery!"); } if (STATUS_BIT(SHUTDOWNIMM)) { alarm_set("Shutdown imminent!"); } if (STATUS_BIT(FANFAIL)) { alarm_set("Fan failure!"); } if (STATUS_BIT(NOBATTERY)) { alarm_set("No battery installed!"); } if (STATUS_BIT(BATTVOLTLO)) { alarm_set("Battery voltage too low!"); } if (STATUS_BIT(BATTVOLTHI)) { alarm_set("Battery voltage too high!"); } if (STATUS_BIT(CHARGERFAIL)) { alarm_set("Battery charger fail!"); } if (STATUS_BIT(OVERHEAT)) { alarm_set("Temperature too high!"); } if (STATUS_BIT(COMMFAULT)) { alarm_set("Communication fault!"); } if (STATUS_BIT(INTERNALFAULT)) { alarm_set("Internal UPS fault!"); } if (STATUS_BIT(FUSEFAULT)) { alarm_set("Fuse fault!"); } if (STATUS_BIT(BYPASSAUTO)) { alarm_set("Automatic bypass mode!"); } if (STATUS_BIT(BYPASSMAN)) { alarm_set("Manual bypass mode!"); } } /* Convert the local status information to NUT format and set NUT status. */ static void netxml_status_set(void) { if (STATUS_BIT(VRANGE)) { dstate_setinfo("input.transfer.reason", "input voltage out of range"); } else if (STATUS_BIT(FRANGE)) { dstate_setinfo("input.transfer.reason", "input frequency out of range"); } else { dstate_delinfo("input.transfer.reason"); } if (STATUS_BIT(ONLINE)) { status_set("OL"); /* on line */ } else { status_set("OB"); /* on battery */ } if (STATUS_BIT(DISCHRG)) { status_set("DISCHRG"); /* discharging */ } if (STATUS_BIT(CHRG)) { status_set("CHRG"); /* charging */ } if (STATUS_BIT(LOWBATT)) { status_set("LB"); /* low battery */ } if (STATUS_BIT(OVERLOAD)) { status_set("OVER"); /* overload */ } if (STATUS_BIT(REPLACEBATT)) { status_set("RB"); /* replace batt */ } if (STATUS_BIT(TRIM)) { status_set("TRIM"); /* SmartTrim */ } if (STATUS_BIT(BOOST)) { status_set("BOOST"); /* SmartBoost */ } if (STATUS_BIT(BYPASSAUTO) || STATUS_BIT(BYPASSMAN)) { status_set("BYPASS"); /* on bypass */ } if (STATUS_BIT(OFF)) { status_set("OFF"); /* ups is off */ } if (STATUS_BIT(SHUTDOWNIMM)) { status_set("FSD"); /* shutdown imminent */ } } /* * *_OBJECT interface implementation */ static object_query_t *object_query_create( object_query_type_t type, object_post_mode_t mode) { object_query_t *handle = (object_query_t *)calloc(1, sizeof(object_query_t)); if (NULL == handle) return NULL; handle->type = type; handle->mode = mode; return handle; } static size_t object_query_size(object_query_t *handle) { assert(NULL != handle); return handle->cnt; } /** * \brief SET_OBJECT request list entry destructor * * \param req SET_OBJECT request list entry */ static void set_object_req_destroy(set_object_req_t *req) { assert(NULL != req); if (NULL != req->name) free(req->name); if (NULL != req->value) free(req->value); } /** * \brief SET_OBJECT response list entry destructor * * \param req SET_OBJECT response list entry */ static void set_object_resp_destroy(set_object_resp_t *resp) { assert(NULL != resp); if (NULL != resp->name) free(resp->name); if (NULL != resp->unit) free(resp->unit); if (NULL != resp->access) free(resp->access); if (NULL != resp->value) free(resp->value); } /** * \brief *_OBJECT query entry destructor * * \param handle SET_OBJECT query handle * \param entry SET_OBJECT query entry */ static void object_entry_destroy(object_query_t *handle, object_entry_t *entry) { assert(NULL != handle); assert(NULL != entry); /* Sanity checks */ assert(0 < handle->cnt); /* Relink list */ if (entry == handle->head) { handle->head = entry->next; } else { assert(NULL != entry->prev); entry->prev->next = entry->next; } if (entry == handle->tail) { handle->tail = entry->prev; } else { assert(NULL != entry->next); entry->next->prev = entry->prev; } --handle->cnt; /* Destroy payload */ switch (handle->type) { case SET_OBJECT_REQUEST: set_object_req_destroy(&entry->payld.req); break; case SET_OBJECT_RESPONSE: set_object_resp_destroy(&entry->payld.resp); break; } /* Destroy entry */ free(entry); } static void object_query_destroy(object_query_t *handle) { assert(NULL != handle); /* Destroy entries */ while (handle->cnt) object_entry_destroy(handle, handle->head); /* Destroy handle */ free(handle); } /** * \brief Add *_OBJECT list entry (at list end) * * \param handle Entry list handle * \param entry Entry */ static void object_add_entry(object_query_t *handle, object_entry_t *entry) { assert(NULL != handle); assert(NULL != entry); /* Sanity checks */ assert(SET_OBJECT_REQUEST == handle->type); /* Add entry at end of bi-directional list */ if (handle->cnt) { assert(NULL != handle->tail); assert(NULL == handle->tail->next); handle->tail->next = entry; entry->prev = handle->tail; } /* Add the very first entry */ else { handle->head = entry; entry->prev = NULL; } handle->tail = entry; entry->next = NULL; ++handle->cnt; } static object_entry_t *set_object_add( object_query_t *handle, const char *name, const char *value) { char *name_cpy; char *value_cpy; assert(NULL != name); assert(NULL != value); object_entry_t *entry = (object_entry_t *)calloc(1, sizeof(object_entry_t)); if (NULL == entry) return NULL; /* Copy payload data */ name_cpy = strdup(name); value_cpy = strdup(value); /* Cleanup in case of memory error */ if (NULL == name_cpy || NULL == value_cpy) { if (NULL != name_cpy) free(name_cpy); if (NULL != value_cpy) free(value_cpy); free(entry); return NULL; } /* Set payload */ entry->payld.req.name = name_cpy; entry->payld.req.value = value_cpy; /* Enlist */ object_add_entry(handle, entry); return entry; } /** * \brief Common SET_OBJECT entries serialiser * * \param buff Buffer * \param entry SET_OBJECT request entry * * \retval OBJECT_OK on success * \retval OBJECT_ERROR otherwise */ static object_query_status_t set_object_serialise_entries(ne_buffer *buff, object_entry_t *entry) { object_query_status_t status = OBJECT_OK; assert(NULL != buff); ne_buffer_zappend(buff, "\r\n"); ne_buffer_zappend(buff, "\r\n"); for (; NULL != entry; entry = entry->next) { const char *vname = vname_nut2mge_xml(entry->payld.req.name); /* Serialise one object */ if (NULL != vname) { ne_buffer_zappend(buff, " "); ne_buffer_zappend(buff, entry->payld.req.value); ne_buffer_zappend(buff, "\r\n"); } /* Var. name resolution error */ else status = OBJECT_ERROR; } ne_buffer_zappend(buff, "\r\n"); return status; } static ne_buffer *set_object_serialise_raw(object_query_t *handle) { assert(NULL != handle); /* Sanity checks */ assert(SET_OBJECT_REQUEST == handle->type); /* Create buffer */ ne_buffer *buff = ne_buffer_create(); /* neon API ref. states that the function always succeeds */ assert(NULL != buff); /* Serialise all entries */ set_object_serialise_entries(buff, handle->head); return buff; } static ne_buffer *set_object_serialise_form(object_query_t *handle) { const char *vname = NULL; assert(NULL != handle); /* Sanity checks */ assert(SET_OBJECT_REQUEST == handle->type); /* Create buffer */ ne_buffer *buff = ne_buffer_create(); /* neon API ref. states that the function always succeeds */ assert(NULL != buff); /* Simple request */ if (1 == object_query_size(handle)) { assert(NULL != handle->head); /* TODO: Simple req. doesn't seem to work vname = vname_nut2mge_xml(handle->head->payld.req.name); */ } if (NULL != vname) { assert(NULL != handle->head); ne_buffer_zappend(buff, "objectName="); ne_buffer_zappend(buff, vname); ne_buffer_zappend(buff, "&objectValue="); ne_buffer_zappend(buff, handle->head->payld.req.value); } /* Multi set request (or empty request) */ else { /* Add request prologue */ ne_buffer_zappend(buff, "--" FORM_POST_BOUNDARY "\r\n"); ne_buffer_zappend(buff, "Content-Disposition: form-data; name=\"file\"; " "filename=\"Configuration.xml\"\r\n"); ne_buffer_zappend(buff, "Content-Type: application/octet-stream\r\n"); ne_buffer_zappend(buff, "\r\n"); /* Serialise all entries */ set_object_serialise_entries(buff, handle->head); /* Add request epilogue */ ne_buffer_zappend(buff, "--" FORM_POST_BOUNDARY "--\r\n"); } return buff; } /** * \brief neon callback for SET_OBJECT RAW POST mode response element start * * \param userdata Obfuscated SET_OBJECT_RESPONSE query handle * \param parent Element parent * \param nspace Element namespace (empty) * \param name Element name * \param attrs Element attributes * * \return \c NE_XML_STATEROOT + distance of the element from root */ static int set_object_raw_resp_start_element( void *userdata, int parent, const char *nspace, const char *name, const char **attrs) { object_query_t *handle = (object_query_t *)userdata; assert(NULL != handle); /* Sanity checks */ assert(SET_OBJECT_RESPONSE == handle->type); /* Check that namespace is empty */ if (NULL != nspace && '\0' != *nspace) { handle->status = OBJECT_PARSE_ERROR; return NE_XML_STATEROOT; } /* OBJECT (as a SET_OBJECT child) */ if (NE_XML_STATEROOT + 1 == parent && 0 == strcasecmp(name, "OBJECT")) { size_t i; object_entry_t *entry = (object_entry_t *)calloc(1, sizeof(object_entry_t)); /* Memory error */ if (NULL == entry) { handle->status = OBJECT_ERROR; return NE_XML_STATEROOT; } /* Set attributes */ for (i = 0; NULL != attrs[i] && NULL != attrs[i + 1]; i += 2) { char **attr = NULL; const char *aval = NULL; /* Skip unset attribute name and/or value (useless) */ if (NULL == attrs[i] || NULL == attrs[i + 1]) continue; /* Obviously, the following holds, now */ assert(NULL != attrs[i]); assert(NULL != attrs[i + 1]); /* name */ if (0 == strcasecmp(attrs[i], "name")) { attr = &entry->payld.resp.name; aval = vname_mge_xml2nut(attrs[i + 1]); } /* unit */ else if (0 == strcasecmp(attrs[i], "unit")) { attr = &entry->payld.resp.unit; aval = attrs[i + 1]; } /* access */ else if (0 == strcasecmp(attrs[i], "access")) { attr = &entry->payld.resp.access; aval = attrs[i + 1]; } /* Set known attribute */ if (NULL != attr) { /* Copy value */ if (NULL != aval) { *attr = strdup(aval); if (NULL == *attr) handle->status = OBJECT_ERROR; } /* Value resolution error */ else handle->status = OBJECT_ERROR; } } object_add_entry(handle, entry); return NE_XML_STATEROOT + 2; /* signal to cdata callback */ } /* SET_OBJECT (as the root child) */ if (NE_XML_STATEROOT == parent && 0 == strcasecmp(name, "SET_OBJECT")) return NE_XML_STATEROOT + 1; /* Unknown element (as a SET_OBJECT child) */ if (NE_XML_STATEROOT + 1 == parent) return NE_XML_STATEROOT + 1; /* Ignore any other root children */ return NE_XML_STATEROOT; } /** * \brief neon callback for SET_OBJECT RAW POST mode response data start * * The callback is used to set OBJECT element value. * This is done for state \c NE_XML_STATEROOT + 2 * (see \ref set_object_raw_resp_start_element). * * \param userdata Obfuscated SET_OBJECT_RESPONSE query handle * \param state Element distance from root * \param cdata Character data * \param len Character data length * * \return state */ static int set_object_raw_resp_cdata( void *userdata, int state, const char *cdata, size_t len) { object_query_t *handle = (object_query_t *)userdata; assert(NULL != handle); /* Sanity checks */ assert(SET_OBJECT_RESPONSE == handle->type); /* Ignore any element except OBJECT */ if (NE_XML_STATEROOT + 2 != state) return state; if (OBJECT_OK == handle->status) { char *value; /* Set last object value */ assert(NULL != handle->tail); assert(NULL != handle->tail->payld.resp.name); value = vvalue_mge_xml2nut(handle->tail->payld.resp.name, cdata, len); handle->tail->payld.resp.value = value; if (NULL == handle->tail->payld.resp.value) handle->status = OBJECT_ERROR; } return state; } /** * \brief neon callback for SET_OBJECT RAW POST mode response element start * * \param userdata Obfuscated SET_OBJECT_RESPONSE query handle * \param state Element distance from root * \param nspace Element namespace (empty) * \param name Element name * * \return \c NE_XML_STATEROOT + distance of the element from root */ static int set_object_raw_resp_end_element( void *userdata, int state, const char *nspace, const char *name) { /* OBJECT (as a SET_OBJECT child) */ if (NE_XML_STATEROOT + 2 == state) { assert(0 == strcasecmp(name, "OBJECT")); return NE_XML_STATEROOT + 1; } /* * Otherwise, state is either NE_XML_STATEROOT or NE_XML_STATEROOT + 1 * In any case, we return NE_XML_STATEROOT */ return NE_XML_STATEROOT; } static object_query_t *set_object_deserialise_raw(ne_buffer *buff) { int ne_status; assert(NULL != buff); /* Create SET_OBJECT query response */ object_query_t *handle = object_query_create(SET_OBJECT_RESPONSE, RAW_POST); if (NULL == handle) return NULL; /* Create XML parser */ ne_xml_parser *parser = ne_xml_create(); /* neon API ref. states that the function always succeeds */ assert(NULL != parser); /* Set element & data handlers */ ne_xml_push_handler( parser, set_object_raw_resp_start_element, set_object_raw_resp_cdata, set_object_raw_resp_end_element, handle); /* Parse the response */ ne_status = ne_xml_parse(parser, buff->data, buff->used); if (NE_OK != ne_status) handle->status = OBJECT_PARSE_ERROR; /* Destroy parser */ ne_xml_destroy(parser); return handle; } /** * \brief Send HTTP request over a session * * The function creates HTTP request, sends it and reads-out the response. * * \param[in] session HTTP session * \param[in] method Request method * \param[in] uri Request URI * \param[in] ct Request content type (optional, \c NULL accepted) * \param[in] req_body Request body (optional, \c NULL is accepted) * \param[out] resp_body Response body (optional, \c NULL is accepted) * * \return HTTP status code if response was sent, 0 on send error */ static int send_http_request( ne_session *session, const char *method, const char *uri, const char *ct, ne_buffer *req_body, ne_buffer *resp_body) { int resp_code = 0; ne_request *req = NULL; /* Create request */ req = ne_request_create(session, method, uri); /* Neon claims that request creation is always successful */ assert(NULL != req); do { /* Pragmatic do ... while (0) loop allowing breaks on error */ const ne_status *req_st; /* Set Content-Type */ if (NULL != ct) ne_add_request_header(req, "Content-Type", ct); /* Set request body */ if (NULL != req_body) /* BEWARE: The terminating '\0' byte is "used", too */ ne_set_request_body_buffer(req, req_body->data, req_body->used - 1); /* Send request */ int status = ne_begin_request(req); if (NE_OK != status) { break; } /* Read response */ assert(NE_OK == status); for (;;) { char buff[512]; ssize_t read; read = ne_read_response_block(req, buff, sizeof(buff)); /* Read failure */ if (0 > read) { status = NE_ERROR; break; } if (0 == read) break; if (NULL != resp_body) ne_buffer_append(resp_body, buff, read); } if (NE_OK != status) { break; } /* Request served */ ne_end_request(req); /* Get response code */ req_st = ne_get_status(req); assert(NULL != req_st); resp_code = req_st->code; } while (0); /* end of do ... while (0) pragmatic loop */ if (NULL != req) ne_request_destroy(req); return resp_code; } static object_query_t *set_object_raw(object_query_t *req) { int resp_code; object_query_t *resp = NULL; ne_buffer *req_body = NULL; ne_buffer *resp_body = NULL; assert(NULL != req); /* Sanity check */ assert(SET_OBJECT_REQUEST == req->type); assert(RAW_POST == req->mode); /* Serialise request POST data */ req_body = set_object_serialise_raw(req); /* Send request */ resp_body = ne_buffer_create(); assert(NULL != resp_body); resp_code = send_http_request(session, "POST", "/set_obj.htm", NULL, req_body, resp_body); /* * Repeat in case of 401 - Unauthorised * * Note that this is a WA of NMC sending Connection: close * header in the 401 response, in which case neon closes * connection (quite rightfully). */ if (401 == resp_code) resp_code = send_http_request(session, "POST", "/set_obj.htm", NULL, req_body, resp_body); /* Deserialise response */ if (200 == resp_code) resp = set_object_deserialise_raw(resp_body); /* Cleanup */ if (NULL != req_body) ne_buffer_destroy(req_body); if (NULL != resp_body) ne_buffer_destroy(resp_body); return resp; } static object_query_t *set_object_form(object_query_t *req) { int resp_code; ne_buffer *req_body = NULL; const char *ct = "multipart/form-data; boundary=" FORM_POST_BOUNDARY; /* TODO: Single request doesn't seem to work if (1 == object_query_size(req)) ct = "application/x-form-urlencoded"; */ assert(NULL != req); /* Sanity check */ assert(SET_OBJECT_REQUEST == req->type); assert(FORM_POST == req->mode); /* Serialise request POST data */ req_body = set_object_serialise_form(req); /* Send request (response is ignored by the proto. spec v3) */ resp_code = send_http_request(session, "POST", "/Forms/set_obj_2", ct, req_body, NULL); /* * Repeat in case of 401 - Unauthorised * * Note that this is a WA of NMC sending Connection: close * header in the 401 response, in which case neon closes * connection (quite rightfully). */ if (401 == resp_code) { resp_code = send_http_request(session, "POST", "/Forms/set_obj_2", ct, req_body, NULL); } /* Cleanup */ if (NULL != req_body) ne_buffer_destroy(req_body); return NULL; } static object_query_t *set_object(object_query_t *req) { object_query_t *resp = NULL; assert(NULL != req); /* Sanity checks */ assert(SET_OBJECT_REQUEST == req->type); /* Select implementation by POST request mode */ switch (req->mode) { case RAW_POST: resp = set_object_raw(req); break; case FORM_POST: resp = set_object_form(req); break; } return resp; } nut-2.7.4/drivers/powerman-pdu.c0000644000175000017500000001607212640473702013526 00000000000000/* powerman-pdu.c - Powerman PDU client driver * * Copyright (C) 2008 Arnaud Quette * * 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 "main.h" #include #define DRIVER_NAME "Powerman PDU client driver" #define DRIVER_VERSION "0.11" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette ", DRV_EXPERIMENTAL, { NULL } }; /* Powerman functions and variables */ static pm_err_t query_one(pm_handle_t pm, char *s, int mode); static pm_err_t query_all(pm_handle_t pm, int mode); pm_handle_t pm; char ebuf[64]; /* modes to snmp_ups_walk. */ #define WALKMODE_INIT 0 #define WALKMODE_UPDATE 1 static int reconnect_ups(void); static int instcmd(const char *cmdname, const char *extra) { pm_err_t rv = -1; char *cmdsuffix = NULL; char *cmdindex = NULL; char outletname[SMALLBUF]; upsdebugx(1, "entering instcmd (%s)", cmdname); /* only consider the end of the command */ if ( (cmdsuffix = strrchr(cmdname, '.')) == NULL ) return STAT_INSTCMD_UNKNOWN; else cmdsuffix++; /* get the outlet name */ if ( (cmdindex = strchr(cmdname, '.')) == NULL ) return STAT_INSTCMD_UNKNOWN; else { char buf[32]; cmdindex++; snprintf(buf, sizeof(buf), "outlet.%i.desc", atoi(cmdindex)); snprintf(outletname, sizeof(outletname), "%s", dstate_getinfo(buf)); } /* Power on the outlet */ if (!strcasecmp(cmdsuffix, "on")) { rv = pm_node_on(pm, outletname); return (rv==PM_ESUCCESS)?STAT_INSTCMD_HANDLED:STAT_SET_INVALID; } /* Power off the outlet */ if (!strcasecmp(cmdsuffix, "off")) { rv = pm_node_off(pm, outletname); return (rv==PM_ESUCCESS)?STAT_INSTCMD_HANDLED:STAT_SET_INVALID; } /* Cycle the outlet */ if (!strcasecmp(cmdsuffix, "cycle")) { rv = pm_node_cycle(pm, outletname); return (rv==PM_ESUCCESS)?STAT_INSTCMD_HANDLED:STAT_SET_INVALID; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_updateinfo(void) { pm_err_t rv = PM_ESUCCESS; if ( (rv = query_all(pm, WALKMODE_UPDATE)) != PM_ESUCCESS) { upslogx(2, "Error: %s (%i)\n", pm_strerror(rv, ebuf, sizeof(ebuf)), errno); /* FIXME: try to reconnect? * dstate_datastale(); */ reconnect_ups(); } } void upsdrv_initinfo(void) { pm_err_t rv = PM_ESUCCESS; /* try to detect the PDU here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ /* FIXME: can we report something useful? */ dstate_setinfo("ups.mfr", "Powerman"); dstate_setinfo("device.mfr", "Powerman"); dstate_setinfo("ups.model", "unknown PDU"); dstate_setinfo("device.model", "unknown PDU"); dstate_setinfo("device.type", "pdu"); /* Now walk the data tree */ if ( (rv = query_all(pm, WALKMODE_INIT)) != PM_ESUCCESS) { upslogx(2, "Error: %s\n", pm_strerror(rv, ebuf, sizeof(ebuf))); /* FIXME: try to reconnect? * dstate_datastale(); */ reconnect_ups(); } upsh.instcmd = instcmd; /* FIXME: no need for setvar (ex for outlet.n.delay.*)!? */ } void upsdrv_shutdown(void) { /* FIXME: shutdown all outlets? */ fatalx(EXIT_FAILURE, "shutdown not supported"); /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ } /* static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "outlet.n.delay.*")) { ... return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } */ void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* FIXME: anything useful to be put here? */ } void upsdrv_initups(void) { pm_err_t rv = PM_ESUCCESS; /* Connect to the PowerMan daemon */ if ((rv = pm_connect(device_path, NULL, &pm, 0)) != PM_ESUCCESS) { fatalx(EXIT_FAILURE, "Can't connect to %s: %s\n", device_path, pm_strerror(rv, ebuf, sizeof(ebuf))); } /* FIXME: suitable? * poll_interval = 30; */ } void upsdrv_cleanup(void) { pm_disconnect(pm); } static int reconnect_ups(void) { pm_err_t rv; upsdebugx(4, "==================================================="); upsdebugx(4, "= connection lost with Powerman, try to reconnect ="); upsdebugx(4, "==================================================="); /* clear the situation */ pm_disconnect(pm); /* Connect to the PowerMan daemon */ if ((rv = pm_connect(device_path, NULL, &pm, 0)) != PM_ESUCCESS) return 0; else { upsdebugx(4, "connection restored with Powerman"); return 1; } } /* * powerman support functions ****************************/ static pm_err_t query_one(pm_handle_t pm, char *s, int outletnum) { pm_err_t rv; pm_node_state_t ns; char outlet_prop[64]; upsdebugx(1, "entering query_one (%s)", s); rv = pm_node_status(pm, s, &ns); if (rv == PM_ESUCCESS) { upsdebugx(3, "updating status"); snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.status", outletnum); dstate_setinfo(outlet_prop, "%s", ns == PM_ON ? "on" : ns == PM_OFF ? "off" : "unknown"); dstate_dataok(); } return rv; } static pm_err_t query_all(pm_handle_t pm, int mode) { pm_err_t rv; pm_node_iterator_t itr; char outlet_prop[64]; char *s; int outletnum = 1; upsdebugx(1, "entering query_all ()"); rv = pm_node_iterator_create(pm, &itr); if (rv != PM_ESUCCESS) return rv; while ((s = pm_node_next(itr))) { /* in WALKMODE_UPDATE, we always call this one for the * status update... */ if ((rv = query_one(pm, s, outletnum)) != PM_ESUCCESS) break; else { /* set the initial generic properties (ie except status) * but only if the status query succeeded */ if (mode == WALKMODE_INIT) { snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.id", outletnum); dstate_setinfo(outlet_prop, "%i", outletnum); snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.desc", outletnum); dstate_setinfo(outlet_prop, "%s", s); /* we assume it's always true! */ snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.switchable", outletnum); dstate_setinfo(outlet_prop, "yes"); /* add instant commands */ snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.load.on", outletnum); dstate_addcmd(outlet_prop); snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.load.off", outletnum); dstate_addcmd(outlet_prop); snprintf(outlet_prop, sizeof(outlet_prop), "outlet.%i.load.cycle", outletnum); dstate_addcmd(outlet_prop); } } outletnum++; } pm_node_iterator_destroy(itr); return rv; } nut-2.7.4/drivers/nutdrv_qx.c0000644000175000017500000022207712667537407013162 00000000000000/* nutdrv_qx.c - Driver for USB and serial UPS units with Q* protocols * * Copyright (C) * 2013 Daniele Pezzini * Based on: * usbhid-ups.c - Copyright (C) * 2003-2012 Arnaud Quette * 2005 John Stamp * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte * blazer.c - Copyright (C) * 2008-2009 Arjen de Korte * 2012 Arnaud Quette * blazer_ser.c - Copyright (C) * 2008 Arjen de Korte * blazer_usb.c - Copyright (C) * 2003-2009 Arjen de Korte * 2011-2012 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define DRIVER_VERSION "0.28" #include "main.h" #include /* note: QX_USB/QX_SERIAL set through Makefile */ #ifdef QX_USB #include "libusb.h" #include "usb-common.h" #ifdef QX_SERIAL #define DRIVER_NAME "Generic Q* USB/Serial driver" #else #define DRIVER_NAME "Generic Q* USB driver" #endif /* QX_SERIAL */ #else #define DRIVER_NAME "Generic Q* Serial driver" #endif /* QX_USB */ #ifdef QX_SERIAL #include "serial.h" #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ #endif /* QX_SERIAL */ #include "nutdrv_qx.h" /* == Subdrivers == */ /* Include all known subdrivers */ #include "nutdrv_qx_bestups.h" #include "nutdrv_qx_mecer.h" #include "nutdrv_qx_megatec.h" #include "nutdrv_qx_megatec-old.h" #include "nutdrv_qx_mustek.h" #include "nutdrv_qx_q1.h" #include "nutdrv_qx_voltronic.h" #include "nutdrv_qx_voltronic-qs.h" #include "nutdrv_qx_voltronic-qs-hex.h" #include "nutdrv_qx_zinto.h" /* Master list of available subdrivers */ static subdriver_t *subdriver_list[] = { &voltronic_subdriver, &voltronic_qs_subdriver, &voltronic_qs_hex_subdriver, &mustek_subdriver, &megatec_old_subdriver, &bestups_subdriver, &mecer_subdriver, &megatec_subdriver, &zinto_subdriver, /* Fallback Q1 subdriver */ &q1_subdriver, NULL }; /* == Driver description structure == */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Daniele Pezzini " \ "Arnaud Quette " \ "John Stamp " \ "Peter Selinger " \ "Arjen de Korte ", DRV_BETA, #ifdef QX_USB { &comm_upsdrv_info, NULL } #else { NULL } #endif /* QX_USB */ }; /* == Data walk modes == */ typedef enum { QX_WALKMODE_INIT = 0, QX_WALKMODE_QUICK_UPDATE, QX_WALKMODE_FULL_UPDATE } walkmode_t; /* == Global vars == */ /* Pointer to the active subdriver object (changed in subdriver_matcher() function) */ static subdriver_t *subdriver = NULL; static int pollfreq = DEFAULT_POLLFREQ; static int ups_status = 0; static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */ static time_t lastpoll; /* Timestamp the last polling */ #if defined(QX_USB) && defined(QX_SERIAL) static int is_usb = 0; /* Whether the device is connected through USB (1) or serial (0) */ #endif /* QX_USB && QX_SERIAL */ static struct { char command[SMALLBUF]; /* Command sent to the UPS to get answer/to execute an instant command */ char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime */ } previous_item = { "", "" }; /* Hold the values of the item processed just before the actual one */ /* == Support functions == */ static int subdriver_matcher(void); static int qx_command(const char *cmd, char *buf, size_t buflen); static int qx_process_answer(item_t *item, const int len); static bool_t qx_ups_walk(walkmode_t mode); static void ups_status_set(void); static void ups_alarm_set(void); static void qx_set_var(item_t *item); /* == Struct & data for status processing == */ typedef struct { const char *status_str; /* UPS status string */ const int status_mask; /* UPS status mask */ } status_lkp_t; static status_lkp_t status_info[] = { /* Map status strings to bit masks */ { "OL", STATUS(OL) }, { "LB", STATUS(LB) }, { "RB", STATUS(RB) }, { "CHRG", STATUS(CHRG) }, { "DISCHRG", STATUS(DISCHRG) }, { "BYPASS", STATUS(BYPASS) }, { "CAL", STATUS(CAL) }, { "OFF", STATUS(OFF) }, { "OVER", STATUS(OVER) }, { "TRIM", STATUS(TRIM) }, { "BOOST", STATUS(BOOST) }, { "FSD", STATUS(FSD) }, { NULL, 0 }, }; /* == battery.{charge,runtime} guesstimation == */ /* Support functions */ static int qx_battery(void); static int qx_load(void); static void qx_initbattery(void); /* Battery data */ static struct { double packs; /* Battery voltage multiplier */ struct { double act; /* Actual runtime on battery */ double nom; /* Nominal runtime on battery (full load) */ double est; /* Estimated runtime remaining (full load) */ double exp; /* Load exponent */ } runt; struct { double act; /* Actual battery voltage */ double high; /* Battery float voltage */ double nom; /* Nominal battery voltage */ double low; /* Battery low voltage */ } volt; struct { double act; /* Actual battery charge */ long time; /* Recharge time from empty to full */ } chrg; } batt = { 1, { -1, -1, 0, 0 }, { -1, -1, -1, -1 }, { -1, 43200 } }; /* Load data */ static struct { double act; /* Actual load (reported by the UPS) */ double low; /* Idle load */ double eff; /* Effective load */ } load = { 0, 0.1, 1 }; static time_t battery_lastpoll = 0; /* Fill batt.volt.act and guesstimate the battery charge if it isn't already available. */ static int qx_battery(void) { const char *val = dstate_getinfo("battery.voltage"); if (!val) { upsdebugx(2, "%s: unable to get battery.voltage", __func__); return -1; } batt.volt.act = batt.packs * strtod(val, NULL); if (batt.chrg.act == -1 && batt.volt.low > 0 && batt.volt.high > batt.volt.low) { batt.chrg.act = 100 * (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low); if (batt.chrg.act < 0) { batt.chrg.act = 0; } if (batt.chrg.act > 100) { batt.chrg.act = 100; } dstate_setinfo("battery.charge", "%.0f", batt.chrg.act); } return 0; } /* Load for battery.{charge,runtime} from runtimecal */ static int qx_load(void) { const char *val = dstate_getinfo("ups.load"); if (!val) { upsdebugx(2, "%s: unable to get ups.load", __func__); return -1; } load.act = strtod(val, NULL); load.eff = pow(load.act / 100, batt.runt.exp); if (load.eff < load.low) { load.eff = load.low; } return 0; } /* Guesstimation: init */ static void qx_initbattery(void) { if (!dstate_getinfo("battery.charge") || !dstate_getinfo("battery.runtime")) { const char *val; val = dstate_getinfo("battery.voltage.high"); if (val) { batt.volt.high = strtod(val, NULL); } val = dstate_getinfo("battery.voltage.low"); if (val) { batt.volt.low = strtod(val, NULL); } val = dstate_getinfo("battery.voltage.nominal"); if (val) { batt.volt.nom = strtod(val, NULL); } /* If no values are available for both battery.voltage.{low,high} either from the UPS or provided by the user in ups.conf, try to guesstimate them, but announce it! */ if (batt.volt.nom != -1 && (batt.volt.low == -1 || batt.volt.high == -1)) { upslogx(LOG_INFO, "No values for battery high/low voltages"); /* Basic formula, which should cover most cases */ batt.volt.low = 104 * batt.volt.nom / 120; batt.volt.high = 130 * batt.volt.nom / 120; /* Publish these data too */ dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high); } val = dstate_getinfo("battery.packs"); if (val && (strspn(val, "0123456789 .") == strlen(val))) { batt.packs = strtod(val, NULL); } else { /* qx_battery -> batt.volt.act */ if (!qx_battery() && batt.volt.nom != -1) { const double packs[] = { 120, 100, 80, 60, 48, 36, 30, 24, 18, 12, 8, 6, 4, 3, 2, 1, 0.5, -1 }; int i; /* The battery voltage will quickly return to at least the nominal value after discharging them. * For overlapping battery.voltage.low/high ranges therefor choose the one with the highest multiplier. */ for (i = 0; packs[i] > 0; i++) { if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) { continue; } if (packs[i] * batt.volt.act < 0.8 * batt.volt.nom) { upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act); break; } batt.packs = packs[i]; break; } } else { upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act); } } /* Update batt.{chrg,volt}.act */ qx_battery(); val = getval("runtimecal"); if (val) { double rh, lh, rl, ll; time(&battery_lastpoll); if (sscanf(val, "%lf,%lf,%lf,%lf", &rh, &lh, &rl, &ll) < 4) { fatalx(EXIT_FAILURE, "Insufficient parameters for runtimecal"); } if ((rl < rh) || (rh <= 0)) { fatalx(EXIT_FAILURE, "Parameter out of range (runtime)"); } if ((lh > 100) || (ll > lh) || (ll <= 0)) { fatalx(EXIT_FAILURE, "Parameter out of range (load)"); } batt.runt.exp = log(rl / rh) / log(lh / ll); upsdebugx(2, "%s: battery runtime exponent: %.3f", __func__, batt.runt.exp); batt.runt.nom = rh * pow(lh / 100, batt.runt.exp); upsdebugx(2, "%s: battery runtime nominal: %.1f", __func__, batt.runt.nom); } else { upslogx(LOG_INFO, "Battery runtime will not be calculated (runtimecal not set)"); return; } val = dstate_getinfo("battery.charge"); if (!val && batt.volt.nom != -1) { batt.volt.low = batt.volt.nom; batt.volt.high = 1.15 * batt.volt.nom; if (qx_battery()) fatalx(EXIT_FAILURE, "Initial battery charge undetermined"); val = dstate_getinfo("battery.charge"); } if (val) { batt.runt.est = batt.runt.nom * strtod(val, NULL) / 100; upsdebugx(2, "%s: battery runtime estimate: %.1f", __func__, batt.runt.est); } else { fatalx(EXIT_FAILURE, "Initial battery charge undetermined"); } val = getval("chargetime"); if (val) { batt.chrg.time = strtol(val, NULL, 10); if (batt.chrg.time <= 0) { fatalx(EXIT_FAILURE, "Charge time out of range [1..s]"); } upsdebugx(2, "%s: battery charge time: %ld", __func__, batt.chrg.time); } else { upslogx(LOG_INFO, "No charge time specified, using built in default [%ld seconds]", batt.chrg.time); } val = getval("idleload"); if (val) { load.low = strtod(val, NULL) / 100; if ((load.low <= 0) || (load.low > 1)) { fatalx(EXIT_FAILURE, "Idle load out of range [0..100]"); } upsdebugx(2, "%s: minimum load used (idle): %.3f", __func__, load.low); } else { upslogx(LOG_INFO, "No idle load specified, using built in default [%.1f %%]", 100 * load.low); } } } /* == USB communication subdrivers == */ #if defined(QX_USB) && !defined(TESTING) static usb_communication_subdriver_t *usb = &usb_subdriver; static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static USBDeviceMatcher_t *reopen_matcher = NULL; static USBDeviceMatcher_t *regex_matcher = NULL; static int langid_fix = -1; static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = NULL; /* Cypress communication subdriver */ static int cypress_command(const char *cmd, char *buf, size_t buflen) { char tmp[SMALLBUF]; int ret; size_t i; /* Send command */ memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), "%s", cmd); for (i = 0; i < strlen(tmp); i += ret) { /* Write data in 8-byte chunks */ /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 5000); if (ret <= 0) { upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } } upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); /* Read reply */ memset(buf, 0, buflen); for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += ret) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000); /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i); upsdebug_hex(5, tmp, &buf[i], ret); } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return i; } /* SGS communication subdriver */ static int sgs_command(const char *cmd, char *buf, size_t buflen) { char tmp[SMALLBUF]; int ret; size_t cmdlen, i; /* Send command */ cmdlen = strlen(cmd); for (i = 0; i < cmdlen; i += ret) { memset(tmp, 0, sizeof(tmp)); ret = (cmdlen - i) < 7 ? (cmdlen - i) : 7; tmp[0] = ret; memcpy(&tmp[1], &cmd[i], ret); /* Write data in 8-byte chunks */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x09, 0x200, 0, tmp, 8, 5000); if (ret <= 0) { upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } ret--; } upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); /* Read reply */ memset(buf, 0, buflen); for (i = 0; i <= buflen - 8; i += ret) { memset(tmp, 0, sizeof(tmp)); /* Read data in 8-byte chunks */ ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000); /* No error!!! */ if (ret == -110) break; /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } /* Every call to read returns 8 bytes * -> actually returned bytes: */ ret = tmp[0] <= 7 ? tmp[0] : 7; if (ret > 0) memcpy(&buf[i], &tmp[1], ret); snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i); upsdebug_hex(5, tmp, &buf[i], ret); } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return i; } /* Phoenix communication subdriver */ static int phoenix_command(const char *cmd, char *buf, size_t buflen) { char tmp[SMALLBUF]; int ret; size_t i; for (i = 0; i < 8; i++) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 1000); */ ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000); /* This USB to serial implementation is crappy. * In order to read correct replies we need to flush the output buffers of the converter until we get no more data (ie, it times out). */ switch (ret) { case -EPIPE: /* Broken pipe */ usb_clear_halt(udev, 0x81); case -ETIMEDOUT: /* Connection timed out */ break; } if (ret < 0) { upsdebugx(3, "flush: %s (%d)", usb_strerror(), ret); break; } upsdebug_hex(4, "dump", tmp, ret); } /* Send command */ memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), "%s", cmd); for (i = 0; i < strlen(tmp); i += ret) { /* Write data in 8-byte chunks */ /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 1000); if (ret <= 0) { upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } } upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); /* Read reply */ memset(buf, 0, buflen); for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += ret) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000); /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i); upsdebug_hex(5, tmp, &buf[i], ret); } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return i; } /* Ippon communication subdriver */ static int ippon_command(const char *cmd, char *buf, size_t buflen) { char tmp[64]; int ret; size_t i, len; /* Send command */ snprintf(tmp, sizeof(tmp), "%s", cmd); for (i = 0; i < strlen(tmp); i += ret) { /* Write data in 8-byte chunks */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x2, 0, &tmp[i], 8, 1000); if (ret <= 0) { upsdebugx(3, "send: %s (%d)", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out", ret); return ret; } } upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); /* Read all 64 bytes of the reply in one large chunk */ ret = usb_interrupt_read(udev, 0x81, tmp, sizeof(tmp), 1000); /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s (%d)", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out", ret); return ret; } /* As Ippon will always return 64 bytes in response, we have to calculate and return length of actual response data here. * Empty response will look like 0x00 0x0D, otherwise it will be data string terminated by 0x0D. */ for (i = 0, len = 0; i < (size_t)ret; i++) { if (tmp[i] != '\r') continue; len = ++i; break; } /* Just in case there wasn't any '\r', fallback to string length, if any */ if (!len) len = strlen(tmp); upsdebug_hex(5, "read", tmp, (int)len); upsdebugx(3, "read: %.*s", (int)strcspn(tmp, "\r"), tmp); len = len < buflen ? len : buflen - 1; memset(buf, 0, buflen); memcpy(buf, tmp, len); return (int)len; } /* Krauler communication subdriver */ static int krauler_command(const char *cmd, char *buf, size_t buflen) { /* Still not implemented: * 0x6 T (don't know how to pass the parameter) * 0x68 and 0x69 both cause shutdown after an undefined interval */ const struct { const char *str; /* Megatec command */ const int index; /* Krauler string index for this command */ const char prefix; /* Character to replace the first byte in reply */ } command[] = { { "Q1\r", 0x03, '(' }, { "F\r", 0x0d, '#' }, { "I\r", 0x0c, '#' }, { "T\r", 0x04, '\r' }, { "TL\r", 0x05, '\r' }, { "Q\r", 0x07, '\r' }, { "C\r", 0x0b, '\r' }, { "CT\r", 0x0b, '\r' }, { NULL } }; int i; upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); for (i = 0; command[i].str; i++) { int retry; if (strcmp(cmd, command[i].str)) { continue; } for (retry = 0; retry < 10; retry++) { int ret; if (langid_fix != -1) { /* Apply langid_fix value */ ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen); } else { ret = usb_get_string_simple(udev, command[i].index, buf, buflen); } if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } /* This may serve in the future */ upsdebugx(1, "received %d (%d)", ret, buf[0]); if (langid_fix != -1) { /* Limit this check, at least for now */ /* Invalid receive size - message corrupted */ if (ret != buf[0]) { upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); continue; } /* Simple unicode -> ASCII inplace conversion * FIXME: this code is at least shared with mge-shut/libshut * Create a common function? */ unsigned int di, si, size = buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) break; if (buf[si + 1]) /* high byte */ buf[di++] = '?'; else buf[di++] = buf[si]; } buf[di] = 0; ret = di; } /* "UPS No Ack" has a special meaning */ if ( strcspn(buf, "\r") == 10 && !strncasecmp(buf, "UPS No Ack", 10) ) { upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); continue; } /* Replace the first byte of what we received with the correct one */ buf[0] = command[i].prefix; upsdebug_hex(5, "read", buf, ret); upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return ret; } return 0; } /* Echo the unknown command back */ upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); return snprintf(buf, buflen, "%s", cmd); } /* Fabula communication subdriver */ static int fabula_command(const char *cmd, char *buf, size_t buflen) { const struct { const char *str; /* Megatec command */ const int index; /* Fabula string index for this command */ } commands[] = { { "Q1\r", 0x03, }, /* Status */ { "F\r", 0x0d, }, /* Ratings */ { "I\r", 0x0c, }, /* Vendor infos */ { "Q\r", 0x07, }, /* Beeper toggle */ { "C\r", 0x0a, }, /* Cancel shutdown/Load on [0x(0..F)A]*/ { NULL } }; int i, ret, index = 0; upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); for (i = 0; commands[i].str; i++) { if (strcmp(cmd, commands[i].str)) continue; index = commands[i].index; break; } if (!index) { int val2 = -1; double val1 = -1; /* Shutdowns */ if ( sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 || sscanf(cmd, "S%lf\r", &val1) == 1 ) { double delay; /* 0x(1+)0 -> shutdown.stayoff (SnR0000) * 0x(1+)8 -> shutdown.return (Sn[Rm], m != 0) [delay before restart is always 10 seconds] * +0x10 (16dec) = next megatec delay (min .5 = hex 0x1*; max 10 = hex 0xF*) -> n < 1 ? -> n += .1; n >= 1 ? -> n += 1 */ /* delay: [.5..10] (-> seconds: [30..600]) */ delay = val1 < .5 ? .5 : val1 > 10 ? 10 : val1; if (delay < 1) index = 16 + round((delay - .5) * 10) * 16; else index = 96 + (delay - 1) * 16; /* shutdown.return (Sn[Rm], m != 0) */ if (val2) index += 8; /* Unknown commands */ } else { /* Echo the unknown command back */ upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); return snprintf(buf, buflen, "%s", cmd); } } upsdebugx(4, "command index: 0x%02x", index); /* Send command/Read reply */ ret = usb_get_string_simple(udev, index, buf, buflen); if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } upsdebug_hex(5, "read", buf, ret); upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); /* The UPS always replies "UPS No Ack" when a supported command is issued (either if it fails or if it succeeds).. */ if ( strcspn(buf, "\r") == 10 && !strncasecmp(buf, "UPS No Ack", 10) ) { /* ..because of that, always return 0 (with buf empty, as if it was a timeout): queries will see it as a failure, instant commands ('megatec' protocol) as a success */ memset(buf, 0, buflen); return 0; } return ret; } /* Fuji communication subdriver */ static int fuji_command(const char *cmd, char *buf, size_t buflen) { unsigned char tmp[8]; char command[SMALLBUF] = "", read[SMALLBUF] = ""; int ret, answer_len, val2; double val1; size_t i; const struct { const char *command; /* Megatec command */ const int answer_len; /* Expected length of the answer to the ongoing query */ } query[] = { { "Q1", 47 }, { "F", 22 }, { "I", 39 }, { NULL } }; /* * Queries (b1..b8) sent (as a 8-bytes interrupt) to the UPS adopt the following scheme: * * b1: 0x80 * b2: 0x06 * b3: * b4: 0x03 * b5..bn: * bn+1..b7: [] * b8: * * Where: * Length (in Hex) of the command (without the trailing CR) + 1 * Command/query (without the trailing CR) * [] 0x00 padding to the 7th byte * Expected length (in Hex) of the answer to the ongoing query (0 when no reply is expected, i.e. commands) * * Replies to queries (commands are followed by action without any reply) are sent from the UPS (in 8-byte chunks) with 0x00 padding after the trailing CR to full 8 bytes. * */ /* Send command */ /* Remove the CR */ snprintf(command, sizeof(command), "%.*s", (int)strcspn(cmd, "\r"), cmd); /* Length of the command that will be sent to the UPS can be at most: 8 - 5 (0x80, 0x06, , 0x03, ) = 3. * As a consequence also 'SnRm' commands (shutdown.{return,stayoff} and load.off) are not supported. * So, map all the 'SnRm' shutdown.returns (m != 0) as the corresponding 'Sn' commands, meanwhile ignoring ups.delay.start and making the UPS turn on the load as soon as power is back. */ if (sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 && val2) { upsdebugx(4, "%s: trimming '%s' to '%.*s'", __func__, command, 3, command); command[3] = 0; } /* Too long command */ if (strlen(command) > 3) { /* Be 'megatec-y': echo the unsupported command back */ upsdebugx(3, "%s: unsupported command %s", __func__, command); return snprintf(buf, buflen, "%s", cmd); } /* Expected length of the answer to the ongoing query (0 when no reply is expected, i.e. commands) */ answer_len = 0; for (i = 0; query[i].command; i++) { if (strcmp(command, query[i].command)) continue; answer_len = query[i].answer_len; break; } memset(tmp, 0, sizeof(tmp)); /* 0x80 */ tmp[0] = 0x80; /* 0x06 */ tmp[1] = 0x06; /* */ tmp[2] = strlen(command) + 1; /* 0x03 */ tmp[3] = 0x03; /* */ memcpy(&tmp[4], command, strlen(command)); /* */ tmp[7] = answer_len; upsdebug_hex(4, "command", (char *)tmp, 8); /* Write data */ ret = usb_interrupt_write(udev, USB_ENDPOINT_OUT | 2, (char *)tmp, 8, USB_TIMEOUT); if (ret <= 0) { upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } upsdebugx(3, "send: %s", command); /* Read reply */ memset(buf, 0, buflen); for (i = 0; (i <= buflen - 8) && (memchr(buf, '\r', buflen) == NULL); i += ret) { /* Read data in 8-byte chunks */ ret = usb_interrupt_read(udev, USB_ENDPOINT_IN | 1, &buf[i], 8, 1000); /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret); return ret; } snprintf(read, sizeof(read), "read [%3d]", (int)i); upsdebug_hex(5, read, &buf[i], ret); } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); /* As Fuji units return the reply in 8-byte chunks always padded to the 8th byte with 0x00, we need to calculate and return the length of the actual response here. */ return (int)strlen(buf); } static void *cypress_subdriver(USBDevice_t *device) { subdriver_command = &cypress_command; return NULL; } static void *sgs_subdriver(USBDevice_t *device) { subdriver_command = &sgs_command; return NULL; } static void *ippon_subdriver(USBDevice_t *device) { subdriver_command = &ippon_command; return NULL; } static void *krauler_subdriver(USBDevice_t *device) { subdriver_command = &krauler_command; return NULL; } static void *phoenix_subdriver(USBDevice_t *device) { subdriver_command = &phoenix_command; return NULL; } static void *fabula_subdriver(USBDevice_t *device) { subdriver_command = &fabula_command; return NULL; } static void *fuji_subdriver(USBDevice_t *device) { subdriver_command = &fuji_command; return NULL; } /* USB device match structure */ typedef struct { const int vendorID; /* USB device's VendorID */ const int productID; /* USB device's ProductID */ const char *vendor; /* USB device's iManufacturer string */ const char *product; /* USB device's iProduct string */ void *(*fun)(USBDevice_t *); /* Handler for specific processing */ } qx_usb_device_id_t; /* USB VendorID/ProductID/iManufacturer/iProduct match - note: rightmost comment is used for naming rules by tools/nut-usbinfo.pl */ static qx_usb_device_id_t qx_usb_id[] = { { USB_DEVICE(0x05b8, 0x0000), NULL, NULL, &cypress_subdriver }, /* Agiler UPS */ { USB_DEVICE(0xffff, 0x0000), NULL, NULL, &krauler_subdriver }, /* Ablerex 625L USB */ { USB_DEVICE(0x0665, 0x5161), NULL, NULL, &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */ { USB_DEVICE(0x06da, 0x0002), NULL, NULL, &cypress_subdriver }, /* Online Yunto YQ450 */ { USB_DEVICE(0x06da, 0x0003), NULL, NULL, &ippon_subdriver }, /* Mustek Powermust */ { USB_DEVICE(0x06da, 0x0004), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */ { USB_DEVICE(0x06da, 0x0005), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova RT */ { USB_DEVICE(0x06da, 0x0201), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova T */ { USB_DEVICE(0x06da, 0x0601), NULL, NULL, &phoenix_subdriver }, /* Online Zinto A */ { USB_DEVICE(0x0f03, 0x0001), NULL, NULL, &cypress_subdriver }, /* Unitek Alpha 1200Sx */ { USB_DEVICE(0x14f0, 0x00c9), NULL, NULL, &phoenix_subdriver }, /* GE EP series */ { USB_DEVICE(0x0483, 0x0035), NULL, NULL, &sgs_subdriver }, /* TS Shara UPSes */ { USB_DEVICE(0x0001, 0x0000), "MEC", "MEC0003", &fabula_subdriver }, /* Fideltronik/MEC LUPUS 500 USB */ { USB_DEVICE(0x0001, 0x0000), "ATCL FOR UPS", "ATCL FOR UPS", &fuji_subdriver }, /* Fuji UPSes */ { USB_DEVICE(0x0001, 0x0000), NULL, NULL, &krauler_subdriver }, /* Krauler UP-M500VA */ /* End of list */ { -1, -1, NULL, NULL, NULL } }; static int qx_is_usb_device_supported(qx_usb_device_id_t *usb_device_id_list, USBDevice_t *device) { int retval = NOT_SUPPORTED; qx_usb_device_id_t *usbdev; for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) { if (usbdev->vendorID != device->VendorID) continue; /* Flag as possibly supported if we see a known vendor */ retval = POSSIBLY_SUPPORTED; if (usbdev->productID != device->ProductID) continue; if (usbdev->vendor && (!device->Vendor || strcasecmp(usbdev->vendor, device->Vendor))) continue; if (usbdev->product && (!device->Product || strcasecmp(usbdev->product, device->Product))) continue; /* Call the specific handler, if it exists */ if (usbdev->fun != NULL) (*usbdev->fun)(device); return SUPPORTED; } return retval; } static int device_match_func(USBDevice_t *hd, void *privdata) { if (subdriver_command) { return 1; } switch (qx_is_usb_device_supported(qx_usb_id, hd)) { case SUPPORTED: return 1; case POSSIBLY_SUPPORTED: case NOT_SUPPORTED: default: return 0; } } static USBDeviceMatcher_t device_matcher = { &device_match_func, NULL, NULL }; #endif /* QX_USB && !TESTING */ /* == Driver functions implementations == */ /* See header file for details. */ int instcmd(const char *cmdname, const char *extradata) { item_t *item; char value[SMALLBUF]; if (!strcasecmp(cmdname, "beeper.off")) { /* Compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.disable'"); return instcmd("beeper.disable", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* Compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } upslogx(LOG_INFO, "%s(%s, %s)", __func__, cmdname, extradata ? extradata : "[NULL]"); /* Retrieve item by command name */ item = find_nut_info(cmdname, QX_FLAG_CMD, QX_FLAG_SKIP); /* Check for fallback if not found */ if (item == NULL) { if (!strcasecmp(cmdname, "load.on")) { return instcmd("load.on.delay", "0"); } if (!strcasecmp(cmdname, "load.off")) { return instcmd("load.off.delay", "0"); } if (!strcasecmp(cmdname, "shutdown.return")) { int ret; /* Ensure "ups.start.auto" is set to "yes", if supported */ if (dstate_getinfo("ups.start.auto")) { if (setvar("ups.start.auto", "yes") != STAT_SET_HANDLED) { upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_INSTCMD_FAILED; } } ret = instcmd("load.on.delay", dstate_getinfo("ups.delay.start")); if (ret != STAT_INSTCMD_HANDLED) { return ret; } return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } if (!strcasecmp(cmdname, "shutdown.stayoff")) { int ret; /* Ensure "ups.start.auto" is set to "no", if supported */ if (dstate_getinfo("ups.start.auto")) { if (setvar("ups.start.auto", "no") != STAT_SET_HANDLED) { upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_INSTCMD_FAILED; } } ret = instcmd("load.on.delay", "-1"); if (ret != STAT_INSTCMD_HANDLED) { return ret; } return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } upsdebugx(2, "%s: command %s unavailable", __func__, cmdname); return STAT_INSTCMD_INVALID; } /* If extradata is empty, use the default value from the QX to NUT table, if any */ extradata = extradata ? extradata : item->dfl; snprintf(value, sizeof(value), "%s", extradata ? extradata : ""); /* Preprocess command */ if (item->preprocess != NULL && item->preprocess(item, value, sizeof(value))) { /* Something went wrong */ upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_INSTCMD_FAILED; } /* No preprocess function -> nothing to do with extradata */ if (item->preprocess == NULL) snprintf(value, sizeof(value), "%s", ""); /* Send the command, get the reply */ if (qx_process(item, strlen(value) > 0 ? value : NULL)) { /* Something went wrong */ upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_INSTCMD_FAILED; } /* We got a reply from the UPS: either subdriver->accepted (-> command handled) or the command itself echoed back (-> command failed) */ if (strlen(item->value) > 0) { if (subdriver->accepted != NULL && !strcasecmp(item->value, subdriver->accepted)) { upslogx(LOG_INFO, "%s: SUCCEED", __func__); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_INSTCMD_HANDLED; } upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_INSTCMD_FAILED; } /* No reply from the UPS -> command handled */ upslogx(LOG_INFO, "%s: SUCCEED", __func__); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_INSTCMD_HANDLED; } /* See header file for details. */ int setvar(const char *varname, const char *val) { item_t *item; char value[SMALLBUF]; st_tree_t *root = (st_tree_t *)dstate_getroot(); int ok = 0; /* Retrieve variable */ item = find_nut_info(varname, QX_FLAG_SETVAR, QX_FLAG_SKIP); if (item == NULL) { upsdebugx(2, "%s: element %s unavailable", __func__, varname); return STAT_SET_UNKNOWN; } /* No NUT variable is available for this item, so we're handling a one-time setvar from ups.conf */ if (item->qxflags & QX_FLAG_NONUT) { const char *userval; /* Nothing to do */ if (!testvar(item->info_type)) { upsdebugx(2, "%s: nothing to do.. [%s]", __func__, item->info_type); return STAT_SET_HANDLED; } userval = getval(item->info_type); upslogx(LOG_INFO, "%s(%s, %s)", __func__, varname, userval ? userval : "[NULL]"); snprintf(value, sizeof(value), "%s", userval ? userval : ""); /* This item is available in NUT */ } else { upslogx(LOG_INFO, "%s(%s, %s)", __func__, varname, strlen(val) ? val : "[NULL]"); if (!strlen(val)) { upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } snprintf(value, sizeof(value), "%s", val); /* Nothing to do */ if (!strcasecmp(dstate_getinfo(item->info_type), value)) { upslogx(LOG_INFO, "%s: nothing to do.. [%s]", __func__, item->info_type); return STAT_SET_HANDLED; } } /* Check if given value is in the range of accepted values (range) */ if (item->qxflags & QX_FLAG_RANGE) { int valuetoset, min, max; if (strspn(value, "0123456789 .") != strlen(value)) { upslogx(LOG_ERR, "%s: non numerical value [%s: %s]", __func__, item->info_type, value); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } valuetoset = strtol(value, NULL, 10); /* No NUT var is available for this item, so take its range from qx2nut table */ if (item->qxflags & QX_FLAG_NONUT) { info_rw_t *rvalue; if (!strlen(value)) { upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } min = max = -1; /* Loop on all existing values */ for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) { if (rvalue->preprocess && rvalue->preprocess(rvalue->value, sizeof(rvalue->value))) continue; if (min < 0) { min = strtol(rvalue->value, NULL, 10); continue; } max = strtol(rvalue->value, NULL, 10); /* valuetoset is in the range */ if (min <= valuetoset && valuetoset <= max) { ok = 1; break; } min = -1; max = -1; } /* We have a NUT var for this item, so check given value against the already set range */ } else { const range_t *range = state_getrangelist(root, item->info_type); /* Unable to find tree node for var */ if (!range) { upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type); return STAT_SET_UNKNOWN; } while (range) { min = range->min; max = range->max; /* valuetoset is in the range */ if (min <= valuetoset && valuetoset <= max) { ok = 1; break; } range = range->next; } } if (!ok) { upslogx(LOG_ERR, "%s: value out of range [%s: %s]", __func__, item->info_type, value); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } /* Check if given value is in the range of accepted values (enum) */ } else if (item->qxflags & QX_FLAG_ENUM) { /* No NUT var is available for this item, so take its range from qx2nut table */ if (item->qxflags & QX_FLAG_NONUT) { info_rw_t *envalue; if (!strlen(value)) { upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } /* Loop on all existing values */ for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) { if (envalue->preprocess && envalue->preprocess(envalue->value, sizeof(envalue->value))) continue; if (strcasecmp(envalue->value, value)) continue; /* value found */ ok = 1; break; } /* We have a NUT var for this item, so check given value against the already set range */ } else { const enum_t *enumlist = state_getenumlist(root, item->info_type); /* Unable to find tree node for var */ if (!enumlist) { upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type); return STAT_SET_UNKNOWN; } while (enumlist) { /* If this is not the right value, go on to the next */ if (strcasecmp(enumlist->val, value)) { enumlist = enumlist->next; continue; } /* value found in enumlist */ ok = 1; break; } } if (!ok) { upslogx(LOG_ERR, "%s: value out of range [%s: %s]", __func__, item->info_type, value); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } /* Check if given value is not too long (string) */ } else if (item->info_flags & ST_FLAG_STRING) { const int aux = state_getaux(root, item->info_type); /* Unable to find tree node for var */ if (aux < 0) { upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type); return STAT_SET_UNKNOWN; } if (aux < (int)strlen(value)) { upslogx(LOG_ERR, "%s: value is too long [%s: %s]", __func__, item->info_type, value); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } } /* Preprocess value: from NUT-compliant to UPS-compliant */ if (item->preprocess != NULL && item->preprocess(item, value, sizeof(value))) { /* Something went wrong */ upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } /* Handle server side variable */ if (item->qxflags & QX_FLAG_ABSENT) { upsdebugx(2, "%s: setting server side variable %s", __func__, item->info_type); dstate_setinfo(item->info_type, "%s", value); upslogx(LOG_INFO, "%s: SUCCEED", __func__); return STAT_SET_HANDLED; } /* No preprocess function -> nothing to do with val */ if (item->preprocess == NULL) snprintf(value, sizeof(value), "%s", ""); /* Actual variable setting */ if (qx_process(item, strlen(value) > 0 ? value : NULL)) { /* Something went wrong */ upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } /* We got a reply from the UPS: either subdriver->accepted (-> command handled) or the command itself echoed back (-> command failed) */ if (strlen(item->value) > 0) { if (subdriver->accepted != NULL && !strcasecmp(item->value, subdriver->accepted)) { upslogx(LOG_INFO, "%s: SUCCEED", __func__); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_SET_HANDLED; } upslogx(LOG_ERR, "%s: FAILED", __func__); return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ } /* No reply from the UPS -> command handled */ upslogx(LOG_INFO, "%s: SUCCEED", __func__); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_SET_HANDLED; } /* Try to shutdown the UPS */ void upsdrv_shutdown(void) { int retry; item_t *item; const char *val; upsdebugx(1, "%s...", __func__); /* Get user-defined delays */ /* Start delay */ item = find_nut_info("ups.delay.start", 0, QX_FLAG_SKIP); /* Don't know what happened */ if (!item) fatalx(EXIT_FAILURE, "Unable to set start delay"); /* Set the default value */ dstate_setinfo(item->info_type, "%s", item->dfl); /* Set var flags/range/enum */ qx_set_var(item); /* Retrieve user defined delay settings */ val = getval(QX_VAR_ONDELAY); if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val); } /* Shutdown delay */ item = find_nut_info("ups.delay.shutdown", 0, QX_FLAG_SKIP); /* Don't know what happened */ if (!item) fatalx(EXIT_FAILURE, "Unable to set shutdown delay"); /* Set the default value */ dstate_setinfo(item->info_type, "%s", item->dfl); /* Set var flags/range/enum */ qx_set_var(item); /* Retrieve user defined delay settings */ val = getval(QX_VAR_OFFDELAY); if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val); } /* Stop pending shutdowns */ if (find_nut_info("shutdown.stop", QX_FLAG_CMD, QX_FLAG_SKIP)) { for (retry = 1; retry <= MAXTRIES; retry++) { if (instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } break; } if (retry > MAXTRIES) { upslogx(LOG_NOTICE, "No shutdown pending"); } } /* Shutdown */ for (retry = 1; retry <= MAXTRIES; retry++) { if (testvar("stayoff")) { if (instcmd("shutdown.stayoff", NULL) != STAT_INSTCMD_HANDLED) { continue; } } else { if (instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } } fatalx(EXIT_SUCCESS, "Shutting down in %s seconds", dstate_getinfo("ups.delay.shutdown")); } fatalx(EXIT_FAILURE, "Shutdown failed!"); } void upsdrv_help(void) { printf("Read The Fine Manual ('man 8 nutdrv_qx')\n"); } /* Adding flags/vars */ void upsdrv_makevartable(void) { char temp[SMALLBUF]; int i; upsdebugx(1, "%s...", __func__); snprintf(temp, sizeof(temp), "Set shutdown delay, in seconds (default=%s)", DEFAULT_OFFDELAY); addvar(VAR_VALUE, QX_VAR_OFFDELAY, temp); snprintf(temp, sizeof(temp), "Set startup delay, in seconds (default=%s)", DEFAULT_ONDELAY); addvar(VAR_VALUE, QX_VAR_ONDELAY, temp); addvar(VAR_FLAG, "stayoff", "If invoked the UPS won't return after a shutdown when FSD arises"); snprintf(temp, sizeof(temp), "Set polling frequency, in seconds, to reduce data flow (default=%d)", DEFAULT_POLLFREQ); addvar(VAR_VALUE, QX_VAR_POLLFREQ, temp); addvar(VAR_VALUE, "protocol", "Preselect communication protocol (skip autodetection)"); /* battery.{charge,runtime} guesstimation */ addvar(VAR_VALUE, "runtimecal", "Parameters used for runtime calculation"); addvar(VAR_VALUE, "chargetime", "Nominal charge time for UPS battery"); addvar(VAR_VALUE, "idleload", "Minimum load to be used for runtime calculation"); #ifdef QX_USB addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection"); /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)"); #endif /* QX_USB */ #ifdef QX_SERIAL addvar(VAR_VALUE, "cablepower", "Set cable power for serial interface"); #endif /* QX_SERIAL */ /* Subdrivers flags/vars */ for (i = 0; subdriver_list[i] != NULL; i++) { if (subdriver_list[i]->makevartable != NULL) subdriver_list[i]->makevartable(); } } /* Update UPS status/infos */ void upsdrv_updateinfo(void) { time_t now; static int retry = 0; upsdebugx(1, "%s...", __func__); time(&now); /* Clear status buffer before beginning */ status_init(); /* Do a full update (polling) every pollfreq or upon data change (i.e. setvar/instcmd) */ if ((now > (lastpoll + pollfreq)) || (data_has_changed == TRUE)) { upsdebugx(1, "Full update..."); /* Clear ups_status */ ups_status = 0; alarm_init(); if (qx_ups_walk(QX_WALKMODE_FULL_UPDATE) == FALSE) { if (retry < MAXTRIES || retry == MAXTRIES) { upsdebugx(1, "Communications with the UPS lost: status read failed!"); retry++; } else { dstate_datastale(); } return; } lastpoll = now; data_has_changed = FALSE; ups_alarm_set(); alarm_commit(); } else { upsdebugx(1, "Quick update..."); /* Quick poll data only to see if the UPS is still connected */ if (qx_ups_walk(QX_WALKMODE_QUICK_UPDATE) == FALSE) { if (retry < MAXTRIES || retry == MAXTRIES) { upsdebugx(1, "Communications with the UPS lost: status read failed!"); retry++; } else { dstate_datastale(); } return; } } ups_status_set(); status_commit(); if (retry > MAXTRIES) { upslogx(LOG_NOTICE, "Communications with the UPS re-established"); } retry = 0; dstate_dataok(); } /* Initialise data from UPS */ void upsdrv_initinfo(void) { char *val; upsdebugx(1, "%s...", __func__); dstate_setinfo("driver.version.data", "%s", subdriver->name); /* Initialise data */ if (qx_ups_walk(QX_WALKMODE_INIT) == FALSE) { fatalx(EXIT_FAILURE, "Can't initialise data from the UPS"); } /* Init battery guesstimation */ qx_initbattery(); if (dstate_getinfo("ups.delay.start")) { /* Retrieve user defined delay settings */ val = getval(QX_VAR_ONDELAY); if (val && setvar("ups.delay.start", val) != STAT_SET_HANDLED) { fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val); } } if (dstate_getinfo("ups.delay.shutdown")) { /* Retrieve user defined delay settings */ val = getval(QX_VAR_OFFDELAY); if (val && setvar("ups.delay.shutdown", val) != STAT_SET_HANDLED) { fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val); } } if (!find_nut_info("load.off", QX_FLAG_CMD, QX_FLAG_SKIP) && find_nut_info("load.off.delay", QX_FLAG_CMD, QX_FLAG_SKIP)) { /* Adds default with a delay value of '0' (= immediate) */ dstate_addcmd("load.off"); } if (!find_nut_info("load.on", QX_FLAG_CMD, QX_FLAG_SKIP) && find_nut_info("load.on.delay", QX_FLAG_CMD, QX_FLAG_SKIP)) { /* Adds default with a delay value of '0' (= immediate) */ dstate_addcmd("load.on"); } /* Init polling frequency */ val = getval(QX_VAR_POLLFREQ); if (val) pollfreq = strtol(val, NULL, 10); dstate_setinfo("driver.parameter.pollfreq", "%d", pollfreq); time(&lastpoll); /* Install handlers */ upsh.setvar = setvar; upsh.instcmd = instcmd; /* Subdriver initinfo */ if (subdriver->initinfo != NULL) subdriver->initinfo(); } /* Open the port and the like and choose the subdriver */ void upsdrv_initups(void) { upsdebugx(1, "%s...", __func__); #if defined(QX_SERIAL) && defined(QX_USB) /* Whether the device is connected through USB or serial */ if ( !strcasecmp(dstate_getinfo("driver.parameter.port"), "auto") || getval("subdriver") || getval("vendorid") || getval("productid") || getval("vendor") || getval("product") || getval("serial") || getval("bus") || getval("langid_fix") ) { /* USB */ is_usb = 1; } else { /* Serial */ is_usb = 0; } #endif /* QX_SERIAL && QX_USB */ /* Serial */ #ifdef QX_SERIAL #ifdef QX_USB if (!is_usb) { #endif /* QX_USB */ #ifndef TESTING const struct { const char *val; const int dtr; const int rts; } cablepower[] = { { "normal", 1, 0 }, /* Default */ { "reverse", 0, 1 }, { "both", 1, 1 }, { "none", 0, 0 }, { NULL } }; int i; const char *val; struct termios tio; /* Open and lock the serial port and set the speed to 2400 baud. */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = '\r'; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } val = getval("cablepower"); for (i = 0; val && cablepower[i].val; i++) { if (!strcasecmp(val, cablepower[i].val)) { break; } } if (!cablepower[i].val) { fatalx(EXIT_FAILURE, "Value '%s' not valid for 'cablepower'", val); } ser_set_dtr(upsfd, cablepower[i].dtr); ser_set_rts(upsfd, cablepower[i].rts); /* Allow some time to settle for the cablepower */ usleep(100000); #endif /* TESTING */ #ifdef QX_USB } else { /* is_usb */ #endif /* QX_USB */ #endif /* QX_SERIAL */ /* USB */ #ifdef QX_USB #ifndef TESTING const struct { const char *name; int (*command)(const char *cmd, char *buf, size_t buflen); } usbsubdriver[] = { { "cypress", &cypress_command }, { "phoenix", &phoenix_command }, { "ippon", &ippon_command }, { "krauler", &krauler_command }, { "fabula", &fabula_command }, { "fuji", &fuji_command }, { "sgs", &sgs_command }, { NULL } }; int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ char *regex_array[6]; char *subdrv = getval("subdriver"); regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); /* Check for language ID workaround (#1) */ if (getval("langid_fix")) { /* Skip "0x" prefix and set back to hexadecimal */ if (sscanf(getval("langid_fix") + 2, "%x", &langid_fix) != 1) { upslogx(LOG_NOTICE, "Error enabling language ID workaround"); } else { upsdebugx(2, "Language ID workaround enabled (using '0x%x')", langid_fix); } } /* Pick up the subdriver name if set explicitly */ if (subdrv) { int i; if (!regex_array[0] || !regex_array[1]) { fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory."); } for (i = 0; usbsubdriver[i].name; i++) { if (strcasecmp(subdrv, usbsubdriver[i].name)) { continue; } subdriver_command = usbsubdriver[i].command; break; } if (!subdriver_command) { fatalx(EXIT_FAILURE, "Subdriver '%s' not found!", subdrv); } } ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch (ret) { case -1: fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); case 0: break; /* All is well */ default: fatalx(EXIT_FAILURE, "Invalid regular expression: %s", regex_array[ret]); } /* Link the matchers */ regex_matcher->next = &device_matcher; ret = usb->open(&udev, &usbdevice, regex_matcher, NULL); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. Please check your device availability with 'lsusb'\n" "and make sure you have an up-to-date version of NUT. If this does not help,\n" "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n" "options specified. Please refer to the man page for details about these options\n" "(man 8 nutdrv_qx).\n"); } if (!subdriver_command) { fatalx(EXIT_FAILURE, "No subdriver selected"); } /* Create a new matcher for later reopening */ ret = USBNewExactMatcher(&reopen_matcher, &usbdevice); if (ret) { fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); } /* Link the matchers */ reopen_matcher->next = regex_matcher; dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); /* Check for language ID workaround (#2) */ if (langid_fix != -1) { /* Future improvement: * Asking for the zero'th index is special - it returns a string descriptor that contains all the language IDs supported by the device. * Typically there aren't many - often only one. * The language IDs are 16 bit numbers, and they start at the third byte in the descriptor. * See USB 2.0 specification, section 9.6.7, for more information on this. * This should allow automatic application of the workaround */ ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf)); if (ret >= 4) { langid = tbuf[2] | (tbuf[3] << 8); upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid); } } #endif /* TESTING */ #ifdef QX_SERIAL } /* is_usb */ #endif /* QX_SERIAL */ #endif /* QX_USB */ /* Choose subdriver */ if (!subdriver_matcher()) fatalx(EXIT_FAILURE, "Device not supported!"); /* Subdriver initups */ if (subdriver->initups != NULL) subdriver->initups(); } /* Close the ports and the like */ void upsdrv_cleanup(void) { upsdebugx(1, "%s...", __func__); #ifndef TESTING #ifdef QX_SERIAL #ifdef QX_USB if (!is_usb) { #endif /* QX_USB */ ser_set_dtr(upsfd, 0); ser_close(upsfd, device_path); #ifdef QX_USB } else { /* is_usb */ #endif /* QX_USB */ #endif /* QX_SERIAL */ #ifdef QX_USB usb->close(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(usbdevice.Vendor); free(usbdevice.Product); free(usbdevice.Serial); free(usbdevice.Bus); #ifdef QX_SERIAL } /* is_usb */ #endif /* QX_SERIAL */ #endif /* QX_USB */ #endif /* TESTING */ } /* == Support functions == */ /* Generic command processing function: send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on success. */ static int qx_command(const char *cmd, char *buf, size_t buflen) { #ifndef TESTING int ret = -1; #ifdef QX_USB #ifdef QX_SERIAL /* Communication: USB */ if (is_usb) { #endif /* QX_SERIAL */ if (udev == NULL) { ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); if (ret < 1) { return ret; } } ret = (*subdriver_command)(cmd, buf, buflen); if (ret >= 0) { return ret; } switch (ret) { case -EBUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); case -EPERM: /* Operation not permitted */ fatal_with_errno(EXIT_FAILURE, "Permissions problem"); case -EPIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; } #ifdef ETIME case -ETIME: /* Timer expired */ #endif /* ETIME */ if (usb_reset(udev) == 0) { upsdebugx(1, "Device reset handled"); } case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ /* Uh oh, got to reconnect! */ usb->close(udev); udev = NULL; break; case -ETIMEDOUT: /* Connection timed out */ case -EOVERFLOW: /* Value too large for defined data type */ #ifdef EPROTO case -EPROTO: /* Protocol error */ #endif default: break; } #ifdef QX_SERIAL /* Communication: serial */ } else { /* !is_usb */ #endif /* QX_SERIAL */ #endif /* QX_USB */ #ifdef QX_SERIAL ser_flush_io(upsfd); ret = ser_send(upsfd, "%s", cmd); if (ret <= 0) { upsdebugx(3, "send: %s (%d)", ret ? strerror(errno) : "timeout", ret); return ret; } upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd); ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0); if (ret <= 0) { upsdebugx(3, "read: %s (%d)", ret ? strerror(errno) : "timeout", ret); return ret; } upsdebug_hex(5, "read", buf, ret); upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf); #ifdef QX_USB } /* !is_usb */ #endif /* QX_USB */ #endif /* QX_SERIAL */ return ret; #else /* TESTING */ testing_t *testing = subdriver->testing; int i; memset(buf, 0, buflen); upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd); for (i = 0; cmd && testing[i].cmd; i++) { if (strcasecmp(cmd, testing[i].cmd)) { continue; } upsdebugx(3, "read: '%.*s'", (int)strcspn(testing[i].answer, "\r"), testing[i].answer); /* If requested to do so and this is the case, try to preserve inner '\0's (treat answer as a sequence of bytes) */ if (testing[i].answer_len > 0 && strlen(testing[i].answer) < (size_t)testing[i].answer_len) { size_t len; len = buflen <= (size_t)testing[i].answer_len ? buflen - 1 : (size_t)testing[i].answer_len; len = len <= sizeof(testing[i].answer) ? len : sizeof(testing[i].answer); memcpy(buf, testing[i].answer, len); upsdebug_hex(4, "read", buf, (int)len); return len; } return snprintf(buf, buflen, "%s", testing[i].answer); } /* If the driver expects some kind of reply in case of error.. */ if (subdriver->rejected != NULL) { /* ..fulfill its expectations.. */ upsdebugx(3, "read: '%.*s'", (int)strcspn(subdriver->rejected, "\r"), subdriver->rejected); return snprintf(buf, buflen, "%s", subdriver->rejected); /* ..otherwise.. */ } else { /* ..echo back the command */ upsdebugx(3, "read: '%.*s'", (int)strcspn(cmd, "\r"), cmd); return snprintf(buf, buflen, "%s", cmd); } #endif /* TESTING */ } /* See header file for details. * Interpretation is done in ups_status_set(). */ void update_status(const char *value) { status_lkp_t *status_item; int clear = 0; upsdebugx(5, "%s: %s", __func__, value); if (*value == '!') { value++; clear = 1; } for (status_item = status_info; status_item->status_str != NULL ; status_item++) { if (strcasecmp(status_item->status_str, value)) continue; if (clear) { ups_status &= ~status_item->status_mask; } else { ups_status |= status_item->status_mask; } return; } upsdebugx(5, "%s: Warning! %s not in list of known values", __func__, value); } /* Choose subdriver */ static int subdriver_matcher(void) { const char *protocol = getval("protocol"); int i; /* Select the subdriver for this device */ for (i = 0; subdriver_list[i] != NULL; i++) { int j; /* If protocol is set in ups.conf, use it */ if (protocol) { char subdrv_name[SMALLBUF]; /* Get rid of subdriver version */ snprintf(subdrv_name, sizeof(subdrv_name), "%.*s", (int)strcspn(subdriver_list[i]->name, " "), subdriver_list[i]->name); if (strcasecmp(subdrv_name, protocol)) { upsdebugx(2, "Skipping protocol %s", subdriver_list[i]->name); continue; } } /* Give every subdriver some tries */ for (j = 0; j < MAXTRIES; j++) { subdriver = subdriver_list[i]; if (subdriver->claim()) { break; } subdriver = NULL; } if (subdriver != NULL) break; } if (!subdriver) { upslogx(LOG_ERR, "Device not supported!"); return 0; } upslogx(LOG_INFO, "Using protocol: %s", subdriver->name); return 1; } /* Set vars boundaries */ static void qx_set_var(item_t *item) { if (!(item->qxflags & QX_FLAG_NONUT)) dstate_setflags(item->info_type, item->info_flags); /* Set max length for strings, if needed */ if (item->info_flags & ST_FLAG_STRING && !(item->qxflags & QX_FLAG_NONUT)) dstate_setaux(item->info_type, strtol(item->info_rw[0].value, NULL, 10)); /* Set enum list */ if (item->qxflags & QX_FLAG_ENUM) { info_rw_t *envalue; char buf[LARGEBUF] = ""; /* Loop on all existing values */ for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) { if (envalue->preprocess && envalue->preprocess(envalue->value, sizeof(envalue->value))) continue; /* This item is not available yet in NUT, so publish these data in the logs */ if (item->qxflags & QX_FLAG_NONUT) { snprintfcat(buf, sizeof(buf), " %s", envalue->value); /* This item is available in NUT, add its enum to the variable */ } else { dstate_addenum(item->info_type, "%s", envalue->value); } } if (item->qxflags & QX_FLAG_NONUT) upslogx(LOG_INFO, "%s, settable values:%s", item->info_type, strlen(buf) > 0 ? buf : " none"); } /* Set range */ if (item->qxflags & QX_FLAG_RANGE) { info_rw_t *rvalue, *from = NULL, *to = NULL; int ok = 0; /* Loop on all existing values */ for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) { if (rvalue->preprocess && rvalue->preprocess(rvalue->value, sizeof(rvalue->value))) continue; if (!from) { from = rvalue; continue; } to = rvalue; /* This item is not available yet in NUT, so publish these data in the logs */ if (item->qxflags & QX_FLAG_NONUT) { upslogx(LOG_INFO, "%s, settable range: %s..%s", item->info_type, from->value, to->value); ok++; /* This item is available in NUT, add its range to the variable */ } else { dstate_addrange(item->info_type, strtol(from->value, NULL, 10), strtol(to->value, NULL, 10)); } from = NULL; to = NULL; } /* This item is not available yet in NUT and we weren't able to get its range; let people know it */ if ((item->qxflags & QX_FLAG_NONUT) && !ok) upslogx(LOG_INFO, "%s, settable range: none", item->info_type); } } /* Walk UPS variables and set elements of the qx2nut array. */ static bool_t qx_ups_walk(walkmode_t mode) { item_t *item; int retcode; /* Clear batt.{chrg,runt}.act for guesstimation */ if (mode == QX_WALKMODE_FULL_UPDATE) { batt.runt.act = -1; batt.chrg.act = -1; } /* Clear data from previous_item */ memset(previous_item.command, 0, sizeof(previous_item.command)); memset(previous_item.answer, 0, sizeof(previous_item.answer)); /* 3 modes: QX_WALKMODE_INIT, QX_WALKMODE_QUICK_UPDATE and QX_WALKMODE_FULL_UPDATE */ /* Device data walk */ for (item = subdriver->qx2nut; item->info_type != NULL; item++) { /* Skip this item */ if (item->qxflags & QX_FLAG_SKIP) continue; upsdebugx(10, "%s: processing: %s", __func__, item->info_type); /* Filter data according to mode */ switch (mode) { /* Device capabilities enumeration */ case QX_WALKMODE_INIT: /* Special case for handling server side variables */ if (item->qxflags & QX_FLAG_ABSENT) { /* Already set */ if (dstate_getinfo(item->info_type)) continue; dstate_setinfo(item->info_type, "%s", item->dfl); /* Set var flags/range/enum */ qx_set_var(item); continue; } /* Allow duplicates for these NUT variables */ if (!strncmp(item->info_type, "ups.alarm", 9) || !strncmp(item->info_type, "ups.status", 10)) break; /* This one doesn't exist yet */ if (dstate_getinfo(item->info_type) == NULL) break; continue; case QX_WALKMODE_QUICK_UPDATE: /* Quick update only deals with status and alarms! */ if (!(item->qxflags & QX_FLAG_QUICK_POLL)) continue; break; case QX_WALKMODE_FULL_UPDATE: /* These don't need polling after initinfo() */ if (item->qxflags & (QX_FLAG_ABSENT | QX_FLAG_CMD | QX_FLAG_SETVAR | QX_FLAG_STATIC)) continue; /* These need to be polled after user changes (setvar / instcmd) */ if ((item->qxflags & QX_FLAG_SEMI_STATIC) && (data_has_changed == FALSE)) continue; break; default: fatalx(EXIT_FAILURE, "%s: unknown update mode!", __func__); } /* Instant commands */ if (item->qxflags & QX_FLAG_CMD) { dstate_addcmd(item->info_type); continue; } /* Setvars */ if (item->qxflags & QX_FLAG_SETVAR) { if (item->qxflags & QX_FLAG_NONUT) { setvar(item->info_type, NULL); item->qxflags |= QX_FLAG_SKIP; } continue; } /* Check whether the previous item uses the same command and then use its answer, if available.. */ if (strlen(previous_item.command) > 0 && strlen(previous_item.answer) > 0 && !strcasecmp(previous_item.command, item->command)) { snprintf(item->answer, sizeof(item->answer), "%s", previous_item.answer); /* Process the answer */ retcode = qx_process_answer(item, strlen(item->answer)); /* ..otherwise: execute command to get answer from the UPS */ } else { retcode = qx_process(item, NULL); } /* Record item as previous_item */ snprintf(previous_item.command, sizeof(previous_item.command), "%s", item->command); snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", item->answer); if (retcode) { /* Clear data from the item */ memset(item->answer, 0, sizeof(item->answer)); memset(item->value, 0, sizeof(item->value)); if (item->qxflags & QX_FLAG_QUICK_POLL) return FALSE; if (mode == QX_WALKMODE_INIT) /* Skip this item from now on */ item->qxflags |= QX_FLAG_SKIP; /* Don't know what happened, try again later... */ continue; } /* Process the value we got back (set status bits and set the value of other parameters) */ retcode = ups_infoval_set(item); /* Clear data from the item */ memset(item->answer, 0, sizeof(item->answer)); memset(item->value, 0, sizeof(item->value)); /* Uh-oh! Some error! */ if (retcode == -1) { if (item->qxflags & QX_FLAG_QUICK_POLL) return FALSE; continue; } /* Set var flags/range/enum (not for ups.{alarm.status}, hence the retcode check) */ if (retcode && mode == QX_WALKMODE_INIT) { qx_set_var(item); } } /* Update battery guesstimation */ if (mode == QX_WALKMODE_FULL_UPDATE && (batt.runt.act == -1 || batt.chrg.act == -1)) { if (getval("runtimecal")) { time_t battery_now; time(&battery_now); /* OL */ if (ups_status & STATUS(OL)) { batt.runt.est += batt.runt.nom * difftime(battery_now, battery_lastpoll) / batt.chrg.time; if (batt.runt.est > batt.runt.nom) { batt.runt.est = batt.runt.nom; } /* OB */ } else { batt.runt.est -= load.eff * difftime(battery_now, battery_lastpoll); if (batt.runt.est < 0) { batt.runt.est = 0; } } if (batt.chrg.act == -1) dstate_setinfo("battery.charge", "%.0f", 100 * batt.runt.est / batt.runt.nom); if (batt.runt.act == -1 && !qx_load()) dstate_setinfo("battery.runtime", "%.0f", batt.runt.est / load.eff); battery_lastpoll = battery_now; } else { qx_battery(); } } return TRUE; } /* Convert the local status information to NUT format and set NUT alarms. */ static void ups_alarm_set(void) { if (ups_status & STATUS(RB)) { alarm_set("Replace battery!"); } if (ups_status & STATUS(FSD)) { alarm_set("Shutdown imminent!"); } } /* Convert the local status information to NUT format and set NUT status. */ static void ups_status_set(void) { if (ups_status & STATUS(OL)) { status_set("OL"); /* On line */ } else { status_set("OB"); /* On battery */ } if (ups_status & STATUS(DISCHRG)) { status_set("DISCHRG"); /* Discharging */ } if (ups_status & STATUS(CHRG)) { status_set("CHRG"); /* Charging */ } if (ups_status & STATUS(LB)) { status_set("LB"); /* Low battery */ } if (ups_status & STATUS(OVER)) { status_set("OVER"); /* Overload */ } if (ups_status & STATUS(RB)) { status_set("RB"); /* Replace battery */ } if (ups_status & STATUS(TRIM)) { status_set("TRIM"); /* SmartTrim */ } if (ups_status & STATUS(BOOST)) { status_set("BOOST"); /* SmartBoost */ } if (ups_status & STATUS(BYPASS)) { status_set("BYPASS"); /* On bypass */ } if (ups_status & STATUS(OFF)) { status_set("OFF"); /* UPS is off */ } if (ups_status & STATUS(CAL)) { status_set("CAL"); /* Calibration */ } if (ups_status & STATUS(FSD)) { status_set("FSD"); /* Forced shutdown */ } } /* See header file for details. */ item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag) { item_t *item; for (item = subdriver->qx2nut; item->info_type != NULL; item++) { if (strcasecmp(item->info_type, varname)) continue; if (flag && ((item->qxflags & flag) != flag)) continue; if (noflag && (item->qxflags & noflag)) continue; return item; } upsdebugx(2, "%s: info type %s not found", __func__, varname); return NULL; } /* Process the answer we got back from the UPS * Return -1 on errors, 0 on success */ static int qx_process_answer(item_t *item, const int len) { /* Query rejected by the UPS */ if (subdriver->rejected && !strcasecmp(item->answer, subdriver->rejected)) { upsdebugx(2, "%s: query rejected by the UPS (%s)", __func__, item->info_type); return -1; } /* Short reply */ if (item->answer_len && len < item->answer_len) { upsdebugx(2, "%s: short reply (%s)", __func__, item->info_type); return -1; } /* Wrong leading character */ if (item->leading && item->answer[0] != item->leading) { upsdebugx(2, "%s: %s - invalid start character [%02x], expected [%02x]", __func__, item->info_type, item->answer[0], item->leading); return -1; } /* Check boundaries */ if (item->to && item->to < item->from) { upsdebugx(1, "%s: in %s, starting char's position (%d) follows ending char's one (%d)", __func__, item->info_type, item->from, item->to); return -1; } /* Get value */ if (strlen(item->answer)) { snprintf(item->value, sizeof(item->value), "%.*s", item->to ? 1 + item->to - item->from : (int)strcspn(item->answer, "\r") - item->from, item->answer + item->from); } else { snprintf(item->value, sizeof(item->value), "%s", ""); } return 0; } /* See header file for details. */ int qx_process(item_t *item, const char *command) { char buf[sizeof(item->answer) - 1] = "", cmd[command ? (strlen(command) >= SMALLBUF ? strlen(command) + 1 : SMALLBUF) : (item->command && strlen(item->command) >= SMALLBUF ? strlen(item->command) + 1 : SMALLBUF)]; int len; /* Prepare the command to be used */ memset(cmd, 0, sizeof(cmd)); snprintf(cmd, sizeof(cmd), "%s", command ? command : item->command); /* Preprocess the command */ if ( item->preprocess_command != NULL && item->preprocess_command(item, cmd, sizeof(cmd)) == -1 ) { upsdebugx(4, "%s: failed to preprocess command [%s]", __func__, item->info_type); return -1; } /* Send the command */ len = qx_command(cmd, buf, sizeof(buf)); memset(item->answer, 0, sizeof(item->answer)); memcpy(item->answer, buf, sizeof(buf)); /* Preprocess the answer */ if (item->preprocess_answer != NULL) { len = item->preprocess_answer(item, len); if (len == -1) { upsdebugx(4, "%s: failed to preprocess answer [%s]", __func__, item->info_type); /* Clear answer, preventing it from being reused by next items with same command */ memset(item->answer, 0, sizeof(item->answer)); return -1; } } /* Process the answer to get the value */ return qx_process_answer(item, len); } /* See header file for details. */ int ups_infoval_set(item_t *item) { char value[SMALLBUF] = ""; /* Item need to be preprocessed? */ if (item->preprocess != NULL){ /* Process the value returned by the UPS to NUT standards */ if (item->preprocess(item, value, sizeof(value))) { upsdebugx(4, "%s: failed to preprocess value [%s: %s]", __func__, item->info_type, item->value); return -1; } /* Deal with status items */ if (!strncmp(item->info_type, "ups.status", 10)) { if (strlen(value) > 0) update_status(value); return 0; } /* Deal with alarm items */ if (!strncmp(item->info_type, "ups.alarm", 9)) { if (strlen(value) > 0) alarm_set(value); return 0; } } else { snprintf(value, sizeof(value), "%s", item->value); /* Cover most of the cases: either left/right filled with hashes, spaces or a mix of both */ if (item->qxflags & QX_FLAG_TRIM) str_trim_m(value, "# "); if (strcasecmp(item->dfl, "%s")) { if (strspn(value, "0123456789 .") != strlen(value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, value); return -1; } snprintf(value, sizeof(value), item->dfl, strtod(value, NULL)); } } if (item->qxflags & QX_FLAG_NONUT) { upslogx(LOG_INFO, "%s: %s", item->info_type, value); return 1; } if (!strlen(value)) { upsdebugx(1, "%s: non significant value [%s]", __func__, item->info_type); return -1; } dstate_setinfo(item->info_type, "%s", value); /* Fill batt.{chrg,runt}.act for guesstimation */ if (!strcasecmp(item->info_type, "battery.charge")) batt.chrg.act = strtol(value, NULL, 10); else if (!strcasecmp(item->info_type, "battery.runtime")) batt.runt.act = strtol(value, NULL, 10); return 1; } /* See header file for details. */ int qx_status(void) { return ups_status; } nut-2.7.4/drivers/raritan-pdu-mib.h0000644000175000017500000000023512640443572014104 00000000000000#ifndef RARITAN_PDU_MIB_H #define RARITAN_PDU_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t raritan; #endif /* RARITAN_PDU_MIB_H */ nut-2.7.4/drivers/belkin.h0000644000175000017500000000365412640473702012363 00000000000000/* belkin.h - serial commands for Belkin smart protocol units Copyright (C) 2000 Marcus Müller This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "serial.h" #include "timehead.h" #define STATUS 'P' #define CONTROL 'S' #define MANUFACTURER "MNU" #define MODEL "MOD" #define VERSION_CMD "VER" #define RATING "RAT" #define STAT_INPUT "STI" #define STAT_OUTPUT "STO" #define STAT_BATTERY "STB" #define STAT_STATUS "STA" #define TEST_RESULT "TSR" #define POWER_ON "RON" #define POWER_OFF "ROF" #define POWER_SDTYPE "SDT" /* shutdown type? */ #define POWER_CYCLE "SDA" /* shutdown, then restore */ #define BUZZER "BUZ" #define BUZZER_ON "1" #define BUZZER_OFF0 "0" /* Switching the buzzer off can be either 0 or 2 */ #define BUZZER_OFF2 "2" /* Seems to be used for F6c1400 */ #define TEST "TST" #define TEST_10SEC "3" #define TEST_DEEP "4" #define TEST_CANCEL "0" /* The UPS Status "low battery" comes up 10s before the UPS actually stops. Therefore a shutdown is done at this battery % */ #define LOW_BAT 20 /* the maximum allowed number of missed replies */ #define MAXTRIES 3 /* dangerous instant commands must be reconfirmed within a 12 second window */ #define CONFIRM_DANGEROUS_COMMANDS 1 #define MINCMDTIME 3 #define MAXCMDTIME 15 nut-2.7.4/drivers/mge-mib.h0000644000175000017500000000020112640443572012417 00000000000000#ifndef MGE_MIB_H #define MGE_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t mge; #endif /* MGE_MIB_H */ nut-2.7.4/drivers/ietf-mib.h0000644000175000017500000000025312667537407012617 00000000000000#ifndef IETF_MIB_H #define IETF_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t ietf; extern mib2nut_info_t tripplite_ietf; #endif /* IETF_MIB_H */ nut-2.7.4/drivers/explore-hid.h0000644000175000017500000000174212640443572013335 00000000000000/* explore-hid.h - this is a "stub" subdriver used to collect data * about HID UPS systems that are not yet supported. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef EXPLORE_HID_H #define EXPLORE_HID_H #include "usbhid-ups.h" extern subdriver_t explore_subdriver; #endif /* EXPLORE_HID_H */ nut-2.7.4/drivers/serial.h0000644000175000017500000000511012640473702012363 00000000000000#ifndef SERIAL_H_SEEN #define SERIAL_H_SEEN 1 #include "attribute.h" #include "config.h" #if defined(HAVE_SYS_TERMIOS_H) # include /* for speed_t */ #else # include #endif /* HAVE_SYS_TERMIOS_H */ /* limit the amount of spew that goes in the syslog when we lose the UPS */ #define SER_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define SER_ERR_RATE 100 /* then only print every 100th error */ int ser_open_nf(const char *port); int ser_open(const char *port); int ser_set_speed_nf(int fd, const char *port, speed_t speed); int ser_set_speed(int fd, const char *port, speed_t speed); /* set the state of modem control lines */ int ser_set_dtr(int fd, int state); int ser_set_rts(int fd, int state); /* get the status of modem control lines */ int ser_get_dsr(int fd); int ser_get_cts(int fd); int ser_get_dcd(int fd); int ser_flush_io(int fd); int ser_close(int fd, const char *port); int ser_send_char(int fd, unsigned char ch); /* send the results of the format string with d_usec delay after each char */ int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); /* send the results of the format string with no delay */ int ser_send(int fd, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); /* send buflen bytes from buf with no delay */ int ser_send_buf(int fd, const void *buf, size_t buflen); /* send buflen bytes from buf with d_usec delay after each char */ int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, size_t buflen); int ser_get_char(int fd, void *ch, long d_sec, long d_usec); int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec); /* keep reading until buflen bytes are received or a timeout occurs */ int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec); /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler (char ch), long d_sec, long d_usec); /* as above, only with no alertset handling (just a wrapper) */ int ser_get_line(int fd, void *buf, size_t buflen, char endchar, const char *ignset, long d_sec, long d_usec); int ser_flush_in(int fd, const char *ignset, int verbose); /* unified failure reporting: call these often */ void ser_comm_fail(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); void ser_comm_good(void); #endif /* SERIAL_H_SEEN */ nut-2.7.4/drivers/explore-hid.c0000644000175000017500000000417012640443572013326 00000000000000/* explore-hid.c - this is a "stub" subdriver used to collect data * about HID UPS systems that are not yet supported. * * This subdriver will match any UPS, but only if the "-x explore" option * is given. * * 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 "main.h" #include "usbhid-ups.h" #include "explore-hid.h" #define EXPLORE_HID_VERSION "EXPLORE HID 0.1" static usage_tables_t explore_utab[] = { hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* Data lookup table (HID <-> NUT) */ /* --------------------------------------------------------------- */ static hid_info_t explore_hid2nut[] = { /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *explore_format_model(HIDDevice_t *hd) { return hd->Product; } static const char *explore_format_mfr(HIDDevice_t *hd) { return hd->Vendor; } static const char *explore_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int explore_claim(HIDDevice_t *hd) { if (testvar("explore")) { return 1; } else { return 0; } } subdriver_t explore_subdriver = { EXPLORE_HID_VERSION, explore_claim, explore_utab, explore_hid2nut, explore_format_model, explore_format_mfr, explore_format_serial, }; nut-2.7.4/drivers/bestfcom.c0000644000175000017500000005122312640444140012701 00000000000000/* bestfcom.c - model specific routines for Best Power F-Command ups models This module is yet another rewritten mangle of the bestuferrups driver. This driver was written in an attempt to consolidate the various Best Fortress/FERRUPS modules that support the 'f'-command set and provide support for more of these models. Models tested with this new version: FortressII LI720 FERRUPS FE2.1K FERRUPS FE4.3K FERRUPS FE18K FERRUPS FD4.3K From bestuferrups.c : This module is a 40% rewritten mangle of the bestfort module by Grant, which is a 75% rewritten mangle of the bestups module by Russell. It has no test battery command since my ME3100 does this by itself. (same as Grant's driver in this respect) Copyright (C) 2002 Andreas Wrede Copyright (C) 2000 John Stone Copyright (C) 2000 Grant Taylor Copyright (C) 1999 Russell Kroll 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 "main.h" #include "serial.h" #define DRIVER_NAME "Best Ferrups/Fortress driver" #define DRIVER_VERSION "0.12" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Andreas Wrede \n" \ "John Stone \n" \ "Grant Taylor \n" \ "Russell Kroll ", DRV_EXPERIMENTAL, { NULL } }; #define ENDCHAR '\r' #define IGNCHARS "\012" #define UPSDELAY 1 /* BEST Factory UPS Model Codes */ #define FORTRESS 00 #define PATRIOT 01 #define FORTRESSII 02 #define FERRUPS 03 #define UNITY1 04 /* Internal driver UPS Model Codes */ #define UNKNOWN 000 #define FDxxxx 100 #define FExxxx 200 #define LIxxxx 300 #define MExxxx 400 #define MDxxxx 500 #include #include #include /* Blob of UPS configuration data from the formatconfig string */ struct { int valid; /* set to 1 when this is filled in */ float idealbvolts; /* various interesting battery voltages */ float fullvolts; float lowvolts; float emptyvolts; int va; /* capacity of UPS in Volt-Amps */ int watts; /* capacity of UPS in watts */ int model; /* enumerated model type */ int type; /* enumerated ups type*/ char name[16]; /* ups type name*/ } fc; static int inverter_status; /* Forward decls */ /* Set up all the funky shared memory stuff used to communicate with upsd */ void upsdrv_initinfo (void) { /* now set up room for all future variables that are supported */ /* dstate_setinfo("driver.name", "%s", "bestfcom"); */ dstate_setinfo("ups.mfr", "%s", "Best Power"); switch(fc.model) { case MExxxx: dstate_setinfo("ups.model", "%s ME%d", fc.name, fc.va); break; case MDxxxx: dstate_setinfo("ups.model", "%s MD%d", fc.name, fc.va); break; case FDxxxx: dstate_setinfo("ups.model", "%s FD%d", fc.name, fc.va); break; case FExxxx: dstate_setinfo("ups.model", "%s FE%d", fc.name, fc.va); break; case LIxxxx: dstate_setinfo("ups.model", "%s LI%d", fc.name, fc.va); break; default: fatalx(EXIT_FAILURE, "Unknown model - oops!"); /* Will never get here, upsdrv_initups() will catch */ } dstate_setinfo("ups.power.nominal", "%d", fc.va); dstate_setinfo("ups.realpower.nominal", "%d", fc.watts); /* Do we really need to waste time on this? */ /* if (fc.model != FDxxxx) { if (execute("d 00\r", tmp, sizeof(tmp)) > 0) sscanf(tmp, "00 Time %8s", time); if (execute("d 10\r", tmp, sizeof(tmp)) > 0) sscanf(tmp, "10 Date %8s", date); dstate_setinfo("ups.time", "%s", time); dstate_setinfo("ups.date", "%s", date); } */ dstate_setinfo("battery.voltage.nominal", "%05.2f", (double)fc.idealbvolts); upsdebugx(1, "Best Power %s detected", dstate_getinfo("ups.model")); upsdebugx(1, "Battery voltages: %5.2f nominal, %5.2f full, %5.2f low, %5.2f empty", fc.idealbvolts, fc.fullvolts, fc.lowvolts, fc.emptyvolts); } /* atoi() without the freebie octal conversion */ int bcd2i (const char *bcdstring, const int bcdlen) { int i, digit, total = 0, factor = 1; for (i = 1; i < bcdlen; i++) factor *= 10; for (i = 0; i < bcdlen; i++) { digit = bcdstring[i] - '0'; if (digit > 9) { digit = 0; } total += digit * factor; factor /= 10; } return total; } #define POLL_ALERT "{" static void alert_handler(char ch) { char buf[256]; /* Received an Inverter status alarm : * "\r\n{Inverter: On}\r\n=>" * Try to flush the message */ ser_get_line(upsfd, buf, sizeof(buf), '\012', "", 0, 20000); } /* Debugging display from kermit: ---------------------------------------------------- time^M^M^JFeb 20, 22:13:32^M^J^M^J=>id^M^JUnit ID "ME3.1K12345"^M^J^M^J=> ---------------------------------------------------- */ static int execute(const char *cmd, char *result, int resultsize) { int ret; char buf[256]; unsigned char ch; /* Check for the Inverter status alarm if pending : * "\r\n{Inverter: On}\r\n=>" */ ser_get_line_alert(upsfd, buf, sizeof(buf), '\012', "", POLL_ALERT, alert_handler, 0, 20000); ser_send(upsfd, "%s", cmd); /* Give the UPS some time to chew on what we just sent */ usleep(50000); /* delete command echo up to \012 but no further */ for (ch = '\0'; ch != '\012'; ser_get_char(upsfd, &ch, 0, 10000)); /* get command response */ ret = ser_get_line(upsfd, result, resultsize, '\015', "\012", 3, 0); return ret; } /* format command response -> 80 chars chrg line status ||alrm || Date Invtr |12| error | ||Time| || |Vi||Vo| |Io|| VA | |Vb||Hz||rt| || |vr|CS 011314581801000000010000011601160000002300026600000265600000190000000000E00106E6\r 01161706430100010001000002040121000000980011890000057959980001002200000064080727\r 011800364801000100010000021301200000003100037100001343599803060024000000680807A0\r 0121022719010001000100000208011900190000000000000005676001082200350000000006101A\r 00000000000100000000000002370236000000220005190000026850000009002600000000030161\r 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 Above f-responses listed in this order: FortressII LI720 FERRUPS FE4.3K FERRUPS FE18K FERRUPS FD4.3K Fortress ?????? (from Holger's old Best Fortress notes) */ void upsdrv_updateinfo(void) { char fstring[512]; if (! fc.valid) { upsdebugx(1, "upsupdate run before ups_ident() read ups config"); assert(0); } if (execute("f\r", fstring, sizeof(fstring)) >= 80) { int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, alstat=0, vaout=0; double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0, upstemp=0.0, acfreq=0.0; char tmp[32]; upsdebugx(3, "f response: %d %s", (int)strlen(fstring), fstring); /* Inverter status. 0=off 1=on */ inverter = bcd2i(&fstring[16], 2); /* Charger status. 0=off 1=on */ charger = bcd2i(&fstring[18], 2); /* Input Voltage. integer number */ vin = bcd2i(&fstring[24], 4); /* Output Voltage. integer number */ vout = bcd2i(&fstring[28], 4); /* Battery voltage. int times 10 */ vbatt = ((double)bcd2i(&fstring[50], 4) / 10.0); /* Alarm status reg 1. Bitmask */ alstat = bcd2i(&fstring[20], 2); /* Alarm status reg 2. Bitmask */ alstat = alstat | (bcd2i(&fstring[22], 2) << 8); /* AC line frequency */ acfreq = ((double)bcd2i(&fstring[54], 4) / 100.0); /* Runtime remaining (UPS reports minutes) */ btimeleft = bcd2i(&fstring[58], 4) * 60; if (fc.model != FDxxxx) { /* Iout. int times 10 */ ampsout = ((double)bcd2i(&fstring[36], 4) / 10.0); /* Volt-amps out. int */ vaout = bcd2i(&fstring[40], 6); /* Line status. Bitmask */ linestat = bcd2i(&fstring[72], 2); } if (fc.model != LIxxxx) { upstemp = (double) bcd2i(&fstring[62], 4); } /* Percent Load */ switch(fc.model) { case LIxxxx: case FDxxxx: case FExxxx: case MExxxx: if (execute("d 16\r", tmp, sizeof(tmp)) > 0) { int l; sscanf(tmp, "16 FullLoad%% %d", &l); loadpercent = (double) l; } break; case MDxxxx: if (execute("d 22\r", tmp, sizeof(tmp)) > 0) { int l; sscanf(tmp, "22 FullLoad%% %d", &l); loadpercent = (double) l; } break; default: /* Will never happen, caught in upsdrv_initups() */ fatalx(EXIT_FAILURE, "Unknown model in upsdrv_updateinfo()"); } /* Compute battery percent left based on battery voltages. */ battpercent = ((vbatt - fc.emptyvolts) / (fc.idealbvolts - fc.emptyvolts) * 100.0); if (battpercent < 0.0) battpercent = 0.0; else if (battpercent > 100.0) battpercent = 100.0; /* Compute status string */ { int lowbatt, lowvolts, overload, replacebatt, boosting, trimming; lowbatt = alstat & (1<<1); overload = alstat & (1<<6); replacebatt = alstat & (1<<10); boosting = inverter && (linestat & (1<<2)) && (vin < 115); trimming = inverter && (linestat & (1<<2)) && (vin > 115); /* status bits can be unreliable, so try to help it out */ lowvolts = (vbatt <= fc.lowvolts); status_init(); if (inverter) { if (inverter_status < 1) { upsdebugx(1, "Inverter On, charger: %d battery time left: %d", charger, btimeleft); } inverter_status = 1; status_set("OB"); } else { if (inverter_status) { upsdebugx(1, "Inverter Off, charger: %d battery time left: %d", charger, btimeleft); } inverter_status = 0; status_set("OL"); } if (lowbatt | lowvolts) status_set("LB"); if (trimming) status_set("TRIM"); if (boosting) status_set("BOOST"); if (replacebatt) status_set("RB"); if (overload) status_set("OVER"); status_commit(); } upsdebugx(2, "Poll: inverter %d charger %d vin %d vout %d vaout %d btimeleft %d", inverter, charger, vin, vout, vaout, btimeleft); upsdebugx(2, " vbatt %5.1f batpcnt %5.1f loadpcnt %5.1f upstemp %5.1f ampsout %5.1f acfreq %5.2f", vbatt, battpercent, loadpercent, upstemp, ampsout, acfreq); /* Stuff information into info structures */ dstate_setinfo("input.voltage", "%05.1f", (double)vin); dstate_setinfo("input.frequency", "%05.2f", acfreq); dstate_setinfo("output.voltage", "%05.1f", (double)vout); dstate_setinfo("output.current", "%04.1f", ampsout); dstate_setinfo("battery.charge", "%02.1f", battpercent); dstate_setinfo("battery.voltage", "%02.1f", vbatt); dstate_setinfo("battery.runtime", "%d", btimeleft); dstate_setinfo("ups.load", "%02.1f", loadpercent); if (vaout) dstate_setinfo("ups.power", "%d", vaout); if (upstemp) dstate_setinfo("ups.temperature", "%05.1f", (double)upstemp); dstate_dataok(); } else { upsdebugx(1, "failed f response. strlen: %d", (int)strlen(fstring)); dstate_datastale(); } /* if (execute("f\r", fstring, sizeof(fstring)) >= 80) */ return; } static void ups_sync(void) { char buf[256]; /* A bit better sanity might be good here. As is, we expect the human to observe the time being totally not a time. */ if (execute("time\r", buf, sizeof(buf)) > 0) { upsdebugx(1, "UPS Time: %s", buf); } else { fatalx(EXIT_FAILURE, "Error connecting to UPS."); } /* old Ferrups prompt for new time so send a blank line */ execute("\r", buf, sizeof(buf)); ser_get_line(upsfd, buf, sizeof(buf), '>', "\012", 3, 0); } /* power down the attached load immediately */ void upsdrv_shutdown(void) { /* NB: hard-wired password */ ser_send(upsfd, "pw377\r"); ser_send(upsfd, "off 1 a\r"); /* power off in 1 second and restart when line power returns */ } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { } void upsdrv_help(void) { } static void sync_serial(void) { char buffer[10]; ser_flush_in(upsfd, "", 1); ser_send(upsfd, "\r"); sleep(UPSDELAY); ser_get_line(upsfd, buffer, sizeof(buffer), '\r', "\012", 3, 0); ser_get_line(upsfd, buffer, sizeof(buffer), ENDCHAR, IGNCHARS, 3, 0); while (ser_get_line(upsfd, buffer, sizeof(buffer), '>', "\012", 3, 0) <= 0) { printf("."); ser_send(upsfd, "\r"); sleep(UPSDELAY); } } /* Begin code stolen from bestups.c */ static void setup_serial(void) { struct termios tio; if (tcgetattr(upsfd, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcgetattr"); tio.c_iflag = IXON | IXOFF; tio.c_oflag = 0; tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); tio.c_lflag = 0; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; #ifdef HAVE_CFSETISPEED cfsetispeed(&tio, B1200); /* baud change here */ cfsetospeed(&tio, B1200); #else #error This system lacks cfsetispeed() and has no other means to set the speed #endif if (tcsetattr(upsfd, TCSANOW, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcsetattr"); /* end code stolen from bestups.c */ sync_serial(); } /* These models don't support the formatconfig (fc) command so use the identify command. "id\r" returns : FERRUPS Uninterruptible Power System By Best Power Technology, Inc. Route 1 Highway 80 / P.O. Box 280 Necedah, WI 54646 USA Sales: (800) 356-5794 Service: (800) 356-5737 FAX: (608) 565-2221 Copyright (C) 1993, 1994, 1995 Best Power Technology, Inc. Model: FE4.3KVA Unit ID: FE4.3K02376 Serial #: FE4.3K02376 Version: 8.07 Released: 08/01/1995 */ void upsdrv_init_nofc(void) { char tmp[256], rstring[1024]; /* This is a Best UPS * Set initial values for old Fortress??? */ /* Attempt the id command */ ser_send(upsfd, "id\r"); /* prevent upsrecv from timing out */ sleep(UPSDELAY); ser_get_line(upsfd, rstring, sizeof(rstring), '>', "", 3, 0); rstring[sizeof(rstring) - 1] = '\0'; upsdebugx(2, "id response: %s", rstring); /* Better way to identify this unit is using "d 15\r", which results in "15 M# MD1KVA", "id\r" yields "Unit ID "C1K03588"" */ if (strstr(rstring, "Unit ID \"C1K")){ fc.model = MDxxxx; snprintf(fc.name, sizeof(fc.name), "%s", "Micro Ferrups"); /* Determine load rating by Unit Id? */ if (strstr(rstring, "Unit ID \"C1K")) { fc.va = 1100; fc.watts = 770; /* Approximate, based on 0.7 power factor */ } } else if (strstr(rstring, "Unit ID \"ME")){ fc.model = MExxxx; snprintf(fc.name, sizeof(fc.name), "%s", "Micro Ferrups"); /* Determine load rating by Unit Id? */ if (strstr(rstring, "Unit ID \"ME3.1K")) { fc.va = 3100; fc.watts = 2200; } } else if (strstr(rstring, "Unit ID \"FD")){ fc.model = FDxxxx; snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups"); /* Determine load rating by Unit Id? */ if (strstr(rstring, "Unit ID \"FD4.3K")) { fc.va = 4300; fc.watts = 3000; } } else if (strstr(rstring, "Model: FE") || strstr(rstring, "Model: FE")){ fc.model = FExxxx; fc.type = FERRUPS; snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups"); } else if (strlen(rstring) < 300 ) { /* How does the old Fortress respond to this? */ upsdebugx(2, "Old Best Fortress???"); /* fc.model = FORTRESS; */ } if (fc.model == UNKNOWN) { fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_nofc()", rstring); } switch(fc.model) { case MExxxx: case MDxxxx: case FDxxxx: /* determine shutdown battery voltage */ if (execute("d 27\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "27 LowBatt %f", &fc.emptyvolts); } /* determine near low battery voltage */ if (execute("d 30\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "30 NLBatt %f", &fc.lowvolts); } /* determine fully charged battery voltage */ if (execute("d 28\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "28 Hi Batt %f", &fc.fullvolts); } fc.fullvolts = 13.70; /* determine "ideal" voltage by a guess */ fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; break; case FExxxx: if (execute("d 45\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "45 RatedVA %d", &fc.va); /* 4300 */ } if (execute("d 46\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "46 RatedW %d", &fc.watts); /* 3000 */ } if (execute("d 65\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "65 LoBatV %f", &fc.emptyvolts); /* 41.00 */ } if (execute("d 66\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "66 NLBatV %f", &fc.lowvolts); /* 44.00 */ } if (execute("d 67\r", tmp, sizeof(tmp)) > 0) { sscanf(tmp, "67 HiBatV %f", &fc.fullvolts); /* 59.60 */ } fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; if (fc.va < 1.0) { fatalx(EXIT_FAILURE, "Error determining Ferrups UPS rating."); } break; default: fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_nofc()", rstring); break; } fc.valid = 1; } /* These models support the formatconfig (fc) command formatconfig (fc) response is a one-line packed string starting with $ Model Wt rat Vout VHi FrLo BatVN BatNLo LRuntime Model | | | | | | | | | | | | | | | | | | E rev VA rat Vin VLo FrN FrHi BatHi BatLo Opt O || | | | | | | | | | | | | | | | T $010207010600720004701201200911446000570063000240028802150190003??????\LI????VA\\| $010207010600720004701201200911446000570063000240028802150190003??????\LI720VU\LI720VU18112\| 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 Model: [0,1] => 00 = unk, 01 = Patriot/SPS, 02 = FortressII, 03 = Ferrups, 04 = Unity/1 [2,3] => 00 = LI520, 01 = LI720, 02 = LI1020, 03 = LI1420, 07 = ??? */ void upsdrv_init_fc(const char *fcstring) { char tmp[256]; upsdebugx(3, "fc response: %d %s", (int)strlen(fcstring), fcstring); /* Obtain Model */ if (memcmp(fcstring, "$", 1)) { fatalx(EXIT_FAILURE, "Bad response from formatconfig command in upsdrv_init_fc()"); } if (memcmp(fcstring+3, "00", 2) == 0) { fatalx(EXIT_FAILURE, "UPS type unknown in upsdrv_init_fc()"); } if (memcmp(fcstring+3, "01", 2) == 0) { fatalx(EXIT_FAILURE, "Best Patriot UPS not supported"); } else if (memcmp(fcstring+3, "02", 2) == 0) { snprintf(fc.name, sizeof(fc.name), "%s", "FortressII"); fc.type = FORTRESSII; } else if (memcmp(fcstring+3, "03", 2) == 0) { snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups"); fc.type = FERRUPS; } else if (memcmp(fcstring+3, "04", 2) == 0) { snprintf(fc.name, sizeof(fc.name), "%s", "Unity/1"); fc.type = UNITY1; } /* (fc.type == FORTRESSII || fc.type == FERRUPS || fc.type == UNITY1) */ if (memcmp(fcstring+5, "00", 2) == 0) { /* fc.model = LI520; */ fc.model = LIxxxx; } else if (memcmp(fcstring+5, "01", 2) == 0) { /* fc.model = LI720; */ fc.model = LIxxxx; } else if (memcmp(fcstring+5, "02", 2) == 0) { /* fc.model = LI1020; */ fc.model = LIxxxx; } else if (memcmp(fcstring+5, "03", 2) == 0) { /* fc.model = LI1420; */ fc.model = LIxxxx; } else if (memcmp(fcstring+71, "LI", 2) == 0) { fc.model = LIxxxx; } switch(fc.model) { case LIxxxx: fc.va = bcd2i(&fcstring[11], 5); fc.watts = bcd2i(&fcstring[16], 5); /* determine shutdown battery voltage */ fc.emptyvolts= ((double)bcd2i(&fcstring[57], 4) / 10.0); /* determine fully charged battery voltage */ fc.lowvolts= ((double)bcd2i(&fcstring[53], 4) / 10.0); /* determine fully charged battery voltage */ fc.fullvolts= ((double)bcd2i(&fcstring[49], 4) / 10.0); /* determine "ideal" voltage by a guess */ fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; break; default: fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_fc()", tmp); } fc.valid = 1; } void upsdrv_initups(void) { char rstring[256]; upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); setup_serial(); ups_sync(); inverter_status = 0; fc.model = UNKNOWN; if (execute("f\r", rstring, sizeof(rstring)) < 1 ) { fatalx(EXIT_FAILURE, "Failed format request in upsdrc_initups()"); } execute("fc\r", rstring, sizeof(rstring)); if (strlen(rstring) < 80 ) { ser_get_line(upsfd, rstring, sizeof(rstring), '>', "\012", 3, 0); upsdrv_init_nofc(); } else { upsdrv_init_fc(rstring); } return; } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/macosx-ups.c0000644000175000017500000003212012640473702013177 00000000000000/* Bridge driver to read Mac OS X UPS status (as displayed in Energy Saver control panel) * * Copyright (C) 2011-2012, 2015 Charles Lepple * * 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 "main.h" #include #include "CoreFoundation/CoreFoundation.h" #include "IOKit/ps/IOPowerSources.h" #include "IOKit/ps/IOPSKeys.h" #define DRIVER_NAME "Mac OS X UPS meta-driver" #define DRIVER_VERSION "1.2" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Charles Lepple ", DRV_EXPERIMENTAL, { NULL } }; #if 0 #define CFRelease(ref) do { upsdebugx(3, "%s:%d: CFRelease(%p)", __FILE__, __LINE__, ref); CFRelease(ref); } while(0) #endif static CFStringRef g_power_source_name = NULL; static double max_capacity_value = 100.0; /*! Copy the current power dictionary. * * Caller must release power dictionary when finished with it. */ static CFDictionaryRef copy_power_dictionary(CFStringRef power_source_name) { CFTypeRef power_sources_info, power_source; CFArrayRef sources_list; CFDictionaryRef this_power_dictionary, power_dictionary = NULL; CFStringRef this_power_source_name; CFIndex num_keys, index; power_sources_info = IOPSCopyPowerSourcesInfo(); assert(power_sources_info); upsdebugx(6, "%s: Got power_sources_info:", __func__); if(nut_debug_level >= 6) CFShow(power_sources_info); upsdebugx(5, "power_source_name = "); if(nut_debug_level >= 5) CFShow(power_source_name); upsdebugx(6, "end power_source_name"); sources_list = IOPSCopyPowerSourcesList(power_sources_info); num_keys = CFArrayGetCount(sources_list); for(index=0; index < num_keys; index++) { upsdebugx(6, "%s: Getting power source %ld/%ld...", __func__, index+1, num_keys); power_source = CFArrayGetValueAtIndex(sources_list, index); assert(power_source); upsdebugx(6, "%s: power source %ld = ", __func__, index+1); if(nut_debug_level >= 6) CFShow(power_source); this_power_dictionary = IOPSGetPowerSourceDescription(power_sources_info, power_source); assert(this_power_dictionary); this_power_source_name = CFDictionaryGetValue(this_power_dictionary, CFSTR(kIOPSNameKey)); assert(this_power_source_name); if(!CFStringCompare(this_power_source_name, power_source_name, 0)) { power_dictionary = this_power_dictionary; CFRetain(power_dictionary); break; } } if(power_dictionary) { upsdebugx(5, "CFShowing 'power_dictionary'"); if(nut_debug_level >= 5) CFShow(power_dictionary); } /* Get a new power_sources_info next time: */ CFRelease(power_sources_info); CFRelease(sources_list); return power_dictionary; } void upsdrv_initinfo(void) { /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ char device_name[80] = ""; CFStringRef device_type_cfstr, device_name_cfstr; CFPropertyListRef power_dictionary; CFNumberRef max_capacity; upsdebugx(1, "upsdrv_initinfo()"); dstate_setinfo("device.mfr", "(unknown)"); power_dictionary = copy_power_dictionary(g_power_source_name); device_type_cfstr = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSTypeKey)); if(device_type_cfstr && !CFStringCompare(device_type_cfstr, CFSTR(kIOPSInternalBatteryType), 0)) { dstate_setinfo("device.type", "battery"); } upsdebugx(2, "Getting 'Name' key"); device_name_cfstr = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSNameKey)); if (!device_name_cfstr) { fatalx(EXIT_FAILURE, "Couldn't retrieve 'Name' key from power dictionary."); } CFRetain(device_name_cfstr); CFStringGetCString(device_name_cfstr, device_name, sizeof(device_name), kCFStringEncodingUTF8); upsdebugx(2, "Got name: %s", device_name); CFRelease(device_name_cfstr); dstate_setinfo("device.model", "%s", device_name); max_capacity = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSMaxCapacityKey)); if(max_capacity) { CFRetain(max_capacity); CFNumberGetValue(max_capacity, kCFNumberDoubleType, &max_capacity_value); CFRelease(max_capacity); upsdebugx(3, "Max Capacity = %.f units (usually 100)", max_capacity_value); if(max_capacity_value != 100) { upsdebugx(1, "Max Capacity: %f != 100", max_capacity_value); } } /* upsh.instcmd = instcmd; */ CFRelease(power_dictionary); } void upsdrv_updateinfo(void) { CFPropertyListRef power_dictionary; CFStringRef power_source_state; CFNumberRef battery_voltage, battery_runtime; CFNumberRef current_capacity; CFBooleanRef is_charging; double max_capacity_value = 100.0, current_capacity_value; upsdebugx(1, "upsdrv_updateinfo()"); power_dictionary = copy_power_dictionary( g_power_source_name ); if(!power_dictionary) { dstate_datastale(); return; } status_init(); /* Retrieve OL/OB state */ power_source_state = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSPowerSourceStateKey)); assert(power_source_state); CFRetain(power_source_state); upsdebugx(3, "Power Source State:"); if(nut_debug_level >= 3) CFShow(power_source_state); if(!CFStringCompare(power_source_state, CFSTR(kIOPSACPowerValue), 0)) { status_set("OL"); } else { status_set("OB"); } CFRelease(power_source_state); /* Retrieve CHRG state */ is_charging = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSIsChargingKey)); if(is_charging) { Boolean is_charging_value; is_charging_value = CFBooleanGetValue(is_charging); if(is_charging_value) { status_set("CHRG"); } } status_commit(); /* Retrieve battery voltage */ battery_voltage = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSVoltageKey)); if(battery_voltage) { int battery_voltage_value; CFNumberGetValue(battery_voltage, kCFNumberIntType, &battery_voltage_value); upsdebugx(2, "battery_voltage = %d mV", battery_voltage_value); dstate_setinfo("battery.voltage", "%.3f", battery_voltage_value/1000.0); } /* Retrieve battery runtime */ battery_runtime = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSTimeToEmptyKey)); if(battery_runtime) { double battery_runtime_value; CFNumberGetValue(battery_runtime, kCFNumberDoubleType, &battery_runtime_value); upsdebugx(2, "battery_runtime = %.f minutes", battery_runtime_value); if(battery_runtime_value > 0) { dstate_setinfo("battery.runtime", "%d", (int)(battery_runtime_value*60)); } else { dstate_delinfo("battery.runtime"); } } else { dstate_delinfo("battery.runtime"); } /* Retrieve current capacity */ current_capacity = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSCurrentCapacityKey)); if(current_capacity) { CFNumberGetValue(current_capacity, kCFNumberDoubleType, ¤t_capacity_value); upsdebugx(2, "Current Capacity = %.f/%.f units", current_capacity_value, max_capacity_value); if(max_capacity_value > 0) { dstate_setinfo("battery.charge", "%.f", 100.0 * current_capacity_value / max_capacity_value); } } /* TODO: it should be possible to set poll_interval (and maxage in the * server) to an absurdly large value, and use notify(3) to get * updates. */ /* * poll_interval = 2; */ dstate_dataok(); CFRelease(power_dictionary); } void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* NOTE: Mac OS X already has shutdown routines - this driver is more for monitoring and notification purposes. Still, there is a key that might be useful to set in SystemConfiguration land. */ fatalx(EXIT_FAILURE, "shutdown not supported"); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ } /* static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } */ /* TODO: There is a configuration file here: /Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist with several keys under UPSDefaultThresholds: * UPSShutdownAfterMinutes * UPSShutdownAtLevel * UPSShutdownAtMinutesLeft It is not likely that these keys can be written, but they might be good values for NUT variables. In conjunction with 'ignorelb' and a delta, this could be used to synthesize a LB status right before the computer shuts down. */ /* static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.test.interval")) { ser_send_buf(upsfd, ...); return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } */ void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* allow '-x xyzzy' */ /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ /* allow '-x foo=' */ /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ addvar(VAR_VALUE, "model", "Regular Expression to match power source model name"); } void upsdrv_initups(void) { CFArrayRef power_source_key_list; CFIndex num_keys, index; CFDictionaryRef power_dictionary; CFTypeRef power_blob; CFStringRef potential_key, potential_model; char *model_name; /* regex(3) */ char potential_model_name[256]; regex_t model_regex; int ret; upsdebugx(3, "upsdrv_initups(): Power Sources blob:"); /* upsfd = ser_open(device_path); */ /* ser_set_speed(upsfd, device_path, B1200); */ power_blob = IOPSCopyPowerSourcesInfo(); if(!power_blob) { fatalx(EXIT_FAILURE, "Couldn't retrieve Power Sources blob"); } if(nut_debug_level >= 3) CFShow(power_blob); /* The CFDictionary through 10.9 has changed to a CFArray, so this part is no longer applicable: */ #if 0 if(!strcmp(device_name, "auto")) { device_name = "/UPS"; } upsdebugx(2, "Matching power supply key names against regex '%s'", device_name); ret = regcomp(&name_regex, device_name, REG_EXTENDED|REG_NOSUB|REG_ICASE); if(ret) { fatalx(EXIT_FAILURE, "Failed to compile regex from 'port' parameter: '%s'.", device_name); } #endif if((model_name = getval("model"))) { upsdebugx(2, "Matching power supply model names against regex '%s'", model_name); ret = regcomp(&model_regex, model_name, REG_EXTENDED|REG_NOSUB|REG_ICASE); if(ret) { fatalx(EXIT_FAILURE, "Failed to compile regex from 'model' parameter: '%s'.", model_name); } } power_source_key_list = IOPSCopyPowerSourcesList(power_blob); num_keys = CFArrayGetCount(power_source_key_list); upsdebugx(1, "Number of power supplies found: %d", (int)num_keys); if(nut_debug_level >= 3) CFShow(power_source_key_list); if(num_keys < 1) { /* bail */ fatalx(EXIT_FAILURE, "Couldn't find any UPS or battery. Is your UPS shown in the 'Energy Saver' control panel?"); } for(index=0; index < num_keys; index++) { upsdebugx(2, "Retrieving power source #%ld", index+1); potential_key = CFArrayGetValueAtIndex(power_source_key_list, index); upsdebugx(3, "Power source key/index:"); if(nut_debug_level >= 3) CFShow(potential_key); power_dictionary = IOPSGetPowerSourceDescription(power_blob, potential_key); assert(power_dictionary); CFRetain(power_dictionary); upsdebugx(2, "Getting 'Name' key (UPS model)"); potential_model = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSNameKey)); CFRetain(potential_model); if(CFStringGetCString(potential_model, potential_model_name, sizeof(potential_model_name), kCFStringEncodingUTF8)) { upsdebugx(1, " model name: %s", potential_model_name); } else { upsdebugx(1, " (failed to retrieve 'Name')"); } CFRelease(power_dictionary); if(model_name) { ret = regexec(&model_regex, potential_model_name, 0,0,0); if(!ret) { upsdebugx(2, "Matched model name"); break; } } CFRelease(potential_model); } if(model_name) { regfree(&model_regex); } if(ret) { if(model_name) { fatalx(EXIT_FAILURE, "Couldn't find UPS or battery matching 'model' (%s)", model_name); } else { fatalx(EXIT_FAILURE, "Couldn't find an UPS or battery."); } } g_power_source_name = potential_model; /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ /* don't try to detect the UPS here */ /* do stuff */ } void upsdrv_cleanup(void) { upsdebugx(1, "Cleanup: release references"); CFRelease(g_power_source_name); /* free(dynamic_mem); */ /* ser_close(upsfd, device_path); */ } nut-2.7.4/drivers/baytech-mib.h0000644000175000017500000000022112640443572013270 00000000000000#ifndef BAYTECH_MIB_H #define BAYTECH_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t baytech; #endif /* BAYTECH_MIB_H */ nut-2.7.4/drivers/riello_usb.c0000644000175000017500000007057312640473702013255 00000000000000/* * riello_usb.c: support for Riello USB protocol based UPSes * * A document describing the protocol implemented by this driver can be * found online at: * * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf * * Copyright (C) 2012 - Elio Parisi * * 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 * * Reference of the derivative work: blazer driver */ #include #include "main.h" #include "libusb.h" #include "usb-common.h" #include "riello.h" #define DRIVER_NAME "Riello USB driver" #define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Elio Parisi ", DRV_EXPERIMENTAL, { NULL } }; uint8_t bufOut[BUFFER_SIZE]; uint8_t bufIn[BUFFER_SIZE]; uint8_t gpser_error_control; uint8_t input_monophase; uint8_t output_monophase; extern uint8_t commbyte; extern uint8_t wait_packet; extern uint8_t foundnak; extern uint8_t foundbadcrc; extern uint8_t buf_ptr_length; extern uint8_t requestSENTR; TRielloData DevData; static usb_communication_subdriver_t *usb = &usb_subdriver; static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static USBDeviceMatcher_t *reopen_matcher = NULL; static USBDeviceMatcher_t *regex_matcher = NULL; static int (*subdriver_command)(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen) = NULL; void ussleep(long usec) { if (usec == 1) usec = 400; else usec *= 1000; usleep(usec); } static int cypress_setfeatures() { int ret; bufOut[0] = 0xB0; bufOut[1] = 0x4; bufOut[2] = 0x0; bufOut[3] = 0x0; bufOut[4] = 0x3; /* Write features report */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, /* HID_REPORT_SET = 0x09 */ 0 + (0x03 << 8), /* HID_REPORT_TYPE_FEATURE */ 0, (char*) bufOut, 0x5, 1000); if (ret <= 0) { upsdebugx(3, "send: %s", ret ? usb_strerror() : "error"); return ret; } upsdebugx(3, "send: features report ok"); return ret; } int Send_USB_Packet(uint8_t *send_str, uint16_t numbytes) { uint8_t USB_buff_pom[10]; int i, err, size, errno; /* is input correct ? */ if ((!send_str) || (!numbytes)) return -1; size = 7; /* routine which parse report into 4-bytes packet */ for (i=0; i<(numbytes/size); i++) { USB_buff_pom[0] = 0x37; USB_buff_pom[1] = send_str[(i*7)]; USB_buff_pom[2] = send_str[(i*7)+1]; USB_buff_pom[3] = send_str[(i*7)+2]; USB_buff_pom[4] = send_str[(i*7)+3]; USB_buff_pom[5] = send_str[(i*7)+4]; USB_buff_pom[6] = send_str[(i*7)+5]; USB_buff_pom[7] = send_str[(i*7)+6]; err = usb_bulk_write(udev, 0x2, (char*) USB_buff_pom, 8, 1000); if (err < 0) { upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno)); return err; } ussleep(USB_WRITE_DELAY); } /* send rest of packet */ if (numbytes % size) { i = numbytes/size; memset(USB_buff_pom, '0', sizeof(USB_buff_pom)); USB_buff_pom[0] = 0x30+(numbytes%7); if ((i*7) 0) upsdebugx(3, "read: %02X %02X %02X %02X %02X %02X %02X %02X", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7]); if (err < 0){ upsdebugx(3, "USB: Get_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno)); return err; } /* copy to buffer */ size = inBuf[0] & 0x07; if (size) memcpy(buffer, &inBuf[1], size); return(size); } static int cypress_command(uint8_t *buffer, uint8_t *buf, uint16_t length, uint16_t buflen) { int loop = 0; int ret, i = 0; uint8_t USB_buff[BUFFER_SIZE]; /* read to flush buffer */ riello_init_serial(); /* send packet */ ret = Send_USB_Packet(buffer, length); if (ret < 0) { upsdebugx(3, "Cypress_command send: err %d", ret ); return ret; } upsdebugx(3, "send ok"); memset(buf, 0, buflen); while ((buf_ptr_length < BUFFER_SIZE) && wait_packet) { memset(USB_buff, 0, sizeof(USB_buff)); ret = Get_USB_Packet(USB_buff); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret < 0) { upsdebugx(3, "Cypress_command read: err %d", ret ); return ret; } for (i = 0; i < ret; i++ ) { commbyte = USB_buff[i]; riello_parse_serialport(DEV_RIELLOGPSER, buf, gpser_error_control); } loop++; if (loop>300){ wait_packet=0; upsdebugx(1, "wait_packet reset"); } ussleep(10); } upsdebugx(3, "in read: %u", buf_ptr_length); return buf_ptr_length; } static void *cypress_subdriver(USBDevice_t *device) { subdriver_command = &cypress_command; return NULL; } /* Riello (Cypress Semiconductor Corp.) */ #define RIELLO_VENDORID 0x04b4 static usb_device_id_t riello_usb_id[] = { /* various models */ { USB_DEVICE(RIELLO_VENDORID, 0x5500), &cypress_subdriver }, /* end of list */ {-1, -1, NULL} }; static int device_match_func(USBDevice_t *hd, void *privdata) { if (subdriver_command) { return 1; } switch (is_usb_device_supported(riello_usb_id, hd)) { case SUPPORTED: return 1; case POSSIBLY_SUPPORTED: case NOT_SUPPORTED: default: return 0; } } static USBDeviceMatcher_t device_matcher = { &device_match_func, NULL, NULL }; /* * Callback that is called by usb_device_open() that handles USB device * settings prior to accepting the devide. At the very least claim the * device here. Detaching the kernel driver will be handled by the * caller, don't do this here. Return < 0 on error, 0 or higher on * success. */ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device, unsigned char *rdbuf, int rdlen) { /*if (usb_set_configuration(handle, 1) < 0) { upslogx(LOG_WARNING, "Can't set USB configuration: %s", usb_strerror()); return -1; } */ if (usb_claim_interface(handle, 0) < 0) { upslogx(LOG_WARNING, "Can't claim USB interface: %s", usb_strerror()); return -1; } /* TODO: HID SET_IDLE to 0 (not necessary?) */ return 1; } /* * Generic command processing function. Send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on * success. */ int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen) { int ret; if (udev == NULL) { ret = usb->open(&udev, &usbdevice, reopen_matcher, &driver_callback); upsdebugx (3, "riello_command err udev NULL : %d ", ret); if (ret < 0) return ret; upsdrv_initinfo(); //reconekt usb cable } ret = (*subdriver_command)(cmd, buf, length, buflen); if (ret >= 0) { upsdebugx (3, "riello_command ok: %u", ret); return ret; } upsdebugx (3, "riello_command err: %d", ret); switch (ret) { case -EBUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); case -EPERM: /* Operation not permitted */ fatal_with_errno(EXIT_FAILURE, "Permissions problem"); case -EPIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; } #ifdef ETIME case -ETIME: /* Timer expired */ #endif if (usb_reset(udev) == 0) { upsdebugx(1, "Device reset handled"); } case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ /* Uh oh, got to reconnect! */ usb->close(udev); udev = NULL; break; case -ETIMEDOUT: /* Connection timed out */ upsdebugx (3, "riello_command err: Resource temporarily unavailable"); case -EOVERFLOW: /* Value too large for defined data type */ #ifdef EPROTO case -EPROTO: /* Protocol error */ #endif break; default: break; } return ret; } int get_ups_nominal() { uint8_t length; int recv; length = riello_prepare_gn(&bufOut[0], gpser_error_control); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GN); if (recv < 0){ upsdebugx (3, "Get nominal err: read byte: %d", recv); return recv; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get nominal Ko: bad CRC or Checksum"); return -1; } /* mandatory */ if (!wait_packet && foundnak) { upsdebugx (3, "Get nominal Ko: command not supported"); return -1; } upsdebugx (3, "Get nominal Ok: read byte: %d", recv); riello_parse_gn(&bufIn[0], &DevData); return 0; } int get_ups_status() { uint8_t numread, length; int recv; length = riello_prepare_rs(&bufOut[0], gpser_error_control); if (input_monophase) numread = LENGTH_RS_MM; else if (output_monophase) numread = LENGTH_RS_TM; else numread = LENGTH_RS_TT; recv = riello_command(&bufOut[0], &bufIn[0], length, numread); if (recv < 0){ upsdebugx (3, "Get status err: read byte: %d", recv); return recv; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get status Ko: bad CRC or Checksum"); return -1; } /* mandatory */ if (!wait_packet && foundnak) { upsdebugx (3, "Get status Ko: command not supported"); return -1; } upsdebugx (3, "Get status Ok: read byte: %d", recv); riello_parse_rs(&bufIn[0], &DevData, numread); return 0; } int get_ups_extended() { uint8_t length; int recv; length = riello_prepare_re(&bufOut[0], gpser_error_control); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RE); if (recv < 0){ upsdebugx (3, "Get extended err: read byte: %d", recv); return recv; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get extended Ko: bad CRC or Checksum"); return -1; } /* optional */ if (!wait_packet && foundnak) { upsdebugx (3, "Get extended Ko: command not supported"); return 0; } upsdebugx (3, "Get extended Ok: read byte: %d", recv); riello_parse_re(&bufIn[0], &DevData); return 0; } int get_ups_statuscode() { uint8_t length; int recv; length = riello_prepare_rc(&bufOut[0], gpser_error_control); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RC); if (recv < 0){ upsdebugx (3, "Get statuscode err: read byte: %d", recv); return recv; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum"); return -1; } /* optional */ if (!wait_packet && foundnak) { upsdebugx (3, "Get statuscode Ko: command not supported"); return 0; } upsdebugx (3, "Get statuscode Ok: read byte: %d", recv); riello_parse_rc(&bufIn[0], &DevData); return 0; } int riello_instcmd(const char *cmdname, const char *extra) { uint8_t length; int recv; uint16_t delay; const char *delay_char; if (!riello_test_bit(&DevData.StatusCode[0], 1)) { if (!strcasecmp(cmdname, "load.off")) { delay = 0; length = riello_prepare_cs(bufOut, gpser_error_control, delay); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command load.off err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.off Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.off Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.off Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.off.delay")) { delay_char = dstate_getinfo("ups.delay.shutdown"); delay = atoi(delay_char); length = riello_prepare_cs(bufOut, gpser_error_control, delay); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command load.off.delay err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.off.delay Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.off.delay Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { delay = 0; length = riello_prepare_cr(bufOut, gpser_error_control, delay); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command load.on err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.on Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.on Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.on Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on.delay")) { delay_char = dstate_getinfo("ups.delay.reboot"); delay = atoi(delay_char); length = riello_prepare_cr(bufOut, gpser_error_control, delay); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command load.on.delay err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.on.delay Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.on.delay Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } } else { if (!strcasecmp(cmdname, "shutdown.return")) { delay_char = dstate_getinfo("ups.delay.shutdown"); delay = atoi(delay_char); length = riello_prepare_cs(bufOut, gpser_error_control, delay); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command shutdown.return err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command shutdown.return Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command shutdown.return Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } } if (!strcasecmp(cmdname, "shutdown.stop")) { length = riello_prepare_cd(bufOut, gpser_error_control); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command shutdown.stop err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command shutdown.stop Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command shutdown.stop Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.panel.start")) { length = riello_prepare_tp(bufOut, gpser_error_control); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command test.panel.start err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command test.panel.start Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command test.panel.start Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { length = riello_prepare_tb(bufOut, gpser_error_control); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); if (recv < 0) { upsdebugx (3, "Command test.battery.start err: read byte: %d", recv); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command test.battery.start Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command test.battery.start Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command test.battery.start Ok: read byte: %d", recv); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } int start_ups_comm() { uint16_t length; int recv; upsdebugx (2, "entering start_ups_comm()\n"); cypress_setfeatures(); length = riello_prepare_gi(&bufOut[0]); recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GI); if (recv < 0) { upsdebugx (3, "Get identif err: read byte: %d", recv); return recv; } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get identif Ko: bad CRC or Checksum"); return 1; } if (!wait_packet && foundnak) { upsdebugx (3, "Get identif Ko: command not supported"); return 1; } upsdebugx (3, "Get identif Ok: read byte: %u", recv); return 0; } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } void upsdrv_initups(void) { const struct { const char *name; int (*command)(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen); } subdriver[] = { { "cypress", &cypress_command }, { NULL } }; int ret; char *regex_array[6]; char *subdrv = getval("subdriver"); regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); /* pick up the subdriver name if set explicitly */ if (subdrv) { int i; if (!regex_array[0] || !regex_array[1]) { fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory."); } for (i = 0; subdriver[i].name; i++) { if (strcasecmp(subdrv, subdriver[i].name)) { continue; } subdriver_command = subdriver[i].command; break; } if (!subdriver_command) { fatalx(EXIT_FAILURE, "Subdriver \"%s\" not found!", subdrv); } } ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch (ret) { case -1: fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); case 0: break; /* all is well */ default: fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]); } /* link the matchers */ regex_matcher->next = &device_matcher; ret = usb->open(&udev, &usbdevice, regex_matcher, &driver_callback); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. Please check your device availability with 'lsusb'\n" "and make sure you have an up-to-date version of NUT. If this does not help,\n" "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n" "options specified. Please refer to the man page for details about these options\n" "(man 8 riello_usb).\n"); } if (!subdriver_command) { fatalx(EXIT_FAILURE, "No subdriver selected"); } /* create a new matcher for later reopening */ ret = USBNewExactMatcher(&reopen_matcher, &usbdevice); if (ret) { fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); } /* link the matchers */ reopen_matcher->next = regex_matcher; dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); } void upsdrv_initinfo(void) { int ret; ret = start_ups_comm(); if (ret < 0) fatalx(EXIT_FAILURE, "No communication with UPS"); else if (ret > 0) fatalx(EXIT_FAILURE, "Bad checksum or NACK"); else upsdebugx(2, "Communication with UPS established"); riello_parse_gi(&bufIn[0], &DevData); gpser_error_control = DevData.Identif_bytes[4]-0x30; if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '2')) input_monophase = 1; else { input_monophase = 0; dstate_setinfo("input.phases", "%u", 3); dstate_setinfo("input.phases", "%u", 3); dstate_setinfo("input.bypass.phases", "%u", 3); } if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '3')) output_monophase = 1; else { output_monophase = 0; dstate_setinfo("output.phases", "%u", 3); } dstate_setinfo("device.mfr", "RPS S.p.a."); dstate_setinfo("device.model", "%s", (unsigned char*) DevData.ModelStr); dstate_setinfo("device.serial", "%s", (unsigned char*) DevData.Identification); dstate_setinfo("device.type", "ups"); dstate_setinfo("ups.mfr", "RPS S.p.a."); dstate_setinfo("ups.model", "%s", (unsigned char*) DevData.ModelStr); dstate_setinfo("ups.serial", "%s", (unsigned char*) DevData.Identification); dstate_setinfo("ups.firmware", "%s", (unsigned char*) DevData.Version); if (get_ups_nominal() == 0) { dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW); dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA); dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout); dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0); dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat); dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap); } /* commands ----------------------------------------------- */ dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("load.off.delay"); dstate_addcmd("load.on.delay"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stop"); dstate_addcmd("test.battery.start"); dstate_addcmd("test.panel.start"); /* install handlers */ /* upsh.setvar = hid_set_value; setvar; */ /* note: for a transition period, these data are redundant! */ /* dstate_setinfo("device.mfr", "skel manufacturer"); */ /* dstate_setinfo("device.model", "longrun 15000"); */ upsh.instcmd = riello_instcmd; } void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ for (retry = 1; retry <= MAXTRIES; retry++) { if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } if (riello_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } fatalx(EXIT_SUCCESS, "Shutting down"); } fatalx(EXIT_FAILURE, "Shutdown failed!"); } void upsdrv_updateinfo(void) { uint8_t getextendedOK; static int countlost = 0; int stat; upsdebugx(1, "countlost %d",countlost); if (countlost > 0){ upsdebugx(1, "Communication with UPS is lost: status read failed!"); if (countlost == COUNTLOST) { dstate_datastale(); upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!"); } } stat = get_ups_status(); upsdebugx(1, "get_ups_status() %d",stat ); if (stat < 0) { if (countlost < COUNTLOST) countlost++; return; } if (get_ups_extended() == 0) getextendedOK = 1; else getextendedOK = 0; if (countlost == COUNTLOST) upslogx(LOG_NOTICE, "Communication with UPS is re-established!"); dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0); dstate_setinfo("input.bypass.frequency", "%.2f", DevData.Fbypass/10.0); dstate_setinfo("output.frequency", "%.2f", DevData.Fout/10.0); dstate_setinfo("battery.voltage", "%.1f", DevData.Ubat/10.0); dstate_setinfo("battery.charge", "%u", DevData.BatCap); dstate_setinfo("battery.runtime", "%u", DevData.BatTime*60); dstate_setinfo("ups.temperature", "%u", DevData.Tsystem); if (input_monophase) { dstate_setinfo("input.voltage", "%u", DevData.Uinp1); dstate_setinfo("input.bypass.voltage", "%u", DevData.Ubypass1); } else { dstate_setinfo("input.L1-N.voltage", "%u", DevData.Uinp1); dstate_setinfo("input.L2-N.voltage", "%u", DevData.Uinp2); dstate_setinfo("input.L3-N.voltage", "%u", DevData.Uinp3); dstate_setinfo("input.bypass.L1-N.voltage", "%u", DevData.Ubypass1); dstate_setinfo("input.bypass.L2-N.voltage", "%u", DevData.Ubypass2); dstate_setinfo("input.bypass.L3-N.voltage", "%u", DevData.Ubypass3); } if (output_monophase) { dstate_setinfo("output.voltage", "%u", DevData.Uout1); dstate_setinfo("output.power.percent", "%u", DevData.Pout1); dstate_setinfo("ups.load", "%u", DevData.Pout1); } else { dstate_setinfo("output.L1-N.voltage", "%u", DevData.Uout1); dstate_setinfo("output.L2-N.voltage", "%u", DevData.Uout2); dstate_setinfo("output.L3-N.voltage", "%u", DevData.Uout3); dstate_setinfo("output.L1.power.percent", "%u", DevData.Pout1); dstate_setinfo("output.L2.power.percent", "%u", DevData.Pout2); dstate_setinfo("output.L3.power.percent", "%u", DevData.Pout3); dstate_setinfo("ups.load", "%u", (DevData.Pout1+DevData.Pout2+DevData.Pout3)/3); } status_init(); /* AC Fail */ if (riello_test_bit(&DevData.StatusCode[0], 1)) status_set("OB"); else status_set("OL"); /* LowBatt */ if ((riello_test_bit(&DevData.StatusCode[0], 1)) && (riello_test_bit(&DevData.StatusCode[0], 0))) status_set("LB"); /* Standby */ if (!riello_test_bit(&DevData.StatusCode[0], 3)) status_set("OFF"); /* On Bypass */ if (riello_test_bit(&DevData.StatusCode[1], 3)) status_set("BYPASS"); /* Overload */ if (riello_test_bit(&DevData.StatusCode[4], 2)) status_set("OVER"); /* Buck */ if (riello_test_bit(&DevData.StatusCode[1], 0)) status_set("TRIM"); /* Boost */ if (riello_test_bit(&DevData.StatusCode[1], 1)) status_set("BOOST"); /* Replace battery */ if (riello_test_bit(&DevData.StatusCode[2], 0)) status_set("RB"); /* Charging battery */ if (riello_test_bit(&DevData.StatusCode[2], 2)) status_set("CHRG"); status_commit(); dstate_dataok(); if (getextendedOK) { dstate_setinfo("output.L1.power", "%u", DevData.Pout1VA); dstate_setinfo("output.L2.power", "%u", DevData.Pout2VA); dstate_setinfo("output.L3.power", "%u", DevData.Pout3VA); dstate_setinfo("output.L1.realpower", "%u", DevData.Pout1W); dstate_setinfo("output.L2.realpower", "%u", DevData.Pout2W); dstate_setinfo("output.L3.realpower", "%u", DevData.Pout3W); dstate_setinfo("output.L1.current", "%u", DevData.Iout1); dstate_setinfo("output.L2.current", "%u", DevData.Iout2); dstate_setinfo("output.L3.current", "%u", DevData.Iout3); } poll_interval = 2; countlost = 0; /* if (get_ups_statuscode() != 0) upsdebugx(2, "Communication is lost"); else { }*/ /* * ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS); * * if (ret < STATUS_LEN) { * upslogx(LOG_ERR, "Short read from UPS"); * dstate_datastale(); * return; * } */ /* dstate_setinfo("var.name", ""); */ /* if (ioctl(upsfd, TIOCMGET, &flags)) { * upslog_with_errno(LOG_ERR, "TIOCMGET"); * dstate_datastale(); * return; * } */ /* status_init(); * * if (ol) * status_set("OL"); * else * status_set("OB"); * ... * * status_commit(); * * dstate_dataok(); */ /* * poll_interval = 2; */ } void upsdrv_cleanup(void) { usb->close(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(usbdevice.Vendor); free(usbdevice.Product); free(usbdevice.Serial); free(usbdevice.Bus); } nut-2.7.4/drivers/blazer_usb.c0000644000175000017500000004055712640473702013245 00000000000000/* * blazer_usb.c: support for Megatec/Q1 USB protocol based UPSes * * A document describing the protocol implemented by this driver can be * found online at "http://www.networkupstools.org/protocols/megatec.html". * * Copyright (C) 2003-2009 Arjen de Korte * Copyright (C) 2011-2012 Arnaud Quette * * 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 "main.h" #include "libusb.h" #include "usb-common.h" #include "blazer.h" #define DRIVER_NAME "Megatec/Q1 protocol USB driver" #define DRIVER_VERSION "0.12" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte \n" \ "Arnaud Quette ", DRV_BETA, { NULL } }; #ifndef TESTING static usb_communication_subdriver_t *usb = &usb_subdriver; static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static USBDeviceMatcher_t *reopen_matcher = NULL; static USBDeviceMatcher_t *regex_matcher = NULL; static int langid_fix = -1; static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = NULL; static int cypress_command(const char *cmd, char *buf, size_t buflen) { char tmp[SMALLBUF]; int ret; size_t i; memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), "%s", cmd); for (i = 0; i < strlen(tmp); i += ret) { /* Write data in 8-byte chunks */ /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 5000); if (ret <= 0) { upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout"); return ret; } } upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); memset(buf, 0, buflen); for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); return ret; } } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return i; } static int phoenix_command(const char *cmd, char *buf, size_t buflen) { char tmp[SMALLBUF]; int ret; size_t i; for (i = 0; i < 8; i++) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 1000); */ ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000); /* * This USB to serial implementation is crappy. In order to read correct * replies we need to flush the output buffers of the converter until we * get no more data (ie, it times out). */ switch (ret) { case -EPIPE: /* Broken pipe */ usb_clear_halt(udev, 0x81); case -ETIMEDOUT: /* Connection timed out */ break; } if (ret < 0) { upsdebugx(3, "flush: %s", usb_strerror()); break; } upsdebug_hex(4, "dump", tmp, ret); } memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), "%s", cmd); for (i = 0; i < strlen(tmp); i += ret) { /* Write data in 8-byte chunks */ /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 1000); if (ret <= 0) { upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout"); return ret; } } upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); memset(buf, 0, buflen); for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); return ret; } } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return i; } static int ippon_command(const char *cmd, char *buf, size_t buflen) { char tmp[64]; int ret, len; size_t i; snprintf(tmp, sizeof(tmp), "%s", cmd); for (i = 0; i < strlen(tmp); i += ret) { /* Write data in 8-byte chunks */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x2, 0, &tmp[i], 8, 1000); if (ret <= 0) { upsdebugx(3, "send: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out"); return ret; } } upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); /* Read all 64 bytes of the reply in one large chunk */ ret = usb_interrupt_read(udev, 0x81, tmp, sizeof(tmp), 1000); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out"); return ret; } /* * As Ippon will always return 64 bytes in response, we have to * calculate and return length of actual response data here. * Empty response will look like 0x00 0x0D, otherwise it will be * data string terminated by 0x0D. */ len = (int)strcspn(tmp, "\r"); upsdebugx(3, "read: %.*s", len, tmp); if (len > 0) { len ++; } snprintf(buf, buflen, "%.*s", len, tmp); return len; } static int krauler_command(const char *cmd, char *buf, size_t buflen) { /* * Still not implemented: * 0x6 T (don't know how to pass the parameter) * 0x68 and 0x69 both cause shutdown after an undefined interval */ const struct { const char *str; /* Megatec command */ const int index; /* Krauler string index for this command */ const char prefix; /* character to replace the first byte in reply */ } command[] = { { "Q1\r", 0x03, '(' }, { "F\r", 0x0d, '#' }, { "I\r", 0x0c, '#' }, { "T\r", 0x04, '\r' }, { "TL\r", 0x05, '\r' }, { "Q\r", 0x07, '\r' }, { "C\r", 0x0b, '\r' }, { "CT\r", 0x0b, '\r' }, { NULL } }; int i; upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); for (i = 0; command[i].str; i++) { int retry; if (strcmp(cmd, command[i].str)) { continue; } for (retry = 0; retry < 10; retry++) { int ret; if (langid_fix != -1) { /* Apply langid_fix value */ ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen); } else { ret = usb_get_string_simple(udev, command[i].index, buf, buflen); } if (ret <= 0) { upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); return ret; } /* this may serve in the future */ upsdebugx(1, "received %d (%d)", ret, buf[0]); if (langid_fix != -1) { /* Limit this check, at least for now */ /* Invalid receive size - message corrupted */ if (ret != buf[0]) { upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); continue; } /* Simple unicode -> ASCII inplace conversion * FIXME: this code is at least shared with mge-shut/libshut * Create a common function? */ unsigned int di, si, size = buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) break; if (buf[si + 1]) /* high byte */ buf[di++] = '?'; else buf[di++] = buf[si]; } buf[di] = 0; ret = di; } /* "UPS No Ack" has a special meaning */ if (!strcasecmp(buf, "UPS No Ack")) { upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); continue; } /* Replace the first byte of what we received with the correct one */ buf[0] = command[i].prefix; upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return ret; } return 0; } /* echo the unknown command back */ upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); return snprintf(buf, buflen, "%s", cmd); } static void *cypress_subdriver(USBDevice_t *device) { subdriver_command = &cypress_command; return NULL; } static void *ippon_subdriver(USBDevice_t *device) { subdriver_command = &ippon_command; return NULL; } static void *krauler_subdriver(USBDevice_t *device) { subdriver_command = &krauler_command; return NULL; } static void *phoenix_subdriver(USBDevice_t *device) { subdriver_command = &phoenix_command; return NULL; } static usb_device_id_t blazer_usb_id[] = { { USB_DEVICE(0x05b8, 0x0000), &cypress_subdriver }, /* Agiler UPS */ { USB_DEVICE(0x0001, 0x0000), &krauler_subdriver }, /* Krauler UP-M500VA */ { USB_DEVICE(0xffff, 0x0000), &krauler_subdriver }, /* Ablerex 625L USB */ { USB_DEVICE(0x0665, 0x5161), &cypress_subdriver }, /* Belkin F6C1200-UNV */ { USB_DEVICE(0x06da, 0x0002), &cypress_subdriver }, /* Online Yunto YQ450 */ { USB_DEVICE(0x06da, 0x0003), &ippon_subdriver }, /* Mustek Powermust */ { USB_DEVICE(0x06da, 0x0004), &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */ { USB_DEVICE(0x06da, 0x0005), &cypress_subdriver }, /* Phoenixtec Innova RT */ { USB_DEVICE(0x06da, 0x0201), &cypress_subdriver }, /* Phoenixtec Innova T */ { USB_DEVICE(0x06da, 0x0601), &phoenix_subdriver }, /* Online Zinto A */ { USB_DEVICE(0x0f03, 0x0001), &cypress_subdriver }, /* Unitek Alpha 1200Sx */ { USB_DEVICE(0x14f0, 0x00c9), &phoenix_subdriver }, /* GE EP series */ /* end of list */ {-1, -1, NULL} }; static int device_match_func(USBDevice_t *hd, void *privdata) { if (subdriver_command) { return 1; } switch (is_usb_device_supported(blazer_usb_id, hd)) { case SUPPORTED: return 1; case POSSIBLY_SUPPORTED: case NOT_SUPPORTED: default: return 0; } } static USBDeviceMatcher_t device_matcher = { &device_match_func, NULL, NULL }; #endif /* TESTING */ /* * Generic command processing function. Send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on * success. */ int blazer_command(const char *cmd, char *buf, size_t buflen) { #ifndef TESTING int ret; if (udev == NULL) { ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); if (ret < 1) { return ret; } } ret = (*subdriver_command)(cmd, buf, buflen); if (ret >= 0) { return ret; } switch (ret) { case -EBUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); case -EPERM: /* Operation not permitted */ fatal_with_errno(EXIT_FAILURE, "Permissions problem"); case -EPIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; } #ifdef ETIME case -ETIME: /* Timer expired */ #endif if (usb_reset(udev) == 0) { upsdebugx(1, "Device reset handled"); } case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ /* Uh oh, got to reconnect! */ usb->close(udev); udev = NULL; break; case -ETIMEDOUT: /* Connection timed out */ case -EOVERFLOW: /* Value too large for defined data type */ #ifdef EPROTO case -EPROTO: /* Protocol error */ #endif default: break; } return ret; #else const struct { const char *command; const char *answer; } testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 2.27 30.0 00101000\r" }, { "F\r", "#230.0 000 024.0 50.0\r" }, { "I\r", "#------------- ------ VT12046Q \r" }, { NULL } }; int i; memset(buf, 0, buflen); for (i = 0; testing[i].command; i++) { if (strcasecmp(cmd, testing[i].command)) { continue; } return snprintf(buf, buflen, "%s", testing[i].answer); } return snprintf(buf, buflen, "%s", testing[i].command); #endif } void upsdrv_help(void) { printf("Read The Fine Manual ('man 8 blazer_usb')\n"); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection"); nut_usb_addvars(); addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)"); blazer_makevartable(); } void upsdrv_initups(void) { #ifndef TESTING const struct { const char *name; int (*command)(const char *cmd, char *buf, size_t buflen); } subdriver[] = { { "cypress", &cypress_command }, { "phoenix", &phoenix_command }, { "ippon", &ippon_command }, { "krauler", &krauler_command }, { NULL } }; int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ char *regex_array[6]; char *subdrv = getval("subdriver"); regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); /* check for language ID workaround (#1) */ if (getval("langid_fix")) { /* skip "0x" prefix and set back to hexadecimal */ if (sscanf(getval("langid_fix") + 2, "%x", &langid_fix) != 1) { upslogx(LOG_NOTICE, "Error enabling language ID workaround"); } else { upsdebugx(2, "language ID workaround enabled (using '0x%x')", langid_fix); } } /* pick up the subdriver name if set explicitly */ if (subdrv) { int i; if (!regex_array[0] || !regex_array[1]) { fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory."); } for (i = 0; subdriver[i].name; i++) { if (strcasecmp(subdrv, subdriver[i].name)) { continue; } subdriver_command = subdriver[i].command; break; } if (!subdriver_command) { fatalx(EXIT_FAILURE, "Subdriver \"%s\" not found!", subdrv); } } ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch (ret) { case -1: fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); case 0: break; /* all is well */ default: fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]); } /* link the matchers */ regex_matcher->next = &device_matcher; ret = usb->open(&udev, &usbdevice, regex_matcher, NULL); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. Please check your device availability with 'lsusb'\n" "and make sure you have an up-to-date version of NUT. If this does not help,\n" "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n" "options specified. Please refer to the man page for details about these options\n" "(man 8 blazer_usb).\n"); } if (!subdriver_command) { fatalx(EXIT_FAILURE, "No subdriver selected"); } /* create a new matcher for later reopening */ ret = USBNewExactMatcher(&reopen_matcher, &usbdevice); if (ret) { fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); } /* link the matchers */ reopen_matcher->next = regex_matcher; dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); /* check for language ID workaround (#2) */ if (langid_fix != -1) { /* Future improvement: * Asking for the zero'th index is special - it returns a string * descriptor that contains all the language IDs supported by the * device. Typically there aren't many - often only one. The * language IDs are 16 bit numbers, and they start at the third byte * in the descriptor. See USB 2.0 specification, section 9.6.7, for * more information on this. * This should allow automatic application of the workaround */ ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf)); if (ret >= 4) { langid = tbuf[2] | (tbuf[3] << 8); upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid); } } #endif blazer_initups(); } void upsdrv_initinfo(void) { blazer_initinfo(); } void upsdrv_cleanup(void) { #ifndef TESTING usb->close(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(usbdevice.Vendor); free(usbdevice.Product); free(usbdevice.Serial); free(usbdevice.Bus); #endif } nut-2.7.4/drivers/apc-hid.h0000644000175000017500000000225612640443572012423 00000000000000/* apc-hid.h - data to monitor APC USB/HID devices with NUT * * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 John Stamp * 2005 Peter Selinger * * Sponsored by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef APC_HID_H #define APC_HID_H #include "usbhid-ups.h" extern subdriver_t apc_subdriver; extern int max_report_size; #endif /* APC_HID_H */ nut-2.7.4/drivers/raritan-pdu-mib.c0000644000175000017500000001421112667537407014110 00000000000000/* raritan-mib.c - data to monitor Raritan PDUs (Basic and Complex) * * Copyright (C) 2008 * Arnaud Quette * * Sponsored by Eaton * and MGE Office Protection Systems * * 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 "raritan-pdu-mib.h" #define RARITAN_MIB_VERSION "0.5" /* Raritan MIB * this one uses the same MIB as Eaton Revelation, * but with a different entry point */ #define RARITAN_BASE_OID ".1.3.6.1.4.1.13742" #define RARITAN_SYSOID RARITAN_BASE_OID #define RARITAN_OID_MODEL_NAME ".1.3.6.1.4.1.13742.1.1.12.0" #define DO_OFF 0 #define DO_ON 1 #define DO_CYCLE 2 static info_lkp_t outlet_status_info[] = { { -1, "error" }, { 0, "off" }, { 1, "on" }, { 2, "cycling" }, /* transitional status */ { 0, NULL } }; /* Snmp2NUT lookup table for Raritan MIB */ static snmp_info_t raritan_mib[] = { /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.6.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.13.0", "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.temperature", 0, 1, ".1.3.6.1.4.1.13742.1.3.1.5.0", NULL, 0, NULL, NULL }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.count", 0, 1, ".1.3.6.1.4.1.13742.1.2.1.0", "0", 0, NULL }, { "outlet.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.1" ".0", NULL, 0, NULL, NULL }, { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.2" ".0", NULL, 0, NULL, NULL }, { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.3" ".0", NULL, 0, NULL, NULL }, { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.4" ".0", NULL, 0, NULL, NULL }, /* outlet template definition * Caution: the index of the data start at 0, while the name is +1 * ie outlet.1 => .0 */ { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL, NULL }, { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.2.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_FLAG_OK | SU_OUTLET, &outlet_status_info[0], NULL }, { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.4.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.current.maximum", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.5.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.7.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.6.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.1.2.2.1.9.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.8.%i", NULL, SU_OUTLET, NULL, NULL }, /* FIXME: * - delay for startup/shutdown sequence * - support for Ambient page temperatureSensorCount" src="snmp:$sysoid.2.1.0 ambient.temperature src="snmp:$sysoid.2.2.1.3.$indiceSensor => seems dumb! ambient.humidity src="snmp:$sysoid.2.4.1.3.$indiceSensor */ /* instant commands. */ /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ /* no counterpart found! { "outlet.load.off", 0, DO_OFF, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", NULL, SU_TYPE_CMD, NULL, NULL }, { "outlet.load.on", 0, DO_ON, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", NULL, SU_TYPE_CMD, NULL, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", NULL, SU_TYPE_CMD, NULL, NULL }, */ { "outlet.%i.load.off", 0, DO_OFF, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.on", 0, DO_ON, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.cycle", 0, DO_CYCLE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } }; mib2nut_info_t raritan = { "raritan", RARITAN_MIB_VERSION, NULL, RARITAN_OID_MODEL_NAME, raritan_mib, RARITAN_SYSOID }; nut-2.7.4/drivers/xppc-mib.c0000644000175000017500000001141712667537407012641 00000000000000/* xppc-mib.c - subdriver to monitor XPPC SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2014 Charles Lepple * * Note: this subdriver was initially generated as a "stub" by the * scripts/subdriver/gen-snmp-subdriver.sh script. * * 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 "xppc-mib.h" #define XPPC_MIB_VERSION "0.2" #define XPPC_SYSOID ".1.3.6.1.4.1.935" /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { * { 1, "OB" }, * { 2, "OL" }, * { 0, NULL } * }; */ /* upsBaseBatteryStatus */ static info_lkp_t xpcc_onbatt_info[] = { { 1, "" }, /* unknown */ { 2, "" }, /* batteryNormal */ { 3, "LB" } /* batteryLow */ }; /* upsBaseOutputStatus OBJECT-TYPE SYNTAX INTEGER { unknown(1), onLine(2), onBattery(3), onBoost(4), sleeping(5), onBypass(6), rebooting(7), standBy(8), onBuck(9) } */ static info_lkp_t xpcc_power_info[] = { { 1, "" }, /* unknown */ { 2, "OL" }, /* onLine */ { 3, "OB" }, /* onBattery */ { 4, "OL BOOST" }, /* onBoost */ { 5, "OFF" }, /* sleeping */ { 6, "BYPASS"}, /* onBypass */ { 7, "" }, /* rebooting */ { 8, "OFF" }, /* standBy */ { 9, "OL TRIM"} /* onBuck */ }; /* XPPC Snmp2NUT lookup table */ static snmp_info_t xppc_mib[] = { /* Data format: * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo * info_len: length of strings if STR * cmd value if CMD, multiplier otherwise * OID: SNMP OID or NULL * dfl: default value * flags: snmp-ups internal flags (FIXME: ...) * oid2info: lookup table between OID and NUT values * setvar: variable to set for SU_FLAG_SETINT * * Example: * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, onbatt_info }, * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { * { 1, "OB" }, * { 2, "OL" }, * { 0, NULL } * }; */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* upsBaseIdentModel.0 = STRING: "Intelligent" */ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.1.1.1.0", "Generic Phoenixtec SNMP device", SU_FLAG_OK, NULL }, /* upsBaseBatteryStatus.0 = INTEGER: batteryNormal(2) */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.2.1.1.0", "", SU_STATUS_BATT | SU_TYPE_INT | SU_FLAG_OK, xpcc_onbatt_info }, /* upsSmartBatteryCapacity.0 = INTEGER: 100 */ { "battery.charge", 0, 1, ".1.3.6.1.4.1.935.1.1.1.2.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, /* upsSmartBatteryTemperature.0 = INTEGER: 260 */ { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.2.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, /* upsSmartInputLineVoltage.0 = INTEGER: 1998 */ { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.3.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, /* upsBaseOutputStatus.0 = INTEGER: onLine(2) */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.4.1.1.0", "", SU_TYPE_INT | SU_STATUS_PWR, xpcc_power_info }, /* upsSmartOutputVoltage.0 = INTEGER: 2309 */ { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, /* upsSmartOutputFrequency.0 = INTEGER: 500 */ { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.2.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, /* upsSmartOutputLoad.0 = INTEGER: 7 */ { "ups.load", 0, 1, ".1.3.6.1.4.1.935.1.1.1.4.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t xppc = { "xppc", XPPC_MIB_VERSION, NULL, NULL, xppc_mib, XPPC_SYSOID }; nut-2.7.4/drivers/nut-ipmipsu.c0000644000175000017500000001461112640443572013401 00000000000000/* nut-ipmipsu.c - Driver for IPMI Power Supply Units (PSU) * * Copyright (C) * 2011 - 2012 Arnaud Quette * * 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 * * TODO list: * - PSU sensor monitoring (how to find the right one?) * - dump all value at init, so that we can check for other interesting data */ #include "main.h" #include "nut-ipmi.h" #define DRIVER_NAME "IPMI PSU driver" #define DRIVER_VERSION "0.30" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette \n", DRV_EXPERIMENTAL, { NULL } }; /* Note on device.status * OL: present and providing power * OFF: present but not providing power (power cable removed) * stale: not present (PSU removed) * => should we prefer RB, MISSING, ABSENT, ??? */ /* Abstract structure to allow different IPMI implementation * We currently use FreeIPMI, but OpenIPMI and others are serious * candidates! */ IPMIDevice_t ipmi_dev; /* Currently used to store FRU ID, but will probably evolve... */ int ipmi_id = -1; void upsdrv_initinfo(void) { /* try to detect the PSU here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ upsdebugx(1, "upsdrv_initinfo..."); /* print what we detected during IPMI open */ upsdebugx(1, "Detected a PSU: %s/%s", ipmi_dev.manufacturer ? ipmi_dev.manufacturer : "unknown", ipmi_dev.product ? ipmi_dev.product : "unknown"); dstate_setinfo ("device.type", "psu"); /* Publish information from the IPMI structure */ if (ipmi_dev.manufacturer) dstate_setinfo("device.mfr", "%s", ipmi_dev.manufacturer); if (ipmi_dev.product) dstate_setinfo("device.model", "%s", ipmi_dev.product); if (ipmi_dev.serial) dstate_setinfo("device.serial", "%s", ipmi_dev.serial); if (ipmi_dev.part) dstate_setinfo("device.part", "%s", ipmi_dev.part); if (ipmi_dev.date) dstate_setinfo("device.mfr.date", "%s", ipmi_dev.date); /* FIXME: move to device.id */ dstate_setinfo("ups.id", "%i", ipmi_id); /* FIXME: move to device.realpower.nominal */ if (ipmi_dev.overall_capacity != -1) dstate_setinfo("ups.realpower.nominal", "%i", ipmi_dev.overall_capacity); if (ipmi_dev.input_minvoltage != -1) dstate_setinfo("input.voltage.minimum", "%i", ipmi_dev.input_minvoltage); if (ipmi_dev.input_maxvoltage != -1) dstate_setinfo("input.voltage.maximum", "%i", ipmi_dev.input_maxvoltage); if (ipmi_dev.input_minfreq != -1) dstate_setinfo("input.frequency.low", "%i", ipmi_dev.input_minfreq); if (ipmi_dev.input_maxfreq != -1) dstate_setinfo("input.frequency.high", "%i", ipmi_dev.input_maxfreq); /* FIXME: move to device.voltage */ if (ipmi_dev.voltage != -1) dstate_setinfo("ups.voltage", "%i", ipmi_dev.voltage); if (nut_ipmi_monitoring_init() != 0) fatalx(EXIT_FAILURE, "Can't initialize IPMI monitoring"); if (nut_ipmi_get_sensors_status(&ipmi_dev) != 0) { upsdebugx(1, "Error while updating sensors values"); dstate_datastale(); } else { dstate_dataok(); } /* upsh.instcmd = instcmd; */ } void upsdrv_updateinfo(void) { upsdebugx(1, "upsdrv_updateinfo..."); /* FIXME: implement sensors monitoring */ if (nut_ipmi_get_sensors_status(&ipmi_dev) != 0) { upsdebugx(1, "Error while updating sensors values"); dstate_datastale(); } else { dstate_dataok(); } /* * poll_interval = 2; */ } void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); } /* static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } */ /* static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.test.interval")) { ser_send_buf(upsfd, ...); return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } */ void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* FIXME: need more params. addvar(VAR_VALUE, "username", "Remote server username"); addvar(VAR_VALUE, "password", "Remote server password"); addvar(VAR_VALUE, "authtype", "Authentication type to use during lan session activation"); addvar(VAR_VALUE, "type", "Type of the device to match ('psu' for \"Power Supply\")"); addvar(VAR_VALUE, "serial", "Serial number to match a specific device"); addvar(VAR_VALUE, "fruid", "FRU identifier to match a specific device"); */ } void upsdrv_initups(void) { upsdebugx(1, "upsdrv_initups..."); /* port can be expressed in various forms: * - inband: * "id?" for device (FRU) ID 0x? * "psu?" for PSU number ? * - out of band * "id?@host" * "host" => requires serial or ... */ if (!strncmp( device_path, "id", 2)) { ipmi_id = atoi(device_path+2); upsdebugx(2, "Device ID 0x%i", ipmi_id); } /* else... to select PSU number X */ /* Clear the interface structure */ ipmi_dev.ipmi_id = -1; ipmi_dev.manufacturer = NULL; ipmi_dev.product = NULL; ipmi_dev.serial = NULL; ipmi_dev.part = NULL; ipmi_dev.date = NULL; ipmi_dev.overall_capacity = -1; ipmi_dev.input_minvoltage = -1; ipmi_dev.input_maxvoltage = -1; ipmi_dev.input_minfreq = -1; ipmi_dev.input_maxfreq = -1; ipmi_dev.voltage = -1; ipmi_dev.sensors_count = 0; ipmi_dev.status = -1; ipmi_dev.input_voltage = -1; ipmi_dev.input_current = -1; ipmi_dev.temperature = -1; /* Open IPMI using the above */ nut_ipmi_open(ipmi_id, &ipmi_dev); /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ /* don't try to detect the UPS here */ } void upsdrv_cleanup(void) { upsdebugx(1, "upsdrv_cleanup..."); nut_ipmi_close(); } nut-2.7.4/drivers/powerware-mib.c0000644000175000017500000005011312667537407013676 00000000000000/* powerware-mib.c - data to monitor Powerware UPS with NUT * (using MIBs described in stdupsv1.mib and Xups.mib) * * Copyright (C) * 2005-2006 Olli Savia * 2005-2006 Niels Baggesen * 2015-2016 Arnaud Quette * * 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 "powerware-mib.h" #define PW_MIB_VERSION "0.87" /* TODO: more sysOID and MIBs support: * * Powerware UPS (Ingrasys X-SLOT and BD-SLOT): ".1.3.6.1.4.1.534.1" * Powerware PXGX cards: ".1.3.6.1.4.1.534.2.12" * PXGX 2000 cards (UPS): Get xupsIdentModel (".1.3.6.1.4.1.534.1.1.2.0") * PXGX 1000 cards (PDU/RPP/RPM): Get pduNumPanels ".1.3.6.1.4.1.534.6.6.4.1.1.1.4.0" */ /* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) */ #define POWERWARE_SYSOID ".1.3.6.1.4.1.534.1" /* Powerware UPS newer PXGX UPS cards (BladeUPS, ...) */ #define EATON_PXGX_SYSOID ".1.3.6.1.4.1.534.2.12" /* SNMP OIDs set */ #define PW_OID_MFR_NAME "1.3.6.1.4.1.534.1.1.1.0" /* XUPS-MIB::xupsIdentManufacturer.0 */ #define PW_OID_MODEL_NAME "1.3.6.1.4.1.534.1.1.2.0" /* XUPS-MIB::xupsIdentModel.0 */ #define PW_OID_FIRMREV "1.3.6.1.4.1.534.1.1.3.0" /* XUPS-MIB::xupsIdentSoftwareVersion.0 */ #define PW_OID_BATT_RUNTIME "1.3.6.1.4.1.534.1.2.1.0" /* XUPS-MIB::xupsBatTimeRemaining.0 */ #define PW_OID_BATT_VOLTAGE "1.3.6.1.4.1.534.1.2.2.0" /* XUPS-MIB::xupsBatVoltage.0 */ #define PW_OID_BATT_CURRENT "1.3.6.1.4.1.534.1.2.3.0" /* XUPS-MIB::xupsBatCurrent.0 */ #define PW_OID_BATT_CHARGE "1.3.6.1.4.1.534.1.2.4.0" /* XUPS-MIB::xupsBatCapacity.0 */ #define PW_OID_BATT_STATUS "1.3.6.1.4.1.534.1.2.5.0" /* XUPS-MIB::xupsBatteryAbmStatus.0 */ #define PW_OID_IN_FREQUENCY "1.3.6.1.4.1.534.1.3.1.0" /* XUPS-MIB::xupsInputFrequency.0 */ #define PW_OID_IN_LINE_BADS "1.3.6.1.4.1.534.1.3.2.0" /* XUPS-MIB::xupsInputLineBads.0 */ #define PW_OID_IN_LINES "1.3.6.1.4.1.534.1.3.3.0" /* XUPS-MIB::xupsInputNumPhases.0 */ #define PW_OID_IN_VOLTAGE "1.3.6.1.4.1.534.1.3.4.1.2" /* XUPS-MIB::xupsInputVoltage */ #define PW_OID_IN_CURRENT "1.3.6.1.4.1.534.1.3.4.1.3" /* XUPS-MIB::xupsInputCurrent */ #define PW_OID_IN_POWER "1.3.6.1.4.1.534.1.3.4.1.4" /* XUPS-MIB::xupsInputWatts */ #define PW_OID_OUT_LOAD "1.3.6.1.4.1.534.1.4.1.0" /* XUPS-MIB::xupsOutputLoad.0 */ #define PW_OID_OUT_FREQUENCY "1.3.6.1.4.1.534.1.4.2.0" /* XUPS-MIB::xupsOutputFrequency.0 */ #define PW_OID_OUT_LINES "1.3.6.1.4.1.534.1.4.3.0" /* XUPS-MIB::xupsOutputNumPhases.0 */ #define PW_OID_OUT_VOLTAGE "1.3.6.1.4.1.534.1.4.4.1.2" /* XUPS-MIB::xupsOutputVoltage */ #define PW_OID_OUT_CURRENT "1.3.6.1.4.1.534.1.4.4.1.3" /* XUPS-MIB::xupsOutputCurrent */ #define PW_OID_OUT_POWER "1.3.6.1.4.1.534.1.4.4.1.4" /* XUPS-MIB::xupsOutputWatts */ #define PW_OID_POWER_STATUS "1.3.6.1.4.1.534.1.4.5.0" /* XUPS-MIB::xupsOutputSource.0 */ #define PW_OID_BY_FREQUENCY "1.3.6.1.4.1.534.1.5.1.0" /* XUPS-MIB::xupsBypassFrequency.0 */ #define PW_OID_BY_LINES "1.3.6.1.4.1.534.1.5.2.0" /* XUPS-MIB::xupsBypassNumPhases.0 */ #define PW_OID_BY_VOLTAGE "1.3.6.1.4.1.534.1.5.3.1.2" /* XUPS-MIB::xupsBypassVoltage */ #define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ #define PW_OID_BATTEST_RES "1.3.6.1.4.1.534.1.8.2" /* XUPS-MIB::xupsTestBatteryStatus */ #define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1" /* XUPS-MIB::xupsControlOutputOffDelay */ #define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2" /* XUPS-MIB::xupsControlOutputOnDelay */ #define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ #define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ #define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ #define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ #define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ #define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ #define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ #define PW_OID_ALARMS "1.3.6.1.4.1.534.1.7.1.0" /* XUPS-MIB::xupsAlarms */ #define PW_OID_ALARM_OB "1.3.6.1.4.1.534.1.7.3" /* XUPS-MIB::xupsOnBattery */ #define PW_OID_ALARM_LB "1.3.6.1.4.1.534.1.7.4" /* XUPS-MIB::xupsLowBattery */ #define IETF_OID_AGENTREV "1.3.6.1.2.1.33.1.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion.0 */ #define IETF_OID_IDENT "1.3.6.1.2.1.33.1.1.5.0" /* UPS-MIB::upsIdentName.0 */ #define IETF_OID_CONF_OUT_VA "1.3.6.1.2.1.33.1.9.5.0" /* UPS-MIB::upsConfigOutputVA.0 */ #define IETF_OID_CONF_RUNTIME_LOW "1.3.6.1.2.1.33.1.9.7.0" /* UPS-MIB::upsConfigLowBattTime.0 */ #define IETF_OID_LOAD_LEVEL "1.3.6.1.2.1.33.1.4.4.1.5" /* UPS-MIB::upsOutputPercentLoad */ #define IETF_OID_AUTO_RESTART "1.3.6.1.2.1.33.1.8.5.0" /* UPS-MIB::upsAutoRestart */ /* Delay before powering off in seconds */ #define DEFAULT_OFFDELAY 30 /* Delay before powering on in seconds */ #define DEFAULT_ONDELAY 20 /* Default shutdown.return delay in seconds */ #define DEFAULT_SHUTDOWNDELAY 0 static info_lkp_t pw_alarm_ob[] = { { 1, "OB" }, { 2, "" }, { 0, NULL } } ; static info_lkp_t pw_alarm_lb[] = { { 1, "LB" }, { 2, "" }, { 0, NULL } } ; static info_lkp_t pw_pwr_info[] = { { 1, "" /* other */ }, { 2, "OFF" /* none */ }, { 3, "OL" /* normal */ }, { 4, "BYPASS" /* bypass */ }, { 5, "OB" /* battery */ }, { 6, "OL BOOST" /* booster */ }, { 7, "OL TRIM" /* reducer */ }, { 8, "OL" /* parallel capacity */ }, { 9, "OL" /* parallel redundancy */ }, { 10, "OL" /* high efficiency */ }, /* Extended status values */ { 240, "OB" /* battery (0xF0) */ }, { 100, "BYPASS" /* maintenanceBypass (0x64) */ }, { 96, "BYPASS" /* Bypass (0x60) */ }, { 81, "OL" /* high efficiency (0x51) */ }, { 80, "OL" /* normal (0x50) */ }, { 64, "OL" /* UPS supporting load, normal degraded mode (0x40) */ }, { 16, "OFF" /* none (0x10) */ }, { 0, NULL } }; static info_lkp_t pw_mode_info[] = { { 1, "" }, { 2, "" }, { 3, "normal" }, { 4, "" }, { 5, "" }, { 6, "" }, { 7, "" }, { 8, "parallel capacity" }, { 9, "parallel redundancy" }, { 10, "high efficiency" }, /* Extended status values */ { 240, "" /* battery (0xF0) */ }, { 100, "" /* maintenanceBypass (0x64) */ }, { 96, "" /* Bypass (0x60) */ }, { 81, "high efficiency" /* high efficiency (0x51) */ }, { 80, "normal" /* normal (0x50) */ }, { 64, "" /* UPS supporting load, normal degraded mode (0x40) */ }, { 16, "" /* none (0x10) */ }, { 0, NULL } }; /* Legacy implementation */ static info_lkp_t pw_battery_abm_status[] = { { 1, "CHRG" }, { 2, "DISCHRG" }, /* { 3, "Floating" }, */ /* { 4, "Resting" }, */ /* { 5, "Unknown" }, */ { 0, NULL } } ; static info_lkp_t eaton_abm_status_info[] = { { 1, "charging" }, { 2, "discharging" }, { 3, "floating" }, { 4, "resting" }, { 5, "unknown" }, /* Undefined - ABM is not activated */ { 6, "disabled" }, /* ABM Charger Disabled */ { 0, NULL } }; static info_lkp_t pw_batt_test_info[] = { { 1, "" }, /* unknown */ { 2, "Done and passed" }, { 3, "Done and error" }, { 4, "In progress" }, { 5, "Not supported" }, { 6, "Inhibited" }, { 7, "Scheduled" }, { 0, NULL } }; static info_lkp_t ietf_yes_no_info[] = { { 1, "yes" }, { 2, "no" }, { 0, NULL } }; /* Snmp2NUT lookup table */ static snmp_info_t pw_mib[] = { /* FIXME: miss device page! */ /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MFR_NAME, "", SU_FLAG_STATIC, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MODEL_NAME, "", SU_FLAG_STATIC, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_FIRMREV, "", SU_FLAG_STATIC, NULL }, { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", SU_FLAG_STATIC, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", SU_FLAG_STATIC, NULL }, { "ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", SU_OUTPUT_1, NULL }, { "ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", 0, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, &pw_pwr_info[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", SU_STATUS_BATT, &pw_alarm_ob[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_LB, "", SU_STATUS_BATT, &pw_alarm_lb[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", SU_STATUS_BATT, &pw_battery_abm_status[0] }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", SU_FLAG_STATIC | SU_FLAG_OK, &pw_mode_info[0] }, { "ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", 0, NULL }, { "ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", 0, NULL }, /* XUPS-MIB::xupsEnvAmbientTemp.0 */ { "ups.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.1.0", "", 0, NULL }, /* FIXME: These 2 data needs RFC! */ /* XUPS-MIB::xupsEnvAmbientLowerLimit.0 */ { "ups.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.2.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvAmbientUpperLimit.0 */ { "ups.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.3.0", "", 0, NULL }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATTEST_RES, "", 0, &pw_batt_test_info[0] }, { "ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AUTO_RESTART, "", SU_FLAG_OK, &ietf_yes_no_info[0] }, { "battery.charger.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", SU_STATUS_BATT, &eaton_abm_status_info[0] }, /* Battery page */ { "battery.charge", 0, 1.0, PW_OID_BATT_CHARGE, "", 0, NULL }, { "battery.runtime", 0, 1.0, PW_OID_BATT_RUNTIME, "", 0, NULL }, { "battery.voltage", 0, 1.0, PW_OID_BATT_VOLTAGE, "", 0, NULL }, { "battery.current", 0, 0.1, PW_OID_BATT_CURRENT, "", 0, NULL }, { "battery.runtime.low", 0, 60.0, IETF_OID_CONF_RUNTIME_LOW, "", 0, NULL }, /* Output page */ { "output.phases", 0, 1.0, PW_OID_OUT_LINES, "", SU_FLAG_SETINT, NULL, &output_phases }, { "output.frequency", 0, 0.1, PW_OID_OUT_FREQUENCY, "", 0, NULL }, { "output.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_1, NULL }, { "output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", SU_OUTPUT_1, NULL }, { "output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", SU_OUTPUT_1, NULL }, { "output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", SU_OUTPUT_3, NULL }, { "output.L3-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".3", "", SU_OUTPUT_3, NULL }, { "output.L1.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", SU_OUTPUT_3, NULL }, { "output.L2.current", 0, 1.0, PW_OID_OUT_CURRENT ".2", "", SU_OUTPUT_3, NULL }, { "output.L3.current", 0, 1.0, PW_OID_OUT_CURRENT ".3", "", SU_OUTPUT_3, NULL }, { "output.L1.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", SU_OUTPUT_3, NULL }, { "output.L2.realpower", 0, 1.0, PW_OID_OUT_POWER ".2", "", SU_OUTPUT_3, NULL }, { "output.L3.realpower", 0, 1.0, PW_OID_OUT_POWER ".3", "", SU_OUTPUT_3, NULL }, /* FIXME: should better be output.Lx.load */ { "output.L1.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".1", "", SU_OUTPUT_3, NULL }, { "output.L2.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".2", "", SU_OUTPUT_3, NULL }, { "output.L3.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".3", "", SU_OUTPUT_3, NULL }, { "output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", 0, NULL }, { "output.frequency.nominal", 0, 0.1, PW_OID_CONF_FREQ, "", 0, NULL }, /* Input page */ { "input.phases", 0, 1.0, PW_OID_IN_LINES, "", SU_FLAG_SETINT, NULL, &input_phases }, { "input.frequency", 0, 0.1, PW_OID_IN_FREQUENCY, "", 0, NULL }, { "input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", SU_INPUT_1, NULL }, { "input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", SU_INPUT_1, NULL }, { "input.L1-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".3", "", SU_INPUT_3, NULL }, { "input.L1.current", 0, 1.0, PW_OID_IN_CURRENT ".1", "", SU_INPUT_3, NULL }, { "input.L2.current", 0, 1.0, PW_OID_IN_CURRENT ".2", "", SU_INPUT_3, NULL }, { "input.L3.current", 0, 1.0, PW_OID_IN_CURRENT ".3", "", SU_INPUT_3, NULL }, { "input.L1.realpower", 0, 1.0, PW_OID_IN_POWER ".1", "", SU_INPUT_3, NULL }, { "input.L2.realpower", 0, 1.0, PW_OID_IN_POWER ".2", "", SU_INPUT_3, NULL }, { "input.L3.realpower", 0, 1.0, PW_OID_IN_POWER ".3", "", SU_INPUT_3, NULL }, { "input.quality", 0, 1.0, PW_OID_IN_LINE_BADS, "", 0, NULL }, { "input.voltage.nominal", 0, 1.0, PW_OID_CONF_IVOLTAGE, "", 0, NULL }, /* this segfaults? do we assume the same number of bypass phases as input phases? { "input.bypass.phases", 0, 1.0, PW_OID_BY_LINES, "", SU_FLAG_SETINT, NULL }, */ { "input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", SU_INPUT_1, NULL }, { "input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", SU_INPUT_3, NULL }, { "input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", SU_INPUT_3, NULL }, /* Ambient page */ /* XUPS-MIB::xupsEnvRemoteTemp.0 */ { "ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ { "ambient.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.9.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteTempUpperLimit.0 */ { "ambient.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.10.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteHumidity.0 */ { "ambient.humidity", 0, 1.0, "1.3.6.1.4.1.534.1.6.6.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteHumidityLowerLimit.0 */ { "ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* Shed load and restart when line power back on; cannot be canceled */ { "shutdown.return", 0, DEFAULT_SHUTDOWNDELAY, PW_OID_CONT_LOAD_SHED_AND_RESTART, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ { "shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* load off after 1 sec, shortest possible delay; 0 cancels */ { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "load.off.delay", 0, DEFAULT_OFFDELAY, PW_OID_CONT_OFFDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* load on after 1 sec, shortest possible delay; 0 cancels */ { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "load.on.delay", 0, DEFAULT_ONDELAY, PW_OID_CONT_ONDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "ups.alarms", 0, 1.0, PW_OID_ALARMS, "", 0, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } } ; static alarms_info_t pw_alarms[] = { /* xupsLowBattery */ { PW_OID_ALARM_LB, "LB", NULL }, /* xupsOutputOverload */ { ".1.3.6.1.4.1.534.1.7.7", "OVER", "Output overload!" }, /* xupsInternalFailure */ { ".1.3.6.1.4.1.534.1.7.8", NULL, "Internal failure!" }, /* xupsBatteryDischarged */ { ".1.3.6.1.4.1.534.1.7.9", NULL, "Battery discharged!" }, /* xupsInverterFailure */ { ".1.3.6.1.4.1.534.1.7.10", NULL, "Inverter failure!" }, /* xupsOnBypass * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.11", "BYPASS", "On bypass!" }, /* xupsBypassNotAvailable * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.12", NULL, "Bypass not available!" }, /* xupsOutputOff * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.13", "OFF", "Output off!" }, /* xupsInputFailure * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.14", NULL, "Input failure!" }, /* xupsBuildingAlarm * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.15", NULL, "Building alarm!" }, /* xupsShutdownImminent */ { ".1.3.6.1.4.1.534.1.7.16", NULL, "Shutdown imminent!" }, /* xupsOnInverter * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.17", NULL, "On inverter!" }, /* xupsBreakerOpen * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.20", NULL, "Breaker open!" }, /* xupsAlarmBatteryBad */ { ".1.3.6.1.4.1.534.1.7.23", "RB", "Battery bad!" }, /* xupsOutputOffAsRequested * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.24", "OFF", "Output off as requested!" }, /* xupsDiagnosticTestFailed * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.25", NULL, "Diagnostic test failure!" }, /* xupsCommunicationsLost */ { ".1.3.6.1.4.1.534.1.7.26", NULL, "Communication with UPS lost!" }, /* xupsUpsShutdownPending */ { ".1.3.6.1.4.1.534.1.7.27", NULL, "Shutdown pending!" }, /* xupsAmbientTempBad */ { ".1.3.6.1.4.1.534.1.7.29", NULL, "Bad ambient temperature!" }, /* xupsLossOfRedundancy */ { ".1.3.6.1.4.1.534.1.7.30", NULL, "Redundancy lost!" }, /* xupsAlarmTempBad */ { ".1.3.6.1.4.1.534.1.7.31", NULL, "Bad temperature!" }, /* xupsAlarmChargerFailed */ { ".1.3.6.1.4.1.534.1.7.32", NULL, "Charger failure!" }, /* xupsAlarmFanFailure */ { ".1.3.6.1.4.1.534.1.7.33", NULL, "Fan failure!" }, /* xupsAlarmFuseFailure */ { ".1.3.6.1.4.1.534.1.7.34", NULL, "Fuse failure!" }, /* xupsPowerSwitchBad */ { ".1.3.6.1.4.1.534.1.7.35", NULL, "Powerswitch failure!" }, /* xupsModuleFailure */ { ".1.3.6.1.4.1.534.1.7.36", NULL, "Parallel or composite module failure!" }, /* xupsOnAlternatePowerSource * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.37", NULL, "Using alternative power source!" }, /* xupsAltPowerNotAvailable * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.38", NULL, "Alternative power source unavailable!" }, /* xupsRemoteTempBad */ { ".1.3.6.1.4.1.534.1.7.40", NULL, "Bad remote temperature!" }, /* xupsRemoteHumidityBad */ { ".1.3.6.1.4.1.534.1.7.41", NULL, "Bad remote humidity!" }, /* xupsAlarmOutputBad */ { ".1.3.6.1.4.1.534.1.7.42", NULL, "Bad output condition!" }, /* xupsAlarmAwaitingPower * FIXME: informational (not an alarm), * to RFC'ed for device.event? */ { ".1.3.6.1.4.1.534.1.7.43", NULL, "Awaiting power!" }, /* xupsOnMaintenanceBypass * FIXME: informational (not an alarm), * to RFC'ed for device.event? * FIXME: NUT currently doesn't distinguish between Maintenance and * Automatic Bypass (both published as "ups.alarm: BYPASS) * Should we make the distinction? */ { ".1.3.6.1.4.1.534.1.7.44", "BYPASS", "On maintenance bypass!" }, /* end of structure. */ { NULL, NULL, NULL } } ; mib2nut_info_t powerware = { "pw", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, POWERWARE_SYSOID , pw_alarms }; mib2nut_info_t pxgx_ups = { "pxgx_ups", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, EATON_PXGX_SYSOID , pw_alarms }; nut-2.7.4/drivers/netvision-mib.h0000644000175000017500000000023112640443572013670 00000000000000#ifndef NETVISION_MIB_H #define NETVISION_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t netvision; #endif /* NETVISION_MIB_H */ nut-2.7.4/drivers/bcmxcp_io.h0000644000175000017500000000122612640444140013045 00000000000000/* * bcmxcp_io.h -- header for BCM/XCP IO module */ #ifndef BCMXCP_IO__ #define BCMXCP_IO__ #include "main.h" /* for usbdrv_info_t */ void send_read_command(unsigned char command); void send_write_command(unsigned char *command, int command_length); int get_answer(unsigned char *data, unsigned char command); int command_read_sequence(unsigned char command, unsigned char *data); int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer); void upsdrv_initups(void); void upsdrv_cleanup(void); void upsdrv_reconnect(void); void upsdrv_comm_good(void); extern upsdrv_info_t comm_upsdrv_info; #endif /* BCMXCP_IO__ */ nut-2.7.4/drivers/libhid.c0000644000175000017500000006666612640473702012361 00000000000000/*! * @file libhid.c * @brief HID Library - User API (Generic HID Access using MGE HIDParser) * * @author Copyright (C) 2003 - 2007 * Arnaud Quette && * John Stamp * Peter Selinger * Arjen de Korte * * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com * * The logic of this file is ripped from mge-shut driver (also from * Arnaud Quette), which is a "HID over serial link" UPS driver for * Network UPS Tools * * 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. * * -------------------------------------------------------------------------- */ #include #include /* #include */ #include "libhid.h" #include "hidparser.h" #include "common.h" /* for xmalloc, upsdebugx prototypes */ /* Communication layers and drivers (USB and MGE SHUT) */ #ifdef SHUT_MODE #include "libshut.h" communication_subdriver_t *comm_driver = &shut_subdriver; #else #include "libusb.h" communication_subdriver_t *comm_driver = &usb_subdriver; #endif /* support functions */ static double logical_to_physical(HIDData_t *Data, long logical); static long physical_to_logical(HIDData_t *Data, double physical); static const char *hid_lookup_path(const HIDNode_t usage, usage_tables_t *utab); static long hid_lookup_usage(const char *name, usage_tables_t *utab); static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *utab); static int path_to_string(char *string, size_t size, const HIDPath_t *path, usage_tables_t *utab); static int8_t get_unit_expo(const HIDData_t *hiddata); static double exponent(double a, int8_t b); /* Tweak flag for APC Back-UPS */ int max_report_size = 0; /* Tweaks for Powercom, at least */ int interrupt_only = 0; int unsigned interrupt_size = 0; /* ---------------------------------------------------------------------- */ /* report buffering system */ /* HID data items are retrieved via "reports". Each report is identified by a report ID, which is an integer in the range 0-255. Each report can hold several items. To avoid retrieving a given report multiple times in short succession, we use a data structure called a "report buffer". The functions in this group operate on entire *reports*, not individual data items. */ void free_report_buffer(reportbuf_t *rbuf) { int i; if (!rbuf) return; for (i=0; i<256; i++) { free(rbuf->data[i]); } free(rbuf); } /* allocate a new report buffer. Return pointer on success, else NULL with errno set. The returned data structure must later be freed with free_report_buffer(). */ reportbuf_t *new_report_buffer(HIDDesc_t *pDesc) { HIDData_t *pData; reportbuf_t *rbuf; int i, id; if (!pDesc) return NULL; rbuf = calloc(1, sizeof(*rbuf)); if (!rbuf) { return NULL; } /* now go through all items that are part of this report */ for (i=0; initems; i++) { pData = &pDesc->item[i]; id = pData->ReportID; /* skip reports that already have been allocated */ if (rbuf->data[id]) continue; /* first byte holds id */ rbuf->len[id] = pDesc->replen[id] + 1; /* skip zero length reports */ if (rbuf->len[id] < 1) { continue; } rbuf->data[id] = calloc(rbuf->len[id], sizeof(*(rbuf->data[id]))); if (rbuf->data[id]) continue; /* on failure, give up what we got so far */ free_report_buffer(rbuf); return NULL; } return rbuf; } /* ---------------------------------------------------------------------- */ /* the functions in this next group operate on buffered reports, but operate on individual items, not whole reports. */ /* refresh the report with the given id in the report buffer rbuf. If the report is not yet in the buffer, or if it is older than "age" seconds, then the report is freshly read from the USB device. Otherwise, it is unchanged. Return 0 on success, -1 on error with errno set. */ /* because buggy firmwares from APC return wrong report size, we either ask the report with the found report size or with the whole buffer size depending on the max_report_size flag */ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, int age) { int id = pData->ReportID; int r; if (interrupt_only || rbuf->ts[id] + age > time(NULL)) { /* buffered report is still good; nothing to do */ upsdebug_hex(3, "Report[buf]", rbuf->data[id], rbuf->len[id]); return 0; } r = comm_driver->get_report(udev, id, rbuf->data[id], max_report_size ? (int)sizeof(rbuf->data[id]):rbuf->len[id]); if (r <= 0) { return -1; } if (rbuf->len[id] != r) { upsdebugx(2, "%s: expected %d bytes, but got %d instead", __func__, rbuf->len[id], r); upsdebug_hex(3, "Report[err]", rbuf->data[id], r); } else { upsdebug_hex(3, "Report[get]", rbuf->data[id], rbuf->len[id]); } /* have (valid) report */ time(&rbuf->ts[id]); return 0; } /* read the logical value for the given pData. No logical to physical conversion is performed. If age>0, the read operation is buffered if the item's age is less than "age". On success, return 0 and store the answer in *value. On failure, return -1 and set errno. */ static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long *Value, int age) { int id = pData->ReportID; int r; r = refresh_report_buffer(rbuf, udev, pData, age); if (r<0) { return -1; } GetValue(rbuf->data[id], pData, Value); return 0; } /* set the logical value for the given pData. No physical to logical conversion is performed. On success, return 0, and failure, return -1 and set errno. The updated value is sent to the device. */ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long Value) { int id = pData->ReportID; int r; SetValue(pData, rbuf->data[id], Value); r = comm_driver->set_report(udev, id, rbuf->data[id], rbuf->len[id]); if (r <= 0) { return -1; } upsdebug_hex(3, "Report[set]", rbuf->data[id], rbuf->len[id]); /* expire report */ rbuf->ts[id] = 0; return 0; } /* file a given report in the report buffer. This is used when the report has been obtained without having been explicitly requested, e.g., it arrived through an interrupt transfer. Returns 0 on success, -1 on error with errno set. */ static int file_report_buffer(reportbuf_t *rbuf, unsigned char *buf, int buflen) { int id = buf[0]; /* broken report descriptors are common, so store whatever we can */ memcpy(rbuf->data[id], buf, (buflen < rbuf->len[id]) ? buflen : rbuf->len[id]); if (rbuf->len[id] != buflen) { upsdebugx(2, "%s: expected %d bytes, but got %d instead", __func__, rbuf->len[id], buflen); upsdebug_hex(3, "Report[err]", buf, buflen); } else { upsdebug_hex(3, "Report[int]", rbuf->data[id], rbuf->len[id]); } /* have (valid) report */ time(&rbuf->ts[id]); return 0; } /* ---------------------------------------------------------------------- */ /* Units and exponents table (HID PDC, 3.2.3) */ #define NB_HID_UNITS 10 static struct { const long Type; const int8_t Expo; } HIDUnits[NB_HID_UNITS] = { { 0x00000000, 0 }, /* None */ { 0x00F0D121, 7 }, /* Voltage */ { 0x00100001, 0 }, /* Ampere */ { 0x0000D121, 7 }, /* VA */ { 0x0000D121, 7 }, /* Watts */ { 0x00001001, 0 }, /* second */ { 0x00010001, 0 }, /* K */ { 0x00000000, 0 }, /* percent */ { 0x0000F001, 0 }, /* Hertz */ { 0x00101001, 0 }, /* As */ }; /* ---------------------------------------------------------------------- */ /* CAUTION: be careful when modifying the output format of this function, * since it's used to produce sub-drivers "stub" using * scripts/subdriver/gen-usbhid-subdriver.sh */ void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab) { int i; #ifndef SHUT_MODE /* extract the VendorId for further testing */ int vendorID = usb_device((struct usb_dev_handle *)udev)->descriptor.idVendor; #endif /* Do not go further if we already know nothing will be displayed. * Some reports take a while before they timeout, so if these are * not used in the driver, they should only be fetched when we're * in debug mode */ if (nut_debug_level < 1) { return; } upsdebugx(1, "%i HID objects found", pDesc->nitems); for (i = 0; i < pDesc->nitems; i++) { double value; HIDData_t *pData = &pDesc->item[i]; /* skip reports 254/255 for Eaton / MGE / Dell due to special handling needs */ #ifdef SHUT_MODE if ((pData->ReportID == 254) || (pData->ReportID == 255)) { continue; } #else if ((vendorID == 0x0463) || (vendorID == 0x047c)) { if ((pData->ReportID == 254) || (pData->ReportID == 255)) { continue; } } #endif /* Get data value */ if (HIDGetDataValue(udev, pData, &value, MAX_TS) == 1) { upsdebugx(1, "Path: %s, Type: %s, ReportID: 0x%02x, Offset: %i, Size: %i, Value: %g", HIDGetDataItem(pData, utab), HIDDataType(pData), pData->ReportID, pData->Offset, pData->Size, value); continue; } upsdebugx(1, "Path: %s, Type: %s, ReportID: 0x%02x, Offset: %i, Size: %i", HIDGetDataItem(pData, utab), HIDDataType(pData), pData->ReportID, pData->Offset, pData->Size); } fflush(stdout); } /* Returns text string which can be used to display type of data */ const char *HIDDataType(const HIDData_t *hiddata) { switch (hiddata->Type) { case ITEM_FEATURE: return "Feature"; case ITEM_INPUT: return "Input"; case ITEM_OUTPUT: return "Output"; default: return "Unknown"; } } /* Returns pointer to the corresponding HIDData_t item * or NULL if path is not found in report descriptor */ HIDData_t *HIDGetItemData(const char *hidpath, usage_tables_t *utab) { int r; HIDPath_t Path; r = string_to_path(hidpath, &Path, utab); if (r <= 0) { return NULL; } /* Get info on object (reportID, offset and size) */ return FindObject_with_Path(pDesc, &Path, interrupt_only ? ITEM_INPUT:ITEM_FEATURE); } char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab) { /* TODO: not thread safe! */ static char itemPath[128]; path_to_string(itemPath, sizeof(itemPath), &hiddata->Path, utab); return itemPath; } /* Return the physical value associated with the given HIDData path. * return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, int age) { int r; long hValue; if (hiddata == NULL) { return 0; } r = get_item_buffered(reportbuf, udev, hiddata, &hValue, age); if (r<0) { upsdebug_with_errno(1, "Can't retrieve Report %02x", hiddata->ReportID); return -errno; } *Value = hValue; /* Convert Logical Min, Max and Value into Physical */ *Value = logical_to_physical(hiddata, hValue); /* Process exponents and units */ *Value *= exponent(10, get_unit_expo(hiddata)); return 1; } /* Return the physical value associated with the given path. * return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ int HIDGetItemValue(hid_dev_handle_t udev, const char *hidpath, double *Value, usage_tables_t *utab) { return HIDGetDataValue(udev, HIDGetItemData(hidpath, utab), Value, MAX_TS); } /* Return pointer to indexed string (empty if not found) */ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_t buflen) { if (comm_driver->get_string(udev, Index, buf, buflen) < 1) buf[0] = '\0'; return str_rtrim(buf, '\n'); } /* Return pointer to indexed string from HID path (empty if not found) */ char *HIDGetItemString(hid_dev_handle_t udev, const char *hidpath, char *buf, size_t buflen, usage_tables_t *utab) { double Index; if (HIDGetDataValue(udev, HIDGetItemData(hidpath, utab), &Index, MAX_TS) != 1) { buf[0] = '\0'; return buf; } return HIDGetIndexString(udev, Index, buf, buflen); } /* Set the given physical value for the variable associated with * path. return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value) { int r; long hValue; if (hiddata == NULL) { return 0; } /* Test if Item is settable */ /* FIXME: not constant == volatile, but * it doesn't imply that it's RW! */ if (hiddata->Attribute == ATTR_DATA_CST) { return 0; } /* Process exponents and units */ Value /= exponent(10, get_unit_expo(hiddata)); /* Convert Physical Min, Max and Value into Logical */ hValue = physical_to_logical(hiddata, Value); r = set_item_buffered(reportbuf, udev, hiddata, hValue); if (r<0) { upsdebug_with_errno(1, "Can't set Report %02x", hiddata->ReportID); return -errno; } /* flush the report buffer (data may have changed) */ memset(reportbuf->ts, 0, sizeof(reportbuf->ts)); upsdebugx(4, "Set report succeeded"); return 1; } bool_t HIDSetItemValue(hid_dev_handle_t udev, const char *hidpath, double Value, usage_tables_t *utab) { if (HIDSetDataValue(udev, HIDGetItemData(hidpath, utab), Value) != 1) return FALSE; return TRUE; } /* On success, return item count >0. When no notifications are available, * return 'error' or 'no event' code. */ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) { unsigned char buf[SMALLBUF]; int itemCount = 0; int buflen, r, i; HIDData_t *pData; /* needs libusb-0.1.8 to work => use ifdef and autoconf */ buflen = comm_driver->get_interrupt(udev, buf, interrupt_size ? interrupt_size:sizeof(buf), 250); if (buflen <= 0) { return buflen; /* propagate "error" or "no event" code */ } r = file_report_buffer(reportbuf, buf, buflen); if (r < 0) { upsdebug_with_errno(1, "%s: failed to buffer report", __func__); return -errno; } /* now read all items that are part of this report */ for (i=0; initems; i++) { pData = &pDesc->item[i]; /* Variable not part of this report */ if (pData->ReportID != buf[0]) continue; /* Not an input report */ if (pData->Type != ITEM_INPUT) continue; /* maximum number of events reached? */ if (itemCount >= eventsize) { upsdebugx(1, "%s: too many events (truncated)", __func__); break; } event[itemCount++] = pData; } if (itemCount == 0) { upsdebugx(1, "%s: unexpected input report (ignored)", __func__); } return itemCount; } /******************************************************* * Support functions *******************************************************/ static double logical_to_physical(HIDData_t *Data, long logical) { double physical; double Factor; upsdebugx(5, "PhyMax = %ld, PhyMin = %ld, LogMax = %ld, LogMin = %ld", Data->PhyMax, Data->PhyMin, Data->LogMax, Data->LogMin); /* HID spec says that if one or both are undefined, or if they are * both 0, then PhyMin = LogMin, PhyMax = LogMax. */ if (!Data->have_PhyMax || !Data->have_PhyMin || (Data->PhyMax == 0 && Data->PhyMin == 0)) { return (double)logical; } /* Paranoia */ if ((Data->PhyMax <= Data->PhyMin) || (Data->LogMax <= Data->LogMin)) { /* this should not really happen */ return (double)logical; } Factor = (double)(Data->PhyMax - Data->PhyMin) / (Data->LogMax - Data->LogMin); /* Convert Value */ physical = (double)((logical - Data->LogMin) * Factor) + Data->PhyMin; if (physical > Data->PhyMax) { return Data->PhyMax; } if (physical < Data->PhyMin) { return Data->PhyMin; } return physical; } static long physical_to_logical(HIDData_t *Data, double physical) { long logical; double Factor; upsdebugx(5, "PhyMax = %ld, PhyMin = %ld, LogMax = %ld, LogMin = %ld", Data->PhyMax, Data->PhyMin, Data->LogMax, Data->LogMin); /* HID spec says that if one or both are undefined, or if they are * both 0, then PhyMin = LogMin, PhyMax = LogMax. */ if (!Data->have_PhyMax || !Data->have_PhyMin || (Data->PhyMax == 0 && Data->PhyMin == 0)) { return (long)physical; } /* Paranoia */ if ((Data->PhyMax <= Data->PhyMin) || (Data->LogMax <= Data->LogMin)) { /* this should not really happen */ return (long)physical; } Factor = (double)(Data->LogMax - Data->LogMin) / (Data->PhyMax - Data->PhyMin); /* Convert Value */ logical = (long)((physical - Data->PhyMin) * Factor) + Data->LogMin; if (logical > Data->LogMax) return Data->LogMax; if (logical < Data->LogMin) return Data->LogMin; return logical; } static int8_t get_unit_expo(const HIDData_t *hiddata) { int i; int8_t unit_expo = hiddata->UnitExp; upsdebugx(5, "Unit = %08x, UnitExp = %d", (uint32_t)(hiddata->Unit), hiddata->UnitExp); for (i = 0; i < NB_HID_UNITS; i++) { if (HIDUnits[i].Type == hiddata->Unit) { unit_expo -= HIDUnits[i].Expo; break; } } upsdebugx(5, "Exponent = %d", unit_expo); return unit_expo; } /* exponent function: return a^b */ static double exponent(double a, int8_t b) { if (b>0) return (a * exponent(a, --b)); /* a * a ... */ if (b<0) return ((1/a) * exponent(a, ++b)); /* (1/a) * (1/a) ... */ return 1; } /* translate HID string path to numeric path and return path depth */ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *utab) { int i = 0; long usage; char buf[SMALLBUF]; char *token, *last; snprintf(buf, sizeof(buf), "%s", string); for (token = strtok_r(buf, ".", &last); token != NULL; token = strtok_r(NULL, ".", &last)) { /* lookup tables first (to override defaults) */ if ((usage = hid_lookup_usage(token, utab)) != -1) { path->Node[i++] = usage; continue; } /* translate unnamed path components such as "ff860024" */ if (strlen(token) == strspn(token, "1234567890abcdefABCDEF")) { path->Node[i++] = strtol(token, NULL, 16); continue; } /* indexed collection */ if (strlen(token) == strspn(token, "[1234567890]")) { path->Node[i++] = 0x00ff0000 + atoi(token+1); continue; } /* Uh oh, typo in usage table? */ upsdebugx(1, "string_to_path: couldn't parse %s from %s", token, string); } path->Size = i; upsdebugx(4, "string_to_path: depth = %d", path->Size); return i; } /* translate HID numeric path to string path and return path depth */ static int path_to_string(char *string, size_t size, const HIDPath_t *path, usage_tables_t *utab) { int i; const char *p; snprintf(string, size, "%s", ""); for (i = 0; i < path->Size; i++) { if (i > 0) snprintfcat(string, size, "."); /* lookup tables first (to override defaults) */ if ((p = hid_lookup_path(path->Node[i], utab)) != NULL) { snprintfcat(string, size, "%s", p); continue; } /* indexed collection */ if ((path->Node[i] & 0xffff0000) == 0x00ff0000) { snprintfcat(string, size, "[%i]", path->Node[i] & 0x0000ffff); continue; } /* unnamed path components such as "ff860024" */ snprintfcat(string, size, "%08x", path->Node[i]); } return i; } /* usage conversion string -> numeric */ static long hid_lookup_usage(const char *name, usage_tables_t *utab) { int i, j; for (i = 0; utab[i] != NULL; i++) { for (j = 0; utab[i][j].usage_name != NULL; j++) { if (strcasecmp(utab[i][j].usage_name, name)) continue; upsdebugx(5, "hid_lookup_usage: %s -> %08x", name, (unsigned int)utab[i][j].usage_code); return utab[i][j].usage_code; } } upsdebugx(5, "hid_lookup_usage: %s -> not found in lookup table", name); return -1; } /* usage conversion numeric -> string */ static const char *hid_lookup_path(const HIDNode_t usage, usage_tables_t *utab) { int i, j; for (i = 0; utab[i] != NULL; i++) { for (j = 0; utab[i][j].usage_name != NULL; j++) { if (utab[i][j].usage_code != usage) continue; upsdebugx(5, "hid_lookup_path: %08x -> %s", (unsigned int)usage, utab[i][j].usage_name); return utab[i][j].usage_name; } } upsdebugx(5, "hid_lookup_path: %08x -> not found in lookup table", (unsigned int)usage); return NULL; } /* Lookup this usage name to find its code (page + index) */ /* temporary usage code lookup */ /* FIXME: put as external data, like in usb.ids (or use * this last?) */ /* Global usage table (from USB HID class definition) */ usage_lkp_t hid_usage_lkp[] = { /* Power Device Page */ { "Undefined", 0x00840000 }, { "iName", 0x00840001 }, { "PresentStatus", 0x00840002 }, { "ChangedStatus", 0x00840003 }, { "UPS", 0x00840004 }, { "PowerSupply", 0x00840005 }, /* 0x00840006-0x0084000f => Reserved */ { "BatterySystem", 0x00840010 }, { "BatterySystemID", 0x00840011 }, { "Battery", 0x00840012 }, { "BatteryID", 0x00840013 }, { "Charger", 0x00840014 }, { "ChargerID", 0x00840015 }, { "PowerConverter", 0x00840016 }, { "PowerConverterID", 0X00840017 }, { "OutletSystem", 0x00840018 }, { "OutletSystemID", 0x00840019 }, { "Input", 0x0084001a }, { "InputID", 0x0084001b }, { "Output", 0x0084001c }, { "OutputID", 0x0084001d }, { "Flow", 0x0084001e }, { "FlowID", 0x0084001f }, { "Outlet", 0x00840020 }, { "OutletID", 0x00840021 }, { "Gang", 0x00840022 }, { "GangID", 0x00840023 }, { "PowerSummary", 0x00840024 }, { "PowerSummaryID", 0x00840025 }, /* 0x00840026-0x0084002f => Reserved */ { "Voltage", 0x00840030 }, { "Current", 0x00840031 }, { "Frequency", 0x00840032 }, { "ApparentPower", 0x00840033 }, { "ActivePower", 0x00840034 }, { "PercentLoad", 0x00840035 }, { "Temperature", 0x00840036 }, { "Humidity", 0x00840037 }, { "BadCount", 0x00840038 }, /* 0x00840039-0x0084003f => Reserved */ { "ConfigVoltage", 0x00840040 }, { "ConfigCurrent", 0x00840041 }, { "ConfigFrequency", 0x00840042 }, { "ConfigApparentPower", 0x00840043 }, { "ConfigActivePower", 0x00840044 }, { "ConfigPercentLoad", 0x00840045 }, { "ConfigTemperature", 0x00840046 }, { "ConfigHumidity", 0x00840047 }, /* 0x00840048-0x0084004f => Reserved */ { "SwitchOnControl", 0x00840050 }, { "SwitchOffControl", 0x00840051 }, { "ToggleControl", 0x00840052 }, { "LowVoltageTransfer", 0x00840053 }, { "HighVoltageTransfer", 0x00840054 }, { "DelayBeforeReboot", 0x00840055 }, { "DelayBeforeStartup", 0x00840056 }, { "DelayBeforeShutdown", 0x00840057 }, { "Test", 0x00840058 }, { "ModuleReset", 0x00840059 }, { "AudibleAlarmControl", 0x0084005a }, /* 0x0084005b-0x0084005f => Reserved */ { "Present", 0x00840060 }, { "Good", 0x00840061 }, { "InternalFailure", 0x00840062 }, { "VoltageOutOfRange", 0x00840063 }, { "FrequencyOutOfRange", 0x00840064 }, { "Overload", 0x00840065 }, /* Note: the correct spelling is "Overload", not "OverLoad", * according to the official specification, "Universal Serial * Bus Usage Tables for HID Power Devices", Release 1.0, * November 1, 1997 */ { "OverCharged", 0x00840066 }, { "OverTemperature", 0x00840067 }, { "ShutdownRequested", 0x00840068 }, { "ShutdownImminent", 0x00840069 }, { "SwitchOn/Off", 0x0084006b }, { "Switchable", 0x0084006c }, { "Used", 0x0084006d }, { "Boost", 0x0084006e }, { "Buck", 0x0084006f }, { "Initialized", 0x00840070 }, { "Tested", 0x00840071 }, { "AwaitingPower", 0x00840072 }, { "CommunicationLost", 0x00840073 }, /* 0x00840074-0x008400fc => Reserved */ { "iManufacturer", 0x008400fd }, { "iProduct", 0x008400fe }, { "iSerialNumber", 0x008400ff }, /* Battery System Page */ { "Undefined", 0x00850000 }, { "SMBBatteryMode", 0x00850001 }, { "SMBBatteryStatus", 0x00850002 }, { "SMBAlarmWarning", 0x00850003 }, { "SMBChargerMode", 0x00850004 }, { "SMBChargerStatus", 0x00850005 }, { "SMBChargerSpecInfo", 0x00850006 }, { "SMBSelectorState", 0x00850007 }, { "SMBSelectorPresets", 0x00850008 }, { "SMBSelectorInfo", 0x00850009 }, /* 0x0085000A-0x0085000f => Reserved */ { "OptionalMfgFunction1", 0x00850010 }, { "OptionalMfgFunction2", 0x00850011 }, { "OptionalMfgFunction3", 0x00850012 }, { "OptionalMfgFunction4", 0x00850013 }, { "OptionalMfgFunction5", 0x00850014 }, { "ConnectionToSMBus", 0x00850015 }, { "OutputConnection", 0x00850016 }, { "ChargerConnection", 0x00850017 }, { "BatteryInsertion", 0x00850018 }, { "Usenext", 0x00850019 }, { "OKToUse", 0x0085001a }, { "BatterySupported", 0x0085001b }, { "SelectorRevision", 0x0085001c }, { "ChargingIndicator", 0x0085001d }, /* 0x0085001e-0x00850027 => Reserved */ { "ManufacturerAccess", 0x00850028 }, { "RemainingCapacityLimit", 0x00850029 }, { "RemainingTimeLimit", 0x0085002a }, { "AtRate", 0x0085002b }, { "CapacityMode", 0x0085002c }, { "BroadcastToCharger", 0x0085002d }, { "PrimaryBattery", 0x0085002e }, { "ChargeController", 0x0085002f }, /* 0x00850030-0x0085003f => Reserved */ { "TerminateCharge", 0x00850040 }, { "TerminateDischarge", 0x00850041 }, { "BelowRemainingCapacityLimit", 0x00850042 }, { "RemainingTimeLimitExpired", 0x00850043 }, { "Charging", 0x00850044 }, { "Discharging", 0x00850045 }, { "FullyCharged", 0x00850046 }, { "FullyDischarged", 0x00850047 }, { "ConditioningFlag", 0x00850048 }, { "AtRateOK", 0x00850049 }, { "SMBErrorCode", 0x0085004a }, { "NeedReplacement", 0x0085004b }, /* 0x0085004c-0x0085005f => Reserved */ { "AtRateTimeToFull", 0x00850060 }, { "AtRateTimeToEmpty", 0x00850061 }, { "AverageCurrent", 0x00850062 }, { "Maxerror", 0x00850063 }, { "RelativeStateOfCharge", 0x00850064 }, { "AbsoluteStateOfCharge", 0x00850065 }, { "RemainingCapacity", 0x00850066 }, { "FullChargeCapacity", 0x00850067 }, { "RunTimeToEmpty", 0x00850068 }, { "AverageTimeToEmpty", 0x00850069 }, { "AverageTimeToFull", 0x0085006a }, { "CycleCount", 0x0085006b }, /* 0x0085006c-0x0085007f => Reserved */ { "BattPackModelLevel", 0x00850080 }, { "InternalChargeController", 0x00850081 }, { "PrimaryBatterySupport", 0x00850082 }, { "DesignCapacity", 0x00850083 }, { "SpecificationInfo", 0x00850084 }, { "ManufacturerDate", 0x00850085 }, { "SerialNumber", 0x00850086 }, { "iManufacturerName", 0x00850087 }, { "iDevicename", 0x00850088 }, /* sic! */ { "iDeviceChemistry", 0x00850089 }, /* misspelled as "iDeviceChemistery" in spec. */ { "ManufacturerData", 0x0085008a }, { "Rechargeable", 0x0085008b }, { "WarningCapacityLimit", 0x0085008c }, { "CapacityGranularity1", 0x0085008d }, { "CapacityGranularity2", 0x0085008e }, { "iOEMInformation", 0x0085008f }, /* 0x00850090-0x008500bf => Reserved */ { "InhibitCharge", 0x008500c0 }, { "EnablePolling", 0x008500c1 }, { "ResetToZero", 0x008500c2 }, /* 0x008500c3-0x008500cf => Reserved */ { "ACPresent", 0x008500d0 }, { "BatteryPresent", 0x008500d1 }, { "PowerFail", 0x008500d2 }, { "AlarmInhibited", 0x008500d3 }, { "ThermistorUnderRange", 0x008500d4 }, { "ThermistorHot", 0x008500d5 }, { "ThermistorCold", 0x008500d6 }, { "ThermistorOverRange", 0x008500d7 }, { "VoltageOutOfRange", 0x008500d8 }, { "CurrentOutOfRange", 0x008500d9 }, { "CurrentNotRegulated", 0x008500da }, { "VoltageNotRegulated", 0x008500db }, { "MasterMode", 0x008500dc }, /* 0x008500dd-0x008500ef => Reserved */ { "ChargerSelectorSupport", 0x008500f0 }, { "ChargerSpec", 0x008500f1 }, { "Level2", 0x008500f2 }, { "Level3", 0x008500f3 }, /* 0x008500f4-0x008500ff => Reserved */ /* end of structure. */ { NULL, 0 } }; nut-2.7.4/drivers/ietf-mib.c0000644000175000017500000004303612667537407012620 00000000000000/* ietf-mib.c - data to monitor SNMP UPS (RFC 1628 compliant) with NUT * * Copyright (C) 2002-2006 * 2002-2012 Arnaud Quette * 2002-2006 Niels Baggesen * 2002-2006 Arjen de Korte * * Sponsored by MGE UPS SYSTEMS * * 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 "ietf-mib.h" #define IETF_MIB_VERSION "1.5" /* SNMP OIDs set */ #define IETF_OID_UPS_MIB "1.3.6.1.2.1.33.1." #define IETF_SYSOID ".1.3.6.1.2.1.33" #define TRIPPLITE_SYSOID ".1.3.6.1.4.1.850.1" /* #define DEBUG */ static info_lkp_t ietf_battery_info[] = { { 1, "" /* unknown */ }, { 2, "" /* batteryNormal */}, { 3, "LB" /* batteryLow */ }, { 4, "LB" /* batteryDepleted */ }, { 0, NULL } }; static info_lkp_t ietf_power_source_info[] = { { 1, "" /* other */ }, { 2, "OFF" /* none */ }, { 3, "OL" /* normal */ }, { 4, "OL BYPASS" /* bypass */ }, { 5, "OB" /* battery */ }, { 6, "OL BOOST" /* booster */ }, { 7, "OL TRIM" /* reducer */ }, { 0, NULL } }; static info_lkp_t ietf_overload_info[] = { { 1, "OVER" }, /* output overload */ { 0, NULL } }; static info_lkp_t ietf_test_active_info[] = { { 1, "" }, /* upsTestNoTestsInitiated */ { 2, "" }, /* upsTestAbortTestInProgress */ { 3, "TEST" }, /* upsTestGeneralSystemsTest */ { 4, "TEST" }, /* upsTestQuickBatteryTest */ { 5, "CAL" }, /* upsTestDeepBatteryCalibration */ { 0, NULL } }; static info_lkp_t ietf_test_result_info[] = { { 1, "done and passed" }, { 2, "done and warning" }, { 3, "done and error" }, { 4, "aborted" }, { 5, "in progress" }, { 6, "no test initiated" }, { 0, NULL } }; #ifdef DEBUG static info_lkp_t ietf_shutdown_type_info[] = { { 1, "output" }, { 2, "system" }, { 0, NULL } }; #endif static info_lkp_t ietf_yes_no_info[] = { { 1, "yes" }, { 2, "no" }, { 0, NULL } }; static info_lkp_t ietf_beeper_status_info[] = { { 1, "disabled" }, { 2, "enabled" }, { 3, "muted" }, { 0, NULL } }; /* Snmp2NUT lookup table info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ static snmp_info_t ietf_mib[] = { /* The Device Identification group */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.1.0", "Generic", SU_FLAG_STATIC, NULL }, /* upsIdentManufacturer */ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.2.0", "Generic SNMP UPS", SU_FLAG_STATIC, NULL }, /* upsIdentModel */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.3.0", "", SU_FLAG_STATIC, NULL }, /* upsIdentUPSSoftwareVersion */ { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.4.0", "", SU_FLAG_STATIC, NULL }, /* upsIdentAgentSoftwareVersion */ #ifdef DEBUG { "debug.upsIdentName", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.5.0", "", 0, NULL }, /* upsIdentName */ { "debug.upsIdentAttachedDevices", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.6.0", "", 0, NULL }, /* upsIdentAttachedDevices */ #endif /* Battery Group */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "2.1.0", "", SU_STATUS_BATT, ietf_battery_info }, /* upsBatteryStatus */ #ifdef DEBUG { "debug.upsSecondsOnBattery", 0, 1.0, IETF_OID_UPS_MIB "2.2.0", "", 0, NULL }, /* upsSecondsOnBattery */ #endif { "battery.runtime", 0, 60.0, IETF_OID_UPS_MIB "2.3.0", "", 0, NULL }, /* upsEstimatedMinutesRemaining */ { "battery.charge", 0, 1, IETF_OID_UPS_MIB "2.4.0", "", 0, NULL }, /* upsEstimatedChargeRemaining */ { "battery.voltage", 0, 0.1, IETF_OID_UPS_MIB "2.5.0", "", 0, NULL }, /* upsBatteryVoltage */ { "battery.current", 0, 0.1, IETF_OID_UPS_MIB "2.6.0", "", 0, NULL }, /* upsBatteryCurrent */ { "battery.temperature", 0, 1.0, IETF_OID_UPS_MIB "2.7.0", "", 0, NULL }, /* upsBatteryTemperature */ /* Input Group */ #ifdef DEBUG { "debug.upsInputLineBads", 0, 1.0, IETF_OID_UPS_MIB "3.1.0", "", 0, NULL }, /* upsInputLineBads */ #endif { "input.phases", 0, 1.0, IETF_OID_UPS_MIB "3.2.0", "", SU_FLAG_SETINT, NULL, &input_phases }, /* upsInputNumLines */ #ifdef DEBUG { "debug.upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_1, NULL }, /* upsInputLineIndex */ { "debug.[1].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_3, NULL }, { "debug.[2].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.2", "", SU_INPUT_3, NULL }, { "debug.[3].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.3", "", SU_INPUT_3, NULL }, #endif { "input.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.1", "", SU_INPUT_1, NULL }, /* upsInputFrequency */ { "input.L1.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.1", "", SU_INPUT_3, NULL }, { "input.L2.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.2", "", SU_INPUT_3, NULL }, { "input.L3.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.3", "", SU_INPUT_3, NULL }, { "input.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.1", "", SU_INPUT_1, NULL }, /* upsInputVoltage */ { "input.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.3", "", SU_INPUT_3, NULL }, { "input.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.1", "", SU_INPUT_1, NULL }, /* upsInputCurrent */ { "input.L1.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.1", "", SU_INPUT_3, NULL }, { "input.L2.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.2", "", SU_INPUT_3, NULL }, { "input.L3.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.3", "", SU_INPUT_3, NULL }, { "input.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.1", "", SU_INPUT_1, NULL }, /* upsInputTruePower */ { "input.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.1", "", SU_INPUT_3, NULL }, { "input.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.2", "", SU_INPUT_3, NULL }, { "input.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.3", "", SU_INPUT_3, NULL }, /* Output Group */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "4.1.0", "", SU_STATUS_PWR, ietf_power_source_info }, /* upsOutputSource */ { "output.frequency", 0, 0.1, IETF_OID_UPS_MIB "4.2.0", "", 0, NULL }, /* upsOutputFrequency */ { "output.phases", 0, 1.0, IETF_OID_UPS_MIB "4.3.0", "", SU_FLAG_SETINT, NULL, &output_phases }, /* upsOutputNumLines */ #ifdef DEBUG { "debug.upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_1, NULL }, /* upsOutputLineIndex */ { "debug.[1].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_3, NULL }, { "debug.[2].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.2", "", SU_OUTPUT_3, NULL }, { "debug.[3].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.3", "", SU_OUTPUT_3, NULL }, #endif { "output.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.1", "", SU_OUTPUT_1, NULL }, /* upsOutputVoltage */ { "output.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.2", "", SU_OUTPUT_3, NULL }, { "output.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.3", "", SU_OUTPUT_3, NULL }, { "output.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.1", "", SU_OUTPUT_1, NULL }, /* upsOutputCurrent */ { "output.L1.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.1", "", SU_OUTPUT_3, NULL }, { "output.L2.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.2", "", SU_OUTPUT_3, NULL }, { "output.L3.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.3", "", SU_OUTPUT_3, NULL }, { "output.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.1", "", SU_OUTPUT_1, NULL }, /* upsOutputPower */ { "output.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.1", "", SU_OUTPUT_3, NULL }, { "output.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.2", "", SU_OUTPUT_3, NULL }, { "output.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.3", "", SU_OUTPUT_3, NULL }, { "ups.load", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.1", "", SU_OUTPUT_1, NULL }, /* upsOutputPercentLoad */ { "output.L1.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.1", "", SU_OUTPUT_3, NULL }, { "output.L2.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.2", "", SU_OUTPUT_3, NULL }, { "output.L3.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.3", "", SU_OUTPUT_3, NULL }, /* Bypass Group */ { "input.bypass.phases", 0, 1.0, IETF_OID_UPS_MIB "5.2.0", "", SU_FLAG_SETINT, NULL, &bypass_phases }, /* upsBypassNumLines */ { "input.bypass.frequency", 0, 0.1, IETF_OID_UPS_MIB "5.1.0", "", SU_BYPASS_1 | SU_BYPASS_3, NULL }, /* upsBypassFrequency */ #ifdef DEBUG { "debug.upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_1, NULL }, /* upsBypassLineIndex */ { "debug.[1].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_3, NULL }, { "debug.[2].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.2", "", SU_BYPASS_3, NULL }, { "debug.[3].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.3", "", SU_BYPASS_3, NULL }, #endif { "input.bypass.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_1, NULL }, /* upsBypassVoltage */ { "input.bypass.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_3, NULL }, { "input.bypass.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.2", "", SU_BYPASS_3, NULL }, { "input.bypass.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.3", "", SU_BYPASS_3, NULL }, { "input.bypass.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_1, NULL }, /* upsBypassCurrent */ { "input.bypass.L1.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_3, NULL }, { "input.bypass.L2.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.2", "", SU_BYPASS_3, NULL }, { "input.bypass.L3.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.3", "", SU_BYPASS_3, NULL }, { "input.bypass.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_1, NULL }, /* upsBypassPower */ { "input.bypass.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_3, NULL }, { "input.bypass.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.2", "", SU_BYPASS_3, NULL }, { "input.bypass.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.3", "", SU_BYPASS_3, NULL }, /* Alarm Group */ #ifdef DEBUG { "debug.upsAlarmsPresent", 0, 1.0, IETF_OID_UPS_MIB "6.1.0", "", 0, NULL }, /* upsAlarmsPresent */ { "debug.upsAlarmBatteryBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.1", "", 0, NULL }, /* upsAlarmBatteryBad */ { "debug.upsAlarmOnBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.2", "", 0, NULL }, /* upsAlarmOnBattery */ { "debug.upsAlarmLowBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.3", "", 0, NULL }, /* upsAlarmLowBattery */ { "debug.upsAlarmDepletedBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.4", "", 0, NULL }, /* upsAlarmDepletedBattery */ { "debug.upsAlarmTempBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.5", "", 0, NULL }, /* upsAlarmTempBad */ { "debug.upsAlarmInputBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.6", "", 0, NULL }, /* upsAlarmInputBad */ { "debug.upsAlarmOutputBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.7", "", 0, NULL }, /* upsAlarmOutputBad */ #endif { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.8", "", 0, ietf_overload_info }, /* upsAlarmOutputOverload */ #ifdef DEBUG { "debug.upsAlarmOnBypass", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.9", "", 0, NULL }, /* upsAlarmOnBypass */ { "debug.upsAlarmBypassBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.10", "", 0, NULL }, /* upsAlarmBypassBad */ { "debug.upsAlarmOutputOffAsRequested", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.11", "", 0, NULL }, /* upsAlarmOutputOffAsRequested */ { "debug.upsAlarmUpsOffAsRequested", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.12", "", 0, NULL }, /* upsAlarmUpsOffAsRequested */ { "debug.upsAlarmChargerFailed", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.13", "", 0, NULL }, /* upsAlarmChargerFailed */ { "debug.upsAlarmUpsOutputOff", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.14", "", 0, NULL }, /* upsAlarmUpsOutputOff */ { "debug.upsAlarmUpsSystemOff", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.15", "", 0, NULL }, /* upsAlarmUpsSystemOff */ { "debug.upsAlarmFanFailure", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.16", "", 0, NULL }, /* upsAlarmFanFailure */ { "debug.upsAlarmFuseFailure", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.17", "", 0, NULL }, /* upsAlarmFuseFailure */ { "debug.upsAlarmGeneralFault", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.18", "", 0, NULL }, /* upsAlarmGeneralFault */ { "debug.upsAlarmDiagnosticTestFailed", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.19", "", 0, NULL }, /* upsAlarmDiagnosticTestFailed */ { "debug.upsAlarmCommunicationsLost", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.20", "", 0, NULL }, /* upsAlarmCommunicationsLost */ { "debug.upsAlarmAwaitingPower", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.21", "", 0, NULL }, /* upsAlarmAwaitingPower */ { "debug.upsAlarmShutdownPending", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.22", "", 0, NULL }, /* upsAlarmShutdownPending */ { "debug.upsAlarmShutdownImminent", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.23", "", 0, NULL }, /* upsAlarmShutdownImminent */ { "debug.upsAlarmTestInProgress", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.24", "", 0, NULL }, /* upsAlarmTestInProgress */ #endif /* Test Group */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.1.0", "", 0, ietf_test_active_info }, /* upsTestId */ { "test.battery.stop", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.2", SU_TYPE_CMD, NULL }, /* upsTestAbortTestInProgress */ { "test.battery.start", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.3", SU_TYPE_CMD, NULL }, /* upsTestGeneralSystemsTest */ { "test.battery.start.quick", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.4", SU_TYPE_CMD, NULL }, /* upsTestQuickBatteryTest */ { "test.battery.start.deep", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.5", SU_TYPE_CMD, NULL }, /* upsTestDeepBatteryCalibration */ #ifdef DEBUG { "debug.upsTestSpinLock", 0, 1.0, IETF_OID_UPS_MIB "7.2.0", "", 0, NULL }, /* upsTestSpinLock */ #endif { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.3.0", "", 0, ietf_test_result_info }, /* upsTestResultsSummary */ #ifdef DEBUG { "debug.upsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.4.0", "", 0, NULL }, /* upsTestResultsDetail */ { "debug.upsTestStartTime", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.5.0", "", 0, NULL }, /* upsTestStartTime */ { "debug.upsTestElapsedTime", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.6.0", "", 0, NULL }, /* upsTestElapsedTime */ #endif /* Control Group */ #ifdef DEBUG { "debug.upsShutdownType", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.1.0", "", 0, ietf_shutdown_type_info }, /* upsShutdownType */ #endif { "ups.timer.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.2.0", "", 0, NULL }, /* upsShutdownAfterDelay*/ { "load.off", 0, 0, IETF_OID_UPS_MIB "8.2.0", "", SU_TYPE_CMD, NULL }, { "ups.timer.start", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.3.0", "", 0, NULL }, /* upsStartupAfterDelay */ { "load.on", 0, 0, IETF_OID_UPS_MIB "8.3.0", "", SU_TYPE_CMD, NULL }, { "ups.timer.reboot", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.4.0", "", 0, NULL }, /* upsRebootWithDuration */ { "ups.start.auto", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.5.0", "", 0, ietf_yes_no_info }, /* upsAutoRestart */ /* Configuration Group */ { "input.voltage.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.1.0", "", 0, NULL }, /* upsConfigInputVoltage */ { "input.frequency.nominal", 0, 0.1, IETF_OID_UPS_MIB "9.2.0", "", 0, NULL }, /* upsConfigInputFreq */ { "output.voltage.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.3.0", "", 0, NULL }, /* upsConfigOutputVoltage */ { "output.frequency.nominal", 0, 0.1, IETF_OID_UPS_MIB "9.4.0", "", 0, NULL }, /* upsConfigOutputFreq */ { "output.power.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.5.0", "", 0, NULL }, /* upsConfigOutputVA */ { "output.realpower.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.6.0", "", 0, NULL }, /* upsConfigOutputPower */ { "battery.runtime.low", 0, 60.0, IETF_OID_UPS_MIB "9.7.0", "", 0, NULL }, /* upsConfigLowBattTime */ { "ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "9.8.0", "", 0, ietf_beeper_status_info }, /* upsConfigAudibleStatus */ { "beeper.disable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "", SU_TYPE_CMD, NULL }, { "beeper.enable", 0, 2, IETF_OID_UPS_MIB "9.8.0", "", SU_TYPE_CMD, NULL }, { "beeper.mute", 0, 3, IETF_OID_UPS_MIB "9.8.0", "", SU_TYPE_CMD, NULL }, { "input.transfer.low", 0, 1.0, IETF_OID_UPS_MIB "9.9.0", "", 0, NULL }, /* upsConfigLowVoltageTransferPoint */ { "input.transfer.high", 0, 1.0, IETF_OID_UPS_MIB "9.10.0", "", 0, NULL }, /* upsConfigHighVoltageTransferPoint */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t ietf = { "ietf", IETF_MIB_VERSION, IETF_OID_UPS_MIB "4.1.0", IETF_OID_UPS_MIB "1.1.0", ietf_mib, IETF_SYSOID }; mib2nut_info_t tripplite_ietf = { "ietf", IETF_MIB_VERSION, NULL, NULL, ietf_mib, TRIPPLITE_SYSOID }; nut-2.7.4/drivers/nutdrv_qx_blazer-common.c0000644000175000017500000002276212640473702015772 00000000000000/* nutdrv_qx_blazer-common.c - Common functions/settings for nutdrv_qx_{mecer,megatec,megatec-old,mustek,q1,voltronic-qs,zinto}.{c,h} * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" /* == Ranges == */ /* Range for ups.delay.start */ info_rw_t blazer_r_ondelay[] = { { "0", 0 }, { "599940", 0 }, { "", 0 } }; /* Range for ups.delay.shutdown */ info_rw_t blazer_r_offdelay[] = { { "12", 0 }, { "600", 0 }, { "", 0 } }; /* == Support functions == */ /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ int blazer_claim(void) { /* To tell whether the UPS is supported or not, we'll check both status (Q1/QS/D) and vendor (I/FW?) - provided that we were not told not to do it with the ups.conf flag 'novendor'. */ item_t *item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value */ if (ups_infoval_set(item) != 1) return 0; if (testvar("novendor")) return 1; /* Vendor */ item = find_nut_info("ups.firmware", 0, 0); /* Don't know what happened */ if (!item) { dstate_delinfo("input.voltage"); return 0; } /* No reply/Unable to get value */ if (qx_process(item, NULL)) { dstate_delinfo("input.voltage"); return 0; } /* Unable to process value */ if (ups_infoval_set(item) != 1) { dstate_delinfo("input.voltage"); return 0; } return 1; } /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. * NOTE: this 'light' version only checks for status (Q1/QS/D/..) */ int blazer_claim_light(void) { /* To tell whether the UPS is supported or not, we'll check just status (Q1/QS/D/..). */ item_t *item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value */ if (ups_infoval_set(item) != 1) return 0; return 1; } /* Subdriver-specific flags/vars */ void blazer_makevartable(void) { addvar(VAR_FLAG, "norating", "Skip reading rating information from UPS"); addvar(VAR_FLAG, "novendor", "Skip reading vendor information from UPS"); blazer_makevartable_light(); } /* Subdriver-specific flags/vars * NOTE: this 'light' version only handles vars/flags related to UPS status query (Q1/QS/D/...) */ void blazer_makevartable_light(void) { addvar(VAR_FLAG, "ignoresab", "Ignore 'Shutdown Active' bit in UPS status"); } /* Subdriver-specific initups */ void blazer_initups(item_t *qx2nut) { int nr, nv, isb; item_t *item; nr = testvar("norating"); nv = testvar("novendor"); isb = testvar("ignoresab"); if (!nr && !nv && !isb) return; for (item = qx2nut; item->info_type != NULL; item++) { if (!item->command) continue; /* norating */ if (nr && !strcasecmp(item->command, "F\r")) { upsdebugx(2, "%s: skipping %s", __func__, item->info_type); item->qxflags |= QX_FLAG_SKIP; } /* novendor */ if (nv && (!strcasecmp(item->command, "I\r") || !strcasecmp(item->command, "FW?\r"))) { upsdebugx(2, "%s: skipping %s", __func__, item->info_type); item->qxflags |= QX_FLAG_SKIP; } /* ignoresab */ if (isb && !strcasecmp(item->info_type, "ups.status") && item->from == 44 && item->to == 44) { upsdebugx(2, "%s: skipping %s ('Shutdown Active' bit)", __func__, item->info_type); item->qxflags |= QX_FLAG_SKIP; } } } /* Subdriver-specific initups * NOTE: this 'light' version only checks for status (Q1/QS/D/..) related items */ void blazer_initups_light(item_t *qx2nut) { item_t *item; if (!testvar("ignoresab")) return; for (item = qx2nut; item->info_type != NULL; item++) { if (strcasecmp(item->info_type, "ups.status") || item->from != 44 || item->to != 44) continue; upsdebugx(2, "%s: skipping %s ('Shutdown Active' bit)", __func__, item->info_type); item->qxflags |= QX_FLAG_SKIP; break; } } /* == Preprocess functions == */ /* Preprocess setvars */ int blazer_process_setvar(item_t *item, char *value, const size_t valuelen) { if (!strlen(value)) { upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); return -1; } if (!strcasecmp(item->info_type, "ups.delay.start")) { int ondelay = strtol(value, NULL, 10); /* Truncate to minute */ ondelay -= (ondelay % 60); snprintf(value, valuelen, "%d", ondelay); } else if (!strcasecmp(item->info_type, "ups.delay.shutdown")) { int offdelay = strtol(value, NULL, 10); /* Truncate to nearest settable value */ if (offdelay < 60) { offdelay -= (offdelay % 6); } else { offdelay -= (offdelay % 60); } snprintf(value, valuelen, "%d", offdelay); } else { /* Don't know what happened */ return -1; } return 0; } /* Preprocess instant commands */ int blazer_process_command(item_t *item, char *value, const size_t valuelen) { if (!strcasecmp(item->info_type, "shutdown.return")) { /* Sn: Shutdown after n minutes and then turn on when mains is back * SnRm: Shutdown after n minutes and then turn on after m minutes * Accepted values for n: .2 -> .9 , 01 -> 10 * Accepted values for m: 0001 -> 9999 * Note: "S01R0001" and "S01R0002" may not work on early (GE) firmware versions. * The failure mode is that the UPS turns off and never returns. * The fix is to push the return value up by 2, i.e. S01R0003, and it will return online properly. * (thus the default of ondelay=3 mins) */ int offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10), ondelay = strtol(dstate_getinfo("ups.delay.start"), NULL, 10) / 60; char buf[SMALLBUF] = ""; if (ondelay == 0) { if (offdelay < 60) { snprintf(buf, sizeof(buf), ".%d", offdelay / 6); } else { snprintf(buf, sizeof(buf), "%02d", offdelay / 60); } } else if (offdelay < 60) { snprintf(buf, sizeof(buf), ".%dR%04d", offdelay / 6, ondelay); } else { snprintf(buf, sizeof(buf), "%02dR%04d", offdelay / 60, ondelay); } snprintf(value, valuelen, item->command, buf); } else if (!strcasecmp(item->info_type, "shutdown.stayoff")) { /* SnR0000 * Shutdown after n minutes and stay off * Accepted values for n: .2 -> .9 , 01 -> 10 */ int offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10); char buf[SMALLBUF] = ""; if (offdelay < 60) { snprintf(buf, sizeof(buf), ".%d", offdelay / 6); } else { snprintf(buf, sizeof(buf), "%02d", offdelay / 60); } snprintf(value, valuelen, item->command, buf); } else if (!strcasecmp(item->info_type, "test.battery.start")) { int delay = strlen(value) > 0 ? strtol(value, NULL, 10) : 600; if ((delay < 60) || (delay > 5940)) { upslogx(LOG_ERR, "%s: battery test time '%d' out of range [60..5940] seconds", item->info_type, delay); return -1; } delay = delay / 60; snprintf(value, valuelen, item->command, delay); } else { /* Don't know what happened */ return -1; } return 0; } /* Process status bits */ int blazer_process_status_bits(item_t *item, char *value, const size_t valuelen) { char *val = ""; if (strspn(item->value, "01") != 1) { upsdebugx(3, "%s: unexpected value %s@%d->%s", __func__, item->value, item->from, item->value); return -1; } switch (item->from) { case 38: /* Utility Fail (Immediate) */ if (item->value[0] == '1') val = "!OL"; else val = "OL"; break; case 39: /* Battery Low */ if (item->value[0] == '1') val = "LB"; else val = "!LB"; break; case 40: /* Bypass/Boost or Buck Active */ if (item->value[0] == '1') { double vi, vo; vi = strtod(dstate_getinfo("input.voltage"), NULL); vo = strtod(dstate_getinfo("output.voltage"), NULL); if (vo < 0.5 * vi) { upsdebugx(2, "%s: output voltage too low", __func__); return -1; } else if (vo < 0.95 * vi) { val = "TRIM"; } else if (vo < 1.05 * vi) { val = "BYPASS"; } else if (vo < 1.5 * vi) { val = "BOOST"; } else { upsdebugx(2, "%s: output voltage too high", __func__); return -1; } } break; case 41: /* UPS Failed - ups.alarm */ if (item->value[0] == '1') val = "UPS selftest failed!"; break; case 42: /* UPS Type - ups.type */ if (item->value[0] == '1') val = "offline / line interactive"; else val = "online"; break; case 43: /* Test in Progress */ if (item->value[0] == '1') val = "CAL"; else val = "!CAL"; break; case 44: /* Shutdown Active */ if (item->value[0] == '1') val = "FSD"; else val = "!FSD"; break; case 45: /* Beeper status - ups.beeper.status */ if (item->value[0] == '1') val = "enabled"; else val = "disabled"; break; default: /* Don't know what happened */ return -1; } snprintf(value, valuelen, "%s", val); return 0; } nut-2.7.4/drivers/openups-hid.h0000644000175000017500000000241012640473702013337 00000000000000/* openups-hid.h - subdriver to monitor Minibox openUPS USB/HID devices with NUT * * Copyright (C) * 2003 - 2009 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * 2012 Nicu Pavel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef OPENUPS_HID_H #define OPENUPS_HID_H #include "usbhid-ups.h" /* Don't put non-extern definitions here - this file gets included by usbhid-ups.c */ extern subdriver_t openups_subdriver; #endif /* OPENUPS_HID_H */ nut-2.7.4/drivers/libshut.c0000644000175000017500000006314112640473702012561 00000000000000/*! * @file libshut.c * @brief HID Library - SHUT communication sub driver * * @author Copyright (C) * 2006 - 2009 Arnaud Quette * * 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. * * -------------------------------------------------------------------------- */ /* TODO list * - cleanup, cleanup, cleanup * - manage interrupt and complete libshut_get_interrupt / shut_control_msg routing * - baudrate negotiation * - complete shut_strerror * - validate / complete commands and data table in mge-hid from mge-shut */ #include #include #include #include #include "nut_stdint.h" /* for uint8_t, uint16_t, uint32_t */ #include "serial.h" #include "libshut.h" #include "common.h" /* for xmalloc, upsdebugx prototypes */ #define SHUT_DRIVER_NAME "SHUT communication driver" #define SHUT_DRIVER_VERSION "0.85" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { SHUT_DRIVER_NAME, SHUT_DRIVER_VERSION, NULL, 0, { NULL } }; #define MAX_TRY 4 #define MAX_STRING_SIZE 128 /*! * HID descriptor, completed with desc{type,len} */ struct my_hid_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t bcdHID; uint8_t bCountryCode; uint8_t bNumDescriptors; uint8_t bReportDescriptorType; uint16_t wDescriptorLength; }; /*!******************************************************** * USB spec information, synchronised with (ripped from) libusb */ /* * Device and/or Interface Class codes */ #define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ #define USB_CLASS_AUDIO 1 #define USB_CLASS_COMM 2 #define USB_CLASS_HID 3 #define USB_CLASS_PRINTER 7 #define USB_CLASS_MASS_STORAGE 8 #define USB_CLASS_HUB 9 #define USB_CLASS_DATA 10 #define USB_CLASS_VENDOR_SPEC 0xff /* * Descriptor types */ #define USB_DT_DEVICE 0x01 #define USB_DT_CONFIG 0x02 #define USB_DT_STRING 0x03 #define USB_DT_INTERFACE 0x04 #define USB_DT_ENDPOINT 0x05 #define USB_DT_HID 0x21 #define USB_DT_REPORT 0x22 #define USB_DT_PHYSICAL 0x23 #define USB_DT_HUB 0x29 /* * Descriptor sizes per descriptor type */ #define USB_DT_DEVICE_SIZE 18 #define USB_DT_CONFIG_SIZE 9 #define USB_DT_INTERFACE_SIZE 9 #define USB_DT_ENDPOINT_SIZE 7 #define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ #define USB_DT_HUB_NONVAR_SIZE 7 /* * Standard requests */ #define USB_REQ_GET_STATUS 0x00 #define USB_REQ_CLEAR_FEATURE 0x01 /* 0x02 is reserved */ #define USB_REQ_SET_FEATURE 0x03 /* 0x04 is reserved */ #define USB_REQ_SET_ADDRESS 0x05 #define USB_REQ_GET_DESCRIPTOR 0x06 #define USB_REQ_SET_DESCRIPTOR 0x07 #define USB_REQ_GET_CONFIGURATION 0x08 #define USB_REQ_SET_CONFIGURATION 0x09 #define USB_REQ_GET_INTERFACE 0x0A #define USB_REQ_SET_INTERFACE 0x0B #define USB_REQ_SYNCH_FRAME 0x0C #define USB_TYPE_STANDARD (0x00 << 5) #define USB_TYPE_CLASS (0x01 << 5) #define USB_TYPE_VENDOR (0x02 << 5) #define USB_TYPE_RESERVED (0x03 << 5) #define USB_RECIP_DEVICE 0x00 #define USB_RECIP_INTERFACE 0x01 #define USB_RECIP_ENDPOINT 0x02 #define USB_RECIP_OTHER 0x03 /* * Various libusb API related stuff */ #define USB_ENDPOINT_IN 0x80 #define USB_ENDPOINT_OUT 0x00 /*! * end of USB spec information *********************************************************/ /*! * HID definitions */ #define HID_REPORT_TYPE_INPUT 0x01 #define HID_REPORT_TYPE_OUTPUT 0x02 #define HID_REPORT_TYPE_FEATURE 0x03 #define REQUEST_TYPE_USB 0x80 #define REQUEST_TYPE_HID 0x81 #define REQUEST_TYPE_GET_REPORT 0xa1 #define REQUEST_TYPE_SET_REPORT 0x21 #define MAX_REPORT_SIZE 0x1800 /*! * SHUT definitions - From Simplified SHUT spec */ #define SHUT_TYPE_REQUEST 0x01 #define SHUT_TYPE_RESPONSE 0x04 #define SHUT_TYPE_NOTIFY 0x05 #define SHUT_OK 0x06 #define SHUT_NOK 0x15 /* sync signals are also used to set the notification level */ #define SHUT_SYNC 0x16 /* complete notifications - not yet managed */ /* but needed for some early Ellipse models */ #define SHUT_SYNC_LIGHT 0x17 /* partial notifications */ #define SHUT_SYNC_OFF 0x18 /* disable notifications - only do polling */ #define SHUT_PKT_LAST 0x80 #define SHUT_TIMEOUT 3000 /*! * SHUT functions for HID marshalling */ int shut_get_descriptor(int upsfd, unsigned char type, unsigned char index, void *buf, int size); int shut_get_string_simple(int upsfd, int index, char *buf, size_t buflen); int libshut_get_report(int upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ); int shut_set_report(int upsfd, int id, unsigned char *pkt, int reportlen); int libshut_get_interrupt(int upsfd, unsigned char *buf, int bufsize, int timeout); void libshut_close(int upsfd); /* FIXME */ const char * shut_strerror(void) { return ""; } /*! * From SHUT specifications * sync'ed with libusb */ typedef struct shut_ctrltransfer_s { uint8_t bRequestType; uint8_t bRequest; uint16_t wValue; uint16_t wIndex; uint16_t wLength; uint32_t timeout; /* in milliseconds */ /* pointer to data */ void *data; /* uint8_t padding[8]; for use with shut_set_report?! */ } shut_ctrltransfer_t; typedef union hid_data_t { shut_ctrltransfer_t hid_pkt; uint8_t raw_pkt[8]; /* max report lengh, was 8 */ } hid_data_t; typedef struct shut_packet_s { uint8_t bType; uint8_t bLength; hid_data_t data; uint8_t bChecksum; } shut_packet_t; typedef union shut_data_t { shut_packet_t shut_pkt; uint8_t raw_pkt[11]; } shut_data_t; typedef union hid_desc_data_t { struct my_hid_descriptor hid_desc; uint8_t raw_desc[9]; /* max report lengh, aws 9 */ } hid_desc_data_t; /* Device descriptor */ typedef struct device_descriptor_s { uint8_t bLength; uint8_t bDescriptorType; uint16_t bcdUSB; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bMaxPacketSize0; uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t iManufacturer; uint8_t iProduct; uint8_t iSerialNumber; uint8_t bNumConfigurations; } device_descriptor_t; #if 0 typedef union device_desc_data_t { device_descriptor_t dev_desc; uint8_t raw_desc[18]; } device_desc_data_t; #endif /* Low level SHUT (Serial HID UPS Transfer) routines */ void setline(int upsfd, int set); int shut_synchronise(int upsfd); int shut_wait_ack(int upsfd); int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, int size, int timeout); int shut_control_msg(int upsfd, int requesttype, int request, int value, int index, unsigned char *bytes, int size, int timeout); /* Data portability */ /* realign packet data according to Endianess */ #define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8)) static void align_request(struct shut_ctrltransfer_s *ctrl ) { #if WORDS_BIGENDIAN /* Sparc/Mips/... are big endian, USB/SHUT little endian */ (*ctrl).wValue = BYTESWAP((*ctrl).wValue); (*ctrl).wIndex = BYTESWAP((*ctrl).wIndex); (*ctrl).wLength = BYTESWAP((*ctrl).wLength); #endif } /* On success, fill in the curDevice structure and return the report * descriptor length. On failure, return -1. * Note: When callback is not NULL, the report descriptor will be * passed to this function together with the upsfd and SHUTDevice_t * information. This callback should return a value > 0 if the device * is accepted, or < 1 if not. */ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, int (*callback)(int upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)) { int ret, res; unsigned char buf[20]; char string[MAX_STRING_SIZE]; struct my_hid_descriptor *desc; struct device_descriptor_s *dev_descriptor; /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; int rdlen; /* All devices use HID descriptor at index 0. However, some newer * Eaton units have a light HID descriptor at index 0, and the full * version is at index 1 (in which case, bcdDevice == 0x0202) */ int hid_desc_index = 0; upsdebugx(2, "libshut_open: using port %s", device_path); /* If device is still open, close it */ if (*upsfd > 0) { ser_close(*upsfd, device_path); } /* initialize serial port */ /* FIXME: add variable baudrate detection */ *upsfd = ser_open(device_path); ser_set_speed(*upsfd, device_path, B2400); setline(*upsfd, 1); /* initialise communication */ if (!shut_synchronise(*upsfd)) { upsdebugx(2, "No communication with UPS"); return -1; } upsdebugx(2, "Communication with UPS established"); /* we can skip the rest due to serial bus specifics! */ if (!callback) { return 1; } /* Get DEVICE descriptor */ dev_descriptor = (struct device_descriptor_s *)buf; res = shut_get_descriptor(*upsfd, USB_DT_DEVICE, 0, buf, USB_DT_DEVICE_SIZE); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_DEVICE << 8) + 0, 0, buf, 0x9, SHUT_TIMEOUT); */ if (res < 0) { upsdebugx(2, "Unable to get DEVICE descriptor (%s)", shut_strerror()); return -1; } if (res < 9) { upsdebugx(2, "DEVICE descriptor too short (expected %d, got %d)", USB_DT_DEVICE_SIZE, res); return -1; } /* collect the identifying information of this device. Note that this is safe, because there's no need to claim an interface for this (and therefore we do not yet need to detach any kernel drivers). */ free(curDevice->Vendor); free(curDevice->Product); free(curDevice->Serial); free(curDevice->Bus); memset(curDevice, '\0', sizeof(*curDevice)); curDevice->VendorID = dev_descriptor->idVendor; curDevice->ProductID = dev_descriptor->idProduct; curDevice->Bus = strdup("serial"); curDevice->bcdDevice = dev_descriptor->bcdDevice; curDevice->Vendor = strdup("Eaton"); if (dev_descriptor->iManufacturer) { ret = shut_get_string_simple(*upsfd, dev_descriptor->iManufacturer, string, MAX_STRING_SIZE); if (ret > 0) { curDevice->Vendor = strdup(string); } } /* ensure iProduct retrieval */ if (dev_descriptor->iProduct) { ret = shut_get_string_simple(*upsfd, dev_descriptor->iProduct, string, MAX_STRING_SIZE); } else { ret = 0; } if (ret > 0) { curDevice->Product = strdup(string); } else { curDevice->Product = strdup("unknown"); } if (dev_descriptor->iSerialNumber) { ret = shut_get_string_simple(*upsfd, dev_descriptor->iSerialNumber, string, 0x25); } else { ret = 0; } if (ret > 0) { curDevice->Serial = strdup(string); } else { curDevice->Serial = strdup("unknown"); } upsdebugx(2, "- VendorID: %04x", curDevice->VendorID); upsdebugx(2, "- ProductID: %04x", curDevice->ProductID); upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor); upsdebugx(2, "- Product: %s", curDevice->Product); upsdebugx(2, "- Serial Number: %s", curDevice->Serial); upsdebugx(2, "- Bus: %s", curDevice->Bus); upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); upsdebugx(2, "Device matches"); if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); hid_desc_index = 1; } /* Get HID descriptor */ desc = (struct my_hid_descriptor *)buf; res = shut_get_descriptor(*upsfd, USB_DT_HID, hid_desc_index, buf, 0x9); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_HID << 8) + 0, 0, buf, 0x9, SHUT_TIMEOUT); */ if (res < 0) { upsdebugx(2, "Unable to get HID descriptor (%s)", shut_strerror()); return -1; } if (res < 9) { upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); return -1; } /* USB_LE16_TO_CPU(desc->wDescriptorLength); */ desc->wDescriptorLength = buf[7] | (buf[8] << 8); upsdebugx(2, "HID descriptor retrieved (Reportlen = %u)", desc->wDescriptorLength); /* if (!dev->config) { upsdebugx(2, " Couldn't retrieve descriptors"); return -1; }*/ rdlen = desc->wDescriptorLength; if (rdlen > (int)sizeof(rdbuf)) { upsdebugx(2, "HID descriptor too long %d (max %d)", rdlen, (int)sizeof(rdbuf)); return -1; } /* Get REPORT descriptor */ res = shut_get_descriptor(*upsfd, USB_DT_REPORT, hid_desc_index, rdbuf, rdlen); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_REPORT << 8) + 0, 0, ReportDesc, desc->wDescriptorLength, SHUT_TIMEOUT); */ if (res == rdlen) { res = callback(*upsfd, curDevice, rdbuf, rdlen); if (res < 1) { upsdebugx(2, "Caller doesn't like this device"); return -1; } upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); upsdebugx(2, "Found HID device"); fflush(stdout); return rdlen; } if (res < 0) { upsdebugx(2, "Unable to get Report descriptor (%d)", res); } else { upsdebugx(2, "Report descriptor too short (expected %d, got %d)", rdlen, res); } upsdebugx(2, "No appropriate HID device found"); fflush(stdout); return -1; } void libshut_close(int upsfd) { if (upsfd < 1) { return; } ser_close(upsfd, NULL); } /* return the report of ID=type in report * return -1 on failure, report length on success */ int libshut_get_report(int upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ) { if (upsfd < 1) { return 0; } upsdebugx(4, "Entering libshut_get_report"); return shut_control_msg(upsfd, REQUEST_TYPE_GET_REPORT, /* == USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, */ 0x01, ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, SHUT_TIMEOUT); } /* return ReportSize upon success ; -1 otherwise */ int libshut_set_report(int upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ) { int ret; if (upsfd < 1) { return 0; } upsdebugx(1, "Entering libshut_set_report (report %x, len %i)", ReportId, ReportSize); upsdebug_hex (4, "==> Report after set", raw_buf, ReportSize); ret = shut_control_msg(upsfd, REQUEST_TYPE_SET_REPORT, /* == USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, */ 0x09, ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, SHUT_TIMEOUT); return ((ret == 0) ? ReportSize : ret); } int libshut_get_string(int upsfd, int StringIdx, char *buf, size_t buflen) { int ret; if (upsfd < 1) { return -1; } ret = shut_get_string_simple(upsfd, StringIdx, buf, buflen); if (ret > 0) upsdebugx(2, "-> String: %s (len = %i/%i)", buf, ret, (int)buflen); else upsdebugx(2, "- Unable to fetch buf"); return ret; } int libshut_get_interrupt(int upsfd, unsigned char *buf, int bufsize, int timeout) { int ret; if (upsfd < 1) { return -1; } /* FIXME: hardcoded interrupt EP => need to get EP descr for IF descr */ ret = shut_interrupt_read(upsfd, 0x81, buf, bufsize, timeout); if (ret > 0) upsdebugx(6, " ok"); else upsdebugx(6, " none (%i)", ret); return ret; } shut_communication_subdriver_t shut_subdriver = { SHUT_DRIVER_NAME, SHUT_DRIVER_VERSION, libshut_open, libshut_close, libshut_get_report, libshut_set_report, libshut_get_string, libshut_get_interrupt }; /***********************************************************************/ /********** Low level SHUT (Serial HID UPS Transfer) routines **********/ /***********************************************************************/ /* * set RTS to on and DTR to off * * set : 1 to set comm * set : 0 to stop commupsh. */ void setline(int upsfd, int set) { if (upsfd < 1) { return; } upsdebugx(3, "entering setline(%i)", set); if (set == 1) { ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 1); } else { ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); } } /***************************************************************************** * shut_synchronise () * * initiate communication with the UPS * * return TRUE on success, FALSE on failure * *****************************************************************************/ int shut_synchronise(int upsfd) { int retCode = 0; u_char c = SHUT_SYNC_OFF, reply; int try; upsdebugx (2, "entering shut_synchronise()"); reply = '\0'; /* FIXME: re enable notification support? switch (notification) { case OFF_NOTIFICATION: c = SHUT_SYNC_OFF; break; case LIGHT_NOTIFICATION: c = SHUT_SYNC_LIGHT; break; default: case COMPLETE_NOTIFICATION: c = SHUT_SYNC; break; } */ /* Sync with the UPS according to notification */ for (try = 0; try < MAX_TRY; try++) { upsdebugx (3, "Syncing communication (try %i)", try); if ((ser_send_char(upsfd, c)) == -1) { upsdebugx (3, "Communication error while writing to port"); continue; } ser_get_char(upsfd, &reply, 1, 0); if (reply == c) { upsdebugx (3, "Syncing and notification setting done"); return 1; } } return retCode; } /*! * Compute a SHUT checksum for the packet "buf" */ u_char shut_checksum(const u_char *buf, int bufsize) { int i; u_char chk=0; for(i=0; i0 && Retry<3) { /* if(serial_read (SHUT_TIMEOUT, &Start[0]) > 0) */ if(ser_get_char(upsfd, &Start[0], SHUT_TIMEOUT/1000, 0) > 0) { /* sdata.shut_pkt.bType = Start[0]; */ if(Start[0]==SHUT_SYNC) { upsdebugx (4, "received SYNC token"); memcpy(Buf, Start, 1); return 1; } else if(Start[0]==SHUT_SYNC_OFF) { upsdebugx (4, "received SYNC_OFF token"); memcpy(Buf, Start, 1); return 1; } else { /* if((serial_read (SHUT_TIMEOUT, &Start[1]) > 0) && */ if( (ser_get_char(upsfd, &Start[1], SHUT_TIMEOUT/1000, 0) > 0) && ((Start[1]>>4)==(Start[1]&0x0F))) { upsdebug_hex(4, "Receive", Start, 2); Size=Start[1]&0x0F; if( Size > 8 ) { upsdebugx (4, "shut_packet_recv: invalid frame size = %d", Size); ser_send_char(upsfd, SHUT_NOK); Retry++; break; } /* sdata.shut_pkt.bLength = Size; */ for(recv=0;recv OK", Chk[0]); memcpy(Buf, Frame, Size); datalen-=Size; Buf+=Size; Pos+=Size; Retry=0; ser_send_char(upsfd, SHUT_OK); /* shut_token_send(SHUT_OK); */ /* Check if there are more data to receive */ if((Start[0] & 0xf0) == SHUT_PKT_LAST) { /* Check if it's a notification */ if ((Start[0] & 0x0f) == SHUT_TYPE_NOTIFY) { /* TODO: process notification (dropped for now) */ upsdebugx (4, "=> notification"); datalen+=Pos; Pos=0; } else return Pos; } else upsdebugx (4, "need more data (%i)!", datalen); } else { upsdebugx (4, "shut_checksum: %02x => NOK", Chk[0]); ser_send_char(upsfd, SHUT_NOK); /* shut_token_send(SHUT_NOK); */ Retry++; } } else return 0; } } else Retry++; } /* while */ return 0; } /**********************************************************************/ int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, int size, int timeout) { /* usleep(timeout * 1000); */ /* FIXME: to be written */ return 0; } /**********************************************************************/ int shut_get_string_simple(int upsfd, int index, char *buf, size_t buflen) { unsigned char tbuf[255]; /* Some devices choke on size > 255 */ int ret, si, di; ret = shut_control_msg(upsfd, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0, tbuf, buflen, SHUT_TIMEOUT); if (ret < 0) return ret; if (tbuf[1] != USB_DT_STRING) return -EIO; if (tbuf[0] > ret) return -EFBIG; /* skip the UTF8 zero'ed high bytes */ for (di = 0, si = 2; si < tbuf[0]; si += 2) { if (di >= (int)(buflen - 1)) break; if (tbuf[si + 1]) /* high byte */ buf[di++] = '?'; else buf[di++] = tbuf[si]; } buf[di] = 0; return di; } /* * Human Interface Device (HID) functions *********************************************************************/ /********************************************************************** * shut_get_descriptor(int desctype, u_char *pkt) * * get descriptor specified by DescType and return it in Buf * * desctype - from shutdataType * pkt - where to store the report received * * return 0 on success, -1 on failure, -2 on NACK * *********************************************************************/ int shut_get_descriptor(int upsfd, unsigned char type, unsigned char index, void *buf, int size) { memset(buf, 0, size); upsdebugx (2, "entering shut_get_descriptor(n %02x, %i)", type, size); return shut_control_msg(upsfd, USB_ENDPOINT_IN+(type>=USB_DT_HID?1:0), USB_REQ_GET_DESCRIPTOR, (type << 8) + index, 0, buf, size, SHUT_TIMEOUT); } /* Take care of a SHUT transfer (sending and receiving data) */ int shut_control_msg(int upsfd, int requesttype, int request, int value, int index, unsigned char *bytes, int size, int timeout) { unsigned char shut_pkt[11]; short Retry=1, set_pass = -1; short data_size, remaining_size = size; int i; struct shut_ctrltransfer_s ctrl; int ret = 0; upsdebugx (3, "entering shut_control_msg"); /* deal for set requests */ if (requesttype == REQUEST_TYPE_SET_REPORT) { set_pass = 1; /* add 8 for the first frame that declares a coming set */ remaining_size+= 8; } /* build the control request */ ctrl.bRequestType = requesttype; ctrl.bRequest = request; ctrl.wValue = value; ctrl.wIndex = index; ctrl.wLength = size; ctrl.data = bytes; ctrl.timeout = timeout; align_request(&ctrl); /* Send all data */ while(remaining_size > 0 && Retry > 0) { if (requesttype == REQUEST_TYPE_SET_REPORT) { if (set_pass == 1) { data_size = 8; set_pass++; /* prepare for the next step */ } else { data_size = size; upsdebug_hex(4, "data", bytes, data_size); } } else { /* Always 8 bytes payload for GET_REPORT with SHUT */ data_size = 8; } /* Forge the SHUT Frame */ shut_pkt[0] = SHUT_TYPE_REQUEST + ( ((requesttype == REQUEST_TYPE_SET_REPORT) && (remaining_size>8))? 0 : SHUT_PKT_LAST); shut_pkt[1] = (data_size<<4) + data_size; if ( (requesttype == REQUEST_TYPE_SET_REPORT) && (remaining_size < 8) ) memcpy(&shut_pkt[2], bytes, data_size); /* we need to send ctrl.data */ else memcpy(&shut_pkt[2], &ctrl, 8); shut_pkt[(data_size+3) - 1] = shut_checksum(&shut_pkt[2], data_size); /* Packets need only to be sent once * NACK handling should take care of the rest */ if (Retry == 1) { ser_send_buf(upsfd, shut_pkt, data_size+3); upsdebug_hex(3, "shut_control_msg", shut_pkt, data_size+3); /* serial_send (shut_pkt, data_size+3); */ } i = shut_wait_ack (upsfd); switch (i) { case 0: if (requesttype == REQUEST_TYPE_SET_REPORT) remaining_size-=data_size; else remaining_size = 0; Retry=1; break; case -1: if (Retry >= MAX_TRY) { upsdebugx(2, "Max tries reached while waiting for ACK, still getting errors"); /* try to resync, and give one more try */ Retry--; shut_synchronise(upsfd); return i; } else { upsdebugx(4, "Retry = %i", Retry); /* Send a NACK to get a resend from the UPS */ ser_send_char(upsfd, SHUT_NOK); Retry++; } break; case -3: /* FIXME: notification caught => to be processed */ /* Send a NACK for the moment, to get a resend from the UPS */ ser_send_char(upsfd, SHUT_NOK); Retry++; default: ; } } if (remaining_size != 0) return -1; /* now receive data, except for SET_REPORT */ if (requesttype != REQUEST_TYPE_SET_REPORT) ret = shut_packet_recv (upsfd, bytes, size); return ret; } /********************************************************************** * shut_wait_ack() * * wait for an ACK packet * * returns 0 on success, -1 on error, -2 on NACK, -3 on NOTIFICATION * *********************************************************************/ int shut_wait_ack(int upsfd) { int retCode = -1; u_char c = '\0'; ser_get_char(upsfd, &c, SHUT_TIMEOUT/1000, 0); if (c == SHUT_OK) { upsdebugx (2, "shut_wait_ack(): ACK received"); retCode = 0; } else if (c == SHUT_NOK) { upsdebugx (2, "shut_wait_ack(): NACK received"); retCode = -2; } else if ((c & 0x0f) == SHUT_TYPE_NOTIFY) { upsdebugx (2, "shut_wait_ack(): NOTIFY received"); retCode = -3; } else if (c == '\0') upsdebugx (2, "shut_wait_ack(): Nothing received"); return retCode; } nut-2.7.4/drivers/nutdrv_qx_voltronic-qs.c0000644000175000017500000001717612640473702015670 00000000000000/* nutdrv_qx_voltronic-qs.c - Subdriver for Voltronic Power UPSes with QS protocol * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_voltronic-qs.h" #define VOLTRONIC_QS_VERSION "Voltronic-QS 0.07" /* Support functions */ static int voltronic_qs_claim(void); static void voltronic_qs_initups(void); /* Preprocess functions */ static int voltronic_qs_protocol(item_t *item, char *value, const size_t valuelen); /* == Ranges == */ /* Range for ups.delay.start */ static info_rw_t voltronic_qs_r_ondelay[] = { { "60", 0 }, { "599940", 0 }, { "", 0 } }; /* Range for ups.delay.shutdown */ static info_rw_t voltronic_qs_r_offdelay[] = { { "12", 0 }, { "540", 0 }, { "", 0 } }; /* == qx2nut lookup table == */ static item_t voltronic_qs_qx2nut[] = { /* Query UPS for protocol * > [M\r] * < [V\r] * 01 * 0 */ { "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "PM-%s", QX_FLAG_STATIC, NULL, NULL, voltronic_qs_protocol }, /* Query UPS for status * > [QS\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "output.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* Query UPS for ratings * > [F\r] * < [#220.0 003 12.00 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "output.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "output.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "output.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, voltronic_qs_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* == Testing table == */ #ifdef TESTING static testing_t voltronic_qs_testing[] = { { "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "F\r", "#220.0 003 12.00 50.0\r", -1 }, { "M\r", "V\r", -1 }, { "Q\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "", -1 }, { "T\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* == Support functions == */ /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ static int voltronic_qs_claim(void) { /* We need at least M and QS to run this subdriver */ /* UPS Protocol */ item_t *item = find_nut_info("ups.firmware.aux", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value/Protocol not supported */ if (ups_infoval_set(item) != 1) return 0; item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) { dstate_delinfo("ups.firmware.aux"); return 0; } /* No reply/Unable to get value */ if (qx_process(item, NULL)) { dstate_delinfo("ups.firmware.aux"); return 0; } /* Unable to process value */ if (ups_infoval_set(item) != 1) { dstate_delinfo("ups.firmware.aux"); return 0; } return 1; } /* Subdriver-specific initups */ static void voltronic_qs_initups(void) { blazer_initups_light(voltronic_qs_qx2nut); } /* == Preprocess functions == */ /* Protocol used by the UPS */ static int voltronic_qs_protocol(item_t *item, char *value, const size_t valuelen) { if (strcasecmp(item->value, "V")) { upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value); return -1; } snprintf(value, valuelen, item->dfl, item->value); return 0; } /* == Subdriver interface == */ subdriver_t voltronic_qs_subdriver = { VOLTRONIC_QS_VERSION, voltronic_qs_claim, voltronic_qs_qx2nut, voltronic_qs_initups, NULL, blazer_makevartable_light, NULL, NULL, #ifdef TESTING voltronic_qs_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/nutdrv_qx_voltronic.h0000644000175000017500000000176312640473702015247 00000000000000/* nutdrv_qx_voltronic.h - Subdriver for Voltronic Power UPSes * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_VOLTRONIC_H #define NUTDRV_QX_VOLTRONIC_H #include "nutdrv_qx.h" extern subdriver_t voltronic_subdriver; #endif /* NUTDRV_QX_VOLTRONIC_H */ nut-2.7.4/drivers/Makefile.in0000644000175000017500000033462312667762001013020 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: drivers VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_USB_TRUE@am__append_1 = $(LIBUSB_CFLAGS) @WITH_SNMP_TRUE@am__append_2 = $(LIBNETSNMP_CFLAGS) @WITH_NEON_TRUE@am__append_3 = $(LIBNEON_CFLAGS) @WITH_LIBPOWERMAN_TRUE@am__append_4 = $(LIBPOWERMAN_CFLAGS) @WITH_IPMI_TRUE@am__append_5 = $(LIBIPMI_CFLAGS) EXTRA_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_4) \ $(am__EXEEXT_5) $(am__EXEEXT_6) @SOME_DRIVERS_FALSE@driverexec_PROGRAMS = $(am__EXEEXT_7) \ @SOME_DRIVERS_FALSE@ $(am__EXEEXT_8) $(am__EXEEXT_9) \ @SOME_DRIVERS_FALSE@ $(am__EXEEXT_10) $(am__EXEEXT_11) \ @SOME_DRIVERS_FALSE@ $(am__EXEEXT_12) $(am__EXEEXT_13) \ @SOME_DRIVERS_FALSE@ $(am__EXEEXT_15) @SOME_DRIVERS_TRUE@driverexec_PROGRAMS = $(DRIVER_BUILD_LIST) \ @SOME_DRIVERS_TRUE@ $(am__EXEEXT_7) $(am__EXEEXT_8) \ @SOME_DRIVERS_TRUE@ $(am__EXEEXT_9) $(am__EXEEXT_10) \ @SOME_DRIVERS_TRUE@ $(am__EXEEXT_11) $(am__EXEEXT_12) \ @SOME_DRIVERS_TRUE@ $(am__EXEEXT_13) $(am__EXEEXT_15) \ @SOME_DRIVERS_TRUE@ skel$(EXEEXT) @SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__append_6 = $(SERIAL_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_SNMP_TRUE@am__append_7 = $(SNMP_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_USB_TRUE@am__append_8 = $(USB_LIBUSB_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_NEON_TRUE@am__append_9 = $(NEONXML_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__append_10 = powerman-pdu @SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__append_11 = nut-ipmipsu @SOME_DRIVERS_FALSE@@WITH_MACOSX_TRUE@am__append_12 = $(MACOSX_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@am__append_13 = $(LINUX_I2C_DRIVERLIST) sbin_PROGRAMS = upsdrvctl$(EXEEXT) @WITH_SSL_TRUE@am__append_14 = $(LIBSSL_CFLAGS) @WITH_SSL_TRUE@am__append_15 = $(LIBSSL_LIBS) @WITH_FREEIPMI_TRUE@am__append_16 = nut-libfreeipmi.c @WITH_SERIAL_TRUE@am__append_17 = -DQX_SERIAL @WITH_SERIAL_TRUE@am__append_18 = $(SERLIBS) serial.o @WITH_USB_TRUE@am__append_19 = -DQX_USB @WITH_USB_TRUE@am__append_20 = libusb.c usb-common.c @WITH_USB_TRUE@am__append_21 = $(LIBUSB_LIBS) subdir = drivers DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp $(dist_noinst_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libdummy_a_AR = $(AR) $(ARFLAGS) libdummy_a_LIBADD = am_libdummy_a_OBJECTS = main.$(OBJEXT) dstate.$(OBJEXT) \ serial.$(OBJEXT) libdummy_a_OBJECTS = $(am_libdummy_a_OBJECTS) am__EXEEXT_1 = al175$(EXEEXT) bcmxcp$(EXEEXT) belkin$(EXEEXT) \ belkinunv$(EXEEXT) bestfcom$(EXEEXT) bestfortress$(EXEEXT) \ bestuferrups$(EXEEXT) bestups$(EXEEXT) dummy-ups$(EXEEXT) \ etapro$(EXEEXT) everups$(EXEEXT) gamatronic$(EXEEXT) \ genericups$(EXEEXT) isbmex$(EXEEXT) liebert$(EXEEXT) \ liebert-esp2$(EXEEXT) masterguard$(EXEEXT) metasys$(EXEEXT) \ oldmge-shut$(EXEEXT) mge-utalk$(EXEEXT) microdowell$(EXEEXT) \ mge-shut$(EXEEXT) oneac$(EXEEXT) optiups$(EXEEXT) \ powercom$(EXEEXT) rhino$(EXEEXT) safenet$(EXEEXT) \ skel$(EXEEXT) solis$(EXEEXT) tripplite$(EXEEXT) \ tripplitesu$(EXEEXT) upscode2$(EXEEXT) victronups$(EXEEXT) \ powerpanel$(EXEEXT) blazer_ser$(EXEEXT) clone$(EXEEXT) \ clone-outlet$(EXEEXT) ivtscd$(EXEEXT) apcsmart$(EXEEXT) \ apcsmart-old$(EXEEXT) apcupsd-ups$(EXEEXT) riello_ser$(EXEEXT) \ nutdrv_qx$(EXEEXT) am__EXEEXT_2 = snmp-ups$(EXEEXT) am__EXEEXT_3 = usbhid-ups$(EXEEXT) bcmxcp_usb$(EXEEXT) \ tripplite_usb$(EXEEXT) blazer_usb$(EXEEXT) \ richcomm_usb$(EXEEXT) riello_usb$(EXEEXT) \ nutdrv_atcl_usb$(EXEEXT) nutdrv_qx$(EXEEXT) am__EXEEXT_4 = $(am__EXEEXT_3) am__EXEEXT_5 = netxml-ups$(EXEEXT) am__EXEEXT_6 = macosx-ups$(EXEEXT) @SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__EXEEXT_7 = $(am__EXEEXT_1) @SOME_DRIVERS_FALSE@@WITH_SNMP_TRUE@am__EXEEXT_8 = $(am__EXEEXT_2) @SOME_DRIVERS_FALSE@@WITH_USB_TRUE@am__EXEEXT_9 = $(am__EXEEXT_3) @SOME_DRIVERS_FALSE@@WITH_NEON_TRUE@am__EXEEXT_10 = $(am__EXEEXT_5) @SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__EXEEXT_11 = powerman-pdu$(EXEEXT) @SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__EXEEXT_12 = \ @SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@ nut-ipmipsu$(EXEEXT) @SOME_DRIVERS_FALSE@@WITH_MACOSX_TRUE@am__EXEEXT_13 = $(am__EXEEXT_6) am__EXEEXT_14 = asem$(EXEEXT) @SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@am__EXEEXT_15 = \ @SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@ $(am__EXEEXT_14) am__installdirs = "$(DESTDIR)$(driverexecdir)" "$(DESTDIR)$(sbindir)" PROGRAMS = $(driverexec_PROGRAMS) $(sbin_PROGRAMS) am_al175_OBJECTS = al175.$(OBJEXT) al175_OBJECTS = $(am_al175_OBJECTS) al175_LDADD = $(LDADD) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) serial.o al175_DEPENDENCIES = $(am__DEPENDENCIES_2) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = am_apcsmart_OBJECTS = apcsmart.$(OBJEXT) apcsmart_tabs.$(OBJEXT) apcsmart_OBJECTS = $(am_apcsmart_OBJECTS) apcsmart_LDADD = $(LDADD) apcsmart_DEPENDENCIES = $(am__DEPENDENCIES_2) am_apcsmart_old_OBJECTS = apcsmart-old.$(OBJEXT) apcsmart_old_OBJECTS = $(am_apcsmart_old_OBJECTS) apcsmart_old_LDADD = $(LDADD) apcsmart_old_DEPENDENCIES = $(am__DEPENDENCIES_2) am_apcupsd_ups_OBJECTS = apcupsd_ups-apcupsd-ups.$(OBJEXT) apcupsd_ups_OBJECTS = $(am_apcupsd_ups_OBJECTS) apcupsd_ups_DEPENDENCIES = $(LDADD_DRIVERS) apcupsd_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(apcupsd_ups_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_asem_OBJECTS = asem.$(OBJEXT) asem_OBJECTS = $(am_asem_OBJECTS) asem_DEPENDENCIES = $(LDADD_DRIVERS) am_bcmxcp_OBJECTS = bcmxcp.$(OBJEXT) bcmxcp_ser.$(OBJEXT) bcmxcp_OBJECTS = $(am_bcmxcp_OBJECTS) am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) bcmxcp_DEPENDENCIES = $(am__DEPENDENCIES_3) am_bcmxcp_usb_OBJECTS = bcmxcp_usb.$(OBJEXT) bcmxcp.$(OBJEXT) \ usb-common.$(OBJEXT) bcmxcp_usb_OBJECTS = $(am_bcmxcp_usb_OBJECTS) bcmxcp_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_belkin_OBJECTS = belkin.$(OBJEXT) belkin_OBJECTS = $(am_belkin_OBJECTS) belkin_LDADD = $(LDADD) belkin_DEPENDENCIES = $(am__DEPENDENCIES_2) am_belkinunv_OBJECTS = belkinunv.$(OBJEXT) belkinunv_OBJECTS = $(am_belkinunv_OBJECTS) belkinunv_LDADD = $(LDADD) belkinunv_DEPENDENCIES = $(am__DEPENDENCIES_2) am_bestfcom_OBJECTS = bestfcom.$(OBJEXT) bestfcom_OBJECTS = $(am_bestfcom_OBJECTS) bestfcom_LDADD = $(LDADD) bestfcom_DEPENDENCIES = $(am__DEPENDENCIES_2) bestfortress_SOURCES = bestfortress.c bestfortress_OBJECTS = bestfortress.$(OBJEXT) bestfortress_LDADD = $(LDADD) bestfortress_DEPENDENCIES = $(am__DEPENDENCIES_2) am_bestuferrups_OBJECTS = bestuferrups.$(OBJEXT) bestuferrups_OBJECTS = $(am_bestuferrups_OBJECTS) bestuferrups_LDADD = $(LDADD) bestuferrups_DEPENDENCIES = $(am__DEPENDENCIES_2) am_bestups_OBJECTS = bestups.$(OBJEXT) bestups_OBJECTS = $(am_bestups_OBJECTS) bestups_LDADD = $(LDADD) bestups_DEPENDENCIES = $(am__DEPENDENCIES_2) am_blazer_ser_OBJECTS = blazer.$(OBJEXT) blazer_ser.$(OBJEXT) blazer_ser_OBJECTS = $(am_blazer_ser_OBJECTS) blazer_ser_DEPENDENCIES = $(am__DEPENDENCIES_3) am_blazer_usb_OBJECTS = blazer.$(OBJEXT) blazer_usb.$(OBJEXT) \ libusb.$(OBJEXT) usb-common.$(OBJEXT) blazer_usb_OBJECTS = $(am_blazer_usb_OBJECTS) blazer_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_clone_OBJECTS = clone.$(OBJEXT) clone_OBJECTS = $(am_clone_OBJECTS) clone_LDADD = $(LDADD) clone_DEPENDENCIES = $(am__DEPENDENCIES_2) am_clone_outlet_OBJECTS = clone-outlet.$(OBJEXT) clone_outlet_OBJECTS = $(am_clone_outlet_OBJECTS) clone_outlet_LDADD = $(LDADD) clone_outlet_DEPENDENCIES = $(am__DEPENDENCIES_2) am_dummy_ups_OBJECTS = dummy_ups-dummy-ups.$(OBJEXT) dummy_ups_OBJECTS = $(am_dummy_ups_OBJECTS) @WITH_SSL_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) dummy_ups_DEPENDENCIES = $(LDADD_DRIVERS) ../clients/libupsclient.la \ $(am__DEPENDENCIES_4) dummy_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(dummy_ups_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_etapro_OBJECTS = etapro.$(OBJEXT) etapro_OBJECTS = $(am_etapro_OBJECTS) etapro_LDADD = $(LDADD) etapro_DEPENDENCIES = $(am__DEPENDENCIES_2) am_everups_OBJECTS = everups.$(OBJEXT) everups_OBJECTS = $(am_everups_OBJECTS) everups_LDADD = $(LDADD) everups_DEPENDENCIES = $(am__DEPENDENCIES_2) am_gamatronic_OBJECTS = gamatronic.$(OBJEXT) gamatronic_OBJECTS = $(am_gamatronic_OBJECTS) gamatronic_LDADD = $(LDADD) gamatronic_DEPENDENCIES = $(am__DEPENDENCIES_2) am_genericups_OBJECTS = genericups.$(OBJEXT) genericups_OBJECTS = $(am_genericups_OBJECTS) genericups_LDADD = $(LDADD) genericups_DEPENDENCIES = $(am__DEPENDENCIES_2) am_isbmex_OBJECTS = isbmex.$(OBJEXT) isbmex_OBJECTS = $(am_isbmex_OBJECTS) isbmex_DEPENDENCIES = $(am__DEPENDENCIES_3) am_ivtscd_OBJECTS = ivtscd.$(OBJEXT) ivtscd_OBJECTS = $(am_ivtscd_OBJECTS) ivtscd_LDADD = $(LDADD) ivtscd_DEPENDENCIES = $(am__DEPENDENCIES_2) am_liebert_OBJECTS = liebert.$(OBJEXT) liebert_OBJECTS = $(am_liebert_OBJECTS) liebert_LDADD = $(LDADD) liebert_DEPENDENCIES = $(am__DEPENDENCIES_2) am_liebert_esp2_OBJECTS = liebert-esp2.$(OBJEXT) liebert_esp2_OBJECTS = $(am_liebert_esp2_OBJECTS) liebert_esp2_LDADD = $(LDADD) liebert_esp2_DEPENDENCIES = $(am__DEPENDENCIES_2) am_macosx_ups_OBJECTS = macosx-ups.$(OBJEXT) macosx_ups_OBJECTS = $(am_macosx_ups_OBJECTS) macosx_ups_DEPENDENCIES = $(LDADD_DRIVERS) macosx_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(macosx_ups_LDFLAGS) $(LDFLAGS) -o $@ am_masterguard_OBJECTS = masterguard.$(OBJEXT) masterguard_OBJECTS = $(am_masterguard_OBJECTS) masterguard_LDADD = $(LDADD) masterguard_DEPENDENCIES = $(am__DEPENDENCIES_2) am_metasys_OBJECTS = metasys.$(OBJEXT) metasys_OBJECTS = $(am_metasys_OBJECTS) metasys_LDADD = $(LDADD) metasys_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mge_shut_OBJECTS = mge_shut-usbhid-ups.$(OBJEXT) \ mge_shut-libshut.$(OBJEXT) mge_shut-libhid.$(OBJEXT) \ mge_shut-hidparser.$(OBJEXT) mge_shut-mge-hid.$(OBJEXT) mge_shut_OBJECTS = $(am_mge_shut_OBJECTS) mge_shut_DEPENDENCIES = $(am__DEPENDENCIES_3) mge_shut_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mge_shut_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_mge_utalk_OBJECTS = mge-utalk.$(OBJEXT) mge_utalk_OBJECTS = $(am_mge_utalk_OBJECTS) mge_utalk_LDADD = $(LDADD) mge_utalk_DEPENDENCIES = $(am__DEPENDENCIES_2) am_microdowell_OBJECTS = microdowell.$(OBJEXT) microdowell_OBJECTS = $(am_microdowell_OBJECTS) microdowell_LDADD = $(LDADD) microdowell_DEPENDENCIES = $(am__DEPENDENCIES_2) am_netxml_ups_OBJECTS = netxml-ups.$(OBJEXT) mge-xml.$(OBJEXT) netxml_ups_OBJECTS = $(am_netxml_ups_OBJECTS) netxml_ups_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am__nut_ipmipsu_SOURCES_DIST = nut-ipmipsu.c nut-libfreeipmi.c @WITH_FREEIPMI_TRUE@am__objects_1 = nut-libfreeipmi.$(OBJEXT) am_nut_ipmipsu_OBJECTS = nut-ipmipsu.$(OBJEXT) $(am__objects_1) nut_ipmipsu_OBJECTS = $(am_nut_ipmipsu_OBJECTS) nut_ipmipsu_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1) am_nutdrv_atcl_usb_OBJECTS = nutdrv_atcl_usb.$(OBJEXT) \ usb-common.$(OBJEXT) nutdrv_atcl_usb_OBJECTS = $(am_nutdrv_atcl_usb_OBJECTS) nutdrv_atcl_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am__nutdrv_qx_SOURCES_DIST = nutdrv_qx.c libusb.c usb-common.c \ nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \ nutdrv_qx_mecer.c nutdrv_qx_megatec.c nutdrv_qx_megatec-old.c \ nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_voltronic.c \ nutdrv_qx_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c \ nutdrv_qx_zinto.c @WITH_USB_TRUE@am__objects_2 = nutdrv_qx-libusb.$(OBJEXT) \ @WITH_USB_TRUE@ nutdrv_qx-usb-common.$(OBJEXT) am__objects_3 = nutdrv_qx-nutdrv_qx_bestups.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_blazer-common.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_mecer.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_megatec.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_megatec-old.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_mustek.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_q1.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_voltronic.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_voltronic-qs.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_voltronic-qs-hex.$(OBJEXT) \ nutdrv_qx-nutdrv_qx_zinto.$(OBJEXT) am_nutdrv_qx_OBJECTS = nutdrv_qx-nutdrv_qx.$(OBJEXT) $(am__objects_2) \ $(am__objects_3) nutdrv_qx_OBJECTS = $(am_nutdrv_qx_OBJECTS) @WITH_SERIAL_TRUE@am__DEPENDENCIES_5 = $(am__DEPENDENCIES_1) serial.o @WITH_USB_TRUE@am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1) nutdrv_qx_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_5) \ $(am__DEPENDENCIES_6) nutdrv_qx_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(nutdrv_qx_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_oldmge_shut_OBJECTS = mge-shut.$(OBJEXT) hidparser.$(OBJEXT) oldmge_shut_OBJECTS = $(am_oldmge_shut_OBJECTS) oldmge_shut_LDADD = $(LDADD) oldmge_shut_DEPENDENCIES = $(am__DEPENDENCIES_2) am_oneac_OBJECTS = oneac.$(OBJEXT) oneac_OBJECTS = $(am_oneac_OBJECTS) oneac_LDADD = $(LDADD) oneac_DEPENDENCIES = $(am__DEPENDENCIES_2) am_optiups_OBJECTS = optiups.$(OBJEXT) optiups_OBJECTS = $(am_optiups_OBJECTS) optiups_LDADD = $(LDADD) optiups_DEPENDENCIES = $(am__DEPENDENCIES_2) am_powercom_OBJECTS = powercom.$(OBJEXT) powercom_OBJECTS = $(am_powercom_OBJECTS) powercom_DEPENDENCIES = $(am__DEPENDENCIES_3) am_powerman_pdu_OBJECTS = powerman-pdu.$(OBJEXT) powerman_pdu_OBJECTS = $(am_powerman_pdu_OBJECTS) powerman_pdu_DEPENDENCIES = $(am__DEPENDENCIES_3) \ $(am__DEPENDENCIES_1) am_powerpanel_OBJECTS = powerpanel.$(OBJEXT) powerp-bin.$(OBJEXT) \ powerp-txt.$(OBJEXT) powerpanel_OBJECTS = $(am_powerpanel_OBJECTS) powerpanel_DEPENDENCIES = $(am__DEPENDENCIES_3) am_rhino_OBJECTS = rhino.$(OBJEXT) rhino_OBJECTS = $(am_rhino_OBJECTS) rhino_DEPENDENCIES = $(am__DEPENDENCIES_3) am_richcomm_usb_OBJECTS = richcomm_usb.$(OBJEXT) usb-common.$(OBJEXT) richcomm_usb_OBJECTS = $(am_richcomm_usb_OBJECTS) richcomm_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_riello_ser_OBJECTS = riello.$(OBJEXT) riello_ser.$(OBJEXT) riello_ser_OBJECTS = $(am_riello_ser_OBJECTS) riello_ser_DEPENDENCIES = $(am__DEPENDENCIES_3) am_riello_usb_OBJECTS = riello.$(OBJEXT) riello_usb.$(OBJEXT) \ libusb.$(OBJEXT) usb-common.$(OBJEXT) riello_usb_OBJECTS = $(am_riello_usb_OBJECTS) riello_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_safenet_OBJECTS = safenet.$(OBJEXT) safenet_OBJECTS = $(am_safenet_OBJECTS) safenet_LDADD = $(LDADD) safenet_DEPENDENCIES = $(am__DEPENDENCIES_2) am_skel_OBJECTS = skel.$(OBJEXT) skel_OBJECTS = $(am_skel_OBJECTS) skel_DEPENDENCIES = $(LDADD_DRIVERS) am_snmp_ups_OBJECTS = snmp-ups.$(OBJEXT) apc-mib.$(OBJEXT) \ baytech-mib.$(OBJEXT) compaq-mib.$(OBJEXT) eaton-mib.$(OBJEXT) \ ietf-mib.$(OBJEXT) mge-mib.$(OBJEXT) netvision-mib.$(OBJEXT) \ powerware-mib.$(OBJEXT) raritan-pdu-mib.$(OBJEXT) \ bestpower-mib.$(OBJEXT) cyberpower-mib.$(OBJEXT) \ delta_ups-mib.$(OBJEXT) xppc-mib.$(OBJEXT) \ huawei-mib.$(OBJEXT) eaton-ats-mib.$(OBJEXT) \ apc-ats-mib.$(OBJEXT) snmp_ups_OBJECTS = $(am_snmp_ups_OBJECTS) snmp_ups_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_solis_OBJECTS = solis.$(OBJEXT) solis_OBJECTS = $(am_solis_OBJECTS) solis_DEPENDENCIES = $(am__DEPENDENCIES_3) am_tripplite_OBJECTS = tripplite.$(OBJEXT) tripplite_OBJECTS = $(am_tripplite_OBJECTS) tripplite_DEPENDENCIES = $(am__DEPENDENCIES_3) am_tripplite_usb_OBJECTS = tripplite_usb.$(OBJEXT) libusb.$(OBJEXT) \ usb-common.$(OBJEXT) tripplite_usb_OBJECTS = $(am_tripplite_usb_OBJECTS) tripplite_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_tripplitesu_OBJECTS = tripplitesu.$(OBJEXT) tripplitesu_OBJECTS = $(am_tripplitesu_OBJECTS) tripplitesu_LDADD = $(LDADD) tripplitesu_DEPENDENCIES = $(am__DEPENDENCIES_2) am_upscode2_OBJECTS = upscode2.$(OBJEXT) upscode2_OBJECTS = $(am_upscode2_OBJECTS) upscode2_DEPENDENCIES = $(am__DEPENDENCIES_3) am_upsdrvctl_OBJECTS = upsdrvctl.$(OBJEXT) upsdrvctl_OBJECTS = $(am_upsdrvctl_OBJECTS) upsdrvctl_DEPENDENCIES = $(LDADD_COMMON) am__objects_4 = apc-hid.$(OBJEXT) belkin-hid.$(OBJEXT) \ cps-hid.$(OBJEXT) explore-hid.$(OBJEXT) liebert-hid.$(OBJEXT) \ mge-hid.$(OBJEXT) powercom-hid.$(OBJEXT) \ tripplite-hid.$(OBJEXT) idowell-hid.$(OBJEXT) \ openups-hid.$(OBJEXT) am_usbhid_ups_OBJECTS = usbhid-ups.$(OBJEXT) libhid.$(OBJEXT) \ libusb.$(OBJEXT) hidparser.$(OBJEXT) usb-common.$(OBJEXT) \ $(am__objects_4) usbhid_ups_OBJECTS = $(am_usbhid_ups_OBJECTS) usbhid_ups_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_victronups_OBJECTS = victronups.$(OBJEXT) victronups_OBJECTS = $(am_victronups_OBJECTS) victronups_LDADD = $(LDADD) victronups_DEPENDENCIES = $(am__DEPENDENCIES_2) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdummy_a_SOURCES) $(al175_SOURCES) $(apcsmart_SOURCES) \ $(apcsmart_old_SOURCES) $(apcupsd_ups_SOURCES) $(asem_SOURCES) \ $(bcmxcp_SOURCES) $(bcmxcp_usb_SOURCES) $(belkin_SOURCES) \ $(belkinunv_SOURCES) $(bestfcom_SOURCES) bestfortress.c \ $(bestuferrups_SOURCES) $(bestups_SOURCES) \ $(blazer_ser_SOURCES) $(blazer_usb_SOURCES) $(clone_SOURCES) \ $(clone_outlet_SOURCES) $(dummy_ups_SOURCES) $(etapro_SOURCES) \ $(everups_SOURCES) $(gamatronic_SOURCES) $(genericups_SOURCES) \ $(isbmex_SOURCES) $(ivtscd_SOURCES) $(liebert_SOURCES) \ $(liebert_esp2_SOURCES) $(macosx_ups_SOURCES) \ $(masterguard_SOURCES) $(metasys_SOURCES) $(mge_shut_SOURCES) \ $(mge_utalk_SOURCES) $(microdowell_SOURCES) \ $(netxml_ups_SOURCES) $(nut_ipmipsu_SOURCES) \ $(nutdrv_atcl_usb_SOURCES) $(nutdrv_qx_SOURCES) \ $(oldmge_shut_SOURCES) $(oneac_SOURCES) $(optiups_SOURCES) \ $(powercom_SOURCES) $(powerman_pdu_SOURCES) \ $(powerpanel_SOURCES) $(rhino_SOURCES) $(richcomm_usb_SOURCES) \ $(riello_ser_SOURCES) $(riello_usb_SOURCES) $(safenet_SOURCES) \ $(skel_SOURCES) $(snmp_ups_SOURCES) $(solis_SOURCES) \ $(tripplite_SOURCES) $(tripplite_usb_SOURCES) \ $(tripplitesu_SOURCES) $(upscode2_SOURCES) \ $(upsdrvctl_SOURCES) $(usbhid_ups_SOURCES) \ $(victronups_SOURCES) DIST_SOURCES = $(libdummy_a_SOURCES) $(al175_SOURCES) \ $(apcsmart_SOURCES) $(apcsmart_old_SOURCES) \ $(apcupsd_ups_SOURCES) $(asem_SOURCES) $(bcmxcp_SOURCES) \ $(bcmxcp_usb_SOURCES) $(belkin_SOURCES) $(belkinunv_SOURCES) \ $(bestfcom_SOURCES) bestfortress.c $(bestuferrups_SOURCES) \ $(bestups_SOURCES) $(blazer_ser_SOURCES) $(blazer_usb_SOURCES) \ $(clone_SOURCES) $(clone_outlet_SOURCES) $(dummy_ups_SOURCES) \ $(etapro_SOURCES) $(everups_SOURCES) $(gamatronic_SOURCES) \ $(genericups_SOURCES) $(isbmex_SOURCES) $(ivtscd_SOURCES) \ $(liebert_SOURCES) $(liebert_esp2_SOURCES) \ $(macosx_ups_SOURCES) $(masterguard_SOURCES) \ $(metasys_SOURCES) $(mge_shut_SOURCES) $(mge_utalk_SOURCES) \ $(microdowell_SOURCES) $(netxml_ups_SOURCES) \ $(am__nut_ipmipsu_SOURCES_DIST) $(nutdrv_atcl_usb_SOURCES) \ $(am__nutdrv_qx_SOURCES_DIST) $(oldmge_shut_SOURCES) \ $(oneac_SOURCES) $(optiups_SOURCES) $(powercom_SOURCES) \ $(powerman_pdu_SOURCES) $(powerpanel_SOURCES) $(rhino_SOURCES) \ $(richcomm_usb_SOURCES) $(riello_ser_SOURCES) \ $(riello_usb_SOURCES) $(safenet_SOURCES) $(skel_SOURCES) \ $(snmp_ups_SOURCES) $(solis_SOURCES) $(tripplite_SOURCES) \ $(tripplite_usb_SOURCES) $(tripplitesu_SOURCES) \ $(upscode2_SOURCES) $(upsdrvctl_SOURCES) $(usbhid_ups_SOURCES) \ $(victronups_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac HEADERS = $(dist_noinst_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # by default, link programs in this directory with libcommon.la # (libtool version of the static lib, in order to access LTLIBOBJS) #FIXME: SERLIBS is only useful for LDADD_DRIVERS_SERIAL not for LDADD_COMMON LDADD_COMMON = ../common/libcommon.la ../common/libparseconf.la LDADD_DRIVERS = $(LDADD_COMMON) main.o dstate.o LDADD_DRIVERS_SERIAL = $(LDADD_DRIVERS) $(SERLIBS) serial.o # most targets are drivers, so make this the default LDADD = $(LDADD_DRIVERS_SERIAL) # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include $(am__append_1) $(am__append_2) \ $(am__append_3) $(am__append_4) $(am__append_5) SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ bestfortress bestuferrups bestups dummy-ups etapro everups \ gamatronic genericups isbmex liebert liebert-esp2 masterguard metasys \ oldmge-shut mge-utalk microdowell mge-shut oneac optiups powercom rhino \ safenet skel solis tripplite tripplitesu upscode2 victronups powerpanel \ blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser \ nutdrv_qx SNMP_DRIVERLIST = snmp-ups USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb \ blazer_usb richcomm_usb riello_usb \ nutdrv_atcl_usb \ nutdrv_qx USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST) NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups LINUX_I2C_DRIVERLIST = asem # ========================================================================== # Driver build details # upsdrvctl: the all-singing all-dancing driver control program upsdrvctl_SOURCES = upsdrvctl.c upsdrvctl_LDADD = $(LDADD_COMMON) # serial drivers: all of them use standard LDADD and CFLAGS al175_SOURCES = al175.c apcsmart_SOURCES = apcsmart.c apcsmart_tabs.c apcsmart_old_SOURCES = apcsmart-old.c bcmxcp_SOURCES = bcmxcp.c bcmxcp_ser.c bcmxcp_LDADD = $(LDADD) -lm belkin_SOURCES = belkin.c belkinunv_SOURCES = belkinunv.c bestfcom_SOURCES = bestfcom.c bestuferrups_SOURCES = bestuferrups.c bestups_SOURCES = bestups.c blazer_ser_SOURCES = blazer.c blazer_ser.c blazer_ser_LDADD = $(LDADD) -lm etapro_SOURCES = etapro.c everups_SOURCES = everups.c gamatronic_SOURCES = gamatronic.c genericups_SOURCES = genericups.c isbmex_SOURCES = isbmex.c isbmex_LDADD = $(LDADD) -lm ivtscd_SOURCES = ivtscd.c liebert_SOURCES = liebert.c liebert_esp2_SOURCES = liebert-esp2.c masterguard_SOURCES = masterguard.c metasys_SOURCES = metasys.c oldmge_shut_SOURCES = mge-shut.c hidparser.c mge_utalk_SOURCES = mge-utalk.c microdowell_SOURCES = microdowell.c oneac_SOURCES = oneac.c optiups_SOURCES = optiups.c powercom_SOURCES = powercom.c powercom_LDADD = $(LDADD) -lm powerpanel_SOURCES = powerpanel.c powerp-bin.c powerp-txt.c powerpanel_LDADD = $(LDADD) -lm rhino_SOURCES = rhino.c rhino_LDADD = $(LDADD) -lm safenet_SOURCES = safenet.c solis_SOURCES = solis.c solis_LDADD = $(LDADD) -lm tripplite_SOURCES = tripplite.c tripplite_LDADD = $(LDADD) -lm tripplitesu_SOURCES = tripplitesu.c upscode2_SOURCES = upscode2.c upscode2_LDADD = $(LDADD) -lm victronups_SOURCES = victronups.c riello_ser_SOURCES = riello.c riello_ser.c riello_ser_LDADD = $(LDADD) -lm # non-serial drivers: these use custom LDADD and/or CFLAGS # dummy dummy_ups_SOURCES = dummy-ups.c dummy_ups_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/clients \ $(am__append_14) dummy_ups_LDADD = $(LDADD_DRIVERS) ../clients/libupsclient.la \ $(am__append_15) # Clone drivers clone_SOURCES = clone.c clone_outlet_SOURCES = clone-outlet.c # apcupsd client driver apcupsd_ups_SOURCES = apcupsd-ups.c apcupsd_ups_CFLAGS = $(AM_CFLAGS) apcupsd_ups_LDADD = $(LDADD_DRIVERS) # sample skeleton driver skel_SOURCES = skel.c skel_LDADD = $(LDADD_DRIVERS) # USB USBHID_UPS_SUBDRIVERS = apc-hid.c belkin-hid.c cps-hid.c explore-hid.c \ liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c \ openups-hid.c usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ usb-common.c $(USBHID_UPS_SUBDRIVERS) usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) tripplite_usb_SOURCES = tripplite_usb.c libusb.c usb-common.c tripplite_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c usb-common.c bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c usb-common.c blazer_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm nutdrv_atcl_usb_SOURCES = nutdrv_atcl_usb.c usb-common.c nutdrv_atcl_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) richcomm_usb_SOURCES = richcomm_usb.c usb-common.c richcomm_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) riello_usb_SOURCES = riello.c riello_usb.c libusb.c usb-common.c riello_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm # HID-over-serial mge_shut_SOURCES = usbhid-ups.c libshut.c libhid.c hidparser.c mge-hid.c # per-target CFLAGS are necessary here mge_shut_CFLAGS = $(AM_CFLAGS) -DSHUT_MODE mge_shut_LDADD = $(LDADD) # SNMP snmp_ups_SOURCES = snmp-ups.c apc-mib.c baytech-mib.c compaq-mib.c eaton-mib.c \ ietf-mib.c mge-mib.c netvision-mib.c powerware-mib.c raritan-pdu-mib.c \ bestpower-mib.c cyberpower-mib.c delta_ups-mib.c xppc-mib.c huawei-mib.c \ eaton-ats-mib.c apc-ats-mib.c snmp_ups_LDADD = $(LDADD_DRIVERS) $(LIBNETSNMP_LIBS) # NEON XML/HTTP netxml_ups_SOURCES = netxml-ups.c mge-xml.c netxml_ups_LDADD = $(LDADD_DRIVERS) $(LIBNEON_LIBS) # Powerman powerman_pdu_SOURCES = powerman-pdu.c powerman_pdu_LDADD = $(LDADD) $(LIBPOWERMAN_LIBS) # IPMI PSU nut_ipmipsu_SOURCES = nut-ipmipsu.c $(am__append_16) nut_ipmipsu_LDADD = $(LDADD) $(LIBIPMI_LIBS) # Mac OS X metadriver macosx_ups_LDADD = $(LDADD_DRIVERS) macosx_ups_LDFLAGS = $(LDFLAGS) -framework IOKit -framework CoreFoundation macosx_ups_SOURCES = macosx-ups.c # Asem asem_LDADD = $(LDADD_DRIVERS) asem_SOURCES = asem.c # nutdrv_qx USB/Serial nutdrv_qx_SOURCES = nutdrv_qx.c $(am__append_20) \ $(NUTDRV_QX_SUBDRIVERS) nutdrv_qx_LDADD = $(LDADD_DRIVERS) -lm $(am__append_18) \ $(am__append_21) nutdrv_qx_CFLAGS = $(AM_CFLAGS) $(am__append_17) $(am__append_19) NUTDRV_QX_SUBDRIVERS = nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \ nutdrv_qx_mecer.c nutdrv_qx_megatec.c nutdrv_qx_megatec-old.c \ nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_voltronic.c \ nutdrv_qx_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c # ---------------------------------------------------------------------- # List of header files. The purpose of this list is not dependency # tracking (which is automatic), but to ensure these files are # distributed by "make dist". dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \ bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ dummy-ups.h eaton-mib.h explore-hid.h gamatronic.h genericups.h \ hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h libusb.h liebert-hid.h \ main.h mge-hid.h mge-mib.h mge-shut.h mge-utalk.h \ mge-xml.h microdowell.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ powercom.h powerpanel.h powerp-bin.h powerp-txt.h powerware-mib.h raritan-pdu-mib.h \ safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ apcsmart.h apcsmart_tabs.h apcsmart-old.h apcupsd-ups.h cyberpower-mib.h riello.h openups-hid.h \ delta_ups-mib.h nutdrv_qx.h nutdrv_qx_bestups.h nutdrv_qx_blazer-common.h nutdrv_qx_mecer.h \ nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ xppc-mib.h huawei-mib.h eaton-ats-mib.h apc-ats-mib.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, EXTRA_LIBRARIES = libdummy.a libdummy_a_SOURCES = main.c dstate.c serial.c all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu drivers/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu drivers/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): libdummy.a: $(libdummy_a_OBJECTS) $(libdummy_a_DEPENDENCIES) $(EXTRA_libdummy_a_DEPENDENCIES) $(AM_V_at)-rm -f libdummy.a $(AM_V_AR)$(libdummy_a_AR) libdummy.a $(libdummy_a_OBJECTS) $(libdummy_a_LIBADD) $(AM_V_at)$(RANLIB) libdummy.a install-driverexecPROGRAMS: $(driverexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(driverexec_PROGRAMS)'; test -n "$(driverexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(driverexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(driverexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(driverexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(driverexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-driverexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(driverexec_PROGRAMS)'; test -n "$(driverexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(driverexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(driverexecdir)" && rm -f $$files clean-driverexecPROGRAMS: @list='$(driverexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list al175$(EXEEXT): $(al175_OBJECTS) $(al175_DEPENDENCIES) $(EXTRA_al175_DEPENDENCIES) @rm -f al175$(EXEEXT) $(AM_V_CCLD)$(LINK) $(al175_OBJECTS) $(al175_LDADD) $(LIBS) apcsmart$(EXEEXT): $(apcsmart_OBJECTS) $(apcsmart_DEPENDENCIES) $(EXTRA_apcsmart_DEPENDENCIES) @rm -f apcsmart$(EXEEXT) $(AM_V_CCLD)$(LINK) $(apcsmart_OBJECTS) $(apcsmart_LDADD) $(LIBS) apcsmart-old$(EXEEXT): $(apcsmart_old_OBJECTS) $(apcsmart_old_DEPENDENCIES) $(EXTRA_apcsmart_old_DEPENDENCIES) @rm -f apcsmart-old$(EXEEXT) $(AM_V_CCLD)$(LINK) $(apcsmart_old_OBJECTS) $(apcsmart_old_LDADD) $(LIBS) apcupsd-ups$(EXEEXT): $(apcupsd_ups_OBJECTS) $(apcupsd_ups_DEPENDENCIES) $(EXTRA_apcupsd_ups_DEPENDENCIES) @rm -f apcupsd-ups$(EXEEXT) $(AM_V_CCLD)$(apcupsd_ups_LINK) $(apcupsd_ups_OBJECTS) $(apcupsd_ups_LDADD) $(LIBS) asem$(EXEEXT): $(asem_OBJECTS) $(asem_DEPENDENCIES) $(EXTRA_asem_DEPENDENCIES) @rm -f asem$(EXEEXT) $(AM_V_CCLD)$(LINK) $(asem_OBJECTS) $(asem_LDADD) $(LIBS) bcmxcp$(EXEEXT): $(bcmxcp_OBJECTS) $(bcmxcp_DEPENDENCIES) $(EXTRA_bcmxcp_DEPENDENCIES) @rm -f bcmxcp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bcmxcp_OBJECTS) $(bcmxcp_LDADD) $(LIBS) bcmxcp_usb$(EXEEXT): $(bcmxcp_usb_OBJECTS) $(bcmxcp_usb_DEPENDENCIES) $(EXTRA_bcmxcp_usb_DEPENDENCIES) @rm -f bcmxcp_usb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bcmxcp_usb_OBJECTS) $(bcmxcp_usb_LDADD) $(LIBS) belkin$(EXEEXT): $(belkin_OBJECTS) $(belkin_DEPENDENCIES) $(EXTRA_belkin_DEPENDENCIES) @rm -f belkin$(EXEEXT) $(AM_V_CCLD)$(LINK) $(belkin_OBJECTS) $(belkin_LDADD) $(LIBS) belkinunv$(EXEEXT): $(belkinunv_OBJECTS) $(belkinunv_DEPENDENCIES) $(EXTRA_belkinunv_DEPENDENCIES) @rm -f belkinunv$(EXEEXT) $(AM_V_CCLD)$(LINK) $(belkinunv_OBJECTS) $(belkinunv_LDADD) $(LIBS) bestfcom$(EXEEXT): $(bestfcom_OBJECTS) $(bestfcom_DEPENDENCIES) $(EXTRA_bestfcom_DEPENDENCIES) @rm -f bestfcom$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bestfcom_OBJECTS) $(bestfcom_LDADD) $(LIBS) bestfortress$(EXEEXT): $(bestfortress_OBJECTS) $(bestfortress_DEPENDENCIES) $(EXTRA_bestfortress_DEPENDENCIES) @rm -f bestfortress$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bestfortress_OBJECTS) $(bestfortress_LDADD) $(LIBS) bestuferrups$(EXEEXT): $(bestuferrups_OBJECTS) $(bestuferrups_DEPENDENCIES) $(EXTRA_bestuferrups_DEPENDENCIES) @rm -f bestuferrups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bestuferrups_OBJECTS) $(bestuferrups_LDADD) $(LIBS) bestups$(EXEEXT): $(bestups_OBJECTS) $(bestups_DEPENDENCIES) $(EXTRA_bestups_DEPENDENCIES) @rm -f bestups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(bestups_OBJECTS) $(bestups_LDADD) $(LIBS) blazer_ser$(EXEEXT): $(blazer_ser_OBJECTS) $(blazer_ser_DEPENDENCIES) $(EXTRA_blazer_ser_DEPENDENCIES) @rm -f blazer_ser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(blazer_ser_OBJECTS) $(blazer_ser_LDADD) $(LIBS) blazer_usb$(EXEEXT): $(blazer_usb_OBJECTS) $(blazer_usb_DEPENDENCIES) $(EXTRA_blazer_usb_DEPENDENCIES) @rm -f blazer_usb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(blazer_usb_OBJECTS) $(blazer_usb_LDADD) $(LIBS) clone$(EXEEXT): $(clone_OBJECTS) $(clone_DEPENDENCIES) $(EXTRA_clone_DEPENDENCIES) @rm -f clone$(EXEEXT) $(AM_V_CCLD)$(LINK) $(clone_OBJECTS) $(clone_LDADD) $(LIBS) clone-outlet$(EXEEXT): $(clone_outlet_OBJECTS) $(clone_outlet_DEPENDENCIES) $(EXTRA_clone_outlet_DEPENDENCIES) @rm -f clone-outlet$(EXEEXT) $(AM_V_CCLD)$(LINK) $(clone_outlet_OBJECTS) $(clone_outlet_LDADD) $(LIBS) dummy-ups$(EXEEXT): $(dummy_ups_OBJECTS) $(dummy_ups_DEPENDENCIES) $(EXTRA_dummy_ups_DEPENDENCIES) @rm -f dummy-ups$(EXEEXT) $(AM_V_CCLD)$(dummy_ups_LINK) $(dummy_ups_OBJECTS) $(dummy_ups_LDADD) $(LIBS) etapro$(EXEEXT): $(etapro_OBJECTS) $(etapro_DEPENDENCIES) $(EXTRA_etapro_DEPENDENCIES) @rm -f etapro$(EXEEXT) $(AM_V_CCLD)$(LINK) $(etapro_OBJECTS) $(etapro_LDADD) $(LIBS) everups$(EXEEXT): $(everups_OBJECTS) $(everups_DEPENDENCIES) $(EXTRA_everups_DEPENDENCIES) @rm -f everups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(everups_OBJECTS) $(everups_LDADD) $(LIBS) gamatronic$(EXEEXT): $(gamatronic_OBJECTS) $(gamatronic_DEPENDENCIES) $(EXTRA_gamatronic_DEPENDENCIES) @rm -f gamatronic$(EXEEXT) $(AM_V_CCLD)$(LINK) $(gamatronic_OBJECTS) $(gamatronic_LDADD) $(LIBS) genericups$(EXEEXT): $(genericups_OBJECTS) $(genericups_DEPENDENCIES) $(EXTRA_genericups_DEPENDENCIES) @rm -f genericups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(genericups_OBJECTS) $(genericups_LDADD) $(LIBS) isbmex$(EXEEXT): $(isbmex_OBJECTS) $(isbmex_DEPENDENCIES) $(EXTRA_isbmex_DEPENDENCIES) @rm -f isbmex$(EXEEXT) $(AM_V_CCLD)$(LINK) $(isbmex_OBJECTS) $(isbmex_LDADD) $(LIBS) ivtscd$(EXEEXT): $(ivtscd_OBJECTS) $(ivtscd_DEPENDENCIES) $(EXTRA_ivtscd_DEPENDENCIES) @rm -f ivtscd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ivtscd_OBJECTS) $(ivtscd_LDADD) $(LIBS) liebert$(EXEEXT): $(liebert_OBJECTS) $(liebert_DEPENDENCIES) $(EXTRA_liebert_DEPENDENCIES) @rm -f liebert$(EXEEXT) $(AM_V_CCLD)$(LINK) $(liebert_OBJECTS) $(liebert_LDADD) $(LIBS) liebert-esp2$(EXEEXT): $(liebert_esp2_OBJECTS) $(liebert_esp2_DEPENDENCIES) $(EXTRA_liebert_esp2_DEPENDENCIES) @rm -f liebert-esp2$(EXEEXT) $(AM_V_CCLD)$(LINK) $(liebert_esp2_OBJECTS) $(liebert_esp2_LDADD) $(LIBS) macosx-ups$(EXEEXT): $(macosx_ups_OBJECTS) $(macosx_ups_DEPENDENCIES) $(EXTRA_macosx_ups_DEPENDENCIES) @rm -f macosx-ups$(EXEEXT) $(AM_V_CCLD)$(macosx_ups_LINK) $(macosx_ups_OBJECTS) $(macosx_ups_LDADD) $(LIBS) masterguard$(EXEEXT): $(masterguard_OBJECTS) $(masterguard_DEPENDENCIES) $(EXTRA_masterguard_DEPENDENCIES) @rm -f masterguard$(EXEEXT) $(AM_V_CCLD)$(LINK) $(masterguard_OBJECTS) $(masterguard_LDADD) $(LIBS) metasys$(EXEEXT): $(metasys_OBJECTS) $(metasys_DEPENDENCIES) $(EXTRA_metasys_DEPENDENCIES) @rm -f metasys$(EXEEXT) $(AM_V_CCLD)$(LINK) $(metasys_OBJECTS) $(metasys_LDADD) $(LIBS) mge-shut$(EXEEXT): $(mge_shut_OBJECTS) $(mge_shut_DEPENDENCIES) $(EXTRA_mge_shut_DEPENDENCIES) @rm -f mge-shut$(EXEEXT) $(AM_V_CCLD)$(mge_shut_LINK) $(mge_shut_OBJECTS) $(mge_shut_LDADD) $(LIBS) mge-utalk$(EXEEXT): $(mge_utalk_OBJECTS) $(mge_utalk_DEPENDENCIES) $(EXTRA_mge_utalk_DEPENDENCIES) @rm -f mge-utalk$(EXEEXT) $(AM_V_CCLD)$(LINK) $(mge_utalk_OBJECTS) $(mge_utalk_LDADD) $(LIBS) microdowell$(EXEEXT): $(microdowell_OBJECTS) $(microdowell_DEPENDENCIES) $(EXTRA_microdowell_DEPENDENCIES) @rm -f microdowell$(EXEEXT) $(AM_V_CCLD)$(LINK) $(microdowell_OBJECTS) $(microdowell_LDADD) $(LIBS) netxml-ups$(EXEEXT): $(netxml_ups_OBJECTS) $(netxml_ups_DEPENDENCIES) $(EXTRA_netxml_ups_DEPENDENCIES) @rm -f netxml-ups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(netxml_ups_OBJECTS) $(netxml_ups_LDADD) $(LIBS) nut-ipmipsu$(EXEEXT): $(nut_ipmipsu_OBJECTS) $(nut_ipmipsu_DEPENDENCIES) $(EXTRA_nut_ipmipsu_DEPENDENCIES) @rm -f nut-ipmipsu$(EXEEXT) $(AM_V_CCLD)$(LINK) $(nut_ipmipsu_OBJECTS) $(nut_ipmipsu_LDADD) $(LIBS) nutdrv_atcl_usb$(EXEEXT): $(nutdrv_atcl_usb_OBJECTS) $(nutdrv_atcl_usb_DEPENDENCIES) $(EXTRA_nutdrv_atcl_usb_DEPENDENCIES) @rm -f nutdrv_atcl_usb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(nutdrv_atcl_usb_OBJECTS) $(nutdrv_atcl_usb_LDADD) $(LIBS) nutdrv_qx$(EXEEXT): $(nutdrv_qx_OBJECTS) $(nutdrv_qx_DEPENDENCIES) $(EXTRA_nutdrv_qx_DEPENDENCIES) @rm -f nutdrv_qx$(EXEEXT) $(AM_V_CCLD)$(nutdrv_qx_LINK) $(nutdrv_qx_OBJECTS) $(nutdrv_qx_LDADD) $(LIBS) oldmge-shut$(EXEEXT): $(oldmge_shut_OBJECTS) $(oldmge_shut_DEPENDENCIES) $(EXTRA_oldmge_shut_DEPENDENCIES) @rm -f oldmge-shut$(EXEEXT) $(AM_V_CCLD)$(LINK) $(oldmge_shut_OBJECTS) $(oldmge_shut_LDADD) $(LIBS) oneac$(EXEEXT): $(oneac_OBJECTS) $(oneac_DEPENDENCIES) $(EXTRA_oneac_DEPENDENCIES) @rm -f oneac$(EXEEXT) $(AM_V_CCLD)$(LINK) $(oneac_OBJECTS) $(oneac_LDADD) $(LIBS) optiups$(EXEEXT): $(optiups_OBJECTS) $(optiups_DEPENDENCIES) $(EXTRA_optiups_DEPENDENCIES) @rm -f optiups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(optiups_OBJECTS) $(optiups_LDADD) $(LIBS) powercom$(EXEEXT): $(powercom_OBJECTS) $(powercom_DEPENDENCIES) $(EXTRA_powercom_DEPENDENCIES) @rm -f powercom$(EXEEXT) $(AM_V_CCLD)$(LINK) $(powercom_OBJECTS) $(powercom_LDADD) $(LIBS) powerman-pdu$(EXEEXT): $(powerman_pdu_OBJECTS) $(powerman_pdu_DEPENDENCIES) $(EXTRA_powerman_pdu_DEPENDENCIES) @rm -f powerman-pdu$(EXEEXT) $(AM_V_CCLD)$(LINK) $(powerman_pdu_OBJECTS) $(powerman_pdu_LDADD) $(LIBS) powerpanel$(EXEEXT): $(powerpanel_OBJECTS) $(powerpanel_DEPENDENCIES) $(EXTRA_powerpanel_DEPENDENCIES) @rm -f powerpanel$(EXEEXT) $(AM_V_CCLD)$(LINK) $(powerpanel_OBJECTS) $(powerpanel_LDADD) $(LIBS) rhino$(EXEEXT): $(rhino_OBJECTS) $(rhino_DEPENDENCIES) $(EXTRA_rhino_DEPENDENCIES) @rm -f rhino$(EXEEXT) $(AM_V_CCLD)$(LINK) $(rhino_OBJECTS) $(rhino_LDADD) $(LIBS) richcomm_usb$(EXEEXT): $(richcomm_usb_OBJECTS) $(richcomm_usb_DEPENDENCIES) $(EXTRA_richcomm_usb_DEPENDENCIES) @rm -f richcomm_usb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(richcomm_usb_OBJECTS) $(richcomm_usb_LDADD) $(LIBS) riello_ser$(EXEEXT): $(riello_ser_OBJECTS) $(riello_ser_DEPENDENCIES) $(EXTRA_riello_ser_DEPENDENCIES) @rm -f riello_ser$(EXEEXT) $(AM_V_CCLD)$(LINK) $(riello_ser_OBJECTS) $(riello_ser_LDADD) $(LIBS) riello_usb$(EXEEXT): $(riello_usb_OBJECTS) $(riello_usb_DEPENDENCIES) $(EXTRA_riello_usb_DEPENDENCIES) @rm -f riello_usb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(riello_usb_OBJECTS) $(riello_usb_LDADD) $(LIBS) safenet$(EXEEXT): $(safenet_OBJECTS) $(safenet_DEPENDENCIES) $(EXTRA_safenet_DEPENDENCIES) @rm -f safenet$(EXEEXT) $(AM_V_CCLD)$(LINK) $(safenet_OBJECTS) $(safenet_LDADD) $(LIBS) skel$(EXEEXT): $(skel_OBJECTS) $(skel_DEPENDENCIES) $(EXTRA_skel_DEPENDENCIES) @rm -f skel$(EXEEXT) $(AM_V_CCLD)$(LINK) $(skel_OBJECTS) $(skel_LDADD) $(LIBS) snmp-ups$(EXEEXT): $(snmp_ups_OBJECTS) $(snmp_ups_DEPENDENCIES) $(EXTRA_snmp_ups_DEPENDENCIES) @rm -f snmp-ups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(snmp_ups_OBJECTS) $(snmp_ups_LDADD) $(LIBS) solis$(EXEEXT): $(solis_OBJECTS) $(solis_DEPENDENCIES) $(EXTRA_solis_DEPENDENCIES) @rm -f solis$(EXEEXT) $(AM_V_CCLD)$(LINK) $(solis_OBJECTS) $(solis_LDADD) $(LIBS) tripplite$(EXEEXT): $(tripplite_OBJECTS) $(tripplite_DEPENDENCIES) $(EXTRA_tripplite_DEPENDENCIES) @rm -f tripplite$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tripplite_OBJECTS) $(tripplite_LDADD) $(LIBS) tripplite_usb$(EXEEXT): $(tripplite_usb_OBJECTS) $(tripplite_usb_DEPENDENCIES) $(EXTRA_tripplite_usb_DEPENDENCIES) @rm -f tripplite_usb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tripplite_usb_OBJECTS) $(tripplite_usb_LDADD) $(LIBS) tripplitesu$(EXEEXT): $(tripplitesu_OBJECTS) $(tripplitesu_DEPENDENCIES) $(EXTRA_tripplitesu_DEPENDENCIES) @rm -f tripplitesu$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tripplitesu_OBJECTS) $(tripplitesu_LDADD) $(LIBS) upscode2$(EXEEXT): $(upscode2_OBJECTS) $(upscode2_DEPENDENCIES) $(EXTRA_upscode2_DEPENDENCIES) @rm -f upscode2$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upscode2_OBJECTS) $(upscode2_LDADD) $(LIBS) upsdrvctl$(EXEEXT): $(upsdrvctl_OBJECTS) $(upsdrvctl_DEPENDENCIES) $(EXTRA_upsdrvctl_DEPENDENCIES) @rm -f upsdrvctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsdrvctl_OBJECTS) $(upsdrvctl_LDADD) $(LIBS) usbhid-ups$(EXEEXT): $(usbhid_ups_OBJECTS) $(usbhid_ups_DEPENDENCIES) $(EXTRA_usbhid_ups_DEPENDENCIES) @rm -f usbhid-ups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(usbhid_ups_OBJECTS) $(usbhid_ups_LDADD) $(LIBS) victronups$(EXEEXT): $(victronups_OBJECTS) $(victronups_DEPENDENCIES) $(EXTRA_victronups_DEPENDENCIES) @rm -f victronups$(EXEEXT) $(AM_V_CCLD)$(LINK) $(victronups_OBJECTS) $(victronups_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/al175.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apc-ats-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apc-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apc-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart-old.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart_tabs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcupsd_ups-apcupsd-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/baytech-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp_ser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp_usb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkin-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkinunv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfcom.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfortress.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestpower-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestuferrups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer_ser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer_usb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone-outlet.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compaq-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cps-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cyberpower-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delta_ups-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstate.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy_ups-dummy-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eaton-ats-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eaton-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etapro.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/everups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/explore-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gamatronic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genericups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hidparser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huawei-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idowell-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ietf-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isbmex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ivtscd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert-esp2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macosx-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterguard.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metasys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-shut.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-utalk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-xml.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-hidparser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-libhid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-libshut.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-mge-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-usbhid-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/microdowell.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netvision-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netxml-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut-ipmipsu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut-libfreeipmi.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_atcl_usb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-libusb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-usb-common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oneac.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openups-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optiups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powercom-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powercom.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerman-pdu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerp-bin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerp-txt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerpanel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerware-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raritan-pdu-mib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rhino.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/richcomm_usb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riello.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riello_ser.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riello_usb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safenet.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serial.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/solis.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite-hid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite_usb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplitesu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upscode2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsdrvctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usb-common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usbhid-ups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/victronups.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xppc-mib.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< apcupsd_ups-apcupsd-ups.o: apcupsd-ups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -MT apcupsd_ups-apcupsd-ups.o -MD -MP -MF $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo -c -o apcupsd_ups-apcupsd-ups.o `test -f 'apcupsd-ups.c' || echo '$(srcdir)/'`apcupsd-ups.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo $(DEPDIR)/apcupsd_ups-apcupsd-ups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apcupsd-ups.c' object='apcupsd_ups-apcupsd-ups.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -c -o apcupsd_ups-apcupsd-ups.o `test -f 'apcupsd-ups.c' || echo '$(srcdir)/'`apcupsd-ups.c apcupsd_ups-apcupsd-ups.obj: apcupsd-ups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -MT apcupsd_ups-apcupsd-ups.obj -MD -MP -MF $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo -c -o apcupsd_ups-apcupsd-ups.obj `if test -f 'apcupsd-ups.c'; then $(CYGPATH_W) 'apcupsd-ups.c'; else $(CYGPATH_W) '$(srcdir)/apcupsd-ups.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo $(DEPDIR)/apcupsd_ups-apcupsd-ups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apcupsd-ups.c' object='apcupsd_ups-apcupsd-ups.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -c -o apcupsd_ups-apcupsd-ups.obj `if test -f 'apcupsd-ups.c'; then $(CYGPATH_W) 'apcupsd-ups.c'; else $(CYGPATH_W) '$(srcdir)/apcupsd-ups.c'; fi` dummy_ups-dummy-ups.o: dummy-ups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -MT dummy_ups-dummy-ups.o -MD -MP -MF $(DEPDIR)/dummy_ups-dummy-ups.Tpo -c -o dummy_ups-dummy-ups.o `test -f 'dummy-ups.c' || echo '$(srcdir)/'`dummy-ups.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dummy_ups-dummy-ups.Tpo $(DEPDIR)/dummy_ups-dummy-ups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dummy-ups.c' object='dummy_ups-dummy-ups.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -c -o dummy_ups-dummy-ups.o `test -f 'dummy-ups.c' || echo '$(srcdir)/'`dummy-ups.c dummy_ups-dummy-ups.obj: dummy-ups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -MT dummy_ups-dummy-ups.obj -MD -MP -MF $(DEPDIR)/dummy_ups-dummy-ups.Tpo -c -o dummy_ups-dummy-ups.obj `if test -f 'dummy-ups.c'; then $(CYGPATH_W) 'dummy-ups.c'; else $(CYGPATH_W) '$(srcdir)/dummy-ups.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dummy_ups-dummy-ups.Tpo $(DEPDIR)/dummy_ups-dummy-ups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dummy-ups.c' object='dummy_ups-dummy-ups.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -c -o dummy_ups-dummy-ups.obj `if test -f 'dummy-ups.c'; then $(CYGPATH_W) 'dummy-ups.c'; else $(CYGPATH_W) '$(srcdir)/dummy-ups.c'; fi` mge_shut-usbhid-ups.o: usbhid-ups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-usbhid-ups.o -MD -MP -MF $(DEPDIR)/mge_shut-usbhid-ups.Tpo -c -o mge_shut-usbhid-ups.o `test -f 'usbhid-ups.c' || echo '$(srcdir)/'`usbhid-ups.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-usbhid-ups.Tpo $(DEPDIR)/mge_shut-usbhid-ups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usbhid-ups.c' object='mge_shut-usbhid-ups.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-usbhid-ups.o `test -f 'usbhid-ups.c' || echo '$(srcdir)/'`usbhid-ups.c mge_shut-usbhid-ups.obj: usbhid-ups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-usbhid-ups.obj -MD -MP -MF $(DEPDIR)/mge_shut-usbhid-ups.Tpo -c -o mge_shut-usbhid-ups.obj `if test -f 'usbhid-ups.c'; then $(CYGPATH_W) 'usbhid-ups.c'; else $(CYGPATH_W) '$(srcdir)/usbhid-ups.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-usbhid-ups.Tpo $(DEPDIR)/mge_shut-usbhid-ups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usbhid-ups.c' object='mge_shut-usbhid-ups.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-usbhid-ups.obj `if test -f 'usbhid-ups.c'; then $(CYGPATH_W) 'usbhid-ups.c'; else $(CYGPATH_W) '$(srcdir)/usbhid-ups.c'; fi` mge_shut-libshut.o: libshut.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libshut.o -MD -MP -MF $(DEPDIR)/mge_shut-libshut.Tpo -c -o mge_shut-libshut.o `test -f 'libshut.c' || echo '$(srcdir)/'`libshut.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libshut.Tpo $(DEPDIR)/mge_shut-libshut.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libshut.c' object='mge_shut-libshut.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libshut.o `test -f 'libshut.c' || echo '$(srcdir)/'`libshut.c mge_shut-libshut.obj: libshut.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libshut.obj -MD -MP -MF $(DEPDIR)/mge_shut-libshut.Tpo -c -o mge_shut-libshut.obj `if test -f 'libshut.c'; then $(CYGPATH_W) 'libshut.c'; else $(CYGPATH_W) '$(srcdir)/libshut.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libshut.Tpo $(DEPDIR)/mge_shut-libshut.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libshut.c' object='mge_shut-libshut.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libshut.obj `if test -f 'libshut.c'; then $(CYGPATH_W) 'libshut.c'; else $(CYGPATH_W) '$(srcdir)/libshut.c'; fi` mge_shut-libhid.o: libhid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libhid.o -MD -MP -MF $(DEPDIR)/mge_shut-libhid.Tpo -c -o mge_shut-libhid.o `test -f 'libhid.c' || echo '$(srcdir)/'`libhid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libhid.Tpo $(DEPDIR)/mge_shut-libhid.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libhid.c' object='mge_shut-libhid.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libhid.o `test -f 'libhid.c' || echo '$(srcdir)/'`libhid.c mge_shut-libhid.obj: libhid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libhid.obj -MD -MP -MF $(DEPDIR)/mge_shut-libhid.Tpo -c -o mge_shut-libhid.obj `if test -f 'libhid.c'; then $(CYGPATH_W) 'libhid.c'; else $(CYGPATH_W) '$(srcdir)/libhid.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libhid.Tpo $(DEPDIR)/mge_shut-libhid.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libhid.c' object='mge_shut-libhid.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libhid.obj `if test -f 'libhid.c'; then $(CYGPATH_W) 'libhid.c'; else $(CYGPATH_W) '$(srcdir)/libhid.c'; fi` mge_shut-hidparser.o: hidparser.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-hidparser.o -MD -MP -MF $(DEPDIR)/mge_shut-hidparser.Tpo -c -o mge_shut-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-hidparser.Tpo $(DEPDIR)/mge_shut-hidparser.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hidparser.c' object='mge_shut-hidparser.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c mge_shut-hidparser.obj: hidparser.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-hidparser.obj -MD -MP -MF $(DEPDIR)/mge_shut-hidparser.Tpo -c -o mge_shut-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-hidparser.Tpo $(DEPDIR)/mge_shut-hidparser.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hidparser.c' object='mge_shut-hidparser.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` mge_shut-mge-hid.o: mge-hid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-mge-hid.o -MD -MP -MF $(DEPDIR)/mge_shut-mge-hid.Tpo -c -o mge_shut-mge-hid.o `test -f 'mge-hid.c' || echo '$(srcdir)/'`mge-hid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-mge-hid.Tpo $(DEPDIR)/mge_shut-mge-hid.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mge-hid.c' object='mge_shut-mge-hid.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-mge-hid.o `test -f 'mge-hid.c' || echo '$(srcdir)/'`mge-hid.c mge_shut-mge-hid.obj: mge-hid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-mge-hid.obj -MD -MP -MF $(DEPDIR)/mge_shut-mge-hid.Tpo -c -o mge_shut-mge-hid.obj `if test -f 'mge-hid.c'; then $(CYGPATH_W) 'mge-hid.c'; else $(CYGPATH_W) '$(srcdir)/mge-hid.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-mge-hid.Tpo $(DEPDIR)/mge_shut-mge-hid.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mge-hid.c' object='mge_shut-mge-hid.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-mge-hid.obj `if test -f 'mge-hid.c'; then $(CYGPATH_W) 'mge-hid.c'; else $(CYGPATH_W) '$(srcdir)/mge-hid.c'; fi` nutdrv_qx-nutdrv_qx.o: nutdrv_qx.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo -c -o nutdrv_qx-nutdrv_qx.o `test -f 'nutdrv_qx.c' || echo '$(srcdir)/'`nutdrv_qx.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx.c' object='nutdrv_qx-nutdrv_qx.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx.o `test -f 'nutdrv_qx.c' || echo '$(srcdir)/'`nutdrv_qx.c nutdrv_qx-nutdrv_qx.obj: nutdrv_qx.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo -c -o nutdrv_qx-nutdrv_qx.obj `if test -f 'nutdrv_qx.c'; then $(CYGPATH_W) 'nutdrv_qx.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx.c' object='nutdrv_qx-nutdrv_qx.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx.obj `if test -f 'nutdrv_qx.c'; then $(CYGPATH_W) 'nutdrv_qx.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx.c'; fi` nutdrv_qx-libusb.o: libusb.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-libusb.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-libusb.Tpo -c -o nutdrv_qx-libusb.o `test -f 'libusb.c' || echo '$(srcdir)/'`libusb.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-libusb.Tpo $(DEPDIR)/nutdrv_qx-libusb.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libusb.c' object='nutdrv_qx-libusb.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-libusb.o `test -f 'libusb.c' || echo '$(srcdir)/'`libusb.c nutdrv_qx-libusb.obj: libusb.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-libusb.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-libusb.Tpo -c -o nutdrv_qx-libusb.obj `if test -f 'libusb.c'; then $(CYGPATH_W) 'libusb.c'; else $(CYGPATH_W) '$(srcdir)/libusb.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-libusb.Tpo $(DEPDIR)/nutdrv_qx-libusb.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libusb.c' object='nutdrv_qx-libusb.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-libusb.obj `if test -f 'libusb.c'; then $(CYGPATH_W) 'libusb.c'; else $(CYGPATH_W) '$(srcdir)/libusb.c'; fi` nutdrv_qx-usb-common.o: usb-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-usb-common.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-usb-common.Tpo -c -o nutdrv_qx-usb-common.o `test -f 'usb-common.c' || echo '$(srcdir)/'`usb-common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-usb-common.Tpo $(DEPDIR)/nutdrv_qx-usb-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usb-common.c' object='nutdrv_qx-usb-common.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-usb-common.o `test -f 'usb-common.c' || echo '$(srcdir)/'`usb-common.c nutdrv_qx-usb-common.obj: usb-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-usb-common.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-usb-common.Tpo -c -o nutdrv_qx-usb-common.obj `if test -f 'usb-common.c'; then $(CYGPATH_W) 'usb-common.c'; else $(CYGPATH_W) '$(srcdir)/usb-common.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-usb-common.Tpo $(DEPDIR)/nutdrv_qx-usb-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usb-common.c' object='nutdrv_qx-usb-common.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-usb-common.obj `if test -f 'usb-common.c'; then $(CYGPATH_W) 'usb-common.c'; else $(CYGPATH_W) '$(srcdir)/usb-common.c'; fi` nutdrv_qx-nutdrv_qx_bestups.o: nutdrv_qx_bestups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_bestups.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo -c -o nutdrv_qx-nutdrv_qx_bestups.o `test -f 'nutdrv_qx_bestups.c' || echo '$(srcdir)/'`nutdrv_qx_bestups.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_bestups.c' object='nutdrv_qx-nutdrv_qx_bestups.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_bestups.o `test -f 'nutdrv_qx_bestups.c' || echo '$(srcdir)/'`nutdrv_qx_bestups.c nutdrv_qx-nutdrv_qx_bestups.obj: nutdrv_qx_bestups.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_bestups.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo -c -o nutdrv_qx-nutdrv_qx_bestups.obj `if test -f 'nutdrv_qx_bestups.c'; then $(CYGPATH_W) 'nutdrv_qx_bestups.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_bestups.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_bestups.c' object='nutdrv_qx-nutdrv_qx_bestups.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_bestups.obj `if test -f 'nutdrv_qx_bestups.c'; then $(CYGPATH_W) 'nutdrv_qx_bestups.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_bestups.c'; fi` nutdrv_qx-nutdrv_qx_blazer-common.o: nutdrv_qx_blazer-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_blazer-common.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo -c -o nutdrv_qx-nutdrv_qx_blazer-common.o `test -f 'nutdrv_qx_blazer-common.c' || echo '$(srcdir)/'`nutdrv_qx_blazer-common.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_blazer-common.c' object='nutdrv_qx-nutdrv_qx_blazer-common.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_blazer-common.o `test -f 'nutdrv_qx_blazer-common.c' || echo '$(srcdir)/'`nutdrv_qx_blazer-common.c nutdrv_qx-nutdrv_qx_blazer-common.obj: nutdrv_qx_blazer-common.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_blazer-common.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo -c -o nutdrv_qx-nutdrv_qx_blazer-common.obj `if test -f 'nutdrv_qx_blazer-common.c'; then $(CYGPATH_W) 'nutdrv_qx_blazer-common.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_blazer-common.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_blazer-common.c' object='nutdrv_qx-nutdrv_qx_blazer-common.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_blazer-common.obj `if test -f 'nutdrv_qx_blazer-common.c'; then $(CYGPATH_W) 'nutdrv_qx_blazer-common.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_blazer-common.c'; fi` nutdrv_qx-nutdrv_qx_mecer.o: nutdrv_qx_mecer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mecer.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo -c -o nutdrv_qx-nutdrv_qx_mecer.o `test -f 'nutdrv_qx_mecer.c' || echo '$(srcdir)/'`nutdrv_qx_mecer.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mecer.c' object='nutdrv_qx-nutdrv_qx_mecer.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mecer.o `test -f 'nutdrv_qx_mecer.c' || echo '$(srcdir)/'`nutdrv_qx_mecer.c nutdrv_qx-nutdrv_qx_mecer.obj: nutdrv_qx_mecer.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mecer.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo -c -o nutdrv_qx-nutdrv_qx_mecer.obj `if test -f 'nutdrv_qx_mecer.c'; then $(CYGPATH_W) 'nutdrv_qx_mecer.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mecer.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mecer.c' object='nutdrv_qx-nutdrv_qx_mecer.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mecer.obj `if test -f 'nutdrv_qx_mecer.c'; then $(CYGPATH_W) 'nutdrv_qx_mecer.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mecer.c'; fi` nutdrv_qx-nutdrv_qx_megatec.o: nutdrv_qx_megatec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec.o `test -f 'nutdrv_qx_megatec.c' || echo '$(srcdir)/'`nutdrv_qx_megatec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec.c' object='nutdrv_qx-nutdrv_qx_megatec.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec.o `test -f 'nutdrv_qx_megatec.c' || echo '$(srcdir)/'`nutdrv_qx_megatec.c nutdrv_qx-nutdrv_qx_megatec.obj: nutdrv_qx_megatec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec.obj `if test -f 'nutdrv_qx_megatec.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec.c' object='nutdrv_qx-nutdrv_qx_megatec.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec.obj `if test -f 'nutdrv_qx_megatec.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec.c'; fi` nutdrv_qx-nutdrv_qx_megatec-old.o: nutdrv_qx_megatec-old.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec-old.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec-old.o `test -f 'nutdrv_qx_megatec-old.c' || echo '$(srcdir)/'`nutdrv_qx_megatec-old.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec-old.c' object='nutdrv_qx-nutdrv_qx_megatec-old.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec-old.o `test -f 'nutdrv_qx_megatec-old.c' || echo '$(srcdir)/'`nutdrv_qx_megatec-old.c nutdrv_qx-nutdrv_qx_megatec-old.obj: nutdrv_qx_megatec-old.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec-old.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec-old.obj `if test -f 'nutdrv_qx_megatec-old.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec-old.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec-old.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec-old.c' object='nutdrv_qx-nutdrv_qx_megatec-old.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec-old.obj `if test -f 'nutdrv_qx_megatec-old.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec-old.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec-old.c'; fi` nutdrv_qx-nutdrv_qx_mustek.o: nutdrv_qx_mustek.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mustek.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo -c -o nutdrv_qx-nutdrv_qx_mustek.o `test -f 'nutdrv_qx_mustek.c' || echo '$(srcdir)/'`nutdrv_qx_mustek.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mustek.c' object='nutdrv_qx-nutdrv_qx_mustek.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mustek.o `test -f 'nutdrv_qx_mustek.c' || echo '$(srcdir)/'`nutdrv_qx_mustek.c nutdrv_qx-nutdrv_qx_mustek.obj: nutdrv_qx_mustek.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mustek.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo -c -o nutdrv_qx-nutdrv_qx_mustek.obj `if test -f 'nutdrv_qx_mustek.c'; then $(CYGPATH_W) 'nutdrv_qx_mustek.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mustek.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mustek.c' object='nutdrv_qx-nutdrv_qx_mustek.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mustek.obj `if test -f 'nutdrv_qx_mustek.c'; then $(CYGPATH_W) 'nutdrv_qx_mustek.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mustek.c'; fi` nutdrv_qx-nutdrv_qx_q1.o: nutdrv_qx_q1.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_q1.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo -c -o nutdrv_qx-nutdrv_qx_q1.o `test -f 'nutdrv_qx_q1.c' || echo '$(srcdir)/'`nutdrv_qx_q1.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_q1.c' object='nutdrv_qx-nutdrv_qx_q1.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_q1.o `test -f 'nutdrv_qx_q1.c' || echo '$(srcdir)/'`nutdrv_qx_q1.c nutdrv_qx-nutdrv_qx_q1.obj: nutdrv_qx_q1.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_q1.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo -c -o nutdrv_qx-nutdrv_qx_q1.obj `if test -f 'nutdrv_qx_q1.c'; then $(CYGPATH_W) 'nutdrv_qx_q1.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_q1.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_q1.c' object='nutdrv_qx-nutdrv_qx_q1.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_q1.obj `if test -f 'nutdrv_qx_q1.c'; then $(CYGPATH_W) 'nutdrv_qx_q1.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_q1.c'; fi` nutdrv_qx-nutdrv_qx_voltronic.o: nutdrv_qx_voltronic.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic.o `test -f 'nutdrv_qx_voltronic.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic.c' object='nutdrv_qx-nutdrv_qx_voltronic.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic.o `test -f 'nutdrv_qx_voltronic.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic.c nutdrv_qx-nutdrv_qx_voltronic.obj: nutdrv_qx_voltronic.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic.obj `if test -f 'nutdrv_qx_voltronic.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic.c' object='nutdrv_qx-nutdrv_qx_voltronic.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic.obj `if test -f 'nutdrv_qx_voltronic.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic.c'; fi` nutdrv_qx-nutdrv_qx_voltronic-qs.o: nutdrv_qx_voltronic-qs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.o `test -f 'nutdrv_qx_voltronic-qs.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.o `test -f 'nutdrv_qx_voltronic-qs.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs.c nutdrv_qx-nutdrv_qx_voltronic-qs.obj: nutdrv_qx_voltronic-qs.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.obj `if test -f 'nutdrv_qx_voltronic-qs.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.obj `if test -f 'nutdrv_qx_voltronic-qs.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs.c'; fi` nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o: nutdrv_qx_voltronic-qs-hex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o `test -f 'nutdrv_qx_voltronic-qs-hex.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs-hex.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs-hex.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o `test -f 'nutdrv_qx_voltronic-qs-hex.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs-hex.c nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj: nutdrv_qx_voltronic-qs-hex.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj `if test -f 'nutdrv_qx_voltronic-qs-hex.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs-hex.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs-hex.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs-hex.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj `if test -f 'nutdrv_qx_voltronic-qs-hex.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs-hex.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs-hex.c'; fi` nutdrv_qx-nutdrv_qx_zinto.o: nutdrv_qx_zinto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_zinto.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo -c -o nutdrv_qx-nutdrv_qx_zinto.o `test -f 'nutdrv_qx_zinto.c' || echo '$(srcdir)/'`nutdrv_qx_zinto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_zinto.c' object='nutdrv_qx-nutdrv_qx_zinto.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_zinto.o `test -f 'nutdrv_qx_zinto.c' || echo '$(srcdir)/'`nutdrv_qx_zinto.c nutdrv_qx-nutdrv_qx_zinto.obj: nutdrv_qx_zinto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_zinto.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo -c -o nutdrv_qx-nutdrv_qx_zinto.obj `if test -f 'nutdrv_qx_zinto.c'; then $(CYGPATH_W) 'nutdrv_qx_zinto.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_zinto.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_zinto.c' object='nutdrv_qx-nutdrv_qx_zinto.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_zinto.obj `if test -f 'nutdrv_qx_zinto.c'; then $(CYGPATH_W) 'nutdrv_qx_zinto.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_zinto.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: for dir in "$(DESTDIR)$(driverexecdir)" "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-driverexecPROGRAMS clean-generic clean-libtool \ clean-sbinPROGRAMS mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-driverexecPROGRAMS install-sbinPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-driverexecPROGRAMS uninstall-sbinPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-driverexecPROGRAMS clean-generic clean-libtool \ clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-driverexecPROGRAMS install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-driverexecPROGRAMS uninstall-sbinPROGRAMS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/drivers/nutdrv_atcl_usb.c0000644000175000017500000003245712640473702014313 00000000000000/* * nutdrv_atcl_usb.c - driver for generic-brand "ATCL FOR UPS" * * Copyright (C) 2013-2014 Charles Lepple * * Loosely based on richcomm_usb.c, * Copyright (C) 2007 Peter van Valderen * Dirk Teurlings * * 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 "main.h" #include "usb-common.h" /* driver version */ #define DRIVER_NAME "'ATCL FOR UPS' USB driver" #define DRIVER_VERSION "1.1" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Charles Lepple ", DRV_EXPERIMENTAL, { NULL } }; #define STATUS_ENDPOINT (USB_ENDPOINT_IN | 1) #define SHUTDOWN_ENDPOINT (USB_ENDPOINT_OUT | 2) #define STATUS_PACKETSIZE 8 #define SHUTDOWN_PACKETSIZE 8 /* Probably can reduce this, since the pcap file shows mostly 1050-ish ms response times */ #define ATCL_USB_TIMEOUT USB_TIMEOUT /* limit the amount of spew that goes in the syslog when we lose the UPS (from nut_usb.h) */ #define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define USB_ERR_RATE 10 /* then only print every 10th error */ #define USB_VENDOR_STRING "ATCL FOR UPS" static usb_device_id_t atcl_usb_id[] = { /* ATCL FOR UPS */ { USB_DEVICE(0x0001, 0x0000), NULL }, /* end of list */ {-1, -1, NULL} }; static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; static unsigned int comm_failures = 0; static int device_match_func(USBDevice_t *device, void *privdata) { char *requested_vendor; switch (is_usb_device_supported(atcl_usb_id, device)) { case SUPPORTED: if(!device->Vendor) { upsdebugx(1, "Couldn't retrieve USB string descriptor for vendor. Check permissions?"); requested_vendor = getval("vendor"); if(requested_vendor) { if(!strcmp("NULL", requested_vendor)) { upsdebugx(3, "Matched device with NULL vendor string."); return 1; } } upsdebugx(1, "To keep trying (in case your device does not have a vendor string), use vendor=NULL"); return 0; } if(!strcmp(device->Vendor, USB_VENDOR_STRING)) { upsdebugx(4, "Matched expected vendor='%s'.", USB_VENDOR_STRING); return 1; } /* Didn't match, but the user provided an alternate vendor ID: */ requested_vendor = getval("vendor"); if(requested_vendor) { if(!strcmp(device->Vendor, requested_vendor)) { upsdebugx(3, "Matched device with vendor='%s'.", requested_vendor); return 1; } else { upsdebugx(2, "idVendor=%04x and idProduct=%04x, but provided vendor '%s' does not match device: '%s'.", device->VendorID, device->ProductID, requested_vendor, device->Vendor); return 0; } } /* TODO: automatic way of suggesting other drivers? */ upsdebugx(2, "idVendor=%04x and idProduct=%04x, but device vendor string '%s' does not match expected string '%s'. " "Have you tried the nutdrv_qx driver?", device->VendorID, device->ProductID, device->Vendor, USB_VENDOR_STRING); return 0; case POSSIBLY_SUPPORTED: case NOT_SUPPORTED: default: return 0; } } static USBDeviceMatcher_t device_matcher = { &device_match_func, NULL, NULL }; static int query_ups(char *reply) { int ret; ret = usb_interrupt_read(udev, STATUS_ENDPOINT, reply, STATUS_PACKETSIZE, ATCL_USB_TIMEOUT); if (ret <= 0) { upsdebugx(2, "status interrupt read: %s", ret ? usb_strerror() : "timeout"); return ret; } upsdebug_hex(3, "read", reply, ret); return ret; } static void usb_comm_fail(const char *fmt, ...) { int ret; char why[SMALLBUF]; va_list ap; /* this means we're probably here because select was interrupted */ if (exit_flag != 0) { return; /* ignored, since we're about to exit anyway */ } comm_failures++; if ((comm_failures == USB_ERR_LIMIT) || ((comm_failures % USB_ERR_RATE) == 0)) { upslogx(LOG_WARNING, "Warning: excessive comm failures, limiting error reporting"); } /* once it's past the limit, only log once every USB_ERR_LIMIT calls */ if ((comm_failures > USB_ERR_LIMIT) && ((comm_failures % USB_ERR_LIMIT) != 0)) { return; } /* generic message if the caller hasn't elaborated */ if (!fmt) { upslogx(LOG_WARNING, "Communications with UPS lost - check cabling"); return; } va_start(ap, fmt); ret = vsnprintf(why, sizeof(why), fmt, ap); va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(why))) { upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed more than %d bytes", (int)sizeof(why)); } upslogx(LOG_WARNING, "Communications with UPS lost: %s", why); } static void usb_comm_good(void) { if (comm_failures == 0) { return; } upslogx(LOG_NOTICE, "Communications with UPS re-established"); comm_failures = 0; } /* * Callback that is called by usb_device_open() that handles USB device * settings prior to accepting the devide. At the very least claim the * device here. Detaching the kernel driver will be handled by the * caller, don't do this here. Return < 0 on error, 0 or higher on * success. */ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) { if (usb_set_configuration(handle, 1) < 0) { upslogx(LOG_WARNING, "Can't set USB configuration: %s", usb_strerror()); return -1; } if (usb_claim_interface(handle, 0) < 0) { upslogx(LOG_WARNING, "Can't claim USB interface: %s", usb_strerror()); return -1; } /* TODO: HID SET_IDLE to 0 (not necessary?) */ return 1; } static int usb_device_close(usb_dev_handle *handle) { if (!handle) { return 0; } /* usb_release_interface() sometimes blocks and goes into uninterruptible sleep. So don't do it. */ /* usb_release_interface(handle, 0); */ return usb_close(handle); } static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *handle, USBDevice_t *device)) { struct usb_bus *bus; /* libusb base init */ usb_init(); usb_find_busses(); usb_find_devices(); #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ usb_device_close(*handlep); #endif for (bus = usb_busses; bus; bus = bus->next) { struct usb_device *dev; usb_dev_handle *handle; for (dev = bus->devices; dev; dev = dev->next) { int i, ret; USBDeviceMatcher_t *m; upsdebugx(3, "Checking USB device [%04x:%04x] (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); /* supported vendors are now checked by the supplied matcher */ /* open the device */ *handlep = handle = usb_open(dev); if (!handle) { upsdebugx(4, "Failed to open USB device, skipping: %s", usb_strerror()); continue; } /* collect the identifying information of this device. Note that this is safe, because there's no need to claim an interface for this (and therefore we do not yet need to detach any kernel drivers). */ free(device->Vendor); free(device->Product); free(device->Serial); free(device->Bus); memset(device, 0, sizeof(*device)); device->VendorID = dev->descriptor.idVendor; device->ProductID = dev->descriptor.idProduct; device->Bus = strdup(bus->dirname); if (dev->descriptor.iManufacturer) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iManufacturer, buf, sizeof(buf)); if (ret > 0) { device->Vendor = strdup(buf); } } if (dev->descriptor.iProduct) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iProduct, buf, sizeof(buf)); if (ret > 0) { device->Product = strdup(buf); } } if (dev->descriptor.iSerialNumber) { char buf[SMALLBUF]; ret = usb_get_string_simple(handle, dev->descriptor.iSerialNumber, buf, sizeof(buf)); if (ret > 0) { device->Serial = strdup(buf); } } upsdebugx(4, "- VendorID : %04x", device->VendorID); upsdebugx(4, "- ProductID : %04x", device->ProductID); upsdebugx(4, "- Manufacturer : %s", device->Vendor ? device->Vendor : "unknown"); upsdebugx(4, "- Product : %s", device->Product ? device->Product : "unknown"); upsdebugx(4, "- Serial Number: %s", device->Serial ? device->Serial : "unknown"); upsdebugx(4, "- Bus : %s", device->Bus ? device->Bus : "unknown"); for (m = matcher; m; m = m->next) { switch (m->match_function(device, m->privdata)) { case 0: upsdebugx(4, "Device does not match - skipping"); goto next_device; case -1: fatal_with_errno(EXIT_FAILURE, "matcher"); goto next_device; case -2: upsdebugx(4, "matcher: unspecified error"); goto next_device; } } for (i = 0; i < 3; i++) { ret = callback(handle, device); if (ret >= 0) { upsdebugx(3, "USB device [%04x:%04x] opened", device->VendorID, device->ProductID); return ret; } #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP /* this method requires at least libusb 0.1.8: * it forces device claiming by unbinding * attached driver... From libhid */ if (usb_detach_kernel_driver_np(handle, 0) < 0) { upsdebugx(1, "failed to detach kernel driver from USB device: %s", usb_strerror()); } else { upsdebugx(4, "detached kernel driver from USB device..."); } #endif } fatalx(EXIT_FAILURE, "USB device [%04x:%04x] matches, but driver callback failed: %s", device->VendorID, device->ProductID, usb_strerror()); next_device: usb_close(handle); } } *handlep = NULL; upsdebugx(3, "No matching USB device found"); return -1; } /* * Initialise the UPS */ void upsdrv_initups(void) { int i; upsdebugx(1, "Searching for USB device..."); for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { if ((i < 3) && (sleep(5) == 0)) { usb_comm_fail("Can't open USB device, retrying ..."); continue; } fatalx(EXIT_FAILURE, "Unable to find ATCL FOR UPS\n\n" "Things to try:\n" " - Connect UPS device to USB bus\n" " - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n" " See upsdrvctl(8) and ups.conf(5).\n\n" "Fatal error: unusable configuration"); } } void upsdrv_cleanup(void) { usb_device_close(udev); free(usbdevice.Vendor); free(usbdevice.Product); free(usbdevice.Serial); free(usbdevice.Bus); } void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "%s", usbdevice.Vendor ? usbdevice.Vendor : "unknown"); dstate_setinfo("ups.model", "%s", usbdevice.Product ? usbdevice.Product : "unknown"); if(usbdevice.Serial && usbdevice.Product && strcmp(usbdevice.Serial, usbdevice.Product)) { /* Only set "ups.serial" if it isn't the same as "ups.model": */ dstate_setinfo("ups.serial", "%s", usbdevice.Serial); } dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); } void upsdrv_updateinfo(void) { char reply[STATUS_PACKETSIZE]; int ret; if (!udev) { ret = usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback); if (ret < 0) { return; } } ret = query_ups(reply); if (ret != STATUS_PACKETSIZE) { usb_comm_fail("Query to UPS failed"); dstate_datastale(); usb_device_close(udev); udev = NULL; return; } usb_comm_good(); dstate_dataok(); status_init(); switch(reply[0]) { case 3: upsdebugx(2, "reply[0] = 0x%02x -> OL", reply[0]); status_set("OL"); break; case 2: upsdebugx(2, "reply[0] = 0x%02x -> LB", reply[0]); status_set("LB"); /* fall through */ case 1: upsdebugx(2, "reply[0] = 0x%02x -> OB", reply[0]); status_set("OB"); break; default: upslogx(LOG_ERR, "Unknown status: 0x%02x", reply[0]); } if(strnlen(reply + 1, 7) != 0) { upslogx(LOG_NOTICE, "Status bytes 1-7 are not all zero"); } status_commit(); } /* If the UPS is on battery, it should shut down about 30 seconds after * receiving this packet. */ void upsdrv_shutdown(void) { const char shutdown_packet[SHUTDOWN_PACKETSIZE] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int ret; upslogx(LOG_DEBUG, "%s: attempting to call usb_interrupt_write(01 00 00 00 00 00 00 00)", __func__); ret = usb_interrupt_write(udev, SHUTDOWN_ENDPOINT, (char *)shutdown_packet, SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); if (ret <= 0) { upslogx(LOG_NOTICE, "%s: first usb_interrupt_write() failed: %s", __func__, ret ? usb_strerror() : "timeout"); } /* Totally guessing from the .pcap file here. TODO: configurable delay? */ usleep(170*1000); ret = usb_interrupt_write(udev, SHUTDOWN_ENDPOINT, (char *)shutdown_packet, SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); if (ret <= 0) { upslogx(LOG_ERR, "%s: second usb_interrupt_write() failed: %s", __func__, ret ? usb_strerror() : "timeout"); } } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "vendor", "USB vendor string (or NULL if none)"); } nut-2.7.4/drivers/eaton-mib.h0000644000175000017500000000046112640443572012765 00000000000000#ifndef EATON_MIB_H #define EATON_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t aphel_genesisII; extern mib2nut_info_t aphel_revelation; extern mib2nut_info_t eaton_marlin; extern mib2nut_info_t pulizzi_switched1; extern mib2nut_info_t pulizzi_switched2; #endif /* EATON_MIB_H */ nut-2.7.4/drivers/oneac.h0000644000175000017500000001470512640443572012205 00000000000000/* oneac.h - Driver for Oneac UPS using the Advanced Interface. * * Copyright (C) * 2003 by Eric Lawson * 2012 by Bill Elliot * * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * * 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 * */ /*misc stuff*/ #define ENDCHAR '\r' #define IGNCHARS "\n" #define COMMAND_END "\r\n" #define DEFAULT_BAT_TEST_TIME "02" /*Information requests -- EG level */ #define GET_ALL '%' #define GETALL_EG_RESP_SIZE 22 #define GETALL_RESP_SIZE 48 #define GET_MFR 'M' #define GET_FAMILY 'F' #define GET_VERSION 'N' #define GET_ON_INVERTER 'G' #define GET_BATLOW 'K' #define GET_STATUS 'X' #define GET_LAST_XFER 'W' #define GET_INVERTER_RDY 'I' #define GET_SHUTDOWN 'O' #define GET_TEST_TIME 'Q' #define GET_NOM_FREQ 'H' /*Information requests -- ON level (EG plus these) */ #define GET_NOM_VOLTAGE 'V' #define GET_DISPLAY_CODE 'D' #define GET_CONDITION_CODE 'C' #define GET_PERCENT_LOAD 'P' #define GET_PERCENT_BAT_REM 'T' #define GET_INPUT_LINE_VOLT 'L' #define GET_MIN_INPUT_VOLT 'A' #define GET_MAX_INPUT_VOLT 'E' #define GET_OUTPUT_VOLT 'S' #define GET_BOOSTING 'B' #define GET_SHUTDOWN_RESP_SIZE 3 /*Information requests -- OZ/OB level (ON plus these) */ #define GETX_ALL_1 '&' #define GETX_ALL1_RESP_SIZE 79 #define GETX_OUTSOURCE 'a' #define GETX_FRONTDISP 'b' #define GETX_INT_TEMP 'g' /* Degrees C */ #define GETX_BATT_STAT 'h' /* Unknown(1), Normal, Low, Depleted */ #define GETX_BATT_CHG_PERC 'i' /* 0 - 100 */ #define GETX_EST_MIN_REM 'j' #define GETX_ONBATT_TIME 'k' /* In seconds */ #define GETX_BATT_VOLTS 'l' /* Read as xxx.x */ #define GETX_INP_FREQ 'p' #define GETX_MIN_IN_VOLTS 'q' #define GETX_MAX_IN_VOLTS 'r' #define GETX_IN_VOLTS 's' #define GETX_IN_WATTS 'u' #define GETX_OUT_VOLTS 'v' #define GETX_OUT_WATTS 'x' #define GETX_OUT_LOAD_PERC 'y' #define GETX_OUT_FREQ 'z' #define GETX_ALL_2 '^' #define GETX_ALL2_RESP_SIZE 92 #define GETX_MODEL 'J' #define GETX_FW_REV 'U' /* Read as xx.x */ #define GETX_SERIAL_NUM 'Y' #define GETX_MAN_DATE '$' /* yymmdd */ #define GETX_BATT_REPLACED '+' /* yymmdd */ #define GETX_DATE_RESP_SIZE 6 /* FIXME: Both of the following constants are unused, and the first is not * valid C syntax (breaks LLVM). */ #if 0 #define GETX_UNIT_KVA '''' /* Read as xxx.xx */ #define GETX_UNIT_WATTS "''" /* 2-character string request */ #endif #define GETX_LOW_OUT_ALLOW '[' /* Tap up or inverter at this point */ #define GETX_HI_OUT_ALLOW ']' /* Tap down or inverter at this point */ #define GETX_NOTIFY_DELAY ',' /* Secs of delay for power fail alert */ #define GETX_ALLOW_RESP_SIZE 3 #define GETX_LOW_BATT_TIME '"' /* Low batt alarm at xx minutes */ #define GETX_RESTART_DLY '_' /* Restart delay */ #define GETX_RESTART_COUNT "_?" /* Returns actual counter value */ #define GETX_RSTRT_RESP_SIZE 4 /*Other requests */ #define GETX_SHUTDOWN '}' /* Shutdown counter (..... for none) */ #define GETX_SHUTDOWN_RESP_SIZE 5 #define GETX_BATT_TEST_DAYS "\x02\x1A" /* Days between battery tests */ #define GETX_BUZZER_WHAT "\x07?" /* What is buzzer state */ #define GETX_AUTO_START " 999 on OZ */ #define MIN_ALLOW_FW "1.9" /* At or above provides output allow range */ /*responses*/ #define FAMILY_EG "EG" /* 3 tri-color LEDs and big ON/OFF on front */ #define FAMILY_ON "ON" /* Serial port avail only on interface card */ #define FAMILY_OZ "OZ" /* DB-25 std., plus interface slot */ #define FAMILY_OB "OB" /* Lg. cab with removable modules */ #define FAMILY_SIZE 2 #define YES 'Y' #define NO 'N' #define V230AC '2' #define V120AC '1' #define XFER_BLACKOUT 'B' #define XFER_LOW_VOLT 'L' #define XFER_HI_VOLT 'H' #define BUZZER_ENABLED '1' #define BUZZER_DISABLED '0' #define BUZZER_MUTED '2' /*front panel alarm codes*/ #define CODE_BREAKER_OPEN "c1" /*input circuit breaker open*/ #define CODE_BAT_FUSE_OPEN "c2" /*battery not connected. Open fuse?*/ #define CODE_TOO_HOT "c3" /*UPS too hot*/ #define CODE_CHARGING "c4" /*recharging battery pack*/ #define CODE_LOW_BAT_CAP "c5" /*batteries getting too old*/ #define CODE_OVERLOAD "c8" /*"slight" overload*/ #define CODE_GROSS_OVLE "c9" /*gross overload 1 minute to power off*/ #define CODE_CHRGR_FUSE_OPEN "u1" /*battery charger fuse probably open*/ nut-2.7.4/drivers/libshut.h0000644000175000017500000000676312640473702012575 00000000000000/*! * @file libshut.h * @brief HID Library - Generic serial SHUT backend for Generic HID Access (using MGE HIDParser) * SHUT stands for Serial HID UPS Transfer, and was created by MGE UPS SYSTEMS * * @author Copyright (C) 2006 - 2007 * Arnaud Quette * * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com * * 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. * * -------------------------------------------------------------------------- */ #ifndef LIBSHUT_H #define LIBSHUT_H #include "main.h" /* for subdrv_info_t */ #include "nut_stdint.h" /* for uint16_t */ extern upsdrv_info_t comm_upsdrv_info; /*! * SHUTDevice_t: Describe a SHUT device. This structure contains exactly * the 5 pieces of information by which a SHUT device identifies * itself, so it serves as a kind of "fingerprint" of the device. This * information must be matched exactly when reopening a device, and * therefore must not be "improved" or updated by a client * program. Vendor, Product, and Serial can be NULL if the * corresponding string did not exist or could not be retrieved. */ typedef struct SHUTDevice_s { uint16_t VendorID; /*!< Device's Vendor ID */ uint16_t ProductID; /*!< Device's Product ID */ char* Vendor; /*!< Device's Vendor Name */ char* Product; /*!< Device's Product Name */ char* Serial; /*!< Product serial number */ char* Bus; /*!< Bus name, e.g. "003" */ uint16_t bcdDevice; /*!< Device release number */ } SHUTDevice_t; /*! * shut_communication_subdriver_s: structure to describe the communication routines */ typedef struct shut_communication_subdriver_s { const char *name; /* name of this subdriver */ const char *version; /* version of this subdriver */ int (*open)(int *upsfd, /* try to open the next available */ SHUTDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ char *device_path, int (*callback)(int upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)); void (*close)(int upsfd); int (*get_report)(int upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ); int (*set_report)(int upsfd, int ReportId, unsigned char *raw_buf, int ReportSize ); int (*get_string)(int upsfd, int StringIdx, char *buf, size_t buflen); int (*get_interrupt)(int upsfd, unsigned char *buf, int bufsize, int timeout); } shut_communication_subdriver_t; extern shut_communication_subdriver_t shut_subdriver; /*! * Notification levels * These are however not processed currently */ #define OFF_NOTIFICATION 1 /* notification off */ #define LIGHT_NOTIFICATION 2 /* light notification */ #define COMPLETE_NOTIFICATION 3 /* complete notification for UPSs which do */ /* not support disabling it like some early */ /* Ellipse models */ #define DEFAULT_NOTIFICATION COMPLETE_NOTIFICATION #endif /* LIBSHUT_H */ nut-2.7.4/drivers/tripplite-hid.h0000644000175000017500000000221212640443572013664 00000000000000/* tripplite-hid.h - data to monitor Tripp Lite USB/HID devices with NUT * * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 Peter Selinger * * Sponsored by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef TRIPPLITE_HID_H #define TRIPPLITE_HID_H #include "usbhid-ups.h" extern subdriver_t tripplite_subdriver; #endif /* TRIPPLITE_HID_H */ nut-2.7.4/drivers/compaq-mib.c0000644000175000017500000004266412667537407013157 00000000000000/* compaq-mib.c - data to monitor SNMP UPS with NUT * * Copyright (C) * 2002-2012 Arnaud Quette * 2002-2006 Niels Baggesen * 2002-2006 Philip Ward * * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * * This version has been tested using: * HP R5500XR UPS with management card AF401A and a single phase input * HP R/T3000 UPS with management card AF465A and a single phase input * * 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 "compaq-mib.h" #define CPQPOWER_MIB_VERSION "1.6" #define DEFAULT_ONDELAY 30 #define DEFAULT_OFFDELAY 20 #define STR_DEFAULT_ONDELAY "30" #define STR_DEFAULT_OFFDELAY "20" /* Note: RFC-1628 (UPS MIB) is also supported on these devices! */ /* SNMP OIDs set */ #define CPQPOWER_OID_UPS_MIB ".1.3.6.1.4.1.232.165.3" /* FIXME: to be verified */ #define CPQPOWER_SYSOID CPQPOWER_OID_UPS_MIB #define CPQPOWER_OID_MFR_NAME ".1.3.6.1.4.1.232.165.3.1.1.0" /* UPS-MIB::upsIdentManufacturer */ #define CPQPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.232.165.3.1.2.0" /* UPS-MIB::upsIdentModel */ #define CPQPOWER_OID_FIRMREV ".1.3.6.1.4.1.232.165.3.1.3.0" /* UPS-MIB::upsIdentUPSSoftwareVersion */ #define CPQPOWER_OID_OEMCODE ".1.3.6.1.4.1.232.165.3.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion */ #define CPQPOWER_OID_BATT_RUNTIME ".1.3.6.1.4.1.232.165.3.2.1.0" /* UPS-MIB::upsEstimatedMinutesRemaining */ #define CPQPOWER_OID_BATT_VOLTAGE ".1.3.6.1.4.1.232.165.3.2.2.0" /* UPS-MIB::upsBatteryVoltage */ #define CPQPOWER_OID_BATT_CURRENT ".1.3.6.1.4.1.232.165.3.2.3.0" /* UPS-MIB::upsBatteryCurrent */ #define CPQPOWER_OID_BATT_CHARGE ".1.3.6.1.4.1.232.165.3.2.4.0" /* UPS-MIB::upsBattCapacity */ #define CPQPOWER_OID_BATT_STATUS ".1.3.6.1.4.1.232.165.3.2.5.0" /* UPS-MIB::upsBatteryAbmStatus */ #define CPQPOWER_OID_IN_FREQ ".1.3.6.1.4.1.232.165.3.3.1.0" /* UPS-MIB::upsInputFrequency */ #define CPQPOWER_OID_IN_LINEBADS ".1.3.6.1.4.1.232.165.3.3.2.0" /* UPS-MIB::upsInputLineBads */ #define CPQPOWER_OID_IN_LINES ".1.3.6.1.4.1.232.165.3.3.3.0" /* UPS-MIB::upsInputNumPhases */ #define CPQPOWER_OID_IN_PHASE ".1.3.6.1.4.1.232.165.3.3.4.1.1" /* UPS-MIB::upsInputPhase */ #define CPQPOWER_OID_IN_VOLTAGE ".1.3.6.1.4.1.232.165.3.3.4.1.2" /* UPS-MIB::upsInputVoltage */ #define CPQPOWER_OID_IN_CURRENT ".1.3.6.1.4.1.232.165.3.3.4.1.3" /* UPS-MIB::upsInputCurrent */ #define CPQPOWER_OID_IN_POWER ".1.3.6.1.4.1.232.165.3.3.4.1.4" /* UPS-MIB::upsInputWatts */ #define CPQPOWER_OID_LOAD_LEVEL ".1.3.6.1.4.1.232.165.3.4.1.0" /* UPS-MIB::upsOutputLoad */ #define CPQPOWER_OID_OUT_FREQUENCY ".1.3.6.1.4.1.232.165.3.4.2.0" /* UPS-MIB::upsOutputFrequency */ #define CPQPOWER_OID_OUT_LINES ".1.3.6.1.4.1.232.165.3.4.3.0" /* UPS-MIB::upsOutputNumPhases */ #define CPQPOWER_OID_OUT_PHASE ".1.3.6.1.4.1.232.165.3.4.4.1.1" /* UPS-MIB::upsOutputPhase */ #define CPQPOWER_OID_OUT_VOLTAGE ".1.3.6.1.4.1.232.165.3.4.4.1.2" /* UPS-MIB::upsOutputVoltage */ #define CPQPOWER_OID_OUT_CURRENT ".1.3.6.1.4.1.232.165.3.4.4.1.3" /* UPS-MIB::upsOutputCurrent */ #define CPQPOWER_OID_OUT_POWER ".1.3.6.1.4.1.232.165.3.4.4.1.4" /* UPS-MIB::upsOutputWatts */ #define CPQPOWER_OID_POWER_STATUS ".1.3.6.1.4.1.232.165.3.4.5.0" /* UPS-MIB::upsOutputSource */ #define CPQPOWER_OID_AMBIENT_TEMP ".1.3.6.1.4.1.232.165.3.6.1.0" /* UPS-MIB::upsEnvAmbientTemp */ #define CPQPOWER_OID_UPS_TEST_BATT ".1.3.6.1.4.1.232.165.3.7.1.0" /* UPS-MIB::upsTestBattery */ #define CPQPOWER_OID_UPS_TEST_RES ".1.3.6.1.4.1.232.165.3.7.2.0" /* UPS-MIB::upsTestBatteryStatus */ #define CPQPOWER_OID_ALARM_OB ".1.3.6.1.4.1.232.165.3.7.3.0" /* UPS-MIB::upsOnBattery */ #define CPQPOWER_OID_ALARM_LB ".1.3.6.1.4.1.232.165.3.7.4.0" /* UPS-MIB::upsLowBattery */ /* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118) static info_lkp_t cpqpower_alarm_ob[] = { { 1, "OB" }, { 0, NULL } }; */ /* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118) static info_lkp_t cpqpower_alarm_lb[] = { { 1, "LB" }, { 0, NULL } }; */ /* Defines for CPQPOWER_OID_POWER_STATUS (1) */ static info_lkp_t cpqpower_pwr_info[] = { { 1, "" /* other */ }, { 2, "OFF" /* none */ }, { 3, "OL" /* normal */ }, { 4, "OL BYPASS" /* bypass */ }, { 5, "OB" /* battery */ }, { 6, "OL BOOST" /* booster */ }, { 7, "OL TRIM" /* reducer */ }, { 8, "OL" /* parallelCapacity */ }, { 9, "OL" /* parallelRedundant */ }, { 10, "OL" /* HighEfficiencyMode */ }, { 0, NULL } } ; static info_lkp_t cpqpower_mode_info[] = { { 1, "" }, { 2, "" }, { 3, "normal" }, { 4, "" }, { 5, "" }, { 6, "" }, { 7, "" }, { 8, "parallel capacity" }, { 9, "parallel redundancy" }, {10, "high efficiency" }, { 0, NULL } }; static info_lkp_t cpqpower_battery_abm_status[] = { { 1, "CHRG" }, { 2, "DISCHRG" }, /* { 3, "Floating" }, */ /* { 4, "Resting" }, */ /* { 5, "Unknown" }, */ { 0, NULL } } ; /* Defines for CPQPOWER_OID_UPS_TEST_RES */ static info_lkp_t cpqpower_test_res_info[] = { { 1, "Unknown" }, { 2, "Done and passed" }, { 3, "Done and error" }, { 4, "In progress" }, { 5, "Not supported" }, { 6, "Inhibited" }, { 7, "Scheduled" }, { 0, NULL } } ; #define CPQPOWER_START_TEST 1 static info_lkp_t cpqpower_outlet_status_info[] = { { 1, "on" }, { 2, "off" }, { 3, "pendingOff" }, /* transitional status */ { 4, "pendingOn" }, /* transitional status */ { 5, "unknown" }, { 0, NULL } }; /* Ugly hack: having the matching OID present means that the outlet is * switchable. So, it should not require this value lookup */ static info_lkp_t cpqpower_outlet_switchability_info[] = { { 1, "yes" }, { 2, "yes" }, { 3, "yes" }, { 4, "yes" }, { 0, NULL } }; #define CPQPOWER_OID_SD_AFTER_DELAY ".1.3.6.1.4.1.232.165.3.8.1.0" /* UPS-MIB::upsControlOutputOffDelay */ #define CPQPOWER_OFF_DO 0 /* Snmp2NUT lookup table */ static snmp_info_t cpqpower_mib[] = { /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MFR_NAME, "HP/Compaq", SU_FLAG_STATIC, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MODEL_NAME, "SNMP UPS", SU_FLAG_STATIC, NULL }, /* { "ups.model.aux", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_OEMCODE, "", SU_FLAG_STATIC, NULL },*/ { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.1.2.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* FIXME: split between firmware and firmware.aux ("00.01.0019;00.01.0004") * UPS Firmware Revision : 00.01.0004 * Communication Board Firmware Revision : 00.01.0019 */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_FIRMREV, "", SU_FLAG_STATIC, NULL }, { "ups.load", 0, 1.0, CPQPOWER_OID_LOAD_LEVEL, "", 0, NULL }, { "ups.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER, "", SU_OUTPUT_1, NULL }, { "ups.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.3.9.3.0", "", SU_OUTPUT_1, NULL }, { "ups.L1.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".1", "", SU_OUTPUT_3, NULL }, { "ups.L2.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".2", "", SU_OUTPUT_3, NULL }, { "ups.L3.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".3", "", SU_OUTPUT_3, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, cpqpower_pwr_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_BATT_STATUS, "", SU_STATUS_PWR, cpqpower_battery_abm_status }, /* The next two lines are no longer supported by MIB ver. 1.76 (Github issue 118) * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_OB, "", SU_STATUS_BATT, cpqpower_alarm_ob }, * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_LB, "", SU_STATUS_BATT, cpqpower_alarm_lb }, */ /* { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_BATT_STATUS, "", SU_STATUS_BATT, ietf_batt_info }, */ /* FIXME: this should use either .1.3.6.1.4.1.232.165.3.11.1.0 (upsTopologyType) * or .1.3.6.1.4.1.232.165.3.11.2.0 (upsTopoMachineCode) */ { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "", SU_STATUS_PWR, cpqpower_mode_info }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_UPS_TEST_RES, "", 0, cpqpower_test_res_info }, /* FIXME: handle ups.date and ups.time * - OID: .1.3.6.1.4.1.232.165.3.9.5.0 * - format MM/DD/YYYY HH:MM:SS */ /* FIXME: handle upsInputSource.0 (".1.3.6.1.4.1.232.165.3.3.5.0") * other(1) * none(2) * primaryUtility(3) * bypassFeed(4) * secondaryUtility(5) * generator(6) * flywheel(7) * fuelcell(8) */ { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.1.0", STR_DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.2.0", STR_DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.timer.shutdown", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_FLAG_OK, NULL }, { "ups.timer.start", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "", SU_FLAG_OK, NULL }, /* Ambient page */ { "ambient.temperature", 0, 1.0, CPQPOWER_OID_AMBIENT_TEMP, "", 0, NULL }, { "ambient.temperature.low", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.2.0", "", 0, NULL }, { "ambient.temperature.high", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.3.0", "", 0, NULL }, /* Battery page */ { "battery.charge", 0, 1.0, CPQPOWER_OID_BATT_CHARGE, "", 0, NULL }, { "battery.runtime", 0, 1.0, CPQPOWER_OID_BATT_RUNTIME, "", 0, NULL }, { "battery.voltage", 0, 0.1, CPQPOWER_OID_BATT_VOLTAGE, "", 0, NULL }, { "battery.current", 0, 0.1, CPQPOWER_OID_BATT_CURRENT, "", 0, NULL }, /* FIXME: need the new variable (for ABM) { "battery.status", 0, 0.1, ".1.3.6.1.4.1.232.165.3.2.5.0", "", 0, NULL }, */ /* Input page */ { "input.phases", 0, 1.0, CPQPOWER_OID_IN_LINES, "", SU_FLAG_SETINT, NULL, &input_phases }, /* { "input.phase", 0, 1.0, CPQPOWER_OID_IN_PHASE, "", SU_OUTPUT_1, NULL }, */ { "input.frequency", 0, 0.1, CPQPOWER_OID_IN_FREQ , "", 0, NULL }, { "input.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE, "", SU_OUTPUT_1, NULL }, { "input.voltage", 0, 1.0, ".1.3.6.1.4.1.232.165.3.3.4.1.2.1", "", SU_OUTPUT_1, NULL }, { "input.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.2.0", "", SU_OUTPUT_1, NULL }, { "input.L1-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".3", "", SU_INPUT_3, NULL }, { "input.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT, "", SU_OUTPUT_1, NULL }, { "input.current", 0, 0.1, ".1.3.6.1.4.1.232.165.3.3.4.1.3.1", "", SU_OUTPUT_1, NULL }, { "input.L1.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".1", "", SU_INPUT_3, NULL }, { "input.L2.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".2", "", SU_INPUT_3, NULL }, { "input.L3.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".3", "", SU_INPUT_3, NULL }, { "input.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER, "", SU_OUTPUT_1, NULL }, { "input.L1.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER ".1", "", SU_INPUT_3, NULL }, { "input.L2.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER ".2", "", SU_INPUT_3, NULL }, { "input.L3.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER ".3", "", SU_INPUT_3, NULL }, { "input.quality", 0, 1.0, CPQPOWER_OID_IN_LINEBADS, "", 0, NULL }, /* Output page */ { "output.phases", 0, 1.0, CPQPOWER_OID_OUT_LINES, "", SU_FLAG_SETINT, NULL, &output_phases }, /* { "output.phase", 0, 1.0, CPQPOWER_OID_OUT_PHASE, "", SU_OUTPUT_1, NULL }, */ { "output.frequency", 0, 0.1, CPQPOWER_OID_OUT_FREQUENCY, "", 0, NULL }, /* FIXME: handle multiplier (0.1 there) */ { "output.frequency.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.4.0", "", SU_OUTPUT_1, NULL }, { "output.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE, "", SU_OUTPUT_1, NULL }, { "output.voltage", 0, 1.0, ".1.3.6.1.4.1.232.165.3.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, { "output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.1.0", "", SU_OUTPUT_1, NULL }, { "output.L1-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".2", "", SU_OUTPUT_3, NULL }, { "output.L3-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".3", "", SU_OUTPUT_3, NULL }, { "output.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT, "", SU_OUTPUT_1, NULL }, { "output.current", 0, 0.1, ".1.3.6.1.4.1.232.165.3.4.4.1.3.1", "", SU_OUTPUT_1, NULL }, /* { "output.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.3.4.4.1.4", "", SU_OUTPUT_1, NULL }, */ { "output.L1.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".1", "", SU_OUTPUT_3, NULL }, { "output.L2.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".2", "", SU_OUTPUT_3, NULL }, { "output.L3.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".3", "", SU_OUTPUT_3, NULL }, /* FIXME: what to map with these? * Name/OID: upsConfigLowOutputVoltageLimit.0; Value (Integer): 160 * => input.transfer.low? * Name/OID: upsConfigHighOutputVoltageLimit.0; Value (Integer): 288 * => input.transfer.high? */ /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.count", 0, 1, ".1.3.6.1.4.1.232.165.3.10.1.0", "0", 0, NULL }, /* upsNumReceptacles */ /* { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL, NULL }, { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL, NULL }, { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL, NULL }, { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL, NULL }, */ /* outlet template definition */ /* FIXME always true? */ { "outlet.%i.switchable", ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &cpqpower_outlet_switchability_info[0], NULL }, { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, /* { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL, NULL }, */ { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.3.10.2.1.2.%i", NULL, SU_FLAG_OK | SU_OUTLET, &cpqpower_outlet_status_info[0], NULL }, /* FIXME: come up with a suitable varname! * - The delay after going On Battery until the Receptacle is automatically turned Off. * A value of -1 means that this Output should never be turned Off automatically, but must be turned Off only by command. * { "outlet.%i.autoswitch.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.10.2.1.5.%i", STR_DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, // upsRecepAutoOffDelay * - Seconds delay after the Outlet is signaled to turn On before the Output is Automatically turned ON. * A value of -1 means that this Output should never be turned On automatically, but only when specifically commanded to do so. * { "outlet.%i.autoswitch.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.10.2.1.5.%i", STR_DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, // upsRecepAutoOnDelay */ /* FIXME: also define .stop (as for 'shutdown.reboot') * and .delay */ { "outlet.%i.load.off", 0, 0, ".1.3.6.1.4.1.232.165.3.10.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.on", 0, 0, ".1.3.6.1.4.1.232.165.3.10.2.1.4.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, /* FIXME: also define a .delay or map to "outlet.%i.delay.shutdown" */ { "outlet.%i.load.cycle", 0, 0, ".1.3.6.1.4.1.232.165.3.10.2.1.7.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, /* instant commands. */ /* We need to duplicate load.{on,off} Vs load.{on,off}.delay, since * "0" cancels the shutdown, so we put "1" (second) for immediate off! */ { "load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_TYPE_CMD, NULL }, { "load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "", SU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* FIXME: need ups.{timer,delay}.{start,shutdown} param counterparts! */ { "load.off.delay", 0, DEFAULT_OFFDELAY, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_TYPE_CMD, NULL }, { "load.on.delay", 0, DEFAULT_ONDELAY, ".1.3.6.1.4.1.232.165.3.8.2.0", "", SU_TYPE_CMD, NULL }, /* { CMD_SHUTDOWN, 0, CPQPOWER_OFF_GRACEFUL, CPQPOWER_OID_OFF, "", 0, NULL }, */ { "shutdown.reboot", 0, 0, ".1.3.6.1.4.1.232.165.3.8.6.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "test.battery.start", 0, CPQPOWER_START_TEST, ".1.3.6.1.4.1.232.165.3.7.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t compaq = { "cpqpower", CPQPOWER_MIB_VERSION, NULL, CPQPOWER_OID_MFR_NAME, cpqpower_mib, CPQPOWER_SYSOID }; nut-2.7.4/drivers/masterguard.c0000644000175000017500000004013012640443572013420 00000000000000/* masterguard.c - support for Masterguard models Copyright (C) 2001 Michael Spanier masterguard.c created on 15.8.2001 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 */ /******************************************************************** * * Please if you edit this code convert tabs to spaces and use * four characters indent. * If you don't know what this means use the vim editor. * * Have fun * Michael * ********************************************************************/ #include "main.h" #include "serial.h" #define DRIVER_NAME "MASTERGUARD UPS driver" #define DRIVER_VERSION "0.24" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Michael Spanier ", DRV_STABLE, { NULL } }; #define UPSDELAY 3 #define MAXTRIES 10 #define UPS_PACE 100 /* 100 us between chars on write */ #define Q1 1 #define Q3 2 #define DEBUG 1 int type; char name[31]; char firmware[6]; /******************************************************************** * * Helper function to split a sting into words by splitting at the * SPACE character. * * Adds up to maxlen characters to the char word. * Returns NULL on reaching the end of the string. * ********************************************************************/ static char *StringSplit( char *source, char *word, int maxlen ) { int i; int len; int wc=0; word[0] = '\0'; len = strlen( source ); for( i = 0; i < len; i++ ) { if( source[i] == ' ' ) { word[wc] = '\0'; return source + i + 1; } word[wc] = source[i]; wc++; } word[wc] = '\0'; return NULL; } /******************************************************************** * * Helper function to drop all whitespaces within a string. * * "word" must be large enought to hold "source", for the worst case * "word" has to be exacly the size of "source". * ********************************************************************/ static void StringStrip( char *source, char *word ) { int wc=0; int i; int len; word[0] = '\0'; len = strlen( source ); for( i = 0; i < len; i++ ) { if( source[i] == ' ' ) continue; if( source[i] == '\n' ) continue; if( source[i] == '\t' ) continue; word[wc] = source[i]; wc++; } word[wc] = '\0'; } /******************************************************************** * * Function parses the status flags which occure in the Q1 and Q3 * command. Sets the INFO_STATUS value ( OL, OB, ... ) * ********************************************************************/ static void parseFlags( char *flags ) { status_init(); if( flags[0] == '1' ) status_set("OB"); else status_set("OL"); if( flags[1] == '1' ) status_set("LB"); if( flags[2] == '1' ) status_set("BOOST"); /* this has no mapping */ #if 0 if( flags[3] == '1' ) setinfo( INFO_ALRM_GENERAL, "1" ); #endif #if 0 /* and these are... ? */ if( flags[5] == '1' ) status_set("TIP"); if( flags[6] == '1' ) status_set("SD"); #endif status_commit(); if( DEBUG ) printf( "Status is %s\n", dstate_getinfo("ups.status")); } /******************************************************************** * * Function parses the response of the query1 ( "Q1" ) command. * Also sets various values (IPFreq ... ) * ********************************************************************/ static void query1( char *buf ) { #define WORDMAXLEN 255 char value[WORDMAXLEN]; char word[WORDMAXLEN]; char *newPOS; char *oldPOS; int count = 0; if( DEBUG ) printf( "Q1 Buffer is : %s\n" , buf + 1 ); oldPOS = buf + 1; newPOS = oldPOS; do { newPOS = StringSplit( oldPOS, word, WORDMAXLEN ); StringStrip( word, value); oldPOS = newPOS; if( DEBUG ) { printf( "value=%s\n", value ); fflush( stdout ); } switch( count ) { case 0: /* IP Voltage */ dstate_setinfo("input.voltage", "%s", value ); break; case 1: /* IP Fault Voltage */ break; case 2: /* OP Voltage */ dstate_setinfo("output.voltage", "%s", value); break; case 3: /* OP Load*/ dstate_setinfo("ups.load", "%s", value ); break; case 4: /* IP Frequency */ dstate_setinfo("input.frequency", "%s", value); break; case 5: /* Battery Cell Voltage */ dstate_setinfo("battery.voltage", "%s", value); break; case 6: /* UPS Temperature */ dstate_setinfo("ups.temperature", "%s", value ); break; case 7: /* Flags */ parseFlags( value ); break; default: /* Should never be reached */ break; } count ++; oldPOS = newPOS; } while( newPOS != NULL ); } /******************************************************************** * * Function parses the response of the query3 ( "Q3" ) command. * Also sets various values (IPFreq ... ) * ********************************************************************/ static void query3( char *buf ) { #define WORDMAXLEN 255 char value[WORDMAXLEN]; char word[WORDMAXLEN]; char *newPOS; char *oldPOS; int count = 0; if( DEBUG ) printf( "Q3 Buffer is : %s\n" , buf+1 ); oldPOS = buf + 1; newPOS = oldPOS; do { newPOS = StringSplit( oldPOS, word, WORDMAXLEN ); StringStrip( word, value); oldPOS = newPOS; /* Shortcut */ if( newPOS == NULL ) break; if( DEBUG ) { printf( "value=%s\n", value ); fflush( stdout ); } switch( count ) { case 0: /* UPS ID */ break; case 1: /* Input Voltage */ dstate_setinfo("input.voltage", "%s", value ); break; case 2: /* Input Fault Voltage */ break; case 3: /* Output Voltage */ dstate_setinfo("output.voltage", "%s", value); break; case 4: /* Output Current */ dstate_setinfo("output.current", "%s", value ); break; case 5: /* Input Frequency */ dstate_setinfo("input.frequency", "%s", value); break; case 6: /* Battery Cell Voltage */ dstate_setinfo("battery.voltage", "%s", value); break; case 7: /* Temperature */ dstate_setinfo("ups.temperature", "%s", value ); break; case 8: /* Estimated Runtime */ dstate_setinfo("battery.runtime", "%s", value); break; case 9: /* Charge Status */ dstate_setinfo("battery.charge", "%s", value); break; case 10: /* Flags */ parseFlags( value ); break; case 11: /* Flags2 */ break; default: /* This should never be reached */ /* printf( "DEFAULT\n" ); */ break; } count ++; oldPOS = newPOS; } while( newPOS != NULL ); } /******************************************************************** * * Function to parse the WhoAmI response of the UPS. Also sets the * values of the firmware version and the UPS identification. * ********************************************************************/ static void parseWH( char *buf ) { strncpy( name, buf + 16, 30 ); name[30] = '\0'; strncpy( firmware, buf + 4, 5 ); firmware[5] = '\0'; if( DEBUG ) printf( "Name = %s, Firmware Version = %s\n", name, firmware ); } /******************************************************************** * * Function to parse the old and possible broken WhoAmI response * and set the values for the firmware Version and the identification * of the UPS. * ********************************************************************/ static void parseOldWH( char *buf ) { strncpy( name, buf + 4, 12 ); name[12] = '\0'; strncpy( firmware, buf, 4 ); firmware[4] = '\0'; if( DEBUG ) printf( "Name = %s, Firmware Version = %s\n", name, firmware ); } /******************************************************************** * * Function to fake a WhoAmI response of a UPS that returns NAK. * ********************************************************************/ static void fakeWH(void) { strcpy( name, "GenericUPS" ); strcpy( firmware, "unkn" ); if( DEBUG ) printf( "Name = %s, Firmware Version = %s\n", name, firmware ); } static int ups_ident( void ) { char buf[255]; int ret; /* Check presence of Q1 */ ret = ser_send_pace(upsfd, UPS_PACE, "%s", "Q1\x0D" ); ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); ret = strlen( buf ); if( ret != 46 ) { /* No Q1 response found */ type = 0; return -1; } else { if( DEBUG ) printf( "Found Q1\n" ); type = Q1; } /* Check presence of Q3 */ ret = ser_send_pace(upsfd, UPS_PACE, "%s", "Q3\x0D" ); ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); ret = strlen( buf ); if( ret == 70 ) { if( DEBUG ) printf( "Found Q3\n" ); type = Q1 | Q3; } /* Check presence of WH ( Who am I ) */ ret = ser_send_pace(upsfd, UPS_PACE, "%s", "WH\x0D" ); ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); ret = strlen( buf ); if( ret == 112 ) { if( DEBUG ) printf( "WH found\n" ); parseWH( buf ); } else if( ret == 53 ) { if( DEBUG ) printf( "Old (broken) WH found\n" ); parseOldWH( buf ); } else if( ret == 3 && strcmp(buf, "NAK") == 0 ) { if( DEBUG ) printf( "WH was NAKed\n" ); fakeWH( ); } else if( ret > 0 ) { if( DEBUG ) printf( "WH says <%s> with length %i\n", buf, ret ); upslog_with_errno( LOG_INFO, "New WH String found. Please report to maintainer\n" ); } return 1; } /******************************************************************** * * * * ********************************************************************/ void upsdrv_help( void ) { } /******************************************************************** * * Function to initialize the fields of the ups driver. * ********************************************************************/ void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "MASTERGUARD"); dstate_setinfo("ups.model", "unknown"); /* dstate_addcmd("test.battery.stop"); dstate_addcmd("test.battery.start"); */ if( strlen( name ) > 0 ) dstate_setinfo("ups.model", "%s", name); if( strlen( firmware ) > 0 ) dstate_setinfo("ups.firmware", "%s", firmware); } /******************************************************************** * * This is the main function. It gets called if the driver wants * to update the ups status and the informations. * ********************************************************************/ void upsdrv_updateinfo(void) { char buf[255]; int ret; int lenRSP=0; if( DEBUG ) printf( "update info\n" ); /* Q3 found ? */ if( type & Q3 ) { ser_send_pace(upsfd, UPS_PACE, "%s", "Q3\x0D" ); lenRSP = 70; } /* Q1 found ? */ else if( type & Q1 ) { ser_send_pace(upsfd, UPS_PACE, "%s", "Q1\x0D" ); lenRSP = 46; } /* Should never be reached */ else { fatalx(EXIT_FAILURE, "Error, no Query mode defined. Please file bug against driver."); } sleep( UPSDELAY ); buf[0] = '\0'; ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); ret = strlen( buf ); if( ret != lenRSP ) { if( DEBUG ) printf( "buf = %s len = %i\n", buf, ret ); upslog_with_errno( LOG_ERR, "Error in UPS response " ); dstate_datastale(); return; } /* Parse the response from the UPS */ if( type & Q3 ) { query3( buf ); dstate_dataok(); return; } if( type & Q1 ) { query1( buf ); dstate_dataok(); return; } } /******************************************************************** * * Called if the driver wants to shutdown the UPS. * ( also used by the "-k" command line switch ) * * This cuts the utility from the UPS after 20 seconds and restores * the utility one minute _after_ the utility to the UPS has restored * ********************************************************************/ void upsdrv_shutdown(void) { /* ups will come up within a minute if utility is restored */ ser_send_pace(upsfd, UPS_PACE, "%s", "S.2R0001\x0D" ); } /******************************************************************** * * Populate the command line switches. * * CS: Cancel the shutdown process * ********************************************************************/ void upsdrv_makevartable(void) { addvar( VAR_FLAG, "CS", "Cancel Shutdown" ); } /******************************************************************** * * This is the first function called by the UPS driver. * Detects the UPS and handles the command line args. * ********************************************************************/ void upsdrv_initups(void) { int count = 0; int fail = 0; int good = 0; /* setup serial port */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); name[0] = '\0'; firmware[0] = '\0'; /* probe ups type */ do { count++; if( ups_ident( ) != 1 ) fail++; /* at least two good identifications */ if( (count - fail) == 2 ) { good = 1; break; } } while( (count * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef BLAZER_H #define BLAZER_H #define MAXTRIES 3 /* * The driver core will do all the protocol handling and provides * the following (interface independent) parts already * * upsdrv_updateinfo() * upsdrv_shutdown() * * Communication with the UPS is done through blazer_command() of which * the prototype is declared below. It shall send a command and reads * a reply if buf is not a NULL pointer and buflen > 0. * * Returns < 0 on error, 0 on timeout and the number of bytes send/read on * success. */ int blazer_command(const char *cmd, char *buf, size_t buflen); void blazer_makevartable(void); void blazer_initups(void); void blazer_initinfo(void); #endif /* BLAZER_H */ nut-2.7.4/drivers/nutdrv_qx_voltronic-qs.h0000644000175000017500000000202312640473702015656 00000000000000/* nutdrv_qx_voltronic-qs.h - Subdriver for Voltronic Power UPSes with QS protocol * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_VOLTRONIC_QS_H #define NUTDRV_QX_VOLTRONIC_QS_H #include "nutdrv_qx.h" extern subdriver_t voltronic_qs_subdriver; #endif /* NUTDRV_QX_VOLTRONIC_QS_H */ nut-2.7.4/drivers/eaton-mib.c0000644000175000017500000014175012667537407013001 00000000000000/* eaton-mib.c - data to monitor Eaton ePDUs: * G1 Aphel based ePDUs (Basic and Complex) * G1 Pulizzi Monitored and Switched ePDUs * G2 Marlin SW / MI / MO / MA * G3 Shark SW / MI / MO / MA * * Copyright (C) 2008 - 2015 * Arnaud Quette * Arnaud Quette * * Supported by Eaton * and previously MGE Office Protection Systems * * 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 "eaton-mib.h" #define EATON_APHEL_MIB_VERSION "0.47" /* APHEL-GENESIS-II-MIB (monitored ePDU) * ************************************* * Note: There is also a basic XML interface, but not worth * implementing in netxml-ups! */ #define APHEL1_OID_MIB ".1.3.6.1.4.1.17373" #define APHEL1_SYSOID APHEL1_OID_MIB #define APHEL1_OID_MODEL_NAME ".1.3.6.1.4.1.17373.3.1.1.0" #define APHEL1_OID_FIRMREV ".1.3.6.1.4.1.17373.3.1.2.0" #define APHEL1_OID_DEVICE_NAME ".1.3.6.1.4.1.17373.3.1.3.0" #define APHEL1_OID_UNIT_MACADDR ".1.3.6.1.4.1.17373.3.1.4.0" /* needs concat ..0 */ #define APHEL1_OID_OUTLET_CURRENT ".1.3.6.1.4.1.17373.3.2" /* Snmp2NUT lookup table for GenesisII MIB */ static snmp_info_t eaton_aphel_genesisII_mib[] = { /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, "Eaton Powerware ePDU Monitored", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_UNIT_MACADDR, "unknown", 0, NULL, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_DEVICE_NAME, "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_FIRMREV, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, /* Outlet page */ /* we can't use template since there is no counterpart to outlet.count */ { "outlet.1.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".1.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.2.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".2.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.3.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".3.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.4.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".4.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.5.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".5.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.6.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".6.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.7.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".7.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "outlet.8.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".8.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } }; /* APHEL PDU-MIB - Revelation MIB (Managed ePDU) * ********************************************* */ #define AR_BASE_OID ".1.3.6.1.4.1.534.6.6.6" #define APHEL2_SYSOID AR_BASE_OID #define APHEL2_OID_MODEL_NAME AR_OID_MODEL_NAME #define AR_OID_MODEL_NAME AR_BASE_OID ".1.1.12.0" #define AR_OID_DEVICE_NAME AR_BASE_OID ".1.1.13.0" #define AR_OID_FIRMREV AR_BASE_OID ".1.1.1.0" #define AR_OID_SERIAL AR_BASE_OID ".1.1.2.0" #define AR_OID_UNIT_MACADDR AR_BASE_OID ".1.1.6.0" #define AR_OID_UNIT_CURRENT AR_BASE_OID ".1.3.1.1" #define AR_OID_UNIT_VOLTAGE AR_BASE_OID ".1.3.1.2" #define AR_OID_UNIT_ACTIVEPOWER AR_BASE_OID ".1.3.1.3" #define AR_OID_UNIT_APPARENTPOWER AR_BASE_OID ".1.3.1.4" #define AR_OID_UNIT_CPUTEMPERATURE AR_BASE_OID ".1.3.1.5.0" #define AR_OID_OUTLET_INDEX AR_BASE_OID ".1.2.2.1.1" #define AR_OID_OUTLET_NAME AR_BASE_OID ".1.2.2.1.2" #define AR_OID_OUTLET_STATUS AR_BASE_OID ".1.2.2.1.3" static info_lkp_t outlet_status_info[] = { { -1, "error" }, { 0, "off" }, { 1, "on" }, { 2, "cycling" }, /* transitional status */ { 0, NULL } }; #define DO_OFF 0 #define DO_ON 1 #define DO_CYCLE 2 #define AR_OID_OUTLET_COUNT AR_BASE_OID ".1.2.1.0" #define AR_OID_OUTLET_CURRENT AR_BASE_OID ".1.2.2.1.4" #define AR_OID_OUTLET_MAXCURRENT AR_BASE_OID ".1.2.2.1.5" #define AR_OID_OUTLET_VOLTAGE AR_BASE_OID ".1.2.2.1.6" #define AR_OID_OUTLET_ACTIVEPOWER AR_BASE_OID ".1.2.2.1.7" #define AR_OID_OUTLET_APPARENTPOWER AR_BASE_OID ".1.2.2.1.8" #define AR_OID_OUTLET_POWERFACTOR AR_BASE_OID ".1.2.2.1.9" /* Snmp2NUT lookup table for Eaton Revelation MIB */ static snmp_info_t eaton_aphel_revelation_mib[] = { /* Device collection */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, "Eaton Powerware ePDU Managed", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_UNIT_MACADDR, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* UPS collection */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_FIRMREV, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.temperature", 0, 1, AR_OID_UNIT_CPUTEMPERATURE, NULL, 0, NULL, NULL }, /* Outlet collection */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.count", 0, 1, AR_OID_OUTLET_COUNT, "0", 0, NULL }, { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL, NULL }, { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL, NULL }, { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL, NULL }, { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL, NULL }, /* outlet template definition * Caution: the index of the data start at 0, while the name is +1 * ie outlet.1 => .0 */ { "outlet.%i.switchable", 0, 1, AR_OID_OUTLET_INDEX ".%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL, NULL }, { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_FLAG_OK | SU_OUTLET, &outlet_status_info[0], NULL }, { "outlet.%i.current", 0, 0.001, AR_OID_OUTLET_CURRENT ".%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.current.maximum", 0, 0.001, AR_OID_OUTLET_MAXCURRENT ".%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.realpower", 0, 1.0, AR_OID_OUTLET_ACTIVEPOWER ".%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage", 0, 1.0, AR_OID_OUTLET_VOLTAGE ".%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.powerfactor", 0, 0.01, AR_OID_OUTLET_POWERFACTOR ".%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.power", 0, 1.0, AR_OID_OUTLET_APPARENTPOWER ".%i", NULL, SU_OUTLET, NULL, NULL }, /* FIXME: * - delay for startup/shutdown sequence * - support for multiple Ambient sensors ( max. 8), starting at index '0' * ambient.%i.temperature => .1.3.6.1.4.1.534.6.6.6.2.2.1.3.%i * ambient.%i.humidity => .1.3.6.1.4.1.534.6.6.6.2.4.1.3.%i */ /* Ambient collection */ /* We use critical levels, for both temperature and humidity, * since warning levels are also available! */ { "ambient.temperature", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.2.1.3.0", NULL, SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.low", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.6.0", NULL, SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.high", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, { "ambient.humidity", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.3.0", NULL, SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.low", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.6.0", NULL, SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.high", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, /* instant commands. */ /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ /* no counterpart found! { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, */ { "outlet.%i.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } }; /* Eaton PDU-MIB - Marlin MIB * ************************** */ #define EATON_MARLIN_MIB_VERSION "0.37" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" static info_lkp_t marlin_outlet_status_info[] = { { 0, "off" }, { 1, "on" }, { 2, "pendingOff" }, /* transitional status */ { 3, "pendingOn" }, /* transitional status */ { 0, NULL } }; static info_lkp_t marlin_outletgroups_status_info[] = { { 0, "off" }, { 1, "on" }, { 2, "rebooting" }, /* transitional status */ { 3, "mixed" }, /* transitional status, not sure what it means! */ { 0, NULL } }; /* Ugly hack: having the matching OID present means that the outlet is * switchable. So, it should not require this value lookup */ static info_lkp_t outlet_switchability_info[] = { { -1, "yes" }, { 0, "yes" }, { 0, NULL } }; static info_lkp_t marlin_ambient_presence_info[] = { { -1, "unknown" }, { 0, "no" }, /* disconnected */ { 1, "yes" }, /* connected */ { 0, NULL } }; static info_lkp_t marlin_threshold_status_info[] = { { 0, "good" }, /* No threshold triggered */ { 1, "warning-low" }, /* Warning low threshold triggered */ { 2, "critical-low" }, /* Critical low threshold triggered */ { 3, "warning-high" }, /* Warning high threshold triggered */ { 4, "critical-high" }, /* Critical high threshold triggered */ { 0, NULL } }; static info_lkp_t marlin_threshold_frequency_status_info[] = { { 0, "good" }, /* No threshold triggered */ { 1, "out-of-range" }, /* Frequency out of range triggered */ { 0, NULL } }; static info_lkp_t marlin_ambient_drycontacts_info[] = { { -1, "unknown" }, { 0, "open" }, { 1, "closed" }, { 0, NULL } }; static info_lkp_t marlin_threshold_voltage_alarms_info[] = { { 0, "" }, /* No threshold triggered */ { 1, "low voltage warning!" }, /* Warning low threshold triggered */ { 2, "low voltage critical!" }, /* Critical low threshold triggered */ { 3, "high voltage warning!" }, /* Warning high threshold triggered */ { 4, "high voltage critical!" }, /* Critical high threshold triggered */ { 0, NULL } }; static info_lkp_t marlin_threshold_current_alarms_info[] = { { 0, "" }, /* No threshold triggered */ { 1, "low current warning!" }, /* Warning low threshold triggered */ { 2, "low current critical!" }, /* Critical low threshold triggered */ { 3, "high current warning!" }, /* Warning high threshold triggered */ { 4, "high current critical!" }, /* Critical high threshold triggered */ { 0, NULL } }; static info_lkp_t marlin_threshold_frequency_alarm_info[] = { { 0, "" }, /* No threshold triggered */ { 1, "frequency out of range!" }, /* Frequency out of range triggered */ { 0, NULL } }; static info_lkp_t marlin_threshold_temperature_alarms_info[] = { { 0, "" }, /* No threshold triggered */ { 1, "low temperature warning!" }, /* Warning low threshold triggered */ { 2, "low temperature critical!" }, /* Critical low threshold triggered */ { 3, "high temperature warning!" }, /* Warning high threshold triggered */ { 4, "high temperature critical!" }, /* Critical high threshold triggered */ { 0, NULL } }; static info_lkp_t marlin_threshold_humidity_alarms_info[] = { { 0, "" }, /* No threshold triggered */ { 1, "low humidity warning!" }, /* Warning low threshold triggered */ { 2, "low humidity critical!" }, /* Critical low threshold triggered */ { 3, "high humidity warning!" }, /* Warning high threshold triggered */ { 4, "high humidity critical!" }, /* Critical high threshold triggered */ { 0, NULL } }; static info_lkp_t marlin_outlet_group_type_info[] = { { 0, "unknown" }, { 1, "breaker1pole" }, { 2, "breaker2pole" }, { 3, "breaker3pole" }, { 4, "outlet-section" }, { 5, "user-defined" }, { 0, NULL } }; /* Snmp2NUT lookup table for Eaton Marlin MIB */ static snmp_info_t eaton_marlin_mib[] = { /* Device collection */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.3.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* UPS collection */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.6.6.7.1.2.1.2.0", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* FIXME: use unitName.0 (ePDU)? * { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, */ { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, /* FIXME: needs a date reformating callback * 2011-8-29,16:27:25.0,+1:0 * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 * { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, * { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, */ /* Input collection */ /* Historically, some of these data were previously published as * outlet.{realpower,...} * However, it's more suitable and logic to have these on input.{...} */ { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", NULL, SU_FLAG_STATIC | SU_FLAG_SETINT, NULL, &input_phases }, /* FIXME: to be implemented * inputType.0.1 iso.3.6.1.4.1.534.6.6.7.3.1.1.2.0.1 * singlePhase (1), ... split phase, three phase delta, or three phase wye */ /* Frequency is measured globally */ { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.0.1", NULL, 0, NULL, NULL }, { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.0.1", NULL, SU_FLAG_OK, &marlin_threshold_frequency_status_info[0], NULL }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.0.1", NULL, SU_FLAG_OK, &marlin_threshold_frequency_alarm_info[0], NULL }, /* inputCurrentPercentLoad (measured globally) * Current percent load, based on the rated current capacity */ /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ { "input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L2.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L3.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, /* FIXME: * - Voltage is only measured per phase, as mV! * so input.voltage == input.L1.voltage for both single and 3phase * - As per NUT namespace (http://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) * Voltage has to be expressed either phase-phase or phase-neutral * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) * => RFC input.Lx.voltage.context */ { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL }, { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0], NULL }, { "input.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL }, { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0], NULL }, { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.2", NULL, 0, NULL, NULL }, { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.2", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.2", NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0], NULL }, { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.3", NULL, 0, NULL, NULL }, { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.3", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.0.1.3", NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0], NULL }, { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, /* FIXME: * - input.current is mapped on input.L1.current for both single and 3phase !!! */ { "input.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL }, { "input.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.0.1.1", NULL, 0, NULL, NULL }, { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0], NULL }, { "input.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL }, { "input.L1.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.0.1.1", NULL, 0, NULL, NULL }, { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.1", NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0], NULL }, { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L1.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.0.1.1", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.2", NULL, 0, NULL, NULL }, { "input.L2.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.0.1.2", NULL, 0, NULL, NULL }, { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.2", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.2", NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0], NULL }, { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L2.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.0.1.2", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.3", NULL, 0, NULL, NULL }, { "input.L3.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.0.1.3", NULL, 0, NULL, NULL }, { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.3", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.0.1.3", NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0], NULL }, { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, { "input.L3.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.0.1.3", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, /* Sum of all phases realpower, valid for Shark 1ph/3ph only */ { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L1.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L2.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, /* Ambient collection */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.0.1", NULL, SU_FLAG_OK, &marlin_ambient_presence_info[0], NULL }, { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.0.1", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.0.1", NULL, SU_FLAG_OK, &marlin_threshold_temperature_alarms_info[0], NULL }, { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.0.1", NULL, SU_FLAG_OK, NULL, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.temperature.low", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.6.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.high", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.8.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.0.1", NULL, SU_FLAG_OK, &marlin_threshold_status_info[0], NULL }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.0.1", NULL, SU_FLAG_OK, &marlin_threshold_humidity_alarms_info[0], NULL }, { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.0.1", NULL, SU_FLAG_OK, NULL, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.humidity.low", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.6.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.high", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.8.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, { "ambient.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, /* Dry contacts on TH module */ { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.0.1", NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0], NULL }, { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.0.2", NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0], NULL }, /* Outlet collection */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.0", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ { "outlet.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.0.1", NULL, 0, NULL, NULL }, { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL }, { "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL }, { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.4", NULL, 0, NULL, NULL }, { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.4", NULL, 0, NULL, NULL }, /* outlet template definition * Indexes start from 1, ie outlet.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.3.0.%i", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.0.%i", NULL, SU_FLAG_OK | SU_OUTLET, &marlin_outlet_status_info[0], NULL }, /* FIXME: or use ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.0.1", though it's related to groups! */ { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.0.%i.1", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET, NULL, NULL }, { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.0.%i.2", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET, NULL, NULL }, { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.0.%i.3", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET, NULL, NULL }, { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.0.%i.4", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET, NULL, NULL }, { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.0.%i.5", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET, NULL, NULL }, { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.0.%i.6", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET, NULL, NULL }, { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.0.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.0.%i", NULL, SU_OUTLET, &marlin_threshold_status_info[0], NULL }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.0.%i", NULL, SU_OUTLET, &marlin_threshold_current_alarms_info[0], NULL }, { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.5.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.6.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.7.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.8.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.0.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.0.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.0.%i", NULL, SU_OUTLET, &marlin_threshold_status_info[0], NULL }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.0.%i", NULL, SU_OUTLET, &marlin_threshold_voltage_alarms_info[0], NULL }, { "outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.4.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.5.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.6.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.7.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL, NULL }, { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.0.%i", NULL, SU_OUTLET, NULL, NULL }, /* FIXME: handle non switchable units (only measurements), which do not expose this OID */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.%i", "no", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, &outlet_switchability_info[0], NULL }, /* TODO: handle statistics * outletWh.0.1 * outletWhTimer.0.1 */ /* Outlet groups collection */ { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.0", "0", SU_FLAG_STATIC, NULL, NULL }, /* outlet groups template definition * Indexes start from 1, ie outlet.group.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ /* groupID.0.1 = OctetString: A */ { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.0.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, NULL, NULL }, /* groupName.0.1 = OctetString: Factory Group 1 */ /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ { "outlet.group.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.0.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, NULL, NULL }, /* groupType.0.1 = Integer: outletSection (4) */ { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.0.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &marlin_outlet_group_type_info[0], NULL }, /* groupControlStatus.0.1 = Integer: on (1) */ { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.0.%i", NULL, SU_FLAG_OK | SU_OUTLET_GROUP, &marlin_outletgroups_status_info[0], NULL }, /* groupChildCount.0.1 = Integer: 12 */ { "outlet.group.%i.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.1.1.6.0.%i", NULL, SU_OUTLET_GROUP, NULL, NULL }, /* groupVoltage.0.1 = Integer: 243080 */ { "outlet.group.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.3.0.%i", NULL, SU_OUTLET_GROUP, NULL, NULL }, /* groupVoltageThStatus.0.1 = Integer: good (0) */ { "outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.3.1.4.0.%i", NULL, SU_OUTLET_GROUP, &marlin_threshold_status_info[0], NULL }, { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.3.1.4.0.%i", NULL, SU_OUTLET_GROUP, &marlin_threshold_voltage_alarms_info[0], NULL }, { "outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.5.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, { "outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.6.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, { "outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.7.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, { "outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.8.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrent.0.1 = Integer: 0 */ { "outlet.group.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.3.0.%i", NULL, SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrentCapacity.0.1 = Integer: 16000 */ { "outlet.group.%i.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.2.0.%i", NULL, SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrentThStatus.0.1 = Integer: good (0) */ { "outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.4.1.4.0.%i", NULL, SU_OUTLET_GROUP, &marlin_threshold_status_info[0], NULL }, { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.4.1.4.0.%i", NULL, SU_OUTLET_GROUP, &marlin_threshold_current_alarms_info[0], NULL }, /* groupCurrentPercentLoad.0.1 = Integer: 0 */ { "outlet.group.%i.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.4.1.10.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrentThLowerWarning.0.1 = Integer: 0 */ { "outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.5.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrentThLowerCritical.0.1 = Integer: -1 */ { "outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.6.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrentThUpperWarning.0.1 = Integer: 12800 */ { "outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.7.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupCurrentThUpperCritical.0.1 = Integer: 16000 */ { "outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.8.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupWatts.0.1 = Integer: 2670 */ { "outlet.group.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.3.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* groupVA.0.1 = Integer: 3132 */ { "outlet.group.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.0.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL, NULL }, /* instant commands. */ /* Notes: * - load.cycle might be replaced by / mapped on shutdown.reboot * - outletControl{Off,On,Reboot}Cmd values: * 0-n : Timer * -1 : Cancel * we currently use "0", so instant On | Off | Reboot... */ /* no counterpart found! { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, */ /* TODO: handle delays */ { "outlet.%i.load.off", 0, 0, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.on", 0, 0, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.cycle", 0, 0, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, /* TODO: handle delays * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ /* groupControlOffCmd.0.1 = Integer: -1 */ { "outlet.group.%i.load.off", 0, 0, ".1.3.6.1.4.1.534.6.6.7.5.6.1.3.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET_GROUP, NULL, NULL }, /* groupControl0nCmd.0.1 = Integer: -1 */ { "outlet.group.%i.load.on", 0, 0, ".1.3.6.1.4.1.534.6.6.7.5.6.1.4.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET_GROUP, NULL, NULL }, /* groupControlRebootCmd.0.1 = Integer: -1 */ { "outlet.group.%i.load.cycle", 0, 0, ".1.3.6.1.4.1.534.6.6.7.5.6.1.5.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET_GROUP, NULL, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } }; /* Pulizzi Monitored ePDU (Basic model, SNMP only) * FIXME: to be completed * * Warning: there are 2 versions: * - SA built MI.mib (old MIB) * #define PULIZZI1_OID_MIB ".1.3.6.1.4.1.20677.3.1.1" * #define PULIZZI1_OID_MODEL_NAME ".1.3.6.1.4.1.20677.3.1.1.1.2.0" * - Eaton-Powerware-Monitored-ePDU_1.0.E.mib (new MIB) Vertical SW */ /* Pulizzi Switched ePDU */ #define EATON_PULIZZI_SW_MIB_VERSION "0.2" #define PULIZZI_SW_OID_MIB ".1.3.6.1.4.1.20677.3.1.1" #define PULIZZI_SW_OID_MODEL_NAME ".1.3.6.1.4.1.20677.2.1.1.0" /* Some buggy FW also report sysOID = ".1.3.6.1.4.1.20677.1" */ #define EATON_PULIZZI_SWITCHED1_SYSOID ".1.3.6.1.4.1.20677.1" #define EATON_PULIZZI_SWITCHED2_SYSOID ".1.3.6.1.4.1.20677.2" static info_lkp_t pulizzi_sw_outlet_status_info[] = { { 1, "on" }, { 2, "off" }, { 0, NULL } }; /* simply remap the above status to "yes" */ static info_lkp_t pulizzi_sw_outlet_switchability_info[] = { { 1, "yes" }, { 2, "yes" }, { 0, NULL } }; /* Snmp2NUT lookup table for Eaton Pulizzi Switched ePDU MIB */ static snmp_info_t eaton_pulizzi_switched_mib[] = { /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.2.6.0", "unknown", 0, NULL, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* FIXME: to be moved to the device collection! */ { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.3.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* Outlet page */ /* Note: outlet.count is deduced, with guestimate_outlet_count() */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.current", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.2.0", NULL, 0, NULL, NULL }, { "outlet.voltage", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.1.0", NULL, 0, NULL, NULL }, { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.3.0", NULL, 0, NULL, NULL }, /* outlet template definition * Notes: * - indexes start from 1, ie outlet.1 => .1 * - the first definition is used to determine the base index (ie 0 or 1) * - outlet.count is estimated, based on the below OID iteration capabilities */ { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.1.%i.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", NULL, SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_status_info[0], NULL }, { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, /* we use the same OID as outlet.n.status..., to expose switchability */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", "yes", SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_switchability_info[0], NULL }, /* FIXME: need to be added to the namespace! */ { "outlet.%i.delay.reboot", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.5.0", NULL, SU_OUTLET, NULL, NULL }, /* "outlet1SequenceTime" is used for global sequence */ { "outlet.%i.delay.start", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.4.0", NULL, SU_OUTLET, NULL, NULL }, /* instant commands. */ /* FIXME: not exposed as "outlet.load...", or otherwise specific processing applies (template instanciation) */ { "load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", NULL, SU_TYPE_CMD, NULL, NULL }, { "load.off", 0, 2, ".1.3.6.1.4.1.20677.2.6.2.1.0", NULL, SU_TYPE_CMD, NULL, NULL }, { "load.on.delay", 0, 3, ".1.3.6.1.4.1.20677.2.6.2.1.0", NULL, SU_TYPE_CMD, NULL, NULL }, { "load.off.delay", 0, 4, ".1.3.6.1.4.1.20677.2.6.2.1.0", NULL, SU_TYPE_CMD, NULL, NULL }, /* WARNING: outlet 1 => index 2! */ { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", NULL, SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL, NULL }, { "outlet.%i.load.off", 0, 2, ".1.3.6.1.4.1.20677.2.6.2.%i.0", NULL, SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL, NULL }, { "outlet.%i.load.cycle", 0, 3, ".1.3.6.1.4.1.20677.2.6.2.%i.0", NULL, SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } }; mib2nut_info_t aphel_genesisII = { "aphel_genesisII", EATON_APHEL_MIB_VERSION, NULL, APHEL1_OID_MODEL_NAME, eaton_aphel_genesisII_mib, APHEL1_SYSOID }; mib2nut_info_t aphel_revelation = { "aphel_revelation", EATON_APHEL_MIB_VERSION, NULL, APHEL2_OID_MODEL_NAME, eaton_aphel_revelation_mib, APHEL2_SYSOID }; mib2nut_info_t eaton_marlin = { "eaton_epdu", EATON_MARLIN_MIB_VERSION, NULL, EATON_MARLIN_OID_MODEL_NAME, eaton_marlin_mib, EATON_MARLIN_SYSOID }; /*mib2nut_info_t pulizzi_monitored = { "pulizzi_monitored", EATON_PULIZZI_MIB_VERSION, NULL, PULIZZI1_OID_MODEL_NAME, eaton_pulizzi_monitored_mib, PULIZZI1_OID_MIB };*/ mib2nut_info_t pulizzi_switched1 = { "pulizzi_switched1", EATON_PULIZZI_SW_MIB_VERSION, NULL, EATON_PULIZZI_SWITCHED1_SYSOID, eaton_pulizzi_switched_mib, EATON_PULIZZI_SWITCHED1_SYSOID }; mib2nut_info_t pulizzi_switched2 = { "pulizzi_switched2", EATON_PULIZZI_SW_MIB_VERSION, NULL, EATON_PULIZZI_SWITCHED1_SYSOID, eaton_pulizzi_switched_mib, EATON_PULIZZI_SWITCHED2_SYSOID }; nut-2.7.4/drivers/apc-mib.h0000644000175000017500000000104212640443572012416 00000000000000#ifndef APC_MIB_H #define APC_MIB_H #include "main.h" #include "snmp-ups.h" /* * FIXME: The below is needed because the main driver body uses this to determine * whether a conversion from Fahrenheit to Celsius is needed (which really should * be solved in subdriver specific formatting functions, like we do in usbhid-ups */ #define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" #define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" #define APCC_IEM_FAHRENHEIT 2 extern mib2nut_info_t apc; #endif /* APC_MIB_H */ nut-2.7.4/drivers/apc-hid.c0000644000175000017500000005735712667537407012444 00000000000000/* apc-hid.c - data to monitor APC USB/HID devices with NUT * * Copyright (C) * 2003 - 2009 Arnaud Quette * 2005 John Stamp * 2005 Peter Selinger * 2009 - 2010 Arjen de Korte * * Sponsored by MGE UPS SYSTEMS * and Eaton * * 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 "main.h" /* for getval() */ #include "usbhid-ups.h" #include "apc-hid.h" #include "usb-common.h" #define APC_HID_VERSION "APC HID 0.96" /* APC */ #define APC_VENDORID 0x051d /* Tweaks */ char * tweak_max_report[] = { /* Back-UPS ES 700 does NOT overflow. */ /* Back-UPS ES 725 does NOT overflow. */ /* Back-UPS ES 525 overflows on ReportID 0x0c (UPS.PowerSummary.RemainingCapacity).*/ "Back-UPS ES 525", /* Back-UPS CS 650 overflows on ReportID 0x46 */ "Back-UPS CS", NULL}; /* Don't use interrupt pipe on 5G models (used by proprietary protocol) */ static void *disable_interrupt_pipe(USBDevice_t *device) { if (use_interrupt_pipe == TRUE) { upslogx(LOG_INFO, "interrupt pipe disabled (add 'pollonly' flag to 'ups.conf' to get rid of this message)"); use_interrupt_pipe= FALSE; } return NULL; } /* Some models need special tweaks */ static void *general_apc_check(USBDevice_t *device) { int i = 0; if (!device->Product) { upslogx(LOG_WARNING, "device->Product is NULL so it is not possible to determine whether to activate max_report_size workaround"); return NULL; } /* Some models of Back-UPS overflow on some ReportID. * This results in some data not being exposed and IO errors on * WIN32, causing endless reconnection or driver's failure */ while( tweak_max_report[i] != NULL ) { if(!strncmp(device->Product, tweak_max_report[i], strlen(tweak_max_report[i]))) { max_report_size = 1; return NULL; } i++; } return NULL; } /* USB IDs device table */ static usb_device_id_t apc_usb_device_table[] = { /* APC AP9584 Serial->USB kit */ { USB_DEVICE(APC_VENDORID, 0x0000), NULL }, /* various models */ { USB_DEVICE(APC_VENDORID, 0x0002), general_apc_check }, /* various 5G models */ { USB_DEVICE(APC_VENDORID, 0x0003), disable_interrupt_pipe }, /* Terminating entry */ { -1, -1, NULL } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *apc_date_conversion_fun(double value) { static char buf[20]; int year, month, day; if ((long)value == 0) { return "not set"; } /* APC apparently uses a hexadecimal-as-decimal format, e.g., 0x102202 = October 22, 2002 */ year = ((long)value & 0xf) + 10 * (((long)value>>4) & 0xf); month = (((long)value>>16) & 0xf) + 10 * (((long)value>>20) & 0xf); day = (((long)value>>8) & 0xf) + 10 * (((long)value>>12) & 0xf); /* Y2K conversion - hope that this format will be retired before 2070 :) */ if (year >= 70) { year += 1900; } else { year += 2000; } snprintf(buf, sizeof(buf), "%04d/%02d/%02d", year, month, day); return buf; } info_lkp_t apc_date_conversion[] = { { 0, NULL, apc_date_conversion_fun } }; /* This was determined empirically from observing a BackUPS LS 500 */ static info_lkp_t apcstatusflag_info[] = { { 8, "!off", NULL }, /* Normal operation */ { 16, "!off", NULL }, /* This occurs briefly during power-on, and corresponds to status 'DISCHRG'. */ { 0, "off", NULL }, { 0, NULL, NULL } }; /* Reason of the last battery transfer (from apcupsd) */ static info_lkp_t apc_linefailcause_vrange_info[] = { { 1, "vrange", NULL }, /* Low line voltage */ { 2, "vrange", NULL }, /* High line voltage */ { 4, "vrange", NULL }, /* notch, spike, or blackout */ { 8, "vrange", NULL }, /* Notch or blackout */ { 9, "vrange", NULL }, /* Spike or blackout */ { 0, "!vrange", NULL }, /* No transfers have ocurred */ { 0, NULL, NULL } }; static info_lkp_t apc_linefailcause_frange_info[] = { { 7, "frange", NULL }, /* Input frequency out of range */ { 0, "!frange", NULL }, /* No transfers have ocurred */ { 0, NULL, NULL } }; #if 0 /* these input.transfer.reason can't be mapped at the moment... */ { 3, "ripple", NULL }, /* Ripple */ { 5, "self test", NULL }, /* Self Test or Discharge Calibration commanded * Test usage, front button, or 2 week self test */ { 6, "forced", NULL }, /* DelayBeforeShutdown or APCDelayBeforeShutdown */ { 10, "forced", NULL }, /* Graceful shutdown by accessories */ { 11, "self test", NULL }, /* Test usage invoked */ { 12, "self test", NULL }, /* Front button initiated self test */ { 13, "self test", NULL }, /* 2 week self test */ { 0, NULL, NULL } #endif static info_lkp_t apc_sensitivity_info[] = { { 0, "low", NULL }, { 1, "medium", NULL }, { 2, "high", NULL }, { 0, NULL, NULL } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* APC usage table */ static usage_lkp_t apc_usage_lkp[] = { { "APCGeneralCollection", 0xff860005 }, { "APCEnvironment", 0xff860006 }, { "APCProbe1", 0xff860007 }, { "APCProbe2", 0xff860008 }, { "APCBattReplaceDate", 0xff860016 }, /* usage seen in dumps but unknown: * - ff860018 * Path: UPS.Battery.ff860018, Type: Feature, ReportID: 0x48, Offset: 0, Size: 32, Value: 0 */ { "APCBattCapBeforeStartup", 0xff860019 }, /* FIXME: exploit */ /* usage seen in dumps but unknown: * - ff86001a * Path: UPS.Battery.ff86001a, Type: Input, ReportID: 0x1b, Offset: 0, Size: 8, Value: 3 * Path: UPS.Battery.ff86001a, Type: Feature, ReportID: 0x1b, Offset: 0, Size: 8, Value: 3 * - ff86001b * Path: UPS.Battery.ff86001b, Type: Input, ReportID: 0x1c, Offset: 0, Size: 8, Value: 0 * Path: UPS.Battery.ff86001b, Type: Feature, ReportID: 0x1c, Offset: 0, Size: 8, Value: 0 * - ff860023 * Path: UPS.ff860001.ff860023, Type: Feature, ReportID: 0x60, Offset: 0, Size: 16, Value: 0 * - ff860024 * Path: UPS.Battery.ff860024, Type: Feature, ReportID: 0x47, Offset: 0, Size: 8, Value: 245 * Path: UPS.PowerConverter.ff860024, Type: Feature, ReportID: 0x51, Offset: 0, Size: 8, Value: 145 * - ff860025 * Path: UPS.ff860001.ff860025, Type: Feature, ReportID: 0x62, Offset: 0, Size: 32, Value: 0 * - ff860026 * Path: UPS.ff860001.ff860026, Type: Feature, ReportID: 0x61, Offset: 0, Size: 8, Value: 10 * - ff860027 * Path: UPS.ff860027, Type: Feature, ReportID: 0x3e, Offset: 0, Size: 32, Value: 0 * - ff860028 * Path: UPS.ff860028, Type: Feature, ReportID: 0x3f, Offset: 0, Size: 32, Value: 0 * - ff860030 * Path: UPS.Output.ff860030, Type: Feature, ReportID: 0x42, Offset: 0, Size: 16, Value: 5.8 */ { "APC_UPS_FirmwareRevision", 0xff860042 }, { "APCLineFailCause", 0xff860052 }, { "APCStatusFlag", 0xff860060 }, { "APCSensitivity", 0xff860061 }, { "APCPanelTest", 0xff860072 }, /* FIXME: exploit */ { "APCShutdownAfterDelay", 0xff860076 }, /* FIXME: exploit */ { "APC_USB_FirmwareRevision", 0xff860079 }, /* FIXME: exploit */ { "APCDelayBeforeReboot", 0xff86007c }, { "APCDelayBeforeShutdown", 0xff86007d }, { "APCDelayBeforeStartup", 0xff86007e }, /* FIXME: exploit */ /* usage seen in dumps but unknown: * - ff860080 * Path: UPS.PresentStatus.ff860080, Type: Input, ReportID: 0x33, Offset: 12, Size: 1, Value: 0 * Path: UPS.PresentStatus.ff860080, Type: Feature, ReportID: 0x33, Offset: 12, Size: 1, Value: 0 * Path: UPS.PowerSummary.PresentStatus.ff860080, Type: Input, ReportID: 0x07, Offset: 12, Size: 1, Value: 0 * Path: UPS.PowerSummary.PresentStatus.ff860080, Type: Feature, ReportID: 0x07, Offset: 12, Size: 1, Value: 0 * - ff860090, ff860091 * Path: UPS.ff860090.ff860091, Type: Feature, ReportID: 0x8c, Offset: 0, Size: 8, Value: 1.000000 * - ff860092 * Path: UPS.ff860090.ff860092, Type: Feature, ReportID: 0x8d, Offset: 0, Size: 8, Value: 25.000000 * - ff860093 * Path: UPS.ff860090.ff860093, Type: Feature, ReportID: 0x8e, Offset: 0, Size: 8, Value: 83.000000 * - ff860094 * Path: UPS.ff860090.ff860094, Type: Feature, ReportID: 0x8f, Offset: 0, Size: 8, Value: 0.000000 * - ff860095 * Path: UPS.ff860090.ff860095, Type: Feature, ReportID: 0x90, Offset: 0, Size: 8, Value: 1.000000 * - ff860096 * Path: UPS.ff860090.ff860096, Type: Feature, ReportID: 0x91, Offset: 0, Size: 16, Value: 4.000000 * - ff860097 * Path: UPS.ff860090.ff860097, Type: Feature, ReportID: 0x92, Offset: 0, Size: 16, Value: 4.000000 */ /* Note (Arnaud): BUP stands for BackUPS Pro * This is a HID uncompliant special (manufacturer) collection * FIXME: these need to be used... */ { "BUPHibernate", 0x00850058 }, /* FIXME: exploit */ { "BUPBattCapBeforeStartup", 0x00860012 }, /* FIXME: exploit */ { "BUPDelayBeforeStartup", 0x00860076 }, /* FIXME: exploit */ { "BUPSelfTest", 0x00860010 }, /* FIXME: exploit */ { NULL, 0 } }; /* * USB USAGE NOTES for APC (from Russell Kroll in the old hidups) * * FIXME: read 0xff86.... instead of 0x(00)86....? * * 0x860013 == 44200155090 - capability again * == locale 4, 4 choices, 2 bytes, 00, 15, 50, 90 * == minimum charge to return online * * 0x860060 == "441HMLL" - looks like a 'capability' string * == locale 4, 4 choices, 1 byte each * == line sensitivity (high, medium, low, low) * NOTE! the above does not seem to correspond to my info * * 0x860062 == D43133136127130 * == locale D, 4 choices, 3 bytes, 133, 136, 127, 130 * == high transfer voltage * * 0x860064 == D43103100097106 * == locale D, 4 choices, 3 bytes, 103, 100, 097, 106 * == low transfer voltage * * 0x860066 == 441HMLL (see 860060) * * 0x860074 == 4410TLN * == locale 4, 4 choices, 1 byte, 0, T, L, N * == alarm setting (5s, 30s, low battery, none) * * 0x860077 == 443060180300600 * == locale 4, 4 choices, 3 bytes, 060,180,300,600 * == wake-up delay (after power returns) */ static usage_tables_t apc_utab[] = { apc_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ static hid_info_t apc_hid2nut[] = { /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.Battery.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Battery.RemainingTimeLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.voltage", 0, 0, "UPS.Battery.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.Battery.ConfigVoltage", NULL, "%.1f", 0, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.1f", 0, NULL }, /* Back-UPS 500 */ { "battery.temperature", 0, 0, "UPS.Battery.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, { "battery.mfr.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", 0, date_conversion }, { "battery.mfr.date", 0, 0, "UPS.PowerSummary.APCBattReplaceDate", NULL, "%s", 0, apc_date_conversion }, /* Back-UPS 500, Back-UPS ES/CyberFort 500 */ { "battery.date", 0, 0, "UPS.Battery.APCBattReplaceDate", NULL, "%s", 0, apc_date_conversion }, /* Observed values: 0x0 on Back-UPS ES 650, 0x92501 on Back-UPS BF500 whose manufacture date was 2005/01/20 - this makes little sense but at least it's a valid date. */ /* UPS page */ { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.1f", 0, NULL }, { "ups.load", 0, 0, "UPS.PowerConverter.PercentLoad", NULL, "%.0f", 0, NULL }, /* USB HID PDC defaults */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, /* used by APC SmartUPS RM */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.Output.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, /* used by APC BackUPS ES */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.APCGeneralCollection.APCDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.APCGeneralCollection.APCDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.test.result", 0, 0, "UPS.Battery.Test", NULL, "%s", 0, test_read_info }, { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, beeper_info }, { "ups.mfr.date", 0, 0, "UPS.ManufacturerDate", NULL, "%s", 0, date_conversion }, { "ups.mfr.date", 0, 0, "UPS.PowerSummary.ManufacturerDate", NULL, "%s", 0, date_conversion }, /* Back-UPS 500 */ { "ups.realpower.nominal", 0, 0, "UPS.PowerConverter.ConfigActivePower", NULL, "%.0f", 0, NULL }, { "ups.realpower.nominal", 0, 0, "UPS.Output.ConfigActivePower", NULL, "%.0f", 0, NULL }, /* the below one need to be discussed as we might need to complete * the ups.test sub collection * { "ups.test.panel", 0, 0, "UPS.APCPanelTest", NULL, "%.0f", 0, NULL }, */ /* Special case: ups.status & ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info }, { "BOOL", 0, 0, "UPS.PowerSummary.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, /* Back-UPS 500 */ { "BOOL", 0, 0, "UPS.PowerSummary.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, /* Back-UPS 500 */ { "BOOL", 0, 0, "UPS.PowerSummary.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, /* Back-UPS 500 */ { "BOOL", 0, 0, "UPS.PowerSummary.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, /* Back-UPS 500 */ { "BOOL", 0, 0, "UPS.PowerSummary.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, { "BOOL", 0, 0, "UPS.PowerSummary.APCStatusFlag", NULL, NULL, HU_FLAG_QUICK_POLL, apcstatusflag_info }, /* APC Back-UPS LS 500 */ /* we map 2 times "input.transfer.reason" to be able to clear * both vrange (voltage) and frange (frequency) */ { "BOOL", 0, 0, "UPS.Input.APCLineFailCause", NULL, NULL, 0, apc_linefailcause_vrange_info }, { "BOOL", 0, 0, "UPS.Input.APCLineFailCause", NULL, NULL, 0, apc_linefailcause_frange_info }, /* Input page */ { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL }, { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", 0, NULL }, { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* used by APC BackUPS RS */ { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.sensitivity", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.APCSensitivity", NULL, "%s", HU_FLAG_SEMI_STATIC, apc_sensitivity_info }, /* Output page */ { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.1f", 0, NULL }, { "output.current", 0, 0, "UPS.Output.Current", NULL, "%.2f", 0, NULL }, { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.1f", 0, NULL }, /* Environmental page */ { "ambient.temperature", 0, 0, "UPS.APCEnvironment.APCProbe1.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "ambient.humidity", 0, 0, "UPS.APCEnvironment.APCProbe1.Humidity", NULL, "%.1f", 0, NULL }, /* { "ambient.temperature", 0, 0, "UPS.APCEnvironment.APCProbe2.Temperature", NULL, "%.1f", 0, kelvin_celsius_conversion }, { "ambient.humidity", 0, 0, "UPS.APCEnvironment.APCProbe2.Humidity", NULL, "%.1f", 0, NULL }, */ /* instant commands. */ /* test.* split into subset while waiting for extradata support * ie: test.battery.start quick */ { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, { "test.battery.start.quick", 0, 0, "UPS.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, /* Back-UPS RS (experimental) */ { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, { "test.battery.start.deep", 0, 0, "UPS.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, /* Back-UPS RS (experimental) */ { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, { "test.battery.stop", 0, 0, "UPS.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, /* Back-UPS RS (experimental) */ { "test.panel.start", 0, 0, "UPS.APCPanelTest", NULL, "1", HU_TYPE_CMD, NULL }, { "test.panel.stop", 0, 0, "UPS.APCPanelTest", NULL, "0", HU_TYPE_CMD, NULL }, { "test.panel.start", 0, 0, "UPS.PowerSummary.APCPanelTest", NULL, "1", HU_TYPE_CMD, NULL }, /* Back-UPS 500 */ { "test.panel.stop", 0, 0, "UPS.PowerSummary.APCPanelTest", NULL, "0", HU_TYPE_CMD, NULL }, /* Back-UPS 500 */ /* USB HID PDC defaults */ { "load.off.delay", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, /* used by APC SmartUPS RM */ { "load.off.delay", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.Output.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, /* used by APC BackUPS ES */ { "load.off.delay", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, /* used by APC BackUPS CS */ { "shutdown.return", 0, 0, "UPS.Output.APCDelayBeforeReboot", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *apc_format_model(HIDDevice_t *hd) { static char model[64]; char *ptr1, *ptr2; /* FIXME?: what is the path "UPS.APC_UPS_FirmwareRevision"? */ snprintf(model, sizeof(model), "%s", hd->Product ? hd->Product : "unknown"); ptr1 = strstr(model, "FW:"); if (ptr1) { *(ptr1 - 1) = '\0'; ptr1 += strlen("FW:"); ptr2 = strstr(ptr1, "USB FW:"); if (ptr2) { *(ptr2 - 1) = '\0'; ptr2 += strlen("USB FW:"); dstate_setinfo("ups.firmware.aux", "%s", ptr2); } dstate_setinfo("ups.firmware", "%s", ptr1); } return model; } static const char *apc_format_mfr(HIDDevice_t *hd) { return hd->Vendor ? hd->Vendor : "APC"; } static const char *apc_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int apc_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(apc_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("APC", hd); return 0; case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t apc_subdriver = { APC_HID_VERSION, apc_claim, apc_utab, apc_hid2nut, apc_format_model, apc_format_mfr, apc_format_serial, }; nut-2.7.4/drivers/cyberpower-mib.c0000644000175000017500000000562712667537407014056 00000000000000/* cyberpower-mib.c - data to monitor Cyberpower RMCARD * * Copyright (C) 2010 - Eric Schultz * * derived (i.e. basically copied and modified) of bestpower by: * Copyright (C) 2010 - Arnaud Quette * * 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 "cyberpower-mib.h" #define CYBERPOWER_MIB_VERSION "0.1" #define CYBERPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0" #define CYBERPOWER_SYSOID ".1.3.6.1.4.1.3808" static info_lkp_t cyberpower_power_status[] = { { 2, "OL" }, { 3, "OB" }, { 4, "OL" }, { 5, "OL" }, { 7, "OL" }, { 1, "NULL" }, { 6, "NULL" }, { 0, NULL } } ; /* Snmp2NUT lookup table for CyberPower MIB */ static snmp_info_t cyberpower_mib[] = { /* Device page */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "CYBERPOWER", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, CYBERPOWER_OID_MODEL_NAME, "CyberPower", SU_FLAG_STATIC, NULL, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.3.0", "", SU_FLAG_STATIC, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.1.0", "", SU_FLAG_STATIC, NULL }, { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.2.0", "", 0, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.4.1.1.0", "", 0 /*SU_STATUS_PWR*/, &cyberpower_power_status[0] }, /* Battery runtime is expressed in minutes */ { "battery.runtime", 0, 60.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.4.0", "", 0, NULL }, /* The elapsed time in seconds since the * UPS has switched to battery power */ { "battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.1.2.0", "", 0, NULL }, { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.2.0", "", 0, NULL }, { "battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.7.0", "", 0, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } } ; mib2nut_info_t cyberpower = { "cyberpower", CYBERPOWER_MIB_VERSION, NULL, CYBERPOWER_OID_MODEL_NAME, cyberpower_mib, CYBERPOWER_SYSOID }; nut-2.7.4/drivers/cps-hid.c0000644000175000017500000002536412640473702012443 00000000000000/* cps-hid.c - subdriver to monitor CPS USB/HID devices with NUT * * Copyright (C) * 2003 - 2008 Arnaud Quette * 2005 - 2006 Peter Selinger * * Note: this subdriver was initially generated as a "stub" by the * gen-usbhid-subdriver script. It must be customized. * * 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 "main.h" /* for getval() */ #include "usbhid-ups.h" #include "cps-hid.h" #include "usb-common.h" #define CPS_HID_VERSION "CyberPower HID 0.4" /* Cyber Power Systems */ #define CPS_VENDORID 0x0764 /*! Battery voltage scale factor. * For some devices, the reported battery voltage is off by factor * of 1.5 so we need to apply a scale factor to it to get the real * battery voltage. By default, the factor is 1 (no scaling). */ static double battery_scale = 1; static int might_need_battery_scale = 0; static int battery_scale_checked = 0; /*! If the ratio of the battery voltage to the nominal battery voltage exceeds * this factor, we assume that the battery voltage needs to be scaled by 2/3. */ static const double battery_voltage_sanity_check = 1.4; static void *cps_battery_scale(USBDevice_t *device) { might_need_battery_scale = 1; return NULL; } /* USB IDs device table */ static usb_device_id_t cps_usb_device_table[] = { /* 900AVR/BC900D */ { USB_DEVICE(CPS_VENDORID, 0x0005), NULL }, /* Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. */ { USB_DEVICE(CPS_VENDORID, 0x0501), &cps_battery_scale }, /* OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U */ { USB_DEVICE(CPS_VENDORID, 0x0601), NULL }, /* Terminating entry */ { -1, -1, NULL } }; /*! Adjusts @a battery_scale if voltage is well above nominal. */ static void cps_adjust_battery_scale(double batt_volt) { const char *batt_volt_nom_str; double batt_volt_nom; if(battery_scale_checked) { return; } batt_volt_nom_str = dstate_getinfo("battery.voltage.nominal"); if(!batt_volt_nom_str) { upsdebugx(2, "%s: 'battery.voltage.nominal' not available yet; skipping scale determination", __func__); return; } batt_volt_nom = strtod(batt_volt_nom_str, NULL); if(batt_volt_nom == 0) { upsdebugx(3, "%s: 'battery.voltage.nominal' is %s", __func__, batt_volt_nom_str); return; } if( (batt_volt / batt_volt_nom) > battery_voltage_sanity_check ) { upslogx(LOG_INFO, "%s: battery readings will be scaled by 2/3", __func__); battery_scale = 2.0/3; } battery_scale_checked = 1; } /* returns statically allocated string - must not use it again before done with result! */ static const char *cps_battvolt_fun(double value) { static char buf[8]; if(might_need_battery_scale) { cps_adjust_battery_scale(value); } upsdebugx(5, "%s: battery_scale = %.3f", __func__, battery_scale); snprintf(buf, sizeof(buf), "%.1f", battery_scale * value); return buf; } static info_lkp_t cps_battvolt[] = { { 0, NULL, &cps_battvolt_fun } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *cps_battcharge_fun(double value) { static char buf[8]; /* clamp battery charge to 100% */ snprintf(buf, sizeof(buf), "%.0f", value < 100.0 ? value : 100.0); return buf; } static info_lkp_t cps_battcharge[] = { { 0, NULL, &cps_battcharge_fun } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* CPS usage table */ static usage_lkp_t cps_usage_lkp[] = { { NULL, 0x0 } }; static usage_tables_t cps_utab[] = { cps_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ static hid_info_t cps_hid2nut[] = { /* { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, */ /* Battery page */ { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, { "battery.mfr.date", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", 0, stringid_conversion }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%s", 0, cps_battcharge }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, cps_battvolt }, /* UPS page */ { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, beeper_info }, { "ups.test.result", 0, 0, "UPS.Output.Test", NULL, "%s", 0, test_read_info }, { "ups.realpower.nominal", 0, 0, "UPS.Output.ConfigActivePower", NULL, "%.0f", 0, NULL }, { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.Output.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, /* Special case: ups.status & ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, 0, fullycharged_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, { "BOOL", 0, 0, "UPS.Output.Boost", NULL, NULL, 0, boost_info }, { "BOOL", 0, 0, "UPS.Output.Overload", NULL, NULL, 0, overload_info }, /* Input page */ { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", 0, NULL }, { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL }, { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* Output page */ { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.0f", 0, NULL }, /* instant commands. */ { "test.battery.start.quick", 0, 0, "UPS.Output.Test", NULL, "1", HU_TYPE_CMD, NULL }, { "test.battery.start.deep", 0, 0, "UPS.Output.Test", NULL, "2", HU_TYPE_CMD, NULL }, { "test.battery.stop", 0, 0, "UPS.Output.Test", NULL, "3", HU_TYPE_CMD, NULL }, { "load.off.delay", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.Output.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *cps_format_model(HIDDevice_t *hd) { return hd->Product; } static const char *cps_format_mfr(HIDDevice_t *hd) { return hd->Vendor ? hd->Vendor : "CPS"; } static const char *cps_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int cps_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(cps_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("CyberPower", hd); return 0; case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t cps_subdriver = { CPS_HID_VERSION, cps_claim, cps_utab, cps_hid2nut, cps_format_model, cps_format_mfr, cps_format_serial, }; nut-2.7.4/drivers/apc-ats-mib.h0000755000175000017500000000176712667537407013236 00000000000000/* apcats-mib.h - subdriver to monitor apcats SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef APCATS_MIB_H #define APCATS_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t apc_ats; #endif /* APCATS_MIB_H */ nut-2.7.4/drivers/mge-utalk.h0000644000175000017500000002210712640444140012771 00000000000000/* mge-utalk.h - monitor MGE UPS for NUT with UTalk protocol * * Copyright (C) 2002 - 2005 * Arnaud Quette & * Hans Ekkehard Plesser * Martin Loyer * Patrick Agrain * Nicholas Reilly * Dave Abbott * Marek Kralewski * * This driver is a collaborative effort by the above people, * Sponsored by MGE UPS SYSTEMS * * 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 * */ /* --------------------------------------------------------------- */ /* Default Values for UPS Variables */ /* --------------------------------------------------------------- */ #define DEFAULT_LOWBATT 30 /* low battery level, in % */ /* delay between return of utility power and powering up of load (in MINUTES) */ #define DEFAULT_ONDELAY 1 #define DEFAULT_OFFDELAY 20 /* delay before power off, in SECONDS */ #define MIN_CONFIRM_TIME 3 /* shutdown must be confirmed in */ #define MAX_CONFIRM_TIME 15 /* this interval */ /* --------------------------------------------------------------- */ /* Model Name formating entries */ /* --------------------------------------------------------------- */ typedef struct { const char *basename; /* as returned by Si 1 */ const char *finalname; } models_name_t; models_name_t Si1_models_names [] = { /* Pulsar EX */ { "Pulsar EX7", "Pulsar EX 7" }, { "Pulsar EX10", "Pulsar EX 10" }, { "Pulsar EX15", "Pulsar EX 15" }, { "Pulsar EX20", "Pulsar EX 20" }, { "Pulsar EX30", "Pulsar EX 30" }, { "Pulsar EX40", "Pulsar EX 40" }, /* Pulsar ES+ */ { "Pulsar ES2+", "Pulsar ES 2+" }, { "Pulsar ES5+", "Pulsar ES 5+" }, /* Pulsar ESV+ */ { "Pulsar ESV5+", "Pulsar ESV 5+" }, { "Pulsar ESV8+", "Pulsar ESV 8+" }, { "Pulsar ESV11+", "Pulsar ESV 11+" }, { "Pulsar ESV14+", "Pulsar ESV 14+" }, { "Pulsar ESV22+", "Pulsar ESV 22+" }, /* Pulsar EXtreme */ { "EXTREME 1500", "Pulsar EXtreme 1500" }, { "EXTREME 2000", "Pulsar EXtreme 2000" }, { "EXTREME 2500", "Pulsar EXtreme 2500" }, { "EXTREME 3000", "Pulsar EXtreme 3000" }, /* Comet EXtreme */ { "EXTREME 4.5", "Comet EXtreme 4.5" }, { "EXTREME 6", "Comet EXtreme 6" }, { "EXTREME 9", "Comet EXtreme 9" }, { "EXTREME 12", "Comet EXtreme 12" }, /* Comet */ { "COMET 5", "Comet 5" }, { "COMET 7", "Comet 7.5" }, { "COMET 10", "Comet 10" }, { "COMET 15", "Comet 15" }, { "COMET 20", "Comet 20" }, { "COMET 30", "Comet 30" }, { "COMET 12", "Comet 12" }, { "COMET 18", "Comet 18" }, { "COMET 24", "Comet 24" }, { "COMET 36", "Comet 36" }, /* FIXME: complete with Pulsar ?EL 2/4/7?, EXL, SX, PSX/CSX, Evolution?,... */ /* end of structure. */ { NULL, "Generic UTalk model" } }; /* --------------------------------------------------------------- */ /* Model Information for legacy models */ /* --------------------------------------------------------------- */ /* Structure defining how to get name and model for a particular Si output * This is for older UPS (Pulsar ESV,SV) which don't support Plug'n'Play 'Si 1' command */ typedef struct { int Data1; /* Data1, Family model */ int Data2; /* Data2, Type */ /* Data3, SoftLevel is not implemented here, while it's always null or zero.*/ const char *name; /* ASCII model name (like 'Si 1' output */ } mge_model_info_t; /* Array containing Model information for legacy models * NOTE: * - Array is terminated by element with type NULL. */ static mge_model_info_t mge_model[] = { /* Pulsar SV page */ { 3000, 5, "Pulsar SV3" }, { 3000, 6, "Pulsar SV5/8/11" }, { 3000, 7, "Pulsar SV3" }, { 3000, 8, "Pulsar SV5/8" }, { 3000, 10, "Pulsar SV11/ESV13" }, /* Pulsar ESV page */ { 3000, 11, "Pulsar ESV17" }, { 3000, 12, "Pulsar ESV20" }, { 3000, 13, "Pulsar ESV13" }, { 3000, 14, "Pulsar ESV17" }, { 3000, 15, "Pulsar ESV20" }, { 3000, 17, "Pulsar ESV8" }, /* Pulsar ESV+ compatibility (though these support Si1) */ { 3000, 51, "Pulsar ESV 11+" }, { 0, 0, NULL } }; /* --------------------------------------------------------------- */ /* Multiplier Tables */ /* --------------------------------------------------------------- */ /* First index : Table number, fetched with "Ai" command * Second index: unit, as in enum above * NOTE: * - to make the table index the same as the MGE table number, * dummy table row multiplier[0][] is inserted * - unit MIN2SEC is used to convert values in minutes sent by * the UPS (WAKEDELAY) to seconds * - the final column corresponds to unit NONE */ /* units in multiplier table */ typedef enum eunits { VOLT = 0, AMPERE, HERTZ, VOLTAMP, WATT, DEGCELS, MIN2SEC, NONE } units_t; static const double multiplier[4][8] = { /* V A Hz VA W C MIN2SEC NONE */ { 1 , 1 , 1 , 1, 1, 1, 60, 1 }, { 1 , 1 , 0.1 , 1000, 1000, 1, 60, 1 }, { 0.01, 0.01, 1 , 1, 1, 1, 60, 1 }, { 1 , 0.01, 0.1 , 1, 1, 1, 60, 1 } }; /* --------------------------------------------------------------- */ /* Explicit Booleans */ /* --------------------------------------------------------------- */ /* use explicit booleans */ #ifdef FALSE #undef FALSE #endif /* FALSE */ #ifdef TRUE #undef TRUE #endif /* TRUE */ typedef enum ebool { FALSE=0, TRUE } bool_t; /* --------------------------------------------------------------- */ /* Query Commands and their Mapping to INFO_ Variables */ /* --------------------------------------------------------------- */ /* Structure defining how to query UPS for a variable and write information to INFO structure. */ typedef struct { const char *type; /* INFO_* element */ int flags; /* INFO-element flags to set in addinfo */ int length; /* INFO-element length of strings */ const char *cmd; /* UPS command string to requets element */ const char *fmt; /* printf format string for INFO entry */ units_t unit; /* unit of measurement, or NONE */ bool_t ok; /* flag indicating if item is available */ } mge_info_item_t; /* Array containing information to translate between UTalk and NUT info * NOTE: * - Array is terminated by element with type NULL. * - Essential INFO items (_MFR, _MODEL, _FIRMWARE, _STATUS) are * handled separately. * - Array is NOT const, since "ok" can be changed. */ static mge_info_item_t mge_info[] = { /* Battery page */ { "battery.charge", 0, 0, "Bl", "%05.1f", NONE, TRUE }, { "battery.runtime", 0, 0, "Bn", "%05d", NONE, TRUE }, { "battery.voltage", 0, 0, "Bv", "%05.1f", VOLT, TRUE }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 2, "Bl ?", "%02d", NONE, TRUE }, { "battery.voltage.nominal", 0, 0, "Bv ?", "%05.1f", VOLT, TRUE }, /* UPS page */ { "ups.temperature", 0, 0, "St", "%05.1f", DEGCELS, TRUE }, { "ups.load", 0, 0, "Ll", "%05.1f", NONE, TRUE }, { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "Sm ?", "%03d", NONE, TRUE }, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "Sn ?", "%03d", NONE, TRUE }, { "ups.test.interval", ST_FLAG_RW | ST_FLAG_STRING, 5, "Bp ?", "%03d", NONE, TRUE }, /* Output page */ { "output.voltage", 0, 0, "Lv", "%05.1f", VOLT, TRUE }, { "output.current", 0, 0, "Lc", "%05.1f", AMPERE, TRUE }, /* Input page */ { "input.voltage", 0, 0, "Uv", "%05.1f", VOLT, TRUE }, { "input.frequency", 0, 0, "Uf", "%05.2f", HERTZ, TRUE }, /* same as LOBOOSTXFER */ { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "Ee ?", "%05.1f", VOLT, TRUE }, { "input.transfer.boost.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "Ee ?", "%05.1f", VOLT, TRUE }, { "input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "Eo ?", "%05.1f", VOLT, TRUE }, { "input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "Ea ?", "%05.1f", VOLT, TRUE }, /* same as HITRIMXFER */ { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "Eu ?", "%05.1f", VOLT, TRUE }, { "input.transfer.trim.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "Eu ?", "%05.1f", VOLT, TRUE }, /* terminating element */ { NULL, 0, 0, "\0", "\0", NONE, FALSE } }; nut-2.7.4/drivers/liebert-esp2.c0000644000175000017500000003651012640443572013406 00000000000000/* liebert-esp2.c - driver for Liebert UPS, using the ESP-II protocol * * Copyright (C) * 2009 Richard Gregory * * 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 "main.h" #include "serial.h" #include "timehead.h" #include "nut_stdint.h" #define sivann #define IsBitSet(val, bit) ((val) & (1 << (bit))) #define DRIVER_NAME "Liebert ESP-II serial UPS driver" #define DRIVER_VERSION "0.03" #define UPS_SHUTDOWN_DELAY 12 /* it means UPS will be shutdown 120 sec */ #define SHUTDOWN_CMD_LEN 8 /* values for sending to UPS */ enum mult_enum { M_10, M_0_1, M_VOLTAGE_I, M_VOLTAGE_O, M_VOLTAGE_B, M_CURRENT_I, M_CURRENT_O, M_CURRENT_B, M_LOAD_VA, M_LOAD_WATT, M_FREQUENCY, M_VOLT_DC, M_TEMPERATURE, M_CURRENT_DC , M_BAT_RUNTIME, M_NOMPOWER, M_POWER, M_REALPOWER, M_LOADPERC }; static float multi[19]={ 10.0, 0.1, 0.1, /* volt */ 0.1, 0.1, 0.1, /* curr */ 0.1, 0.1, 100.0, /* va */ 100.0, /* W */ 0.01, /* FREQ */ 0.1, /* V DC*/ 0.1, /* TEMP*/ 0.01, /* CUR DC*/ 60.0, /* BAT RUNTIME*/ 100.0, /* NOMPOWER*/ 100.0, /* POWER*/ 100.0, /* REAL POWER*/ 1.0 /* LOADPERC*/ }; static int instcmd(const char *cmdname, const char *extra); static int setvar(const char *varname, const char *val); /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Richard Gregory \n" \ "Robert Jobbagy 1){ cmdin_p=vartab3i; } else { cmdin_p=vartab1i; } if (num_outphases>1){ cmdout_p=vartab3o; } else { cmdout_p=vartab1o; } for (i = 0; cmdin_p[i].var; i++) { int16_t val; ret = do_command(cmdin_p[i].cmd, reply, 6); if (ret < 8) { continue; } val = (unsigned char)reply[5]; val <<= 8; val += (unsigned char)reply[6]; dstate_setinfo(cmdin_p[i].var, cmdin_p[i].fmt, val * multi[cmdin_p[i].multindex]); } for (i = 0; cmdout_p[i].var; i++) { int16_t val; ret = do_command(cmdout_p[i].cmd, reply, 6); if (ret < 8) { continue; } val = (unsigned char)reply[5]; val <<= 8; val += (unsigned char)reply[6]; dstate_setinfo(cmdout_p[i].var, cmdout_p[i].fmt, val * multi[cmdout_p[i].multindex]); } status_init(); ret = do_command(cmd_bitfield1, reply, 6); if (ret < 8) { upslogx(LOG_ERR, "Failed reading bitfield #1"); dstate_datastale(); return; } if (reply[5] & (1<<0)) { /* ON_BATTERY */ status_set("OB"); } else { status_set("OL"); } val = dstate_getinfo("battery.current"); if (val) { if (atof(val) > 0.05) { status_set("CHRG"); } if (atof(val) < -0.05) { status_set("DISCHRG"); } } ret = do_command(cmd_bitfield2, reply, 6); if (ret < 8) { upslogx(LOG_ERR, "Failed reading bitfield #2"); dstate_datastale(); return; } if (reply[6] & (1<<0)) { /* ON_BYPASS */ status_set("BYPASS"); } if (reply[6] & (1<<5)) { /* REPLACE_BATTERY */ status_set("RB"); } if (reply[6] & (1<<6)) { /* BOOST_ON */ status_set("BOOST"); } if (reply[5] & (1<<1)) { /* BUCK_ON */ status_set("TRIM"); } ret = do_command(cmd_bitfield3, reply, 6); if (ret < 8) { upslogx(LOG_ERR, "Failed reading bitfield #3"); dstate_datastale(); return; } if (reply[6] & (1<<0) ) { /* UPS_OVERLOAD */ status_set("OVER"); } if (reply[6] & (1<<5) ) { /* LOW_BATTERY */ status_set("LB"); } status_commit(); dstate_dataok(); } void upsdrv_shutdown(void) { char reply[8]; if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) && (do_command(cmd_setOutOffDelay, reply, 8) != -1) && (do_command(cmd_sysLoadKey, reply, 6) != -1) && (do_command(cmd_shutdown, reply, 8) != -1)) upslogx(LOG_ERR, "Failed to shutdown UPS"); } static int instcmd(const char *cmdname, const char *extra) { /* if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } */ upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { /* if (!strcasecmp(varname, "ups.test.interval")) { ser_send_buf(upsfd, ...); return STAT_SET_HANDLED; } */ upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar (VAR_VALUE, "baudrate", "serial line speed"); } void upsdrv_initups(void) { const char *val = getval("baudrate"); speed_t baudrate = B2400; if (val) { switch (atoi(val)) { case 1200: baudrate = B1200; break; case 2400: baudrate = B2400; break; case 4800: baudrate = B4800; break; case 9600: baudrate = B9600; break; case 19200: baudrate = B19200; break; default: fatalx(EXIT_FAILURE, "Baudrate [%s] unsupported", val); } } upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, baudrate); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/bcmxcp_ser.c0000644000175000017500000001663512640444140013234 00000000000000#include "main.h" #include "bcmxcp.h" #include "bcmxcp_io.h" #include "serial.h" #define SUBDRIVER_NAME "RS-232 communication subdriver" #define SUBDRIVER_VERSION "0.20" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { SUBDRIVER_NAME, SUBDRIVER_VERSION, NULL, 0, { NULL } }; #define PW_MAX_BAUD 5 struct pw_baud_rate { int rate; int name; } pw_baud_rates[] = { { B19200, 19200 }, { B9600, 9600 }, { B4800, 4800 }, { B2400, 2400 }, { B1200, 1200 }, /* end of structure. */ { 0, 0 } }; unsigned char AUT[4] = {0xCF, 0x69, 0xE8, 0xD5}; /* Autorisation command */ static void send_command(unsigned char *command, int command_length) { int retry = 0, sent; unsigned char sbuf[128]; /* Prepare the send buffer */ sbuf[0] = PW_COMMAND_START_BYTE; sbuf[1] = (unsigned char)(command_length); memcpy(sbuf+2, command, command_length); command_length += 2; /* Add checksum */ sbuf[command_length] = calc_checksum(sbuf); command_length += 1; upsdebug_hex (3, "send_command", sbuf, command_length); while (retry++ < PW_MAX_TRY) { if (retry == PW_MAX_TRY) { ser_send_char(upsfd, 0x1d); /* last retry is preceded by a ESC.*/ usleep(250000); } sent = ser_send_buf(upsfd, sbuf, command_length); if (sent == command_length) { return; } } } void send_read_command(unsigned char command) { send_command(&command, 1); } void send_write_command(unsigned char *command, int command_length) { send_command(command, command_length); } /* get the answer of a command from the ups. And check that the answer is for this command */ int get_answer(unsigned char *data, unsigned char command) { unsigned char my_buf[128]; /* packet has a maximum length of 121+5 bytes */ int length, end_length = 0, res, endblock = 0, start = 0; unsigned char block_number, sequence, pre_sequence = 0; while (endblock != 1){ do { /* Read PW_COMMAND_START_BYTE byte */ res = ser_get_char(upsfd, my_buf, 1, 0); if (res != 1) { upsdebugx(1,"Receive error (PW_COMMAND_START_BYTE): %d, cmd=%x!!!\n", res, command); return -1; } start++; } while ((my_buf[0] != PW_COMMAND_START_BYTE) && (start < 128)); if (start == 128) { ser_comm_fail("Receive error (PW_COMMAND_START_BYTE): packet not on start!!%x\n", my_buf[0]); return -1; } /* Read block number byte */ res = ser_get_char(upsfd, my_buf+1, 1, 0); if (res != 1) { ser_comm_fail("Receive error (Block number): %d!!!\n", res); return -1; } block_number = (unsigned char)my_buf[1]; if (command <= 0x43) { if ((command - 0x30) != block_number){ ser_comm_fail("Receive error (Request command): %x!!!\n", block_number); return -1; } } if (command >= 0x89) { if ((command == 0xA0) && (block_number != 0x01)){ ser_comm_fail("Receive error (Requested only mode command): %x!!!\n", block_number); return -1; } if ((command != 0xA0) && (block_number != 0x09)){ ser_comm_fail("Receive error (Control command): %x!!!\n", block_number); return -1; } } /* Read data length byte */ res = ser_get_char(upsfd, my_buf+2, 1, 0); if (res != 1) { ser_comm_fail("Receive error (length): %d!!!\n", res); return -1; } length = (unsigned char)my_buf[2]; if (length < 1) { ser_comm_fail("Receive error (length): packet length %x!!!\n", length); return -1; } /* Read sequence byte */ res = ser_get_char(upsfd, my_buf+3, 1, 0); if (res != 1) { ser_comm_fail("Receive error (sequence): %d!!!\n", res); return -1; } sequence = (unsigned char)my_buf[3]; if ((sequence & 0x80) == 0x80) { endblock = 1; } if ((sequence & 0x07) != (pre_sequence + 1)) { ser_comm_fail("Not the right sequence received %x!!!\n", sequence); return -1; } pre_sequence = sequence; /* Try to read all the remainig bytes */ res = ser_get_buf_len(upsfd, my_buf+4, length, 1, 0); if (res != length) { ser_comm_fail("Receive error (data): got %d bytes instead of %d!!!\n", res, length); return -1; } /* Get the checksum byte */ res = ser_get_char(upsfd, my_buf+(4+length), 1, 0); if (res != 1) { ser_comm_fail("Receive error (checksum): %x!!!\n", res); return -1; } /* now we have the whole answer from the ups, we can checksum it */ if (!checksum_test(my_buf)) { ser_comm_fail("checksum error! "); return -1; } memcpy(data+end_length, my_buf+4, length); end_length += length; } upsdebug_hex (5, "get_answer", data, end_length); ser_comm_good(); return end_length; } static int command_sequence(unsigned char *command, int command_length, unsigned char *answer) { int bytes_read, retry = 0; while (retry++ < PW_MAX_TRY) { if (retry == PW_MAX_TRY) { ser_flush_in(upsfd, "", 0); } send_write_command(command, command_length); bytes_read = get_answer(answer, *command); if (bytes_read > 0) { return bytes_read; } } return -1; } /* Sends a single command (length=1). and get the answer */ int command_read_sequence(unsigned char command, unsigned char *answer) { int bytes_read; bytes_read = command_sequence(&command, 1, answer); if (bytes_read < 1) { ser_comm_fail("Error executing command"); } return bytes_read; } /* Sends a setup command (length > 1) */ int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer) { int bytes_read; bytes_read = command_sequence(command, command_length, answer); if (bytes_read < 1) { ser_comm_fail("Error executing command"); } return bytes_read; } void upsdrv_comm_good() { ser_comm_good(); } void pw_comm_setup(const char *port) { unsigned char command = PW_SET_REQ_ONLY_MODE; unsigned char id_command = PW_ID_BLOCK_REQ; unsigned char answer[256]; int i = 0, baud, mybaud = 0, ret = -1; if (getval("baud_rate") != NULL) { baud = atoi(getval("baud_rate")); for(i = 0; i < PW_MAX_BAUD; i++) { if (baud == pw_baud_rates[i].name) { mybaud = pw_baud_rates[i].rate; break; } } if (mybaud == 0) { fatalx(EXIT_FAILURE, "Specified baudrate \"%s\" is invalid!", getval("baud_rate")); } ser_set_speed(upsfd, device_path, mybaud); ser_send_char(upsfd, 0x1d); /* send ESC to take it out of menu */ usleep(90000); send_write_command(AUT, 4); usleep(500000); ret = command_sequence(&command, 1, answer); if (ret <= 0) { usleep(500000); ret = command_sequence(&id_command, 1, answer); } if (ret > 0) { upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %d", port, baud); return; } upslogx(LOG_ERR, "No response from UPS on %s with baudrate %d", port, baud); } upslogx(LOG_INFO, "Attempting to autodect baudrate"); for (i=0; i 0) { upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %d", port, pw_baud_rates[i].name); return; } upsdebugx(2, "No response from UPS on %s with baudrate %d", port, pw_baud_rates[i].name); } fatalx(EXIT_FAILURE, "Can't connect to the UPS on port %s!\n", port); } void upsdrv_initups(void) { upsfd = ser_open(device_path); pw_comm_setup(device_path); } void upsdrv_cleanup(void) { /* free(dynamic_mem); */ ser_close(upsfd, device_path); } void upsdrv_reconnect(void) { } nut-2.7.4/drivers/riello.h0000644000175000017500000001200412640444140012364 00000000000000/* * riello.h: defines/macros for Riello protocol based UPSes * * Documents describing the protocol implemented by this driver can be * found online at: * * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf * * Copyright (C) 2012 - Elio Parisi * * 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 * * Reference of the derivative work: blazer driver */ #ifndef dev_dataH #define dev_dataH #include #define CTRL_RETRIES 50 #define CTRL_TIMEOUT 100 #define USB_ENDPOINT_IN 0x80 #define USB_ENDPOINT_OUT 0x00 #define MAX_READ_WRITE (16 * 1024) #define USB_WRITE_DELAY 200 #define MAXTRIES 3 #define COUNTLOST 10 #define DEV_RIELLOSENTRY 14 #define DEV_RIELLOGPSER 21 #define LENGTH_GI 68 #define LENGTH_GN 34 #define LENGTH_RS_MM 42 #define LENGTH_RS_TM 48 #define LENGTH_RS_TT 64 #define LENGTH_RE 70 #define LENGTH_RC 56 #define LENGTH_DEF 12 #define SENTR_EXT176 176 #define SENTR_ALSO240 240 #define SENTR_ONLY192 192 #define BUFFER_SIZE 220 typedef struct { uint16_t SWversion; uint16_t Model; uint16_t Uinp1; uint16_t Uinp2; uint16_t Uinp3; uint16_t Iinp1; uint16_t Iinp2; uint16_t Iinp3; uint16_t Finp; uint16_t Uout1; uint16_t Uout2; uint16_t Uout3; uint16_t Iout1; uint16_t Iout2; uint16_t Iout3; uint16_t Pout1; uint16_t Pout2; uint16_t Pout3; uint16_t Ipout1; uint16_t Ipout2; uint16_t Ipout3; uint16_t Fout; uint16_t BatTime; uint16_t BatCap; uint16_t Ubat; uint16_t Ibat; uint16_t Tsystem; uint16_t NomBatCap; uint16_t Ubypass1; uint16_t Ubypass2; uint16_t Ubypass3; uint16_t Fbypass; uint16_t LockUPS; uint8_t AlarmCode[4]; char AlarmCodeT[12]; uint8_t StatusCode[12]; char StatusCodeT[42]; char Identification[18]; char ModelStr[18]; char Version[14]; uint16_t NomPowerKVA; uint16_t NomPowerKW; uint16_t NomUbat; uint16_t NumBat; uint16_t UbatPerc; uint16_t NominalUout; uint16_t Boost; uint16_t Buck; uint8_t Identif_bytes[12]; uint16_t NomFout; uint32_t Pout1VA; uint32_t Pout2VA; uint32_t Pout3VA; uint32_t Pout1W; uint32_t Pout2W; uint32_t Pout3W; } TRielloData; /* CRC and Checksum functions */ uint16_t riello_calc_CRC(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum); void riello_create_crc(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum); uint8_t riello_test_crc(uint8_t type, uint8_t *buff, uint16_t size, uint8_t chacksum); uint8_t riello_test_bit(uint8_t *basic_address, uint8_t bit); /* send GPSER command functions */ uint8_t riello_prepare_gi(uint8_t* buffer); uint8_t riello_prepare_gn(uint8_t* buffer, uint8_t gpser_error_control); uint8_t riello_prepare_rs(uint8_t* buffer, uint8_t gpser_error_control); uint8_t riello_prepare_re(uint8_t* buffer, uint8_t gpser_error_control); uint8_t riello_prepare_rc(uint8_t* buffer, uint8_t gpser_error_control); uint8_t riello_prepare_cs(uint8_t* buffer, uint8_t gpser_error_control, uint16_t delay); uint8_t riello_prepare_cr(uint8_t* buffer, uint8_t gpser_error_control, uint16_t delay); uint8_t riello_prepare_cd(uint8_t* buffer, uint8_t gpser_error_control); uint8_t riello_prepare_tp(uint8_t* buffer, uint8_t gpser_error_control); uint8_t riello_prepare_tb(uint8_t* buffer, uint8_t gpser_error_control); /* send SENTR command functions */ uint8_t riello_prepare_shutsentr(uint8_t* buffer, uint16_t delay); uint8_t riello_prepare_cancelsentr(uint8_t* buffer); uint8_t riello_prepare_setrebsentr(uint8_t* buffer, uint16_t delay); uint8_t riello_prepare_rebsentr(uint8_t* buffer, uint16_t delay); uint8_t riello_prepare_tbsentr(uint8_t* buffer); /* parse GPSER ups responses */ void riello_parse_gi(uint8_t* buffer, TRielloData* data); void riello_parse_gn(uint8_t* buffer, TRielloData* data); void riello_parse_rs(uint8_t* buffer, TRielloData* data, uint8_t numread); void riello_parse_re(uint8_t* buffer, TRielloData* data); void riello_parse_rc(uint8_t* buffer, TRielloData* data); /* parse SENTR ups responses */ void riello_parse_sentr(uint8_t* buffer, TRielloData* data); /* communication functions */ void riello_init_serial(); uint8_t riello_header(uint8_t type, uint8_t a, uint8_t* length); uint8_t riello_tail(uint8_t type, uint8_t length); uint8_t riello_test_nak(uint8_t type, uint8_t* buffer); void riello_parse_serialport(uint8_t typedev, uint8_t* buffer, uint8_t checksum); void riello_comm_setup(const char *port); #endif nut-2.7.4/drivers/dstate.c0000644000175000017500000004277712640473702012407 00000000000000/* dstate.c - Network UPS Tools driver-side state management Copyright (C) 2003 Russell Kroll 2008 Arjen de Korte 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "common.h" #include "dstate.h" #include "state.h" #include "parseconf.h" static int sockfd = -1, stale = 1, alarm_active = 0, ignorelb = 0; static char *sockfn = NULL; static char status_buf[ST_MAX_VALUE_LEN], alarm_buf[LARGEBUF]; static st_tree_t *dtree_root = NULL; static conn_t *connhead = NULL; static cmdlist_t *cmdhead = NULL; struct ups_handler upsh; /* this may be a frequent stumbling point for new users, so be verbose here */ static void sock_fail(const char *fn) { int sockerr; struct passwd *user; /* save this so it doesn't get overwritten */ sockerr = errno; /* dispense with the usual upslog stuff since we have stderr here */ printf("\nFatal error: unable to create listener socket\n\n"); printf("bind %s failed: %s\n", fn, strerror(sockerr)); user = getpwuid(getuid()); if (!user) { fatal_with_errno(EXIT_FAILURE, "getpwuid"); } /* deal with some common problems */ switch (errno) { case EACCES: printf("\nCurrent user: %s (UID %d)\n\n", user->pw_name, (int)user->pw_uid); printf("Things to try:\n\n"); printf(" - set different owners or permissions on %s\n\n", dflt_statepath()); printf(" - run this as some other user " "(try -u )\n"); break; case ENOENT: printf("\nThings to try:\n\n"); printf(" - mkdir %s\n", dflt_statepath()); break; case ENOTDIR: printf("\nThings to try:\n\n"); printf(" - rm %s\n\n", dflt_statepath()); printf(" - mkdir %s\n", dflt_statepath()); break; } /* * there - that wasn't so bad. every helpful line of code here * prevents one more "help me" mail to the list a year from now */ printf("\n"); fatalx(EXIT_FAILURE, "Exiting."); } static int sock_open(const char *fn) { int ret, fd; struct sockaddr_un ssaddr; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { fatal_with_errno(EXIT_FAILURE, "Can't create a unix domain socket"); } /* keep this around for the unlink() when exiting */ sockfn = xstrdup(fn); ssaddr.sun_family = AF_UNIX; snprintf(ssaddr.sun_path, sizeof(ssaddr.sun_path), "%s", sockfn); unlink(sockfn); /* group gets access so upsd can be a different user but same group */ umask(0007); ret = bind(fd, (struct sockaddr *) &ssaddr, sizeof ssaddr); if (ret < 0) { sock_fail(sockfn); } ret = chmod(sockfn, 0660); if (ret < 0) { fatal_with_errno(EXIT_FAILURE, "chmod(%s, 0660) failed", sockfn); } ret = listen(fd, DS_LISTEN_BACKLOG); if (ret < 0) { fatal_with_errno(EXIT_FAILURE, "listen(%d, %d) failed", fd, DS_LISTEN_BACKLOG); } return fd; } static void sock_disconnect(conn_t *conn) { close(conn->fd); pconf_finish(&conn->ctx); if (conn->prev) { conn->prev->next = conn->next; } else { connhead = conn->next; } if (conn->next) { conn->next->prev = conn->prev; } else { /* conntail = conn->prev; */ } free(conn); } static void send_to_all(const char *fmt, ...) { int ret; char buf[ST_SOCK_BUF_LEN]; va_list ap; conn_t *conn, *cnext; va_start(ap, fmt); ret = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (ret < 1) { upsdebugx(2, "%s: nothing to write", __func__); return; } upsdebugx(5, "%s: %.*s", __func__, ret-1, buf); for (conn = connhead; conn; conn = cnext) { cnext = conn->next; ret = write(conn->fd, buf, strlen(buf)); if (ret != (int)strlen(buf)) { upsdebugx(1, "write %d bytes to socket %d failed", (int)strlen(buf), conn->fd); sock_disconnect(conn); } } } static int send_to_one(conn_t *conn, const char *fmt, ...) { int ret; va_list ap; char buf[ST_SOCK_BUF_LEN]; va_start(ap, fmt); ret = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (ret < 1) { upsdebugx(2, "%s: nothing to write", __func__); return 1; } upsdebugx(5, "%s: %.*s", __func__, ret-1, buf); ret = write(conn->fd, buf, strlen(buf)); if (ret != (int)strlen(buf)) { upsdebugx(1, "write %d bytes to socket %d failed", (int)strlen(buf), conn->fd); sock_disconnect(conn); return 0; /* failed */ } return 1; /* OK */ } static void sock_connect(int sock) { int fd, ret; conn_t *conn; struct sockaddr_un sa; #if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) int salen; #else socklen_t salen; #endif salen = sizeof(sa); fd = accept(sock, (struct sockaddr *) &sa, &salen); if (fd < 0) { upslog_with_errno(LOG_ERR, "accept on unix fd failed"); return; } /* enable nonblocking I/O */ if (!do_synchronous) { ret = fcntl(fd, F_GETFL, 0); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on unix fd failed"); close(fd); return; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on unix fd failed"); close(fd); return; } } conn = xcalloc(1, sizeof(*conn)); conn->fd = fd; pconf_init(&conn->ctx, NULL); if (connhead) { conn->next = connhead; connhead->prev = conn; } connhead = conn; upsdebugx(3, "new connection on fd %d", fd); } static int st_tree_dump_conn(st_tree_t *node, conn_t *conn) { int ret; enum_t *etmp; range_t *rtmp; if (!node) { return 1; /* not an error */ } if (node->left) { ret = st_tree_dump_conn(node->left, conn); if (!ret) { return 0; /* write failed in the child */ } } if (!send_to_one(conn, "SETINFO %s \"%s\"\n", node->var, node->val)) { return 0; /* write failed, bail out */ } /* send any enums */ for (etmp = node->enum_list; etmp; etmp = etmp->next) { if (!send_to_one(conn, "ADDENUM %s \"%s\"\n", node->var, etmp->val)) { return 0; } } /* send any ranges */ for (rtmp = node->range_list; rtmp; rtmp = rtmp->next) { if (!send_to_one(conn, "ADDRANGE %s %i %i\n", node->var, rtmp->min, rtmp->max)) { return 0; } } /* provide any auxiliary data */ if (node->aux) { if (!send_to_one(conn, "SETAUX %s %d\n", node->var, node->aux)) { return 0; } } /* finally report any flags */ if (node->flags) { char flist[SMALLBUF]; /* build the list */ snprintf(flist, sizeof(flist), "%s", node->var); if (node->flags & ST_FLAG_RW) { snprintfcat(flist, sizeof(flist), " RW"); } if (node->flags & ST_FLAG_STRING) { snprintfcat(flist, sizeof(flist), " STRING"); } if (!send_to_one(conn, "SETFLAGS %s\n", flist)) { return 0; } } if (node->right) { return st_tree_dump_conn(node->right, conn); } return 1; /* everything's OK here ... */ } static int cmd_dump_conn(conn_t *conn) { cmdlist_t *cmd; for (cmd = cmdhead; cmd; cmd = cmd->next) { if (!send_to_one(conn, "ADDCMD %s\n", cmd->name)) { return 0; } } return 1; } static int sock_arg(conn_t *conn, int numarg, char **arg) { if (numarg < 1) { return 0; } if (!strcasecmp(arg[0], "DUMPALL")) { /* first thing: the staleness flag */ if ((stale == 1) && !send_to_one(conn, "DATASTALE\n")) { return 1; } if (!st_tree_dump_conn(dtree_root, conn)) { return 1; } if (!cmd_dump_conn(conn)) { return 1; } if ((stale == 0) && !send_to_one(conn, "DATAOK\n")) { return 1; } send_to_one(conn, "DUMPDONE\n"); return 1; } if (!strcasecmp(arg[0], "PING")) { send_to_one(conn, "PONG\n"); return 1; } if (numarg < 2) { return 0; } /* INSTCMD []*/ if (!strcasecmp(arg[0], "INSTCMD")) { /* try the new handler first if present */ if (upsh.instcmd) { if (numarg > 2) { upsh.instcmd(arg[1], arg[2]); return 1; } upsh.instcmd(arg[1], NULL); return 1; } upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); return 1; } if (numarg < 3) { return 0; } /* SET */ if (!strcasecmp(arg[0], "SET")) { /* try the new handler first if present */ if (upsh.setvar) { upsh.setvar(arg[1], arg[2]); return 1; } upslogx(LOG_NOTICE, "Got SET, but driver lacks a handler"); return 1; } /* unknown */ return 0; } static void sock_read(conn_t *conn) { int i, ret; char buf[SMALLBUF]; ret = read(conn->fd, buf, sizeof(buf)); if (ret < 0) { switch(errno) { case EINTR: case EAGAIN: return; default: sock_disconnect(conn); return; } } for (i = 0; i < ret; i++) { switch(pconf_char(&conn->ctx, buf[i])) { case 0: /* nothing to parse yet */ continue; case 1: /* try to use it, and complain about unknown commands */ if (!sock_arg(conn, conn->ctx.numargs, conn->ctx.arglist)) { size_t arg; upslogx(LOG_INFO, "Unknown command on socket: "); for (arg = 0; arg < conn->ctx.numargs; arg++) { upslogx(LOG_INFO, "arg %d: %s", (int)arg, conn->ctx.arglist[arg]); } } continue; default: /* nothing parsed */ upslogx(LOG_NOTICE, "Parse error on sock: %s", conn->ctx.errmsg); return; } } } static void sock_close(void) { conn_t *conn, *cnext; if (sockfd != -1) { close(sockfd); sockfd = -1; if (sockfn) { unlink(sockfn); free(sockfn); sockfn = NULL; } } for (conn = connhead; conn; conn = cnext) { cnext = conn->next; sock_disconnect(conn); } connhead = NULL; /* conntail = NULL; */ } /* interface */ void dstate_init(const char *prog, const char *devname) { char sockname[SMALLBUF]; /* do this here for now */ signal(SIGPIPE, SIG_IGN); if (devname) { snprintf(sockname, sizeof(sockname), "%s/%s-%s", dflt_statepath(), prog, devname); } else { snprintf(sockname, sizeof(sockname), "%s/%s", dflt_statepath(), prog); } sockfd = sock_open(sockname); upsdebugx(2, "dstate_init: sock %s open on fd %d", sockname, sockfd); } /* returns 1 if timeout expired or data is available on UPS fd, 0 otherwise */ int dstate_poll_fds(struct timeval timeout, int extrafd) { int ret, maxfd, overrun = 0; fd_set rfds; struct timeval now; conn_t *conn, *cnext; FD_ZERO(&rfds); FD_SET(sockfd, &rfds); maxfd = sockfd; if (extrafd != -1) { FD_SET(extrafd, &rfds); if (extrafd > maxfd) { maxfd = extrafd; } } for (conn = connhead; conn; conn = conn->next) { FD_SET(conn->fd, &rfds); if (conn->fd > maxfd) { maxfd = conn->fd; } } gettimeofday(&now, NULL); /* number of microseconds should always be positive */ if (timeout.tv_usec < now.tv_usec) { timeout.tv_sec -= 1; timeout.tv_usec += 1000000; } if (timeout.tv_sec < now.tv_sec) { timeout.tv_sec = 0; timeout.tv_usec = 0; overrun = 1; /* no time left */ } else { timeout.tv_sec -= now.tv_sec; timeout.tv_usec -= now.tv_usec; } ret = select(maxfd + 1, &rfds, NULL, NULL, &timeout); if (ret == 0) { return 1; /* timer expired */ } if (ret < 0) { switch (errno) { case EINTR: case EAGAIN: /* ignore interruptions from signals */ break; default: upslog_with_errno(LOG_ERR, "select unix sockets failed"); } return overrun; } if (FD_ISSET(sockfd, &rfds)) { sock_connect(sockfd); } for (conn = connhead; conn; conn = cnext) { cnext = conn->next; if (FD_ISSET(conn->fd, &rfds)) { sock_read(conn); } } /* tell the caller if that fd woke up */ if ((extrafd != -1) && (FD_ISSET(extrafd, &rfds))) { return 1; } return overrun; } int dstate_setinfo(const char *var, const char *fmt, ...) { int ret; char value[ST_MAX_VALUE_LEN]; va_list ap; va_start(ap, fmt); vsnprintf(value, sizeof(value), fmt, ap); va_end(ap); ret = state_setinfo(&dtree_root, var, value); if (ret == 1) { send_to_all("SETINFO %s \"%s\"\n", var, value); } return ret; } int dstate_addenum(const char *var, const char *fmt, ...) { int ret; char value[ST_MAX_VALUE_LEN]; va_list ap; va_start(ap, fmt); vsnprintf(value, sizeof(value), fmt, ap); va_end(ap); ret = state_addenum(dtree_root, var, value); if (ret == 1) { send_to_all("ADDENUM %s \"%s\"\n", var, value); } return ret; } int dstate_addrange(const char *var, const int min, const int max) { int ret; ret = state_addrange(dtree_root, var, min, max); if (ret == 1) { send_to_all("ADDRANGE %s %i %i\n", var, min, max); } return ret; } void dstate_setflags(const char *var, int flags) { st_tree_t *sttmp; char flist[SMALLBUF]; /* find the dtree node for var */ sttmp = state_tree_find(dtree_root, var); if (!sttmp) { upslogx(LOG_ERR, "%s: base variable (%s) does not exist", __func__, var); return; } if (sttmp->flags & ST_FLAG_IMMUTABLE) { upslogx(LOG_WARNING, "%s: base variable (%s) is immutable", __func__, var); return; } if (sttmp->flags == flags) { return; /* no change */ } sttmp->flags = flags; /* build the list */ snprintf(flist, sizeof(flist), "%s", var); if (flags & ST_FLAG_RW) { snprintfcat(flist, sizeof(flist), " RW"); } if (flags & ST_FLAG_STRING) { snprintfcat(flist, sizeof(flist), " STRING"); } /* update listeners */ send_to_all("SETFLAGS %s\n", flist); } void dstate_setaux(const char *var, int aux) { st_tree_t *sttmp; /* find the dtree node for var */ sttmp = state_tree_find(dtree_root, var); if (!sttmp) { upslogx(LOG_ERR, "dstate_setaux: base variable (%s) does not exist", var); return; } if (sttmp->aux == aux) { return; /* no change */ } sttmp->aux = aux; /* update listeners */ send_to_all("SETAUX %s %d\n", var, aux); } const char *dstate_getinfo(const char *var) { return state_getinfo(dtree_root, var); } void dstate_addcmd(const char *cmdname) { int ret; ret = state_addcmd(&cmdhead, cmdname); /* update listeners */ if (ret == 1) { send_to_all("ADDCMD %s\n", cmdname); } } int dstate_delinfo(const char *var) { int ret; ret = state_delinfo(&dtree_root, var); /* update listeners */ if (ret == 1) { send_to_all("DELINFO %s\n", var); } return ret; } int dstate_delenum(const char *var, const char *val) { int ret; ret = state_delenum(dtree_root, var, val); /* update listeners */ if (ret == 1) { send_to_all("DELENUM %s \"%s\"\n", var, val); } return ret; } int dstate_delrange(const char *var, const int min, const int max) { int ret; ret = state_delrange(dtree_root, var, min, max); /* update listeners */ if (ret == 1) { send_to_all("DELRANGE %s \"%i %i\"\n", var, min, max); } return ret; } int dstate_delcmd(const char *cmd) { int ret; ret = state_delcmd(&cmdhead, cmd); /* update listeners */ if (ret == 1) { send_to_all("DELCMD %s\n", cmd); } return ret; } void dstate_free(void) { state_infofree(dtree_root); dtree_root = NULL; state_cmdfree(cmdhead); cmdhead = NULL; sock_close(); } const st_tree_t *dstate_getroot(void) { return dtree_root; } const cmdlist_t *dstate_getcmdlist(void) { return cmdhead; } void dstate_dataok(void) { if (stale == 1) { stale = 0; send_to_all("DATAOK\n"); } } void dstate_datastale(void) { if (stale == 0) { stale = 1; send_to_all("DATASTALE\n"); } } int dstate_is_stale(void) { return stale; } /* ups.status management functions - reducing duplication in the drivers */ /* clean out the temp space for a new pass */ void status_init(void) { if (dstate_getinfo("driver.flag.ignorelb")) { ignorelb = 1; } memset(status_buf, 0, sizeof(status_buf)); } /* add a status element */ void status_set(const char *buf) { if (ignorelb && !strcasecmp(buf, "LB")) { upsdebugx(2, "%s: ignoring LB flag from device", __func__); return; } /* separate with a space if multiple elements are present */ if (strlen(status_buf) > 0) { snprintfcat(status_buf, sizeof(status_buf), " %s", buf); } else { snprintfcat(status_buf, sizeof(status_buf), "%s", buf); } } /* write the status_buf into the externally visible dstate storage */ void status_commit(void) { while (ignorelb) { const char *val, *low; val = dstate_getinfo("battery.charge"); low = dstate_getinfo("battery.charge.low"); if (val && low && (strtol(val, NULL, 10) < strtol(low, NULL, 10))) { snprintfcat(status_buf, sizeof(status_buf), " LB"); upsdebugx(2, "%s: appending LB flag [charge '%s' below '%s']", __func__, val, low); break; } val = dstate_getinfo("battery.runtime"); low = dstate_getinfo("battery.runtime.low"); if (val && low && (strtol(val, NULL, 10) < strtol(low, NULL, 10))) { snprintfcat(status_buf, sizeof(status_buf), " LB"); upsdebugx(2, "%s: appending LB flag [runtime '%s' below '%s']", __func__, val, low); break; } /* LB condition not detected */ break; } if (alarm_active) { dstate_setinfo("ups.status", "ALARM %s", status_buf); } else { dstate_setinfo("ups.status", "%s", status_buf); } } /* similar handlers for ups.alarm */ void alarm_init(void) { memset(alarm_buf, 0, sizeof(alarm_buf)); } void alarm_set(const char *buf) { if (strlen(alarm_buf) > 0) { snprintfcat(alarm_buf, sizeof(alarm_buf), " %s", buf); } else { snprintfcat(alarm_buf, sizeof(alarm_buf), "%s", buf); } } /* write the status_buf into the info array */ void alarm_commit(void) { if (strlen(alarm_buf) > 0) { dstate_setinfo("ups.alarm", "%s", alarm_buf); alarm_active = 1; } else { dstate_delinfo("ups.alarm"); alarm_active = 0; } } nut-2.7.4/drivers/apcsmart_tabs.h0000644000175000017500000000674412640444140013737 00000000000000/* * apcsmart_tabs.h - tables for apcsmart driver * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __apcsmart_tabs_h__ #define __apcsmart_tabs_h__ #include "main.h" #define APC_TABLE_VERSION "version 3.1" /* common flags */ #define APC_PRESENT 0x00000001 /* capability seen on this UPS */ /* instant commands' flags */ #define APC_NASTY 0x00000002 /* Nasty command - must be reconfirmed */ #define APC_REPEAT 0x00000004 /* Command needs sending twice */ /* variables' flags */ #define APC_POLL 0x00000100 /* poll this variable regularly */ #define APC_RW 0x00000200 /* read-write variable */ #define APC_ENUM 0x00000400 /* enumerated type variable */ #define APC_STRING 0x00000800 /* string variable */ #define APC_MULTI 0x00001000 /* there're other vars like that */ #define APC_PACK 0x00002000 /* packed variable */ #define APC_PACK_MAX 4 /* max count of subfields in packed var */ /* variables' format */ #define APC_F_MASK 0xFF000000 /* Mask for apc data formats */ #define APC_F_LEAVE 0x00000000 /* Just pass this through */ #define APC_F_PERCENT 0x01000000 /* Data in a percent format */ #define APC_F_VOLT 0x02000000 /* Data in a voltage format */ #define APC_F_AMP 0x03000000 /* Data in a current/amp format */ #define APC_F_CELSIUS 0x04000000 /* Data in a temp/C format */ #define APC_F_HEX 0x05000000 /* Data in a hex number format */ #define APC_F_DEC 0x06000000 /* Data in a decimal format */ #define APC_F_SECONDS 0x07000000 /* Time in seconds */ #define APC_F_MINUTES 0x08000000 /* Time in minutes */ #define APC_F_HOURS 0x09000000 /* Time in hours */ #define APC_F_REASON 0x0A000000 /* Reason of transfer */ /* instant commands */ #define APC_CMD_CUSTOM 0 /* command uses separate function */ #define APC_CMD_OFF 'Z' #define APC_CMD_ON '\016' /* ^N */ #define APC_CMD_FPTEST 'A' #define APC_CMD_SIMPWF 'U' #define APC_CMD_BTESTTOGGLE 'W' #define APC_CMD_GRACEDOWN '@' #define APC_CMD_SOFTDOWN 'S' #define APC_CMD_SHUTDOWN 'K' #define APC_CMD_CALTOGGLE 'D' #define APC_CMD_BYPTOGGLE '^' typedef struct { const char *name; /* the variable name */ char cmd; /* variable character */ unsigned int flags; /* various flags */ const char *regex; /* variable must match this regex */ size_t nlen0; /* var name + null len */ int cnt; /* curr. count of subs */ } apc_vartab_t; typedef struct { const char *name, *ext; char cmd; int flags; } apc_cmdtab_t; typedef struct { const char *firmware; const char *cmdchars; int flags; } apc_compattab_t; extern apc_vartab_t apc_vartab[]; extern apc_cmdtab_t apc_cmdtab[]; extern apc_compattab_t apc_compattab[]; extern upsdrv_info_t apc_tab_info; #endif nut-2.7.4/drivers/mge-mib.c0000644000175000017500000003136512667537407012443 00000000000000/* mge-mib.c - data to monitor MGE UPS SYSTEMS SNMP devices with NUT * * Copyright (C) * 2002-2012 Arnaud Quette * 2002-2003 J.W. Hoogervorst * * Sponsored by MGE UPS SYSTEMS * MGE Office Protection Systems * Eaton * * 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 "mge-mib.h" #define MGE_MIB_VERSION "0.5" /* TODO: * - MGE PDU MIB and sysOID (".1.3.6.1.4.1.705.2") */ /* SNMP OIDs set */ #define MGE_BASE_OID ".1.3.6.1.4.1.705.1" #define MGE_SYSOID MGE_BASE_OID #define MGE_OID_MODEL_NAME MGE_BASE_OID ".1.1.0" static info_lkp_t mge_lowbatt_info[] = { { 1, "LB" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_onbatt_info[] = { { 1, "OB" }, { 2, "OL" }, { 0, NULL } }; static info_lkp_t mge_bypass_info[] = { { 1, "BYPASS" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_boost_info[] = { { 1, "BOOST" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_trim_info[] = { { 1, "TRIM" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_overload_info[] = { { 1, "OVER" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_replacebatt_info[] = { { 1, "RB" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_output_util_off_info[] = { { 1, "OFF" }, { 2, "" }, { 0, NULL } }; static info_lkp_t mge_transfer_reason_info[] = { { 1, "" }, { 2, "input voltage out of range" }, { 3, "input frequency out of range" }, { 4, "utility off" }, { 0, NULL } }; static info_lkp_t ietf_test_result_info[] = { { 1, "done and passed" }, { 2, "done and warning" }, { 3, "done and error" }, { 4, "aborted" }, { 5, "in progress" }, { 6, "no test initiated" }, { 0, NULL } }; static info_lkp_t ietf_beeper_status_info[] = { { 1, "disabled" }, { 2, "enabled" }, { 3, "muted" }, { 0, NULL } }; static info_lkp_t ietf_yes_no_info[] = { { 1, "yes" }, { 2, "no" }, { 0, NULL } }; /* FIXME: the below may introduce status redundancy, that needs to be * adressed by the driver, as for usbhid-ups! */ static info_lkp_t ietf_power_source_info[] = { { 1, "" /* other */ }, { 2, "OFF" /* none */ }, #if 0 { 3, "OL" /* normal */ }, #endif { 4, "BYPASS" /* bypass */ }, { 5, "OB" /* battery */ }, { 6, "BOOST" /* booster */ }, { 7, "TRIM" /* reducer */ }, { 0, NULL } }; /* Parameters default values */ #define STR_DEFAULT_ONDELAY "30" /* Delay between return of utility power */ /* and powering up of load, in seconds */ /* CAUTION: ondelay > offdelay */ #define DEFAULT_ONDELAY 30 #define STR_DEFAULT_OFFDELAY "20" /* Delay before power off, in seconds */ #define DEFAULT_OFFDELAY 20 #define MGE_NOTHING_VALUE 1 #define MGE_START_VALUE 2 #define MGE_STOP_VALUE 3 /* TODO: PowerShare (per plug .1, .2, .3) and deals with delays */ /* Snmp2NUT lookup table */ static snmp_info_t mge_mib[] = { /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Eaton", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.1.0", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.12.12.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_1, NULL }, { "ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.9.8.0", "", 0, ietf_beeper_status_info }, { "ups.L1.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_3, NULL }, { "ups.L2.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.2", "", SU_OUTPUT_3, NULL }, { "ups.L3.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.3", "", SU_OUTPUT_3, NULL }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, ietf_test_result_info }, { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.2.0", STR_DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.3.0", STR_DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.timer.shutdown", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", "", SU_FLAG_OK, NULL }, { "ups.timer.start", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", "", SU_FLAG_OK, NULL }, { "ups.timer.reboot", 0, 1, "1.3.6.1.2.1.33.1.8.4.0", "", SU_FLAG_OK, NULL }, { "ups.start.auto", ST_FLAG_RW, 1, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, ietf_yes_no_info }, /* status data */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.11.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_replacebatt_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.14.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.16.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_onbatt_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.4.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_bypass_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.7.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_output_util_off_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.8.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_boost_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.10.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_overload_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.12.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_trim_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.4.1.0", "", SU_STATUS_PWR | SU_FLAG_OK, ietf_power_source_info }, /* FIXME: Alarms * - upsmgBatteryChargerFault (.1.3.6.1.4.1.705.1.5.15.0), yes (1), no (2) * => Battery charger fail! * - upsmgBatteryFaultBattery (.1.3.6.1.4.1.705.1.5.9.0), yes (1), no (2) * => "Battery fault!" or? * - upsmgOutputOverTemp (.1.3.6.1.4.1.705.1.7.11.0), yes (1), no (2) * => "Temperature too high!" * - upsmgOutputInverterOff (.1.3.6.1.4.1.705.1.7.9.0), yes (1), no (2) * => "??" * - upsmgInputBadStatus (.1.3.6.1.4.1.705.1.6.3.0), yes (1), no (2) * => "bad volt or bad freq" */ /* Input page */ { "input.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.6.1.0", "", SU_FLAG_SETINT, NULL, &input_phases }, { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, { "input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.3", "", SU_INPUT_3, NULL }, { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_1, NULL }, { "input.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_3, NULL }, { "input.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.2", "", SU_INPUT_3, NULL }, { "input.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.3", "", SU_INPUT_3, NULL }, { "input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_1, NULL }, { "input.L1-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.3", "", SU_INPUT_3, NULL }, { "input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_1, NULL }, { "input.L1-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.3", "", SU_INPUT_3, NULL }, { "input.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_1, NULL }, { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_3, NULL }, { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.2", "", SU_INPUT_3, NULL }, { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.3", "", SU_INPUT_3, NULL }, { "input.transfer.reason", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.6.4.0", "", SU_FLAG_OK, mge_transfer_reason_info }, /* Output page */ { "output.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.7.1.0", "", SU_FLAG_SETINT, NULL, &output_phases }, { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_1, NULL }, { "output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.2", "", SU_OUTPUT_3, NULL }, { "output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.3", "", SU_OUTPUT_3, NULL }, { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_1, NULL }, { "output.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_3, NULL }, { "output.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.2", "", SU_OUTPUT_3, NULL }, { "output.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.3", "", SU_OUTPUT_3, NULL }, { "output.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_1, NULL }, { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_3, NULL }, { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.2", "", SU_OUTPUT_3, NULL }, { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.3", "", SU_OUTPUT_3, NULL }, /* Battery page */ { "battery.charge", 0, 1, ".1.3.6.1.4.1.705.1.5.2.0", "", SU_FLAG_OK, NULL }, { "battery.runtime", 0, 1, ".1.3.6.1.4.1.705.1.5.1.0", "", SU_FLAG_OK, NULL }, { "battery.runtime.low", 0, 1, ".1.3.6.1.4.1.705.1.4.7.0", "", SU_FLAG_OK, NULL }, { "battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW, 2, ".1.3.6.1.4.1.705.1.4.8.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.5.5.0", "", SU_FLAG_OK, NULL }, /* Ambient page: Environment Sensor (ref 66 846) */ { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.705.1.8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.705.1.8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "Main Outlet", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* instant commands. */ { "test.battery.start", 0, MGE_START_VALUE, ".1.3.6.1.4.1.705.1.10.4.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* Also use IETF OIDs * { "test.battery.stop", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.2", SU_TYPE_CMD, NULL }, * { "test.battery.start", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.3", SU_TYPE_CMD, NULL }, * { "test.battery.start.quick", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.4", SU_TYPE_CMD, NULL }, * { "test.battery.start.deep", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.5", SU_TYPE_CMD, NULL }, */ /* { "load.off", 0, MGE_START_VALUE, ".1.3.6.1.4.1.705.1.9.1.1.6.1", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, * { "load.on", 0, MGE_START_VALUE, ".1.3.6.1.4.1.705.1.9.1.1.3.1", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, * { "shutdown.return", 0, MGE_START_VALUE, ".1.3.6.1.4.1.705.1.9.1.1.9.1", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ /* IETF MIB fallback */ { "beeper.disable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "", SU_TYPE_CMD, NULL }, { "beeper.enable", 0, 2, "1.3.6.1.2.1.33.1.9.8.0", "", SU_TYPE_CMD, NULL }, { "beeper.mute", 0, 3, "1.3.6.1.2.1.33.1.9.8.0", "", SU_TYPE_CMD, NULL }, /* Use ST_FLAG_STRING to get default value from ->dfl instead of info_len */ { "load.off.delay", 0, DEFAULT_OFFDELAY, "1.3.6.1.2.1.33.1.8.2.0", "", SU_TYPE_CMD, NULL }, { "load.on.delay", 0, DEFAULT_ONDELAY, "1.3.6.1.2.1.33.1.8.3.0", "", SU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t mge = { "mge", MGE_MIB_VERSION, NULL, MGE_OID_MODEL_NAME, mge_mib, MGE_SYSOID }; nut-2.7.4/drivers/apcupsd-ups.h0000644000175000017500000001476112640473702013364 00000000000000/* apcupsd-ups.h - NUT client driver to apcupsd Copyright (C) 2005 - 2010 Arnaud Quette 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 */ /* from usbhid-ups.h */ /* --------------------------------------------------------------- */ /* Struct & data for ups.status processing */ /* --------------------------------------------------------------- */ typedef struct { const char *status_str; /* ups.status string */ int status_value; /* ups.status value */ } status_lkp_t; #define STATUS_CAL 1 /* calibration */ #define STATUS_TRIM 2 /* SmartTrim */ #define STATUS_BOOST 4 /* SmartBoost */ #define STATUS_OL 8 /* on line */ #define STATUS_OB 16 /* on battery */ #define STATUS_OVER 32 /* overload */ #define STATUS_LB 64 /* low battery */ #define STATUS_RB 128 /* replace battery */ #define STATUS_BYPASS 256 /* on bypass */ #define STATUS_OFF 512 /* ups is off */ #define STATUS_CHRG 1024 /* charging */ #define STATUS_DISCHRG 2048 /* discharging */ status_lkp_t status_info[] = { { "CAL", STATUS_CAL }, { "TRIM", STATUS_TRIM }, { "BOOST", STATUS_BOOST }, { "OL", STATUS_OL }, { "OB", STATUS_OB }, { "OVER", STATUS_OVER }, { "LB", STATUS_LB }, { "RB", STATUS_RB }, { "BYPASS", STATUS_BYPASS }, { "OFF", STATUS_OFF }, { "CHRG", STATUS_CHRG }, { "DISCHRG", STATUS_DISCHRG }, { "NULL", 0 }, }; /* from usbhid-ups.h */ typedef struct { char hid_value; /* HID value */ char *nut_value; /* NUT value */ } info_enum_t; /* --------------------------------------------------------------- */ /* Structure containing information about how to get/set data */ /* from/to the UPS and convert these to/from NUT standard */ /* --------------------------------------------------------------- */ typedef struct { const char *apcupsd_item; const char *info_type; /* NUT variable name */ int info_flags; /* NUT flags (to set in addinfo) */ float info_len; /* if ST_FLAG_STRING: length of the string */ /* if HU_TYPE_CMD: command value ; multiplier (or max len) otherwise */ const char *default_value; /* if HU_FLAG_ABSENT: default value ; format otherwise */ int drv_flags; /* */ char **var_values; /* all possible values for this variable (allows to check data...) */ /* FIXME: "void *" so we can have bound or enum */ /* interpreter interpret; */ /* FFE: interpreter fct, NULL if not needed */ } apcuspd_info_t; /* data flags */ #define DU_FLAG_NONE 0 #define DU_FLAG_INIT 1 /* intialy show element to upsd */ #define DU_FLAG_STATUS 2 #define DU_FLAG_DATE 4 #define DU_FLAG_TIME 8 #define DU_FLAG_FW1 16 #define DU_FLAG_FW2 32 /* ------------ */ /* Data table */ /* ------------ */ static apcuspd_info_t nut_data[] = { { NULL, "ups.mfr", ST_FLAG_STRING | ST_FLAG_RW, 32, "APC", DU_FLAG_INIT, NULL }, { "MODEL", "ups.model", ST_FLAG_STRING | ST_FLAG_RW, 32, "Unknown UPS", DU_FLAG_INIT, NULL }, { "STATUS", "ups.status", ST_FLAG_STRING | ST_FLAG_RW, 32, "OFF", DU_FLAG_INIT|DU_FLAG_STATUS, NULL }, { "SERIALNO", "ups.serial", ST_FLAG_STRING | ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, { "LOADPCT", "ups.load", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "DATE", "ups.time", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_TIME, NULL }, { "DATE", "ups.date", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_DATE, NULL }, { "MANDATE", "ups.mfr.date", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "FIRMWARE", "ups.firmware", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_FW1, NULL }, { "FIRMWARE", "ups.firmware.aux", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_FW2, NULL }, { "ITEMP", "ups.temperature", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "UPSNAME", "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "DWAKE", "ups.delay.start", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, { "DSHUTD", "ups.delay.shutdown", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, { "STESTI", "ups.test.interval", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, { "SELFTEST", "ups.test.result", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, { "LINEV", "input.voltage", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "MAXLINEV", "input.voltage.maximum", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "MINLINEV", "input.voltage.minimum", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "NOMINV", "input.voltage.nominal", 0, 1, "%.0f", DU_FLAG_NONE, NULL }, { "NOMOUTV", "output.voltage.nominal", 0, 1, "%.0f", DU_FLAG_NONE, NULL }, { "LASTXFER", "input.transfer.reason", ST_FLAG_STRING | ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, { "LOTRANS", "input.transfer.low", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "HITRANS", "input.transfer.high", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "SENSE", "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, { "LINEFREQ", "input.frequency", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "OUTPUTV", "output.voltage", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "LINEFREQ", "output.frequency", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "BCHARGE", "battery.charge", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "MBATTCHG", "battery.charge.low", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, { "BATTDATE", "battery.date", ST_FLAG_STRING /* | ST_FLAG_RW */, 16, NULL, DU_FLAG_DATE, NULL }, { "BATTV", "battery.voltage", 0, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "NOMBATTV", "battery.voltage.nominal", 0, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "TIMELEFT", "battery.runtime", ST_FLAG_RW, 60, "%1.1f", DU_FLAG_NONE, NULL }, { "MINTIMEL", "battery.runtime.low", ST_FLAG_RW, 60, "%.0f", DU_FLAG_NONE, NULL }, { "RETPCT", "battery.charge.restart", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, { "NOMPOWER", "ups.realpower.nominal", 0, 1, "%1.1f", DU_FLAG_INIT, NULL }, { NULL, NULL, 0, 0, NULL, DU_FLAG_NONE, NULL } }; nut-2.7.4/drivers/liebert-hid.h0000644000175000017500000000210212640443572013274 00000000000000/* liebert-hid.h - subdriver to monitor Liebert USB/HID devices with NUT * * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 - 2006 Peter Selinger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef LIEBERT_HID_H #define LIEBERT_HID_H #include "usbhid-ups.h" extern subdriver_t liebert_subdriver; #endif /* LIEBERT_HID_H */ nut-2.7.4/drivers/tripplite_usb.c0000644000175000017500000012317712640473702014002 00000000000000/*!@file tripplite_usb.c * @brief Driver for Tripp Lite non-PDC/HID USB models. */ /* tripplite_usb.c was derived from tripplite.c by Charles Lepple tripplite.c was derived from Russell Kroll's bestups.c by Rik Faith. Copyright (C) 1999 Russell Kroll Copyright (C) 2001 Rickard E. (Rik) Faith Copyright (C) 2004 Nicholas J. Kain Copyright (C) 2005-2008, 2014 Charles Lepple 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 */ /* % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * Protocol 1001 * * OMNIVS Commands: (capital letters are literals, lower-case are variables) * :B -> Bxxxxyy (xxxx/55.0: Hz in, yy/16: battery voltage) * :F -> F1143_A (where _ = \0) Firmware version? * :L -> LvvvvXX (vvvv/2.0: VAC out) * :P -> P01000X (1000VA unit) * :S -> Sbb_XXX (bb = 10: on-line, 11: on battery) * :V -> V102XXX (firmware/protocol version?) * :Wt -> Wt (watchdog; t = time in seconds (binary, not hex), * 0 = disable; if UPS is not pinged in this interval, it * will power off the load, and then power it back on after * a delay.) * * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * The outgoing commands are sent with HID Set_Report commands over EP0 * (control message), and incoming commands are received on EP1IN (interrupt * endpoint). The UPS completely ignores the conventions of Set_Idle (where * you NAK the interrupt read if you have no new data), so you constantly have * to poll EP1IN. * * The descriptors say that bInterval is 10 ms. You generally need to wait at * least 80-90 ms to get some characters back from the device. If it takes * more than 250 ms, you probably need to resend the command. * * All outgoing commands are followed by a checksum, which is 255 - (sum of * characters after ':'), and then by '\r'. All responses should start with * the command letter that was sent (no colon), and should be followed by * '\r'. If the command is not supported (or apparently if there is a serial * timeout internally), the previous response will be echoed back. * * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * SMARTPRO commands (3003): * * :A -> ? (start self-test) * :D -> D7187 (? - doesn't match tripplite.c) * :F -> F1019 A firmware rev * :H__ -> H (delay before action?) * :I_ -> I (set flags for conditions that cause a reset?) * :J__ -> J (set 16-bit unit ID) * :K#0 -> (turn outlet off: # in 0..2; 0 is main relay) * :K#1 -> (turn outlet on: # in 0..2) * :L -> L290D_X * :M -> M007F (min/max voltage seen) * :N__ -> N * :P -> P01500X (max power) * :Q -> (while online: reboot) * :R -> R<01> (query flags for conditions that cause a reset?) * :S -> S100_Z0 (status?) * :T -> T7D2581 (temperature, frequency) * :U -> U (unit ID, 1-65535) * :V -> V1062XX (outlets in groups of 2-2-4, with the groups of 2 * individually switchable.) * :W_ -> W_ (watchdog) * :Z -> Z (reset for max/min; takes a moment to complete) * * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * The SMARTPRO unit seems to be slightly saner with regard to message * polling. It specifies an interrupt in interval of 100 ms, but I just * started at a 2 second timeout to obtain the above table. * * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * Commands from serial tripplite.c: * * :N%02X -- delay the UPS for provided time (hex seconds) * :H%06X -- reboot the UPS. UPS will restart after provided time (hex s) * :A -- begins a self-test * :C -- fetches result of a self-test * :K1 -- turns on power receptacles * :K0 -- turns off power receptacles * :G -- unconfirmed: shuts down UPS until power returns * :Q1 -- enable "Remote Reboot" * :Q0 -- disable "Remote Reboot" * :W -- returns 'W' data * :L -- returns 'L' data * :V -- returns 'V' data (firmware revision) * :X -- returns 'X' data (firmware revision) * :D -- returns general status data * :B -- returns battery voltage (hexadecimal decivolts) * :I -- returns minimum input voltage (hexadecimal hertz) [sic] * :M -- returns maximum input voltage (hexadecimal hertz) [sic] * :P -- returns power rating * :Z -- unknown * :U -- unknown * :O -- unknown * :E -- unknown * :Y -- returns mains frequency (':D' is preferred) * :T -- returns ups temperature (':D' is preferred) * :R -- returns input voltage (':D' is preferred) * :F -- returns load percentage (':D' is preferred) * :S -- enables remote reboot/remote power on */ /* Watchdog for 3005 is 15 - 255 seconds. */ #include "main.h" #include "libusb.h" #include #include #include #include "usb-common.h" #define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" #define DRIVER_VERSION "0.29" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Charles Lepple \n" \ "Russell Kroll \n" \ "Rickard E. (Rik) Faith \n" \ "Nicholas J. Kain ", DRV_EXPERIMENTAL, { NULL } }; /* TrippLite */ #define TRIPPLITE_VENDORID 0x09ae /* USB IDs device table */ static usb_device_id_t tripplite_usb_device_table[] = { /* e.g. OMNIVS1000, SMART550USB, ... */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x0001), NULL }, /* Terminating entry */ { -1, -1, NULL } }; static int subdriver_match_func(USBDevice_t *hd, void *privdata) { switch (is_usb_device_supported(tripplite_usb_device_table, hd)) { case SUPPORTED: return 1; case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } case NOT_SUPPORTED: default: return 0; } } static USBDeviceMatcher_t subdriver_matcher = { &subdriver_match_func, NULL, NULL }; static enum tl_model_t { TRIPP_LITE_UNKNOWN = 0, TRIPP_LITE_OMNIVS, TRIPP_LITE_OMNIVS_2001, TRIPP_LITE_SMARTPRO, TRIPP_LITE_SMART_0004, TRIPP_LITE_SMART_3005 } tl_model = TRIPP_LITE_UNKNOWN; /*! Are the values encoded in ASCII or binary? * TODO: Add 3004? */ static int is_binary_protocol() { switch(tl_model) { case TRIPP_LITE_SMART_3005: return 1; default: return 0; } } /*! Is this the "SMART" family of protocols? * TODO: Add 3004? */ static int is_smart_protocol() { switch(tl_model) { case TRIPP_LITE_SMARTPRO: case TRIPP_LITE_SMART_0004: case TRIPP_LITE_SMART_3005: return 1; default: return 0; } } /*!@brief If a character is not printable, return a dot. */ #define toprint(x) (isalnum((unsigned)x) ? (x) : '.') #define ENDCHAR 13 #define MAX_SEND_TRIES 10 #define SEND_WAIT_SEC 0 #define SEND_WAIT_NSEC (1000*1000*100) #define MAX_RECV_TRIES 10 #define RECV_WAIT_MSEC 1000 /*!< was 100 for OMNIVS; SMARTPRO units need longer */ #define MAX_RECONNECT_TRIES 10 #define DEFAULT_OFFDELAY 64 /*!< seconds (max 0xFF) */ #define DEFAULT_STARTDELAY 60 /*!< seconds (max 0xFFFFFF) */ #define DEFAULT_BOOTDELAY 64 /*!< seconds (max 0xFF) */ #define MAX_VOLT 13.4 /*!< Max battery voltage (100%) */ #define MIN_VOLT 11.0 /*!< Min battery voltage (10%) */ static USBDevice_t *hd = NULL; static USBDevice_t curDevice; static USBDeviceMatcher_t *reopen_matcher = NULL; static USBDeviceMatcher_t *regex_matcher = NULL; static usb_dev_handle *udev; static usb_communication_subdriver_t *comm_driver = &usb_subdriver; /* We calculate battery charge (q) as a function of voltage (V). * It seems that this function probably varies by firmware revision or * UPS model - the Windows monitoring software gives different q for a * given V than the old open source Tripp Lite monitoring software. * * The discharge curve should be the same for any given battery chemistry, * so it should only be necessary to specify the minimum and maximum * voltages likely to be seen in operation. */ /* Interval notation for Q% = 10% <= [minV, maxV] <= 100% */ static double V_interval[2] = {MIN_VOLT, MAX_VOLT}; static int battery_voltage_nominal = 12, input_voltage_nominal = 120, input_voltage_scaled = 120, /* input_voltage_maximum = -1, input_voltage_minimum = -1, */ switchable_load_banks = 0, unit_id = -1; /*!< range: 1-65535, most likely */ /*! Time in seconds to delay before shutting down. */ static unsigned int offdelay = DEFAULT_OFFDELAY; /* static unsigned int bootdelay = DEFAULT_BOOTDELAY; */ /*!@brief Try to reconnect once. * @return 1 if reconnection was successful. */ static int reconnect_ups(void) { int ret; if (hd != NULL) { return 1; } upsdebugx(2, "=================================================="); upsdebugx(2, "= device has been disconnected, try to reconnect ="); upsdebugx(2, "=================================================="); ret = comm_driver->open(&udev, &curDevice, reopen_matcher, NULL); if (ret < 1) { upslogx(LOG_INFO, "Reconnecting to UPS failed; will retry later..."); dstate_datastale(); return 0; } hd = &curDevice; return ret; } /*!@brief Convert a string to printable characters (in-place) * * @param[in,out] str String to convert * @param[in] len Maximum number of characters to convert, or <= 0 to * convert all. * * Uses toprint() macro defined above. */ void toprint_str(char *str, int len) { int i; if(len <= 0) len = strlen(str); for(i=0; i < len; i++) str[i] = toprint(str[i]); } /*!@brief Convert N characters from hex to decimal * * @param start Beginning of string to convert * @param len Maximum number of characters to consider (max 32) * * @a len characters of @a start are copied to a temporary buffer, then passed * to strtol() to be converted to decimal. * * @return See strtol(3) */ static int hex2d(const unsigned char *start, unsigned int len) { unsigned char buf[32]; buf[31] = '\0'; strncpy((char *)buf, (const char *)start, (len < (sizeof buf) ? len : (sizeof buf - 1))); if(len < sizeof(buf)) buf[len] = '\0'; return strtol((char *)buf, NULL, 16); } /*!@brief Convert N characters from big-endian binary to decimal * * @param start Beginning of string to convert * @param len Maximum number of characters to consider (max 32) * * @a len characters of @a start are shifted into an accumulator. * * We assume len < sizeof(int), and that value > 0. * * @return the value */ static unsigned int bin2d(const unsigned char *start, unsigned int len) { unsigned int value = 0, index = 0; for(index = 0; index < len; index++) { value <<= 8; value |= start[index]; } return value; } static int hex_or_bin2d(const unsigned char *start, unsigned int len) { if(is_binary_protocol()) { return bin2d(start, len); } return hex2d(start, len); } /*!@brief Dump message in both hex and ASCII * * @param[in] msg Buffer to dump * @param[in] len Number of bytes to dump * * @return Pointer to static buffer with decoded message */ static const char *hexascdump(unsigned char *msg, size_t len) { size_t i; static unsigned char buf[256]; unsigned char *bufp, *end; bufp = buf; end = bufp + sizeof(buf); buf[0] = 0; /* Dump each byte in hex: */ for(i=0; i=3; i++) { bufp += sprintf((char *)bufp, "%02x ", msg[i]); } /* Dump single-quoted string with printable version of each byte: */ if (end-bufp > 0) *bufp++ = '\''; for(i=0; i0; i++) { *bufp++ = toprint(msg[i]); } if (end-bufp > 0) *bufp++ = '\''; if (end-bufp > 0) *bufp = '\0'; else *--end='\0'; return (char *)buf; } enum tl_model_t decode_protocol(unsigned int proto) { switch(proto) { case 0x0004: upslogx(3, "Using older SMART protocol (%04x)", proto); return TRIPP_LITE_SMART_0004; case 0x1001: upslogx(3, "Using OMNIVS protocol (%x)", proto); return TRIPP_LITE_OMNIVS; case 0x2001: upslogx(3, "Using OMNIVS 2001 protocol (%x)", proto); return TRIPP_LITE_OMNIVS_2001; case 0x3003: upslogx(3, "Using SMARTPRO protocol (%x)", proto); return TRIPP_LITE_SMARTPRO; case 0x3005: upslogx(3, "Using binary SMART protocol (%x)", proto); return TRIPP_LITE_SMART_3005; default: printf("Unknown protocol (%04x)", proto); break; } return TRIPP_LITE_UNKNOWN; } void decode_v(const unsigned char *value) { unsigned char ivn, lb; int bv; if(is_binary_protocol()) { /* 0x00 0x0c -> 12V ? */ battery_voltage_nominal = (value[2] << 8) | value[3]; } else { bv = hex2d(value+2, 2); battery_voltage_nominal = bv * 6; } ivn = value[1]; lb = value[4]; switch(ivn) { case '0': input_voltage_nominal = input_voltage_scaled = 100; break; case 2: /* protocol 3005 */ case '1': input_voltage_nominal = input_voltage_scaled = 120; break; case '2': input_voltage_nominal = input_voltage_scaled = 230; break; case '3': input_voltage_nominal = 208; input_voltage_scaled = 230; break; default: upslogx(2, "Unknown input voltage range: 0x%02x", (unsigned int)ivn); break; } if( (lb >= '0') && (lb <= '9') ) { switchable_load_banks = lb - '0'; } else { if(is_binary_protocol()) { switchable_load_banks = lb; } else { if( lb != 'X' ) { upslogx(2, "Unknown number of switchable load banks: 0x%02x", (unsigned int)lb); } } } upsdebugx(2, "Switchable load banks: %d", switchable_load_banks); } void upsdrv_initinfo(void); /*!@brief Report a USB comm failure, and reconnect if necessary * * @param[in] res Result code from libusb/libhid call * @param[in] msg Error message to display */ void usb_comm_fail(int res, const char *msg) { static int try = 0; switch(res) { case -EBUSY: upslogx(LOG_WARNING, "%s: Device claimed by another process", msg); fatalx(EXIT_FAILURE, "Terminating: EBUSY"); break; default: upslogx(LOG_WARNING, "%s: Device detached? (error %d: %s)", msg, res, usb_strerror()); upslogx(LOG_NOTICE, "Reconnect attempt #%d", ++try); hd = NULL; reconnect_ups(); if(hd) { upslogx(LOG_NOTICE, "Successfully reconnected"); try = 0; upsdrv_initinfo(); } else { if(try > MAX_RECONNECT_TRIES) { fatalx(EXIT_FAILURE, "Too many unsuccessful reconnection attempts"); } } break; } } /*!@brief Send a command to the UPS, and wait for a reply. * * All of the UPS commands are challenge-response. If a command does not have * anything to return, it simply returns the command character. * * @param[in] msg Command string, minus the ':' or CR * @param[in] msg_len Be sure to use sizeof(msg) instead of strlen(msg), * since some commands have embedded NUL characters * @param[out] reply Reply (but check return code for validity) * @param[out] reply_len (currently unused) * * @return number of chars in reply, excluding terminating NUL * @return 0 if command was not accepted */ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *reply, size_t reply_len) { unsigned char buffer_out[8]; unsigned char csum = 0; int ret = 0, send_try, recv_try=0, done = 0; size_t i = 0; upsdebugx(3, "send_cmd(msg_len=%u, type='%c')", (unsigned)msg_len, msg[0]); if(msg_len > 5) { fatalx(EXIT_FAILURE, "send_cmd(): Trying to pass too many characters to UPS (%u)", (unsigned)msg_len); } buffer_out[0] = ':'; for(i=1; i<8; i++) buffer_out[i] = '\0'; for(i=0; iset_report(udev, 0, buffer_out, sizeof(buffer_out)); if(ret != sizeof(buffer_out)) { upslogx(1, "libusb_set_report() returned %d instead of %u", ret, (unsigned)(sizeof(buffer_out))); return ret; } #if ! defined(__FreeBSD__) if(!done) { usleep(1000*100); /* TODO: nanosleep */ } #endif for(recv_try=0; !done && recv_try < MAX_RECV_TRIES; recv_try++) { upsdebugx(7, "send_cmd recv_try %d", recv_try+1); ret = comm_driver->get_interrupt(udev, reply, sizeof(buffer_out), RECV_WAIT_MSEC); if(ret != sizeof(buffer_out)) { upslogx(1, "libusb_get_interrupt() returned %d instead of %u while sending %s", ret, (unsigned)(sizeof(buffer_out)), hexascdump(buffer_out, sizeof(buffer_out))); } done = (ret == sizeof(buffer_out)) && (buffer_out[1] == reply[0]); } } if(ret == sizeof(buffer_out)) { upsdebugx(5, "send_cmd: received %s (%s)", hexascdump(reply, sizeof(buffer_out)), done ? "OK" : "bad"); } upsdebugx(((send_try > 2) || (recv_try > 2)) ? 3 : 6, "send_cmd: send_try = %d, recv_try = %d\n", send_try, recv_try); return done ? sizeof(buffer_out) : 0; } /*!@brief Send an unknown command to the UPS, and store response in a variable * * @param msg Command string (usually a character and a null) * @param len Length of command plus null * * The variables are of the form "ups.debug.X" where "X" is the command * character. */ void debug_message(const char *msg, int len) { int ret; unsigned char tmp_value[9]; char var_name[20], err_msg[80]; snprintf(var_name, sizeof(var_name), "ups.debug.%c", *msg); ret = send_cmd((const unsigned char *)msg, len, tmp_value, sizeof(tmp_value)); if(ret <= 0) { sprintf(err_msg, "Error reading '%c' value", *msg); usb_comm_fail(ret, err_msg); return; } dstate_setinfo(var_name, "%s", hexascdump(tmp_value+1, 7)); } #if 0 /* using the watchdog to reboot won't work while polling */ static void do_reboot_wait(unsigned dly) { int ret; char buf[256], cmd_W[]="Wx"; cmd_W[1] = dly; upsdebugx(3, "do_reboot_wait(wait=%d): N", dly); ret = send_cmd(cmd_W, sizeof(cmd_W), buf, sizeof(buf)); } static int do_reboot_now(void) { do_reboot_wait(1); return 0; } static void do_reboot(void) { do_reboot_wait(bootdelay); } #endif /*! Called by 'tripplite_usb -k' */ static int soft_shutdown(void) { int ret; unsigned char buf[256], cmd_N[]="N\0x", cmd_G[] = "G"; /* Already binary: */ cmd_N[2] = offdelay; cmd_N[1] = offdelay >> 8; upsdebugx(3, "soft_shutdown(offdelay=%d): N", offdelay); ret = send_cmd(cmd_N, sizeof(cmd_N), buf, sizeof(buf)); if(ret != 8) { upslogx(LOG_ERR, "Could not set offdelay to %d", offdelay); return ret; } sleep(2); /*! The unit must be on battery for this to work. * * @todo check for on-battery condition, and print error if not. * @todo Find an equivalent command for non-OMNIVS models. */ ret = send_cmd(cmd_G, sizeof(cmd_G), buf, sizeof(buf)); if(ret != 8) { upslogx(LOG_ERR, "Could not turn off UPS (is it still on battery?)"); return 0; } return 1; } #if 0 static int hard_shutdown(void) { int ret; char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; cmd_N[2] = offdelay; cmd_N[1] = offdelay >> 8; upsdebugx(3, "hard_shutdown(offdelay=%d): N", offdelay); ret = send_cmd(cmd_N, sizeof(cmd_N), buf, sizeof(buf)); if(ret != 8) return ret; sleep(2); ret = send_cmd(cmd_K, sizeof(cmd_K), buf, sizeof(buf)); return (ret == 8); } #endif /*!@brief Turn an outlet on or off. * * @return 1 if the command worked, 0 if not. */ static int control_outlet(int outlet_id, int state) { char k_cmd[10], buf[10]; int ret; switch(tl_model) { case TRIPP_LITE_SMARTPRO: /* tested */ case TRIPP_LITE_SMART_0004: /* untested */ snprintf(k_cmd, sizeof(k_cmd)-1, "N%02X", 5); ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf); snprintf(k_cmd, sizeof(k_cmd)-1, "K%d%d", outlet_id, state & 1); ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf); if(ret != 8) { upslogx(LOG_ERR, "Could not set outlet %d to state %d, ret = %d", outlet_id, state, ret); return 0; } else { return 1; } break; case TRIPP_LITE_SMART_3005: snprintf(k_cmd, sizeof(k_cmd)-1, "N%c", 5); ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf); snprintf(k_cmd, sizeof(k_cmd)-1, "K%c%c", outlet_id, state & 1); ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf); if(ret != 8) { upslogx(LOG_ERR, "Could not set outlet %d to state %d, ret = %d", outlet_id, state, ret); return 0; } else { return 1; } break; default: upslogx(LOG_ERR, "control_outlet unimplemented for protocol %04x", tl_model); } return 0; } /*!@brief Handler for "instant commands" */ static int instcmd(const char *cmdname, const char *extra) { unsigned char buf[10]; if(is_smart_protocol()) { if (!strcasecmp(cmdname, "test.battery.start")) { send_cmd((const unsigned char *)"A", 2, buf, sizeof buf); return STAT_INSTCMD_HANDLED; } if(!strcasecmp(cmdname, "reset.input.minmax")) { return (send_cmd((const unsigned char *)"Z", 2, buf, sizeof buf) == 2) ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_UNKNOWN; } } if (!strcasecmp(cmdname, "load.off")) { return control_outlet(0, 0) ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_UNKNOWN; } if (!strcasecmp(cmdname, "load.on")) { return control_outlet(0, 1) ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_UNKNOWN; } /* code for individual outlets is in setvar() */ #if 0 if (!strcasecmp(cmdname, "shutdown.reboot")) { do_reboot_now(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) { do_reboot(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stayoff")) { hard_shutdown(); return STAT_INSTCMD_HANDLED; } #endif if (!strcasecmp(cmdname, "shutdown.return")) { soft_shutdown(); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.delay.shutdown")) { offdelay = atoi(val); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); return STAT_SET_HANDLED; } if (unit_id >= 0 && !strcasecmp(varname, "ups.id")) { int new_unit_id, ret; unsigned char J_msg[] = "J__", buf[9]; new_unit_id = atoi(val); J_msg[1] = new_unit_id >> 8; J_msg[2] = new_unit_id & 0xff; ret = send_cmd(J_msg, sizeof(J_msg), buf, sizeof(buf)); if(ret <= 0) { upslogx(LOG_NOTICE, "Could not set Unit ID (return code: %d).", ret); return STAT_SET_UNKNOWN; } dstate_setinfo("ups.id", "%s", val); return STAT_SET_HANDLED; } if(!strncmp(varname, "outlet.", strlen("outlet."))) { char outlet_name[80]; char index_str[10], *first_dot, *next_dot; int index_chars, index, state, ret; first_dot = strstr(varname, "."); next_dot = strstr(first_dot + 1, "."); index_chars = next_dot - (first_dot + 1); if(index_chars > 9) return STAT_SET_UNKNOWN; if(strcmp(next_dot, ".switch")) return STAT_SET_UNKNOWN; strncpy(index_str, first_dot + 1, index_chars); index_str[index_chars] = 0; index = atoi(index_str); upslogx(LOG_DEBUG, "outlet.%d.switch = %s", index, val); if(!strcasecmp(val, "on") || !strcmp(val, "1")) { state = 1; } else { state = 0; } upslogx(LOG_DEBUG, "outlet.%d.switch = %s -> %d", index, val, state); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.switch", index); ret = control_outlet(index, state); if(ret) { dstate_setinfo(outlet_name, "%d", state); return STAT_SET_HANDLED; } else { return STAT_SET_UNKNOWN; } } #if 0 if (!strcasecmp(varname, "ups.delay.start")) { startdelay = atoi(val); dstate_setinfo("ups.delay.start", val); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "ups.delay.reboot")) { bootdelay = atoi(val); dstate_setinfo("ups.delay.reboot", val); return STAT_SET_HANDLED; } #endif return STAT_SET_UNKNOWN; } void upsdrv_initinfo(void) { const unsigned char proto_msg[] = "\0", f_msg[] = "F", p_msg[] = "P", s_msg[] = "S", u_msg[] = "U", v_msg[] = "V", w_msg[] = "W\0"; char *model, *model_end; unsigned char proto_value[9], f_value[9], p_value[9], s_value[9], u_value[9], v_value[9], w_value[9]; int va, ret; unsigned int proto_number = 0; /* Read protocol: */ ret = send_cmd(proto_msg, sizeof(proto_msg), proto_value, sizeof(proto_value)-1); if(ret <= 0) { fatalx(EXIT_FAILURE, "Error reading protocol"); } proto_number = ((unsigned)(proto_value[1]) << 8) | (unsigned)(proto_value[2]); tl_model = decode_protocol(proto_number); if(tl_model == TRIPP_LITE_UNKNOWN) dstate_setinfo("ups.debug.0", "%s", hexascdump(proto_value+1, 7)); dstate_setinfo("ups.firmware.aux", "protocol %04x", proto_number); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ /* Reset watchdog: */ /* Watchdog not supported on TRIPP_LITE_SMARTPRO models */ if(tl_model != TRIPP_LITE_SMARTPRO ) { ret = send_cmd(w_msg, sizeof(w_msg), w_value, sizeof(w_value)-1); if(ret <= 0) { if(ret == -EPIPE) { fatalx(EXIT_FAILURE, "Could not reset watchdog. Please check and" "see if usbhid-ups(8) works with this UPS."); } else { upslogx(3, "Could not reset watchdog. Please send model " "information to nut-upsdev mailing list"); } } } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ ret = send_cmd(s_msg, sizeof(s_msg), s_value, sizeof(s_value)-1); if(ret <= 0) { fatalx(EXIT_FAILURE, "Could not retrieve status ... is this an OMNIVS model?"); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ dstate_setinfo("ups.mfr", "%s", "Tripp Lite"); /* Get nominal power: */ ret = send_cmd(p_msg, sizeof(p_msg), p_value, sizeof(p_value)-1); va = strtol((char *)(p_value+1), NULL, 10); if(tl_model == TRIPP_LITE_SMART_0004) { dstate_setinfo("ups.debug.P","%s", hexascdump(p_value+1, 7)); va *= 10; /* TODO: confirm? */ } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ /* trim "TRIPP LITE" from beginning of model */ model = strdup(hd->Product); if(strstr(model, hd->Vendor) == model) { model += strlen(hd->Vendor); } /* trim leading spaces: */ for(; *model == ' '; model++); /* Trim trailing spaces */ for(model_end = model + strlen(model) - 1; model_end > model && *model_end == ' '; model_end--) { *model_end = '\0'; } dstate_setinfo("ups.model", "%s", model); dstate_setinfo("ups.power.nominal", "%d", va); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ /* Fetch firmware version: */ ret = send_cmd(f_msg, sizeof(f_msg), f_value, sizeof(f_value)-1); toprint_str((char *)(f_value+1), 6); f_value[7] = 0; dstate_setinfo("ups.firmware", "F%s", f_value+1); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ /* Get configuration: */ ret = send_cmd(v_msg, sizeof(v_msg), v_value, sizeof(v_value)-1); decode_v(v_value); /* FIXME: redundant, but since it's static, we don't need to poll * every time. */ debug_message("V", 2); if(switchable_load_banks > 0) { int i; char outlet_name[80]; for(i = 1; i <= switchable_load_banks + 1; i++) { snprintf(outlet_name, sizeof(outlet_name), "outlet.%d.id", i); dstate_setinfo(outlet_name, "%d", i); snprintf(outlet_name, sizeof(outlet_name), "outlet.%d.desc", i); dstate_setinfo(outlet_name, "Load %d", i); snprintf(outlet_name, sizeof(outlet_name), "outlet.%d.switchable", i); if( i <= switchable_load_banks ) { dstate_setinfo(outlet_name, "1"); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.switch", i); dstate_setinfo(outlet_name, "1"); dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(outlet_name, 3); } else { /* Last bank is not switchable: */ dstate_setinfo(outlet_name, "0"); } } /* For the main relay: */ dstate_addcmd("load.on"); dstate_addcmd("load.off"); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if(tl_model != TRIPP_LITE_OMNIVS && tl_model != TRIPP_LITE_SMART_0004) { /* Unit ID might not be supported by all models: */ ret = send_cmd(u_msg, sizeof(u_msg), u_value, sizeof(u_value)-1); if(ret <= 0) { upslogx(LOG_INFO, "Unit ID not retrieved (not available on all models)"); } else { unit_id = (int)((unsigned)(u_value[1]) << 8) | (unsigned)(u_value[2]); } if(tl_model == TRIPP_LITE_SMART_0004) { debug_message("U", 2); } } if(unit_id >= 0) { dstate_setinfo("ups.id", "%d", unit_id); dstate_setflags("ups.id", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.id", 5); upslogx(LOG_DEBUG,"Unit ID: %d", unit_id); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ dstate_setinfo("input.voltage.nominal", "%d", input_voltage_nominal); dstate_setinfo("battery.voltage.nominal", "%d", battery_voltage_nominal); dstate_setinfo("ups.debug.load_banks", "%d", switchable_load_banks); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.shutdown", 3); #if 0 dstate_setinfo("ups.delay.start", "%d", startdelay); dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.start", 8); dstate_setinfo("ups.delay.reboot", "%d", bootdelay); dstate_setflags("ups.delay.reboot", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.reboot", 3); #endif if(is_smart_protocol()) { dstate_addcmd("test.battery.start"); dstate_addcmd("reset.input.minmax"); } dstate_addcmd("shutdown.return"); #if 0 dstate_addcmd("shutdown.stayoff"); dstate_addcmd("test.battery.start"); /* Turns off automatically */ dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("shutdown.reboot"); dstate_addcmd("shutdown.reboot.graceful"); #endif upsh.instcmd = instcmd; upsh.setvar = setvar; printf("Attached to %s %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model")); } void upsdrv_shutdown(void) { soft_shutdown(); } void upsdrv_updateinfo(void) { unsigned char b_msg[] = "B", d_msg[] = "D", l_msg[] = "L", s_msg[] = "S", m_msg[] = "M", t_msg[] = "T"; unsigned char b_value[9], d_value[9], l_value[9], s_value[9], m_value[9], t_value[9]; int bp, freq; double bv_12V = 0.0; /*!< battery voltage, relative to a 12V battery */ double battery_voltage; /*!< the total battery voltage */ unsigned int s_value_1; int ret; status_init(); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ /* General status (e.g. "S10") */ ret = send_cmd(s_msg, sizeof(s_msg), s_value, sizeof(s_value)); if(ret <= 0) { dstate_datastale(); usb_comm_fail(ret, "Error reading S value"); return; } if(tl_model != TRIPP_LITE_OMNIVS && tl_model != TRIPP_LITE_OMNIVS_2001) { dstate_setinfo("ups.debug.S","%s", hexascdump(s_value+1, 7)); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if(tl_model == TRIPP_LITE_OMNIVS) { switch(s_value[2]) { case '0': status_set("OL"); break; case '1': status_set("OB"); break; case '2': /* "charge-only" mode, no AC in or out... the PC * shouldn't see this, because there is no power in * that case (OMNIVS), but it's here for testing. * * Entered by holding down the power button. */ status_set("BYPASS"); break; case '3': /* I have seen this once when switching from off+LB to charging */ upslogx(LOG_WARNING, "Unknown value for s[2]: 0x%02x", s_value[2]); break; default: upslogx(LOG_ERR, "Unknown value for s[2]: 0x%02x", s_value[2]); dstate_datastale(); break; } } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if(is_smart_protocol() || tl_model == TRIPP_LITE_OMNIVS_2001) { unsigned int s_value_2 = s_value[2]; if(is_binary_protocol()) { s_value_2 += '0'; } switch(s_value_2) { case '0': dstate_setinfo("battery.test.status", "Battery OK"); break; case '1': dstate_setinfo("battery.test.status", "Battery bad - replace"); break; case '2': status_set("CAL"); break; case '3': status_set("OVER"); dstate_setinfo("battery.test.status", "Overcurrent?"); break; case '4': /* The following message is confusing, and may not be accurate: */ /* dstate_setinfo("battery.test.status", "Battery state unknown"); */ break; case '5': status_set("OVER"); dstate_setinfo("battery.test.status", "Battery fail - overcurrent?"); break; default: upslogx(LOG_ERR, "Unknown value for s[2]: 0x%02x", s_value[2]); dstate_datastale(); break; } if(s_value[4] & 4) { status_set("OFF"); } else { /* Online/on battery: */ if(s_value[4] & 1) { status_set("OB"); } else { status_set("OL"); } } #if 0 /* Apparently, this value changes more frequently when the * battery is discharged, but it does not track the actual * state-of-charge. See battery.charge calculation below. */ if(tl_model == TRIPP_LITE_SMARTPRO) { unsigned battery_charge; battery_charge = (unsigned)(s_value[5]); dstate_setinfo("battery.charge", "%u", battery_charge); } #endif } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ s_value_1 = s_value[1]; if(is_binary_protocol()) { s_value_1 += '0'; } switch(s_value_1) { case '0': status_set("LB"); break; case '1': /* Depends on s_value[2] */ break; case '2': if( tl_model == TRIPP_LITE_SMARTPRO ) { status_set("RB"); break; } /* else fall through: */ default: upslogx(LOG_ERR, "Unknown value for s[1]: 0x%02x", s_value[1]); dstate_datastale(); break; } status_commit(); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if( tl_model == TRIPP_LITE_OMNIVS || tl_model == TRIPP_LITE_OMNIVS_2001 ) { ret = send_cmd(b_msg, sizeof(b_msg), b_value, sizeof(b_value)); if(ret <= 0) { dstate_datastale(); usb_comm_fail(ret, "Error reading B value"); return; } dstate_setinfo("input.voltage", "%.2f", hex2d(b_value+1, 4)/3600.0*input_voltage_scaled); bv_12V = hex2d(b_value+5, 2)/16.0; /* TODO: use battery_voltage_nominal, even though it is most likely 12V */ dstate_setinfo("battery.voltage", "%.2f", bv_12V); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if( is_smart_protocol() ) { ret = send_cmd(d_msg, sizeof(d_msg), d_value, sizeof(d_value)); if(ret <= 0) { dstate_datastale(); usb_comm_fail(ret, "Error reading D value"); return; } dstate_setinfo("input.voltage", "%d", hex_or_bin2d(d_value+1, 2) * input_voltage_scaled / 120); /* TODO: factor out the two constants */ bv_12V = hex_or_bin2d(d_value+3, 2) / 10.0 ; battery_voltage = bv_12V * battery_voltage_nominal / 12.0; dstate_setinfo("battery.voltage", "%.2f", battery_voltage); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ ret = send_cmd(m_msg, sizeof(m_msg), m_value, sizeof(m_value)); if(m_value[5] != 0x0d) { /* we only expect 4 hex/binary digits */ dstate_setinfo("ups.debug.M", "%s", hexascdump(m_value+1, 7)); } if(ret <= 0) { dstate_datastale(); usb_comm_fail(ret, "Error reading M (min/max input voltage)"); return; } dstate_setinfo("input.voltage.minimum", "%3d", hex_or_bin2d(m_value+1, 2) * input_voltage_scaled / 120); dstate_setinfo("input.voltage.maximum", "%3d", hex_or_bin2d(m_value+3, 2) * input_voltage_scaled / 120); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ ret = send_cmd(t_msg, sizeof(t_msg), t_value, sizeof(t_value)); dstate_setinfo("ups.debug.T", "%s", hexascdump(t_value+1, 7)); if(ret <= 0) { dstate_datastale(); usb_comm_fail(ret, "Error reading T value"); return; } if( tl_model == TRIPP_LITE_SMARTPRO ) { freq = hex2d(t_value + 3, 3); dstate_setinfo("input.frequency", "%.1f", freq / 10.0); switch(t_value[6]) { case '1': dstate_setinfo("input.frequency.nominal", "%d", 60); break; case '0': dstate_setinfo("input.frequency.nominal", "%d", 50); break; } } if( tl_model == TRIPP_LITE_SMART_0004 ) { freq = hex2d(t_value + 3, 4); dstate_setinfo("input.frequency", "%.1f", freq / 10.0); } if( tl_model == TRIPP_LITE_SMART_3005 ) { dstate_setinfo("ups.temperature", "%d", (unsigned)(hex2d(t_value+1, 1))); } else { /* I'm guessing this is a calibration constant of some sort. */ dstate_setinfo("ups.temperature", "%.1f", (unsigned)(hex2d(t_value+1, 2)) * 0.3636 - 21); } } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if( tl_model == TRIPP_LITE_OMNIVS || tl_model == TRIPP_LITE_OMNIVS_2001 || tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 ) { /* dq ~= sqrt(dV) is a reasonable approximation * Results fit well against the discrete function used in the Tripp Lite * source, but give a continuous result. */ if (bv_12V >= V_interval[1]) bp = 100; else if (bv_12V <= V_interval[0]) bp = 10; else bp = (int)(100*sqrt((bv_12V - V_interval[0]) / (V_interval[1] - V_interval[0]))); dstate_setinfo("battery.charge", "%3d", bp); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ ret = send_cmd(l_msg, sizeof(l_msg), l_value, sizeof(l_value)); if(ret <= 0) { dstate_datastale(); usb_comm_fail(ret, "Error reading L value"); return; } switch(tl_model) { case TRIPP_LITE_OMNIVS: case TRIPP_LITE_OMNIVS_2001: dstate_setinfo("output.voltage", "%.1f", hex2d(l_value+1, 4)/240.0*input_voltage_scaled); break; case TRIPP_LITE_SMARTPRO: dstate_setinfo("ups.load", "%d", hex2d(l_value+1, 2)); break; case TRIPP_LITE_SMART_0004: dstate_setinfo("ups.load", "%d", hex2d(l_value+1, 2)); dstate_setinfo("ups.debug.L","%s", hexascdump(l_value+1, 7)); break; default: dstate_setinfo("ups.debug.L","%s", hexascdump(l_value+1, 7)); break; } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if(tl_model != TRIPP_LITE_OMNIVS && tl_model != TRIPP_LITE_OMNIVS_2001) { debug_message("D", 2); /* We already grabbed these above: */ if(tl_model != TRIPP_LITE_SMARTPRO) { debug_message("V", 2); /* Probably not necessary - seems to be static */ debug_message("M", 2); debug_message("T", 2); debug_message("P", 2); } /* debug_message("U", 2); */ } dstate_dataok(); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { char msg[256]; snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d)", DEFAULT_OFFDELAY); addvar(VAR_VALUE, "offdelay", msg); /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); snprintf(msg, sizeof msg, "Minimum battery voltage, corresponding to 10%% charge (default=%.1f)", MIN_VOLT); addvar(VAR_VALUE, "battery_min", msg); snprintf(msg, sizeof msg, "Maximum battery voltage, corresponding to 100%% charge (default=%.1f)", MAX_VOLT); addvar(VAR_VALUE, "battery_max", msg); #if 0 snprintf(msg, sizeof msg, "Set start delay, in seconds (default=%d).", DEFAULT_STARTDELAY); addvar(VAR_VALUE, "startdelay", msg); snprintf(msg, sizeof msg, "Set reboot delay, in seconds (default=%d).", DEFAULT_BOOTDELAY); addvar(VAR_VALUE, "rebootdelay", msg); #endif } /*!@brief Initialize UPS and variables from ups.conf * * @todo Allow binding based on firmware version (which seems to vary wildly * from unit to unit) */ void upsdrv_initups(void) { char *regex_array[6]; char *value; int r; /* process the UPS selection options */ regex_array[0] = NULL; /* handled by USB IDs device table */ regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); /* vendor string */ regex_array[3] = getval("product"); /* product string */ regex_array[4] = getval("serial"); /* probably won't see this */ regex_array[5] = getval("bus"); r = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); if (r==-1) { fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); } else if (r) { fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[r]); } /* link the matchers */ regex_matcher->next = &subdriver_matcher; /* Search for the first supported UPS matching the regular * expression */ r = comm_driver->open(&udev, &curDevice, regex_matcher, NULL); if (r < 1) { fatalx(EXIT_FAILURE, "No matching USB/HID UPS found"); } hd = &curDevice; upslogx(1, "Detected a UPS: %s/%s", hd->Vendor ? hd->Vendor : "unknown", hd->Product ? hd->Product : "unknown"); dstate_setinfo("ups.vendorid", "%04x", hd->VendorID); dstate_setinfo("ups.productid", "%04x", hd->ProductID); /* create a new matcher for later reopening */ r = USBNewExactMatcher(&reopen_matcher, hd); if (r) { fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); } /* link the two matchers */ reopen_matcher->next = regex_matcher; value = getval("offdelay"); if (value) { offdelay = atoi(value); upsdebugx(2, "Setting 'offdelay' to %d", offdelay); } value = getval("battery_min"); if (value) { V_interval[0] = atof(value); upsdebugx(2, "Setting 'battery_min' to %.g", V_interval[0]); } value = getval("battery_max"); if (value) { V_interval[1] = atof(value); upsdebugx(2, "Setting 'battery_max' to %.g", V_interval[1]); } #if 0 if (getval("startdelay")) startdelay = atoi(getval("startdelay")); if (getval("rebootdelay")) bootdelay = atoi(getval("rebootdelay")); #endif } void upsdrv_cleanup(void) { comm_driver->close(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); } nut-2.7.4/drivers/powerpanel.h0000644000175000017500000000260512640443572013270 00000000000000/* * powerpanel.h - Model specific data/definitions for CyberPower text/binary * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2008 Arjen de Korte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef POWERPANEL_H #define POWERPANEL_H #define ENDCHAR '\r' #define IGNCHAR "" #define MAXTRIES 3 #define UPSDELAY 50000 #define SER_WAIT_SEC 0 #define SER_WAIT_USEC 250000 typedef struct { const char *version; int (*instcmd)(const char *cmdname, const char *extra); int (*setvar)(const char *varname, const char *val); int (*initups)(void); void (*initinfo)(void); int (*updateinfo)(void); } subdriver_t; #endif /* POWERPANEL_H */ nut-2.7.4/drivers/serial.c0000644000175000017500000002341012640473702012361 00000000000000/* serial.c - common serial port functions for Network UPS Tools drivers Copyright (C) 2003 Russell Kroll 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 "common.h" #include "timehead.h" #include "serial.h" #include "main.h" #include #include #include #include #include #include #include #ifdef HAVE_UU_LOCK #include #endif static unsigned int comm_failures = 0; static void ser_open_error(const char *port) { struct stat fs; struct passwd *user; struct group *group; printf("\n"); printf("Unable to open %s: %s\n\n", port, strerror(errno)); /* check for bad port definitions first */ if (stat(port, &fs)) { printf("Things to try:\n\n"); printf(" - Check 'port=' in ups.conf\n\n"); printf(" - Check owner/permissions of all parts of path\n\n"); fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } user = getpwuid(getuid()); if (user) printf(" Current user id: %s (%d)\n", user->pw_name, (int) user->pw_uid); user = getpwuid(fs.st_uid); if (user) printf("Serial port owner: %s (%d)\n", user->pw_name, (int) fs.st_uid); group = getgrgid(fs.st_gid); if (group) printf("Serial port group: %s (%d)\n", group->gr_name, (int) fs.st_gid); printf(" Mode of port: %04o\n\n", (int) fs.st_mode & 07777); printf("Things to try:\n\n"); printf(" - Use another port (with the right permissions)\n\n"); printf(" - Fix the port owner/group or permissions on this port\n\n"); printf(" - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n"); printf(" See upsdrvctl(8) and ups.conf(5).\n\n"); fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } static void lock_set(int fd, const char *port) { int ret; if (fd < 0) fatal_with_errno(EXIT_FAILURE, "lock_set: programming error: fd = %d", fd); if (do_lock_port == 0) return; #ifdef HAVE_UU_LOCK ret = uu_lock(xbasename(port)); if (ret != 0) fatalx(EXIT_FAILURE, "Can't uu_lock %s: %s", xbasename(port), uu_lockerr(ret)); #elif defined(HAVE_FLOCK) ret = flock(fd, LOCK_EX | LOCK_NB); if (ret != 0) fatalx(EXIT_FAILURE, "%s is locked by another process", port); #elif defined(HAVE_LOCKF) lseek(fd, 0L, SEEK_SET); ret = lockf(fd, F_TLOCK, 0L); if (ret != 0) fatalx(EXIT_FAILURE, "%s is locked by another process", port); #else upslog_with_errno(LOG_WARNING, "Warning: no locking method is available"); #endif } /* Non fatal version of ser_open */ int ser_open_nf(const char *port) { int fd; fd = open(port, O_RDWR | O_NOCTTY | O_EXCL | O_NONBLOCK); if (fd < 0) { return -1; } lock_set(fd, port); return fd; } int ser_open(const char *port) { int res; res = ser_open_nf(port); if(res == -1) { ser_open_error(port); } return res; } int ser_set_speed_nf(int fd, const char *port, speed_t speed) { struct termios tio; if (tcgetattr(fd, &tio) != 0) { return -1; } 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; #ifdef HAVE_CFSETISPEED cfsetispeed(&tio, speed); cfsetospeed(&tio, speed); #else #error This system lacks cfsetispeed() and has no other means to set the speed #endif tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &tio); return 0; } int ser_set_speed(int fd, const char *port, speed_t speed) { int res; res = ser_set_speed_nf(fd,port,speed); if(res == -1) { fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", port); } return 0; } static int ser_set_control(int fd, int line, int state) { if (state) { return ioctl(fd, TIOCMBIS, &line); } else { return ioctl(fd, TIOCMBIC, &line); } } int ser_set_dtr(int fd, int state) { return ser_set_control(fd, TIOCM_DTR, state); } int ser_set_rts(int fd, int state) { return ser_set_control(fd, TIOCM_RTS, state); } static int ser_get_control(int fd, int line) { int flags; ioctl(fd, TIOCMGET, &flags); return (flags & line); } int ser_get_dsr(int fd) { return ser_get_control(fd, TIOCM_DSR); } int ser_get_cts(int fd) { return ser_get_control(fd, TIOCM_CTS); } int ser_get_dcd(int fd) { return ser_get_control(fd, TIOCM_CD); } int ser_flush_io(int fd) { return tcflush(fd, TCIOFLUSH); } int ser_close(int fd, const char *port) { if (fd < 0) fatal_with_errno(EXIT_FAILURE, "ser_close: programming error: fd=%d port=%s", fd, port); if (close(fd) != 0) return -1; #ifdef HAVE_UU_LOCK if (do_lock_port) uu_unlock(xbasename(port)); #endif return 0; } int ser_send_char(int fd, unsigned char ch) { return ser_send_buf_pace(fd, 0, &ch, 1); } static int send_formatted(int fd, const char *fmt, va_list va, unsigned long d_usec) { int ret; char buf[LARGEBUF]; ret = vsnprintf(buf, sizeof(buf), fmt, va); if (ret >= (int)sizeof(buf)) { upslogx(LOG_WARNING, "vsnprintf needed more than %d bytes", (int)sizeof(buf)); } return ser_send_buf_pace(fd, d_usec, buf, strlen(buf)); } /* send the results of the format string with d_usec delay after each char */ int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = send_formatted(fd, fmt, ap, d_usec); va_end(ap); return ret; } /* send the results of the format string with no delay */ int ser_send(int fd, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = send_formatted(fd, fmt, ap, 0); va_end(ap); return ret; } /* send buflen bytes from buf with no delay */ int ser_send_buf(int fd, const void *buf, size_t buflen) { return ser_send_buf_pace(fd, 0, buf, buflen); } /* send buflen bytes from buf with d_usec delay after each char */ int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, size_t buflen) { int ret; size_t sent; const char *data = buf; for (sent = 0; sent < buflen; sent += ret) { ret = write(fd, &data[sent], (d_usec == 0) ? (buflen - sent) : 1); if (ret < 1) { return ret; } usleep(d_usec); } return sent; } int ser_get_char(int fd, void *ch, long d_sec, long d_usec) { return select_read(fd, ch, 1, d_sec, d_usec); } int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec) { memset(buf, '\0', buflen); return select_read(fd, buf, buflen, d_sec, d_usec); } /* keep reading until buflen bytes are received or a timeout occurs */ int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec) { int ret; size_t recv; char *data = buf; memset(buf, '\0', buflen); for (recv = 0; recv < buflen; recv += ret) { ret = select_read(fd, &data[recv], buflen - recv, d_sec, d_usec); if (ret < 1) { return ret; } } return recv; } /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler(char ch), long d_sec, long d_usec) { int i, ret; char tmp[64]; char *data = buf; size_t count = 0, maxcount; memset(buf, '\0', buflen); maxcount = buflen - 1; /* for trailing \0 */ while (count < maxcount) { ret = select_read(fd, tmp, sizeof(tmp), d_sec, d_usec); if (ret < 1) { return ret; } for (i = 0; i < ret; i++) { if ((count == maxcount) || (tmp[i] == endchar)) { return count; } if (strchr(ignset, tmp[i])) continue; if (strchr(alertset, tmp[i])) { if (handler) handler(tmp[i]); continue; } data[count++] = tmp[i]; } } return count; } /* as above, only with no alertset handling (just a wrapper) */ int ser_get_line(int fd, void *buf, size_t buflen, char endchar, const char *ignset, long d_sec, long d_usec) { return ser_get_line_alert(fd, buf, buflen, endchar, ignset, "", NULL, d_sec, d_usec); } int ser_flush_in(int fd, const char *ignset, int verbose) { int ret, extra = 0; char ch; while ((ret = ser_get_char(fd, &ch, 0, 0)) > 0) { if (strchr(ignset, ch)) continue; extra++; if (verbose == 0) continue; if (isprint(ch & 0xFF)) upslogx(LOG_INFO, "ser_flush_in: read char %c", ch); else upslogx(LOG_INFO, "ser_flush_in: read char 0x%02x", ch); } return extra; } void ser_comm_fail(const char *fmt, ...) { int ret; char why[SMALLBUF]; va_list ap; /* this means we're probably here because select was interrupted */ if (exit_flag != 0) return; /* ignored, since we're about to exit anyway */ comm_failures++; if ((comm_failures == SER_ERR_LIMIT) || ((comm_failures % SER_ERR_RATE) == 0)) { upslogx(LOG_WARNING, "Warning: excessive comm failures, " "limiting error reporting"); } /* once it's past the limit, only log once every SER_ERR_LIMIT calls */ if ((comm_failures > SER_ERR_LIMIT) && ((comm_failures % SER_ERR_LIMIT) != 0)) return; va_start(ap, fmt); ret = vsnprintf(why, sizeof(why), fmt, ap); va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(why))) upslogx(LOG_WARNING, "ser_comm_fail: vsnprintf needed " "more than %d bytes", (int)sizeof(why)); upslogx(LOG_WARNING, "Communications with UPS lost: %s", why); } void ser_comm_good(void) { if (comm_failures == 0) return; upslogx(LOG_NOTICE, "Communications with UPS re-established"); comm_failures = 0; } nut-2.7.4/drivers/riello_ser.c0000644000175000017500000006657212640473702013261 00000000000000/* * riello_ser.c: support for Riello serial protocol based UPSes * * A document describing the protocol implemented by this driver can be * found online at "http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf" * and "http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf". * * Copyright (C) 2012 - Elio Parisi * * 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 * * Reference of the derivative work: blazer driver */ #include #include #include "config.h" #include "main.h" #include "serial.h" #include "timehead.h" #include "hidparser.h" #include "hidtypes.h" #include "common.h" /* for upsdebugx() etc */ #include "riello.h" #define DRIVER_NAME "Riello serial driver" #define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Elio Parisi ", DRV_EXPERIMENTAL, { NULL } }; uint8_t bufOut[BUFFER_SIZE]; uint8_t bufIn[BUFFER_SIZE]; uint8_t gpser_error_control; uint8_t typeRielloProtocol; uint8_t input_monophase; uint8_t output_monophase; extern uint8_t commbyte; extern uint8_t wait_packet; extern uint8_t foundnak; extern uint8_t foundbadcrc; extern uint8_t buf_ptr_length; extern uint8_t requestSENTR; TRielloData DevData; /********************************************************************** * char_read (char *bytes, int size, int read_timeout) * * reads size bytes from the serial port * * bytes - buffer to store the data * size - size of the data to get * read_timeout - serial timeout (in milliseconds) * * return -1 on error, -2 on timeout, nb_bytes_readen on success * *********************************************************************/ static int char_read (char *bytes, int size, int read_timeout) { struct timeval serial_timeout; fd_set readfs; int readen = 0; int rc = 0; FD_ZERO (&readfs); FD_SET (upsfd, &readfs); serial_timeout.tv_usec = (read_timeout % 1000) * 1000; serial_timeout.tv_sec = (read_timeout / 1000); rc = select (upsfd + 1, &readfs, NULL, NULL, &serial_timeout); if (0 == rc) return -2; /* timeout */ if (FD_ISSET (upsfd, &readfs)) { int now = read (upsfd, bytes, size - readen); if (now < 0) { return -1; } else { bytes += now; readen += now; } } else { return -1; } return readen; } /********************************************************************** * serial_read (int read_timeout) * * return data one byte at a time * * read_timeout - serial timeout (in milliseconds) * * returns 0 on success, -1 on error, -2 on timeout * **********************************************************************/ int serial_read (int read_timeout, u_char *readbuf) { static u_char cache[512]; static u_char *cachep = cache; static u_char *cachee = cache; int recv; *readbuf = '\0'; /* if still data in cache, get it */ if (cachep < cachee) { *readbuf = *cachep++; return 0; /* return (int) *cachep++; */ } recv = char_read ((char *)cache, 1, read_timeout); if ((recv == -1) || (recv == -2)) return recv; cachep = cache; cachee = cache + recv; cachep = cache; cachee = cache + recv; if (recv) { upsdebugx(5,"received: %02x", *cachep); *readbuf = *cachep++; return 0; } return -1; } void riello_serialcomm(uint8_t* bufIn, uint8_t typedev) { time_t realt, nowt; uint8_t commb = 0; realt = time(NULL); while (wait_packet) { serial_read(1000, &commb); nowt = time(NULL); commbyte = commb; riello_parse_serialport(typedev, bufIn, gpser_error_control); if ((nowt - realt) > 4) break; } } int get_ups_nominal() { uint8_t length; riello_init_serial(); length = riello_prepare_gn(&bufOut[0], gpser_error_control); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get nominal Ko: bad CRC or Checksum"); return -1; } /* mandatory */ if (!wait_packet && foundnak) { upsdebugx (3, "Get nominal Ko: command not supported"); return -1; } upsdebugx (3, "Get nominal Ok: received byte %u", buf_ptr_length); riello_parse_gn(&bufIn[0], &DevData); return 0; } int get_ups_status() { uint8_t numread, length; riello_init_serial(); length = riello_prepare_rs(&bufOut[0], gpser_error_control); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } if (input_monophase) numread = LENGTH_RS_MM; else if (output_monophase) numread = LENGTH_RS_TM; else numread = LENGTH_RS_TT; riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get status Ko: bad CRC or Checksum"); return -1; } /* mandatory */ if (!wait_packet && foundnak) { upsdebugx (3, "Get status Ko: command not supported"); return -1; } upsdebugx (3, "Get status Ok: received byte %u", buf_ptr_length); riello_parse_rs(&bufIn[0], &DevData, numread); return 0; } int get_ups_extended() { uint8_t length; riello_init_serial(); length = riello_prepare_re(&bufOut[0], gpser_error_control); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get extended Ko: bad CRC or Checksum"); return -1; } /* optonal */ if (!wait_packet && foundnak) { upsdebugx (3, "Get extended Ko: command not supported"); return 0; } upsdebugx (3, "Get extended Ok: received byte %u", buf_ptr_length); riello_parse_re(&bufIn[0], &DevData); return 0; } int get_ups_statuscode() { uint8_t length; riello_init_serial(); length = riello_prepare_rc(&bufOut[0], gpser_error_control); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum"); return -1; } /* optional */ if (!wait_packet && foundnak) { upsdebugx (3, "Get statuscode Ko: command not supported"); return 0; } upsdebugx (3, "Get statuscode Ok: received byte %u", buf_ptr_length); riello_parse_rc(&bufIn[0], &DevData); return 0; } int get_ups_sentr() { uint8_t length; riello_init_serial(); bufOut[0] = requestSENTR; if (requestSENTR == SENTR_EXT176) { bufOut[1] = 103; bufOut[2] = 1; bufOut[3] = 0; bufOut[4] = 24; length = 5; } else length = 1; if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get sentry Ko: bad CRC or Checksum"); return -1; } /* mandatory */ if (!wait_packet && foundnak) { upsdebugx (3, "Get sentry Ko: command not supported"); return -1; } upsdebugx (3, "Get sentry Ok: received byte %u", buf_ptr_length); riello_parse_sentr(&bufIn[0], &DevData); return 0; } int riello_instcmd(const char *cmdname, const char *extra) { uint8_t length; uint16_t delay; const char *delay_char; if (!riello_test_bit(&DevData.StatusCode[0], 1)) { if (!strcasecmp(cmdname, "load.off")) { delay = 0; riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_cs(bufOut, gpser_error_control, delay); else length = riello_prepare_shutsentr(bufOut, delay); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command load.off communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.off Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.off Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.off Ok"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.off.delay")) { delay_char = dstate_getinfo("ups.delay.shutdown"); delay = atoi(delay_char); riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_cs(bufOut, gpser_error_control, delay); else length = riello_prepare_shutsentr(bufOut, delay); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command load.off delay communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.off.delay Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.off delay Ok"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { delay = 0; riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_cr(bufOut, gpser_error_control, delay); else { length = riello_prepare_setrebsentr(bufOut, delay); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command load.on communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.on Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.on Ko: command not supported"); return STAT_INSTCMD_FAILED; } length = riello_prepare_rebsentr(bufOut, delay); } if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command load.on communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.on Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.on Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.on Ok"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on.delay")) { delay_char = dstate_getinfo("ups.delay.reboot"); delay = atoi(delay_char); riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_cr(bufOut, gpser_error_control, delay); else { length = riello_prepare_setrebsentr(bufOut, delay); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command load.on delay communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.on delay Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.on delay Ko: command not supported"); return STAT_INSTCMD_FAILED; } length = riello_prepare_rebsentr(bufOut, delay); } if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command load.on delay communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command load.on.delay Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command load.on delay Ok"); return STAT_INSTCMD_HANDLED; } } else { if (!strcasecmp(cmdname, "shutdown.return")) { delay_char = dstate_getinfo("ups.delay.shutdown"); delay = atoi(delay_char); riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_cs(bufOut, gpser_error_control, delay); else length = riello_prepare_shutsentr(bufOut, delay); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command shutdown.return communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command shutdown.return Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command shutdown.return Ok"); return STAT_INSTCMD_HANDLED; } } if (!strcasecmp(cmdname, "shutdown.stop")) { riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_cd(bufOut, gpser_error_control); else length = riello_prepare_cancelsentr(bufOut); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command shutdown.stop communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command shutdown.stop Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command shutdown.stop Ok"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.panel.start")) { riello_init_serial(); length = riello_prepare_tp(bufOut, gpser_error_control); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command test.panel.start communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command test.panel.start Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command test.panel.start Ok"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) length = riello_prepare_tb(bufOut, gpser_error_control); else length = riello_prepare_tbsentr(bufOut); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Command test.battery.start communication error"); return STAT_INSTCMD_FAILED; } riello_serialcomm(&bufIn[0], typeRielloProtocol); if (!wait_packet && foundbadcrc) { upsdebugx (3, "Command battery.start Ko: bad CRC or Checksum"); return STAT_INSTCMD_FAILED; } if (!wait_packet && foundnak) { upsdebugx (3, "Command battery.start Ko: command not supported"); return STAT_INSTCMD_FAILED; } upsdebugx (3, "Command test.battery.start Ok"); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } int start_ups_comm() { uint8_t length; upsdebugx (2, "entering start_ups_comm()\n"); riello_init_serial(); if (typeRielloProtocol == DEV_RIELLOGPSER) { length = riello_prepare_gi(&bufOut[0]); if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); } else { bufOut[0] = 192; length = 1; if (ser_send_buf(upsfd, bufOut, length) == 0) { upsdebugx (3, "Communication error while writing to port"); return -1; } riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); } if (!wait_packet && foundbadcrc) { upsdebugx (3, "Get identif Ko: bad CRC or Checksum"); return 1; } if (!wait_packet && foundnak) { upsdebugx (3, "Get identif Ko: command not supported"); return 1; } upsdebugx (3, "Get identif Ok: received byte %u", buf_ptr_length); return 0; } void upsdrv_initinfo(void) { int ret; ret = start_ups_comm(); if (ret < 0) fatalx(EXIT_FAILURE, "No communication with UPS"); else if (ret > 0) fatalx(EXIT_FAILURE, "Bad checksum or NACK"); else upsdebugx(2, "Communication with UPS established"); if (typeRielloProtocol == DEV_RIELLOGPSER) riello_parse_gi(&bufIn[0], &DevData); else riello_parse_sentr(&bufIn[0], &DevData); gpser_error_control = DevData.Identif_bytes[4]-0x30; if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '2')) input_monophase = 1; else { input_monophase = 0; dstate_setinfo("input.phases", "%u", 3); dstate_setinfo("input.phases", "%u", 3); dstate_setinfo("input.bypass.phases", "%u", 3); } if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '3')) output_monophase = 1; else { output_monophase = 0; dstate_setinfo("output.phases", "%u", 3); } dstate_setinfo("device.mfr", "RPS S.p.a."); dstate_setinfo("device.model", "%s", (unsigned char*) DevData.ModelStr); dstate_setinfo("device.serial", "%s", (unsigned char*) DevData.Identification); dstate_setinfo("device.type", "ups"); dstate_setinfo("ups.mfr", "RPS S.p.a."); dstate_setinfo("ups.model", "%s", (unsigned char*) DevData.ModelStr); dstate_setinfo("ups.serial", "%s", (unsigned char*) DevData.Identification); dstate_setinfo("ups.firmware", "%s", (unsigned char*) DevData.Version); if (typeRielloProtocol == DEV_RIELLOGPSER) { if (get_ups_nominal() == 0) { dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW); dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA); dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout); dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0); dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat); dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap); } } else { if (get_ups_sentr() == 0) { dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW); dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA); dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout); dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0); dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat); dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap); } } /* commands ----------------------------------------------- */ dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("load.off.delay"); dstate_addcmd("load.on.delay"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stop"); dstate_addcmd("test.battery.start"); if (typeRielloProtocol == DEV_RIELLOGPSER) dstate_addcmd("test.panel.start"); /* install handlers */ /* upsh.setvar = hid_set_value; setvar; */ upsh.instcmd = riello_instcmd; } void upsdrv_updateinfo(void) { uint8_t getextendedOK; static int countlost = 0; int stat; upsdebugx(1, "countlost %d",countlost); if (countlost > 0){ upsdebugx(1, "Communication with UPS is lost: status read failed!"); if (countlost == COUNTLOST) { dstate_datastale(); upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!"); } } if (typeRielloProtocol == DEV_RIELLOGPSER) stat = get_ups_status(); else stat = get_ups_sentr(); if (stat < 0) { if (countlost < COUNTLOST) countlost++; return; } if (typeRielloProtocol == DEV_RIELLOGPSER) { if (get_ups_extended() == 0) getextendedOK = 1; else getextendedOK = 0; } else getextendedOK = 1; if (countlost == COUNTLOST) upslogx(LOG_NOTICE, "Communication with UPS is re-established!"); dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0); dstate_setinfo("input.bypass.frequency", "%.2f", DevData.Fbypass/10.0); dstate_setinfo("output.frequency", "%.2f", DevData.Fout/10.0); dstate_setinfo("battery.voltage", "%.1f", DevData.Ubat/10.0); dstate_setinfo("battery.charge", "%u", DevData.BatCap); dstate_setinfo("battery.runtime", "%u", DevData.BatTime*60); dstate_setinfo("ups.temperature", "%u", DevData.Tsystem); if (input_monophase) { dstate_setinfo("input.voltage", "%u", DevData.Uinp1); dstate_setinfo("input.bypass.voltage", "%u", DevData.Ubypass1); } else { dstate_setinfo("input.L1-N.voltage", "%u", DevData.Uinp1); dstate_setinfo("input.L2-N.voltage", "%u", DevData.Uinp2); dstate_setinfo("input.L3-N.voltage", "%u", DevData.Uinp3); dstate_setinfo("input.bypass.L1-N.voltage", "%u", DevData.Ubypass1); dstate_setinfo("input.bypass.L2-N.voltage", "%u", DevData.Ubypass2); dstate_setinfo("input.bypass.L3-N.voltage", "%u", DevData.Ubypass3); } if (output_monophase) { dstate_setinfo("output.voltage", "%u", DevData.Uout1); dstate_setinfo("output.power.percent", "%u", DevData.Pout1); dstate_setinfo("ups.load", "%u", DevData.Pout1); } else { dstate_setinfo("output.L1-N.voltage", "%u", DevData.Uout1); dstate_setinfo("output.L2-N.voltage", "%u", DevData.Uout2); dstate_setinfo("output.L3-N.voltage", "%u", DevData.Uout3); dstate_setinfo("output.L1.power.percent", "%u", DevData.Pout1); dstate_setinfo("output.L2.power.percent", "%u", DevData.Pout2); dstate_setinfo("output.L3.power.percent", "%u", DevData.Pout3); dstate_setinfo("ups.load", "%u", (DevData.Pout1+DevData.Pout2+DevData.Pout3)/3); } status_init(); /* AC Fail */ if (riello_test_bit(&DevData.StatusCode[0], 1)) status_set("OB"); else status_set("OL"); /* LowBatt */ if ((riello_test_bit(&DevData.StatusCode[0], 1)) && (riello_test_bit(&DevData.StatusCode[0], 0))) status_set("LB"); /* Standby */ if (!riello_test_bit(&DevData.StatusCode[0], 3)) status_set("OFF"); /* On Bypass */ if (riello_test_bit(&DevData.StatusCode[1], 3)) status_set("BYPASS"); /* Overload */ if (riello_test_bit(&DevData.StatusCode[4], 2)) status_set("OVER"); /* Buck */ if (riello_test_bit(&DevData.StatusCode[1], 0)) status_set("TRIM"); /* Boost */ if (riello_test_bit(&DevData.StatusCode[1], 1)) status_set("BOOST"); /* Replace battery */ if (riello_test_bit(&DevData.StatusCode[2], 0)) status_set("RB"); /* Charging battery */ if (riello_test_bit(&DevData.StatusCode[2], 2)) status_set("CHRG"); status_commit(); dstate_dataok(); if (getextendedOK) { dstate_setinfo("output.L1.power", "%u", DevData.Pout1VA); dstate_setinfo("output.L2.power", "%u", DevData.Pout2VA); dstate_setinfo("output.L3.power", "%u", DevData.Pout3VA); dstate_setinfo("output.L1.realpower", "%u", DevData.Pout1W); dstate_setinfo("output.L2.realpower", "%u", DevData.Pout2W); dstate_setinfo("output.L3.realpower", "%u", DevData.Pout3W); dstate_setinfo("output.L1.current", "%u", DevData.Iout1); dstate_setinfo("output.L2.current", "%u", DevData.Iout2); dstate_setinfo("output.L3.current", "%u", DevData.Iout3); } poll_interval = 2; countlost = 0; /* if (get_ups_statuscode() != 0) upsdebugx(2, "Communication is lost"); else { }*/ /* * poll_interval = 2; */ } void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ int retry; /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ upsdebugx(2, "upsdrv Shutdown execute"); for (retry = 1; retry <= MAXTRIES; retry++) { if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } if (riello_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } fatalx(EXIT_SUCCESS, "Shutting down"); } fatalx(EXIT_FAILURE, "Shutdown failed!"); } /* static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.test.interval")) { ser_send_buf(upsfd, ...); return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } */ void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* allow '-x xyzzy' */ /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ /* allow '-x foo=' */ /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ } void upsdrv_initups(void) { upsdebugx(2, "entering upsdrv_initups()"); upsfd = ser_open(device_path); riello_comm_setup(device_path); /* probe ups type */ /* to get variables and flags from the command line, use this: * * first populate with upsdrv_buildvartable above, then... * * set flag foo : /bin/driver -x foo * set variable 'cable' to '1234' : /bin/driver -x cable=1234 * * to test flag foo in your code: * * if (testvar("foo")) * do_something(); * * to show the value of cable: * * if ((cable = getval("cable"))) * printf("cable is set to %s\n", cable); * else * printf("cable is not set!\n"); * * don't use NULL pointers - test the return result first! */ /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ /* don't try to detect the UPS here */ /* initialise communication */ } void upsdrv_cleanup(void) { /* free(dynamic_mem); */ ser_close(upsfd, device_path); } void riello_comm_setup(const char *port) { uint8_t length; upsdebugx(2, "set baudrate 9600"); ser_set_speed(upsfd, device_path, B9600); upsdebugx(2, "try to detect SENTR"); riello_init_serial(); bufOut[0] = 192; ser_send_buf(upsfd, bufOut, 1); riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); if (buf_ptr_length == 103) { typeRielloProtocol = DEV_RIELLOSENTRY; upslogx(LOG_INFO, "Connected to UPS SENTR on %s with baudrate %d", port, 9600); return; } upsdebugx(2, "try to detect GPSER"); riello_init_serial(); length = riello_prepare_gi(&bufOut[0]); ser_send_buf(upsfd, bufOut, length); riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && !foundbadcrc && !foundnak) { typeRielloProtocol = DEV_RIELLOGPSER; upslogx(LOG_INFO, "Connected to UPS GPSER on %s with baudrate %d", port, 9600); return; } upsdebugx(2, "set baudrate 1200"); ser_set_speed(upsfd, device_path, B1200); upsdebugx(2, "try to detect SENTR"); riello_init_serial(); bufOut[0] = 192; ser_send_buf(upsfd, bufOut, 1); riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); if (buf_ptr_length == 103) { typeRielloProtocol = DEV_RIELLOSENTRY; upslogx(LOG_INFO, "Connected to UPS SENTR on %s with baudrate %d", port, 1200); return; } upsdebugx(2, "try to detect GPSER"); riello_init_serial(); length = riello_prepare_gi(&bufOut[0]); ser_send_buf(upsfd, bufOut, length); riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); if (!wait_packet && !foundbadcrc && !foundnak) { typeRielloProtocol = DEV_RIELLOGPSER; upslogx(LOG_INFO, "Connected to UPS GPSER on %s with baudrate %d", port, 1200); return; } fatalx(EXIT_FAILURE, "Can't connect to the UPS on port %s!\n", port); } nut-2.7.4/drivers/nutdrv_qx_mustek.c0000644000175000017500000001470712640473702014535 00000000000000/* nutdrv_qx_mustek.c - Subdriver for Mustek protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_mustek.h" #define MUSTEK_VERSION "Mustek 0.07" /* qx2nut lookup table */ static item_t mustek_qx2nut[] = { /* * > [QS\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* * > [I\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* Testing table */ #ifdef TESTING static testing_t mustek_testing[] = { { "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "F\r", "#230.0 000 024.0 50.0\r", -1 }, { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, { "Q\r", "", -1 }, { "S03\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "", -1 }, { "T04\r", "", -1 }, { "TL\r", "", -1 }, { "T\r", "", -1 }, { "CT\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* Subdriver-specific initups */ static void mustek_initups(void) { blazer_initups(mustek_qx2nut); } /* Subdriver interface */ subdriver_t mustek_subdriver = { MUSTEK_VERSION, blazer_claim_light, mustek_qx2nut, mustek_initups, NULL, blazer_makevartable, "ACK", NULL, #ifdef TESTING mustek_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/dummy-ups.c0000644000175000017500000003176412640473702013055 00000000000000/* dummy-ups.c - NUT simulation and device repeater driver Copyright (C) 2005 - 2015 Arnaud Quette 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 */ /* TODO list: * - separate the code between dummy and repeater/meta * - for repeater/meta: * * add support for instant commands and setvar * - for dummy: * * variable/value enforcement using cmdvartab for testing * the variable existance, and possible values * * allow variable creation on the fly (using upsrw) * * poll the "port" file for change */ #include #include #include #include #include "main.h" #include "parseconf.h" #include "upsclient.h" #include "dummy-ups.h" #define DRIVER_NAME "Device simulation and repeater driver" #define DRIVER_VERSION "0.14" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette ", DRV_STABLE, { NULL } }; #define MODE_NONE 0 #define MODE_DUMMY 1 /* use the embedded defintion or a definition file */ #define MODE_REPEATER 2 /* use libupsclient to repeat an UPS */ #define MODE_META 3 /* consolidate data from several UPSs (TBS) */ int mode=MODE_NONE; /* parseconf context, for dummy mode using a file */ PCONF_CTX_t *ctx=NULL; time_t next_update = -1; #define MAX_STRING_SIZE 128 static int setvar(const char *varname, const char *val); static int instcmd(const char *cmdname, const char *extra); static int parse_data_file(int upsfd); static dummy_info_t *find_info(const char *varname); static int is_valid_data(const char* varname); static int is_valid_value(const char* varname, const char *value); /* libupsclient update */ static int upsclient_update_vars(void); /* connection information */ static char *client_upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; static int port; /* Driver functions */ void upsdrv_initinfo(void) { dummy_info_t *item; switch (mode) { case MODE_DUMMY: /* Initialise basic essential variables */ for ( item = nut_data ; item->info_type != NULL ; item++ ) { if (item->drv_flags & DU_FLAG_INIT) { dstate_setinfo(item->info_type, "%s", item->default_value); dstate_setflags(item->info_type, item->info_flags); /* Set max length for strings, if needed */ if (item->info_flags & ST_FLAG_STRING) dstate_setaux(item->info_type, item->info_len); } } /* Now get user's defined variables */ if (parse_data_file(upsfd) < 0) upslogx(LOG_NOTICE, "Unable to parse the definition file %s", device_path); /* Initialize handler */ upsh.setvar = setvar; dstate_dataok(); break; case MODE_META: case MODE_REPEATER: /* Obtain the target name */ if (upscli_splitname(device_path, &client_upsname, &hostname, &port) != 0) { fatalx(EXIT_FAILURE, "Error: invalid UPS definition.\nRequired format: upsname[@hostname[:port]]"); } /* Connect to the target */ ups = xmalloc(sizeof(*ups)); if (upscli_connect(ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) { fatalx(EXIT_FAILURE, "Error: %s", upscli_strerror(ups)); } else { upsdebugx(1, "Connected to %s@%s", client_upsname, hostname); } if (upsclient_update_vars() < 0) { /* check for an old upsd */ if (upscli_upserror(ups) == UPSCLI_ERR_UNKCOMMAND) { fatalx(EXIT_FAILURE, "Error: upsd is too old to support this query"); } fatalx(EXIT_FAILURE, "Error: %s", upscli_strerror(ups)); } /* FIXME: commands and settable variable! */ break; default: case MODE_NONE: fatalx(EXIT_FAILURE, "no suitable definition found!"); break; } upsh.instcmd = instcmd; dstate_addcmd("load.off"); } void upsdrv_updateinfo(void) { upsdebugx(1, "upsdrv_updateinfo..."); sleep(1); switch (mode) { case MODE_DUMMY: /* Now get user's defined variables */ if (parse_data_file(upsfd) >= 0) dstate_dataok(); break; case MODE_META: case MODE_REPEATER: if (upsclient_update_vars() > 0) { dstate_dataok(); } else { /* try to reconnect */ upscli_disconnect(ups); if (upscli_connect(ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) { upsdebugx(1, "Error reconnecting: %s", upscli_strerror(ups)); } else { upsdebugx(1, "Reconnected"); } } break; case MODE_NONE: default: break; } } void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); } static int instcmd(const char *cmdname, const char *extra) { /* if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } */ /* FIXME: the below is only valid if (mode == MODE_DUMMY) * if (mode == MODE_REPEATER) => forward * if (mode == MODE_META) => ? */ upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } void upsdrv_initups(void) { /* check the running mode... */ if (strchr(device_path, '@')) { upsdebugx(1, "Repeater mode"); mode = MODE_REPEATER; dstate_setinfo("driver.parameter.mode", "repeater"); /* FIXME: if there is at least one more => MODE_META... */ } else { upsdebugx(1, "Dummy (simulation) mode"); mode = MODE_DUMMY; dstate_setinfo("driver.parameter.mode", "dummy"); } } void upsdrv_cleanup(void) { if ( (mode == MODE_META) || (mode == MODE_REPEATER) ) { if (ups) { upscli_disconnect(ups); } if (ctx) { pconf_finish(ctx); free(ctx); } free(client_upsname); free(hostname); free(ups); } } static int setvar(const char *varname, const char *val) { dummy_info_t *item; upsdebugx(2, "entering setvar(%s, %s)", varname, val); /* FIXME: the below is only valid if (mode == MODE_DUMMY) * if (mode == MODE_REPEATER) => forward * if (mode == MODE_META) => ? */ if (!strncmp(varname, "ups.status", 10)) { status_init(); /* FIXME: split and check values (support multiple values), à la usbhid-ups */ status_set(val); status_commit(); return STAT_SET_HANDLED; } /* Check variable validity */ if (!is_valid_data(varname)) { upsdebugx(2, "setvar: invalid variable name (%s)", varname); return STAT_SET_UNKNOWN; } /* Check value validity */ if (!is_valid_value(varname, val)) { upsdebugx(2, "setvar: invalid value (%s) for variable (%s)", val, varname); return STAT_SET_UNKNOWN; } /* If value is empty, remove the variable (FIXME: do we need * a magic word?) */ if (strlen(val) == 0) { dstate_delinfo(varname); } else { dstate_setinfo(varname, "%s", val); if ( (item = find_info(varname)) != NULL) { dstate_setflags(item->info_type, item->info_flags); /* Set max length for strings, if needed */ if (item->info_flags & ST_FLAG_STRING) dstate_setaux(item->info_type, item->info_len); } else { upsdebugx(2, "setvar: unknown variable (%s), using default flags", varname); dstate_setflags(varname, ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux(varname, 32); } } return STAT_SET_HANDLED; } /*************************************************/ /* Support functions */ /*************************************************/ static int upsclient_update_vars(void) { int ret; unsigned int numq, numa; const char *query[4]; char **answer; query[0] = "VAR"; query[1] = client_upsname; numq = 2; ret = upscli_list_start(ups, numq, query); if (ret < 0) { upsdebugx(1, "Error: %s (%i)", upscli_strerror(ups), upscli_upserror(ups)); return ret; } while (upscli_list_next(ups, numq, query, &numa, &answer) == 1) { /* VAR */ if (numa < 4) { upsdebugx(1, "Error: insufficient data (got %d args, need at least 4)", numa); } upsdebugx(5, "Received: %s %s %s %s", answer[0], answer[1], answer[2], answer[3]); /* do not override the driver collection */ if (strncmp(answer[2], "driver.", 7)) setvar(answer[2], answer[3]); } return 1; } /* find info element definition in info array */ static dummy_info_t *find_info(const char *varname) { dummy_info_t *item; for ( item = nut_data ; item->info_type != NULL ; item++ ) { if (!strcasecmp(item->info_type, varname)) return item; } upsdebugx(2, "find_info: unknown variable: %s\n", varname); return NULL; } /* check if data exists in our data table */ static int is_valid_data(const char* varname) { dummy_info_t *item; if ( (item = find_info(varname)) != NULL) { return 1; } /* FIXME: we need to have the full data set before * enforcing controls! We also need a way to automate * the update / sync process (with cmdvartab?!) */ upsdebugx(1, "Unknown data. Committing anyway..."); return 1; /* return 0;*/ } /* check if data's value validity */ static int is_valid_value(const char* varname, const char *value) { dummy_info_t *item; if ( (item = find_info(varname)) != NULL) { /* FIXME: test enum or bound against value */ return 1; } /* FIXME: we need to have the full data set before * enforcing controls! We also need a way to automate * the update / sync process (with cmdvartab?) */ upsdebugx(1, "Unknown data. Committing value anyway..."); return 1; /* return 0;*/ } /* called for fatal errors in parseconf like malloc failures */ static void upsconf_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf(ups.conf): %s", errmsg); } /* for dummy mode * parse the definition file and process its content */ static int parse_data_file(int upsfd) { char fn[SMALLBUF]; char *ptr, var_value[MAX_STRING_SIZE]; int value_args = 0, counter; time_t now; time(&now); upsdebugx(1, "entering parse_data_file()"); if (now < next_update) { upsdebugx(1, "leaving (paused)..."); return 1; } /* initialise everything, to loop back at the beginning of the file */ if (ctx == NULL) { ctx = (PCONF_CTX_t *)xmalloc(sizeof(PCONF_CTX_t)); if (device_path[0] == '/') snprintf(fn, sizeof(fn), "%s", device_path); else snprintf(fn, sizeof(fn), "%s/%s", confpath(), device_path); pconf_init(ctx, upsconf_err); if (!pconf_file_begin(ctx, fn)) fatalx(EXIT_FAILURE, "Can't open dummy-ups definition file %s: %s", fn, ctx->errmsg); } /* Reset the next call time, so that we can loop back on the file * if there is no blocking action (ie TIMER) until the end of the file */ next_update = -1; /* Now start or continue parsing... */ while (pconf_file_next(ctx)) { if (pconf_parse_error(ctx)) { upsdebugx(2, "Parse error: %s:%d: %s", fn, ctx->linenum, ctx->errmsg); continue; } /* Check if we have something to process */ if (ctx->numargs < 1) continue; /* Process actions (only "TIMER" ATM) */ if (!strncmp(ctx->arglist[0], "TIMER", 5)) { /* TIMER will wait "seconds" before * continuing the parsing */ int delay = atoi (ctx->arglist[1]); time(&next_update); next_update += delay; upsdebugx(1, "suspending execution for %i seconds...", delay); break; } /* Remove ":" suffix, after the variable name */ if ((ptr = strchr(ctx->arglist[0], ':')) != NULL) *ptr = '\0'; upsdebugx(3, "parse_data_file: variable \"%s\" with %d args", ctx->arglist[0], (int)ctx->numargs); /* Skip the driver.* collection data */ if (!strncmp(ctx->arglist[0], "driver.", 7)) { upsdebugx(2, "parse_data_file: skipping %s", ctx->arglist[0]); continue; } /* From there, we get varname in arg[0], and values in other arg[1...x] */ /* special handler for status */ if (!strncmp( ctx->arglist[0], "ups.status", 10)) { status_init(); for (counter = 1, value_args = ctx->numargs ; counter < value_args ; counter++) { status_set(ctx->arglist[counter]); } status_commit(); } else { for (counter = 1, value_args = ctx->numargs ; counter < value_args ; counter++) { if (counter == 1) /* don't append the first space separator */ snprintf(var_value, sizeof(var_value), "%s", ctx->arglist[counter]); else snprintfcat(var_value, sizeof(var_value), " %s", ctx->arglist[counter]); } if (setvar(ctx->arglist[0], var_value) == STAT_SET_UNKNOWN) { upsdebugx(2, "parse_data_file: can't add \"%s\" with value \"%s\"\nError: %s", ctx->arglist[0], var_value, ctx->errmsg); } else { upsdebugx(3, "parse_data_file: added \"%s\" with value \"%s\"", ctx->arglist[0], var_value); } } } /* Cleanup parseconf if there is no pending action */ if (next_update == -1) { pconf_finish(ctx); free(ctx); ctx=NULL; } return 1; } nut-2.7.4/drivers/snmp-ups.h0000644000175000017500000002310412640473702012671 00000000000000/* snmp-ups.h - NUT Meta SNMP driver (support different MIBS) * * Based on NET-SNMP API (Simple Network Management Protocol V1-2) * * Copyright (C) * 2002-2010 Arnaud Quette * 2002-2006 Dmitry Frolov * J.W. Hoogervorst * Niels Baggesen * * Sponsored by MGE UPS SYSTEMS * * 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 * */ /* TODO list: - add syscontact/location (to all mib.h or centralized?) - complete shutdown - add enum values to OIDs. - optimize network flow by: 1) caching OID values (as in usbhid-ups) with timestamping and lifetime 2) constructing one big packet (calling snmp_add_null_var for each OID request we made), instead of sending many small packets - add support for registration and traps (manager mode) => Issue: 1 trap listener for N snmp-ups drivers! - complete mib2nut data (add all OID translation to NUT) - externalize mib2nut data in .m2n files and load at driver startup using parseconf()... - adjust information logging. - move numeric OIDs into th mib2nut tables and remove defines - move mib2nut into c files (à la usbhid-ups)? - add a claim function and move to usbhid-ups style for specific processing - rework the flagging system */ #ifndef SNMP_UPS_H #define SNMP_UPS_H /* FIXME: still needed? * workaround for buggy Net-SNMP config */ #ifdef PACKAGE_BUGREPORT #undef PACKAGE_BUGREPORT #endif #ifdef PACKAGE_NAME #undef PACKAGE_NAME #endif #ifdef PACKAGE_VERSION #undef PACKAGE_VERSION #endif #ifdef PACKAGE_STRING #undef PACKAGE_STRING #endif #ifdef PACKAGE_TARNAME #undef PACKAGE_TARNAME #endif #ifdef HAVE_DMALLOC_H #undef HAVE_DMALLOC_H #endif #include #include /* Force numeric OIDs by disabling MIB loading */ #define DISABLE_MIB_LOADING 1 /* Parameters default values */ #define DEFAULT_POLLFREQ 30 /* in seconds */ #define DEFAULT_NETSNMP_RETRIES 5 #define DEFAULT_NETSNMP_TIMEOUT 1 /* in seconds */ /* use explicit booleans */ #ifndef FALSE typedef enum ebool { FALSE, TRUE } bool_t; #else typedef int bool_t; #endif /* Common SNMP data and lookup definitions */ /* special functions to interpret items: take UPS answer, return string to set in INFO, max len NOTE: FFE means For Future Extensions */ /* typedef void (*interpreter)(char *, char *, int); */ /* for lookup between OID values and INFO_ value */ typedef struct { int oid_value; /* OID value */ const char *info_value; /* INFO_* value */ } info_lkp_t; /* Structure containing info about one item that can be requested from UPS and set in INFO. If no interpreter functions is defined, use sprintf with given format string. If unit is not NONE, values are converted according to the multiplier table */ typedef struct { const char *info_type; /* INFO_ or CMD_ element */ int info_flags; /* flags to set in addinfo */ double info_len; /* length of strings if STR, * cmd value if CMD, multiplier otherwise. */ const char *OID; /* SNMP OID or NULL */ const char *dfl; /* default value */ unsigned long flags; /* my flags */ info_lkp_t *oid2info; /* lookup table between OID and NUT values */ int *setvar; /* variable to set for SU_FLAG_SETINT */ } snmp_info_t; #define SU_FLAG_OK (1 << 0) /* show element to upsd. */ #define SU_FLAG_STATIC (1 << 1) /* retrieve info only once. */ #define SU_FLAG_ABSENT (1 << 2) /* data is absent in the device, * use default value. */ #define SU_FLAG_STALE (1 << 3) /* data stale, don't try too often. */ #define SU_FLAG_NEGINVALID (1 << 4) /* Invalid if negative value */ #define SU_FLAG_UNIQUE (1 << 5) /* There can be only be one * provider of this info, * disable the other providers */ #define SU_FLAG_SETINT (1 << 6) /* save value */ #define SU_OUTLET (1 << 7) /* outlet template definition */ #define SU_CMD_OFFSET (1 << 8) /* Add +1 to the OID index */ /* Notes on outlet templates usage: * - outlet.count MUST exist and MUST be declared before any outlet template * Otherwise, the driver will try to determine it by itself... * - the first outlet template MUST NOT be a server side variable (ie MUST have * a valid OID) in order to detect the base SNMP index (0 or 1) */ /* status string components * FIXME: these should be removed, since there is no added value. * Ie, this can be guessed from info->type! */ #define SU_STATUS_PWR (0 << 8) /* indicates power status element */ #define SU_STATUS_BATT (1 << 8) /* indicates battery status element */ #define SU_STATUS_CAL (2 << 8) /* indicates calibration status element */ #define SU_STATUS_RB (3 << 8) /* indicates replace battery status element */ #define SU_STATUS_NUM_ELEM 4 #define SU_STATUS_INDEX(t) (((t) >> 8) & 7) #define SU_OUTLET_GROUP (1 << 10) /* outlet group template definition */ /* Phase specific data */ #define SU_PHASES (0x3F << 12) #define SU_INPHASES (0x3 << 12) #define SU_INPUT_1 (1 << 12) /* only if 1 input phase */ #define SU_INPUT_3 (1 << 13) /* only if 3 input phases */ #define SU_OUTPHASES (0x3 << 14) #define SU_OUTPUT_1 (1 << 14) /* only if 1 output phase */ #define SU_OUTPUT_3 (1 << 15) /* only if 3 output phases */ #define SU_BYPPHASES (0x3 << 16) #define SU_BYPASS_1 (1 << 16) /* only if 1 bypass phase */ #define SU_BYPASS_3 (1 << 17) /* only if 3 bypass phases */ /* FIXME: use input.phases and output.phases to replace this */ /* hints for su_ups_set, applicable only to rw vars */ #define SU_TYPE_INT (0 << 18) /* cast to int when setting value */ #define SU_TYPE_STRING (1 << 18) /* cast to string. FIXME: redundant with ST_FLAG_STRING */ #define SU_TYPE_TIME (2 << 18) /* cast to int */ #define SU_TYPE_CMD (3 << 18) /* instant command */ #define SU_TYPE(t) ((t)->flags & (7 << 18)) #define SU_VAR_COMMUNITY "community" #define SU_VAR_VERSION "snmp_version" #define SU_VAR_RETRIES "snmp_retries" #define SU_VAR_TIMEOUT "snmp_timeout" #define SU_VAR_MIBS "mibs" #define SU_VAR_POLLFREQ "pollfreq" /* SNMP v3 related parameters */ #define SU_VAR_SECLEVEL "secLevel" #define SU_VAR_SECNAME "secName" #define SU_VAR_AUTHPASSWD "authPassword" #define SU_VAR_PRIVPASSWD "privPassword" #define SU_VAR_AUTHPROT "authProtocol" #define SU_VAR_PRIVPROT "privProtocol" #define SU_INFOSIZE 128 #define SU_BUFSIZE 32 #define SU_LARGEBUF 256 #define SU_STALE_RETRY 10 /* retry to retrieve stale element */ /* after this number of iterations. */ /* FIXME: this is for *all* elements */ /* modes to snmp_ups_walk. */ #define SU_WALKMODE_INIT 0 #define SU_WALKMODE_UPDATE 1 /* log spew limiters */ #define SU_ERR_LIMIT 10 /* start limiting after this many errors in a row */ #define SU_ERR_RATE 100 /* only print every nth error once limiting starts */ typedef struct { const char * OID; const char *status_value; /* when not NULL, set ups.status to this */ const char *alarm_value; /* when not NULL, set ups.alarm to this */ } alarms_info_t; typedef struct { const char *mib_name; const char *mib_version; const char *oid_pwr_status; const char *oid_auto_check; /* FIXME: rename to SysOID */ snmp_info_t *snmp_info; /* pointer to the good Snmp2Nut lookup data */ const char *sysOID; /* OID to match against sysOID, aka MIB * main entry point */ alarms_info_t *alarms_info; } mib2nut_info_t; /* Common SNMP functions */ void nut_snmp_init(const char *type, const char *hostname); void nut_snmp_cleanup(void); struct snmp_pdu *nut_snmp_get(const char *OID); bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info); bool_t nut_snmp_get_int(const char *OID, long *pval); bool_t nut_snmp_set(const char *OID, char type, const char *value); bool_t nut_snmp_set_str(const char *OID, const char *value); bool_t nut_snmp_set_int(const char *OID, long value); void nut_snmp_perror(struct snmp_session *sess, int status, struct snmp_pdu *response, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 4, 5))); void su_startup(void); void su_cleanup(void); void su_init_instcmds(void); void su_setuphandlers(void); /* need to deal with external function ptr */ void su_setinfo(snmp_info_t *su_info_p, const char *value); void su_status_set(snmp_info_t *, long value); snmp_info_t *su_find_info(const char *type); bool_t snmp_ups_walk(int mode); bool_t su_ups_get(snmp_info_t *su_info_p); bool_t load_mib2nut(const char *mib); const char *su_find_infoval(info_lkp_t *oid2info, long value); long su_find_valinfo(info_lkp_t *oid2info, const char* value); int su_setvar(const char *varname, const char *val); int su_instcmd(const char *cmdname, const char *extradata); void su_shutdown_ups(void); void read_mibconf(char *mib); extern struct snmp_session g_snmp_sess, *g_snmp_sess_p; extern const char *OID_pwr_status; extern int g_pwr_battery; extern int pollfreq; /* polling frequency */ extern int input_phases, output_phases, bypass_phases; #endif /* SNMP_UPS_H */ nut-2.7.4/drivers/powerp-txt.h0000644000175000017500000000205312640443572013242 00000000000000/* * powerp-txt.h - Model specific data/definitions for CyberPower text * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2008 Arjen de Korte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef POWERP_TXT_H #define POWERP_TXT_H #include "powerpanel.h" extern subdriver_t powpan_text; #endif nut-2.7.4/drivers/hidparser.c0000644000175000017500000004165412640473702013075 00000000000000/* * hidparser.c: HID Parser * * This file is part of the MGE UPS SYSTEMS HID Parser * * Copyright (C) * 1998-2003 MGE UPS SYSTEMS, Luc Descotils * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * -------------------------------------------------------------------------- */ #include #include #include "config.h" #include "hidparser.h" #include "nut_stdint.h" /* for int8_t, int16_t, int32_t */ #include "common.h" /* for fatalx() */ static const uint8_t ItemSize[4] = { 0, 1, 2, 4 }; /* * HIDParser struct * -------------------------------------------------------------------------- */ typedef struct { const unsigned char *ReportDesc; /* Report Descriptor */ int ReportDescSize; /* Size of Report Descriptor */ uint16_t Pos; /* Store current pos in descriptor */ uint8_t Item; /* Store current Item */ long Value; /* Store current Value */ HIDData_t Data; /* Store current environment */ uint8_t OffsetTab[MAX_REPORT][4]; /* Store ID, Type, offset & timestamp of report */ uint8_t ReportCount; /* Store Report Count */ uint8_t Count; /* Store local report count */ uint16_t UPage; /* Global UPage */ HIDNode_t UsageTab[USAGE_TAB_SIZE]; /* Usage stack */ uint8_t UsageSize; /* Design number of usage used */ } HIDParser_t; /* return 1 + the position of the leftmost "1" bit of an int, or 0 if none. */ static inline unsigned int hibit(unsigned int x) { unsigned int res = 0; while (x > 0xff) { x >>= 8; res += 8; } while (x) { x >>= 1; res += 1; } return res; } /* Note: The USB HID specification states that Local items do not carry over to the next Main item (version 1.11, section 6.2.2.8). Therefore the local state must be reset after each main item. In particular, any unused usages on the Usage tabs must be discarded and must not carry over to the next Main item. Some APC equipment actually sends multiple redundant "usage" commands for a single control, so resetting the local state is important. */ /* Also note: UsageTab[0] is used as the usage of the next control, even if UsageSize=0. Therefore, this must be initialized */ static void ResetLocalState(HIDParser_t* pParser) { pParser->UsageSize = 0; memset(pParser->UsageTab, 0, sizeof(pParser->UsageTab)); } /* * GetReportOffset * * Return pointer on current offset value for Report designed by * ReportID/ReportType * -------------------------------------------------------------------------- */ static uint8_t *GetReportOffset(HIDParser_t* pParser, const uint8_t ReportID, const uint8_t ReportType) { int Pos; for (Pos = 0; Pos < MAX_REPORT; Pos++) { if (pParser->OffsetTab[Pos][0] == 0) { pParser->OffsetTab[Pos][0] = ReportID; pParser->OffsetTab[Pos][1] = ReportType; pParser->OffsetTab[Pos][2] = 0; } if (pParser->OffsetTab[Pos][0] != ReportID) { continue; } if (pParser->OffsetTab[Pos][1] != ReportType) { continue; } return &pParser->OffsetTab[Pos][2]; } return NULL; } /* * FormatValue(long Value, uint8_t Size) * Format Value to fit with long format with respect of negative values * -------------------------------------------------------------------------- */ static long FormatValue(long Value, uint8_t Size) { switch(Size) { case 1: return (long)(int8_t)Value; case 2: return (long)(int16_t)Value; case 4: return (long)(int32_t)Value; default: return Value; } } /* * HIDParse(HIDParser_t* pParser, HIDData_t *pData) * * Analyse Report descriptor stored in HIDParser struct and store local and * global context. * Return in pData the last object found. * Return -1 when there is no other Item to parse, 1 if a new object was found * or 0 if a continuation of a previous object was found. * -------------------------------------------------------------------------- */ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) { int Found = -1; while ((Found < 0) && (pParser->Pos < pParser->ReportDescSize)) { /* Get new pParser->Item if current pParser->Count is empty */ if (pParser->Count == 0) { pParser->Item = pParser->ReportDesc[pParser->Pos++]; pParser->Value = 0; #if WORDS_BIGENDIAN { int i; unsigned long valTmp = 0; for (i = 0; i < ItemSize[pParser->Item & SIZE_MASK]; i++) { memcpy(&valTmp, &pParser->ReportDesc[(pParser->Pos)+i], 1); pParser->Value += valTmp >> ((3-i)*8); valTmp = 0; } } #else memcpy(&pParser->Value, &pParser->ReportDesc[pParser->Pos], ItemSize[pParser->Item & SIZE_MASK]); #endif /* Pos on next item */ pParser->Pos += ItemSize[pParser->Item & SIZE_MASK]; } switch (pParser->Item & ITEM_MASK) { case ITEM_UPAGE: /* Copy UPage in Usage stack */ pParser->UPage=(uint16_t)pParser->Value; break; case ITEM_USAGE: /* Copy global or local UPage if any, in Usage stack */ if ((pParser->Item & SIZE_MASK) > 2) { pParser->UsageTab[pParser->UsageSize] = pParser->Value; } else { pParser->UsageTab[pParser->UsageSize] = (pParser->UPage << 16) | (pParser->Value & 0xFFFF); } /* Increment Usage stack size */ pParser->UsageSize++; break; case ITEM_COLLECTION: /* Get UPage/Usage from UsageTab and store them in pParser->Data.Path */ pParser->Data.Path.Node[pParser->Data.Path.Size] = pParser->UsageTab[0]; pParser->Data.Path.Size++; /* Unstack UPage/Usage from UsageTab (never remove the last) */ if (pParser->UsageSize > 0) { int i; for (i = 0; i < pParser->UsageSize; i++) { pParser->UsageTab[i] = pParser->UsageTab[i+1]; } /* Remove Usage */ pParser->UsageSize--; } /* Get Index if any */ if (pParser->Value >= 0x80) { pParser->Data.Path.Node[pParser->Data.Path.Size] = 0x00ff0000 | (pParser->Value & 0x7F); pParser->Data.Path.Size++; } ResetLocalState(pParser); break; case ITEM_END_COLLECTION : pParser->Data.Path.Size--; /* Remove Index if any */ if((pParser->Data.Path.Node[pParser->Data.Path.Size] & 0xffff0000) == 0x00ff0000) { pParser->Data.Path.Size--; } ResetLocalState(pParser); break; case ITEM_FEATURE: case ITEM_INPUT: case ITEM_OUTPUT: if (pParser->UsageTab[0] != 0x00000000) { /* An object was found if the path does not end with 0x00000000 */ Found = 1; } else { /* It is a continuation of a previous object */ Found = 0; } /* Get new pParser->Count from global value */ if(pParser->Count == 0) { pParser->Count = pParser->ReportCount; } /* Get UPage/Usage from UsageTab and store them in pParser->Data.Path */ pParser->Data.Path.Node[pParser->Data.Path.Size] = pParser->UsageTab[0]; pParser->Data.Path.Size++; /* Unstack UPage/Usage from UsageTab (never remove the last) */ if(pParser->UsageSize > 0) { int i; for (i = 0; i < pParser->UsageSize; i++) { pParser->UsageTab[i] = pParser->UsageTab[i+1]; } /* Remove Usage */ pParser->UsageSize--; } /* Copy data type */ pParser->Data.Type = (uint8_t)(pParser->Item & ITEM_MASK); /* Copy data attribute */ pParser->Data.Attribute = (uint8_t)pParser->Value; /* Store offset */ pParser->Data.Offset = *GetReportOffset(pParser, pParser->Data.ReportID, (uint8_t)(pParser->Item & ITEM_MASK)); /* Get Object in pData */ /* -------------------------------------------------------------------------- */ memcpy(pData, &pParser->Data, sizeof(HIDData_t)); /* -------------------------------------------------------------------------- */ /* Increment Report Offset */ *GetReportOffset(pParser, pParser->Data.ReportID, (uint8_t)(pParser->Item & ITEM_MASK)) += pParser->Data.Size; /* Remove path last node */ pParser->Data.Path.Size--; /* Decrement count */ pParser->Count--; if (pParser->Count == 0) { ResetLocalState(pParser); } break; case ITEM_REP_ID : pParser->Data.ReportID = (uint8_t)pParser->Value; break; case ITEM_REP_SIZE : pParser->Data.Size = (uint8_t)pParser->Value; break; case ITEM_REP_COUNT : pParser->ReportCount = (uint8_t)pParser->Value; break; case ITEM_UNIT_EXP : pParser->Data.UnitExp = (int8_t)pParser->Value; if (pParser->Data.UnitExp > 7) { pParser->Data.UnitExp |= 0xF0; } break; case ITEM_UNIT : pParser->Data.Unit = pParser->Value; break; case ITEM_LOG_MIN : pParser->Data.LogMin = FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); break; case ITEM_LOG_MAX : pParser->Data.LogMax = FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); break; case ITEM_PHY_MIN : pParser->Data.PhyMin=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); pParser->Data.have_PhyMin = 1; break; case ITEM_PHY_MAX : pParser->Data.PhyMax=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); pParser->Data.have_PhyMax = 1; break; case ITEM_LONG : /* can't handle long items, but should at least skip them */ pParser->Pos += (uint8_t)(pParser->Value & 0xff); break; } } /* while ((Found < 0) && (pParser->Pos < pParser->ReportDescSize)) */ if(pParser->Data.Path.Size >= PATH_SIZE) upslogx(LOG_ERR, "%s: HID path too long", __func__); if(pParser->ReportDescSize >= REPORT_DSC_SIZE) upslogx(LOG_ERR, "%s: Report descriptor too big", __func__); if(pParser->UsageSize >= USAGE_TAB_SIZE) upslogx(LOG_ERR, "%s: HID Usage too high", __func__); if(pParser->Data.ReportID >= MAX_REPORT) upslogx(LOG_ERR, "%s: Too many HID reports", __func__); return Found; } /* * FindObject * Get pData characteristics from pData->Path * Return TRUE if object was found * -------------------------------------------------------------------------- */ int FindObject(HIDDesc_t *pDesc, HIDData_t *pData) { HIDData_t *pFoundData = FindObject_with_Path(pDesc, &pData->Path, pData->Type); if (!pFoundData) { return 0; } memcpy(pData, pFoundData, sizeof(*pData)); return 1; } /* * FindObject_with_Path * Get pData item with given Path and Type. Return NULL if not found. * -------------------------------------------------------------------------- */ HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type) { int i; for (i = 0; i < pDesc->nitems; i++) { HIDData_t *pData = &pDesc->item[i]; if (pData->Type != Type) { continue; } if (memcmp(pData->Path.Node, Path->Node, (Path->Size) * sizeof(HIDNode_t))) { continue; } return pData; } return NULL; } /* * FindObject_with_ID * Get pData item with given ReportID, Offset, and Type. Return NULL * if not found. * -------------------------------------------------------------------------- */ HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc, uint8_t ReportID, uint8_t Offset, uint8_t Type) { int i; for (i = 0; i < pDesc->nitems; i++) { HIDData_t *pData = &pDesc->item[i]; if (pData->ReportID != ReportID) { continue; } if (pData->Type != Type) { continue; } if (pData->Offset != Offset) { continue; } return pData; } return NULL; } /* * GetValue * Extract data from a report stored in Buf. * Use Value, Offset, Size and LogMax of pData. * Return response in Value. * -------------------------------------------------------------------------- */ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue) { int Weight, Bit; long value = 0, rawvalue; long range, mask, signbit, b, m; Bit = pData->Offset + 8; /* First byte of report is report ID */ for (Weight = 0; Weight < pData->Size; Weight++, Bit++) { int State = Buf[Bit >> 3] & (1 << (Bit & 7)); if(State) { value += (1 << Weight); } } /* translate Value into a signed/unsigned value in the range LogMin..LogMax, as appropriate. See HID spec, p.38: "If both the Logical Minimum and Logical Maximum extents are defined as positive values (0 or greater), then the report field can be assumed to be an unsigned value. Otherwise, all integer values are signed values represented in 2's complement format." Also note that the variable can take values from LogMin (inclusive) to LogMax (inclusive), so there are LogMax - LogMin + 1 possible values. Special cases arise if the value that has been read lies outside the interval LogMin..LogMax. Some devices, notably the APC Back-UPS BF500, do this. In one case I observed, LogMin=0, LogMax=0xffff, Size=32, and the supplied value is 0xffffffff80080a00. Presumably they expect us to throw away the higher-order bits, and use 0x0a00, rather than choosing the closest value in the interval, which would be 0xffff. However, if LogMax - LogMin + 1 isn't a power of 2, it is not clear what "throwing away higher-order bits" exacly means, so we try to do something sensible. -PS */ rawvalue = value; /* remember this for later */ /* figure out how many bits are significant */ range = pData->LogMax - pData->LogMin + 1; if (range <= 0) { /* makes no sense, give up */ *pValue = value; return; } b = hibit(range-1); /* throw away insignificant bits; the result is >= 0 */ mask = (1 << b) - 1; signbit = 1 << (b - 1); value = value & mask; /* sign-extend it, if appropriate */ if (pData->LogMin < 0 && (value & signbit) != 0) { value |= ~mask; } /* if the resulting value is in the desired range, stop */ if (value >= pData->LogMin && value <= pData->LogMax) { *pValue = value; return; } /* else, try to reach interval by adjusting high-order bits */ m = (value - pData->LogMin) & mask; value = pData->LogMin + m; if (value <= pData->LogMax) { *pValue = value; return; } /* if everything else failed, sign-extend the original raw value, and simply round it to the closest point in the interval. */ value = rawvalue; mask = (1 << pData->Size) - 1; signbit = 1 << (pData->Size - 1); if (pData->LogMin < 0 && (value & signbit) != 0) { value |= ~mask; } if (value < pData->LogMin) { value = pData->LogMin; } else if (value > pData->LogMax) { value = pData->LogMax; } *pValue = value; return; } /* * SetValue * Set a data in a report stored in Buf. Use Value, Offset and Size of pData. * Return response in Buf. * -------------------------------------------------------------------------- */ void SetValue(const HIDData_t *pData, unsigned char *Buf, long Value) { int Weight, Bit; Bit = pData->Offset + 8; /* First byte of report is report ID */ for (Weight = 0; Weight < pData->Size; Weight++, Bit++) { int State = Value & (1 << Weight); if (State) { Buf[Bit >> 3] |= (1 << (Bit & 7)); } else { Buf[Bit >> 3] &= ~(1 << (Bit & 7)); } } } /* ---------------------------------------------------------------------- */ /* parse HID Report Descriptor. Input: byte array ReportDesc[n]. Output: parsed data structure. Returns allocated HIDDesc structure on success, NULL on failure with errno set. Note: the value returned by this function must be freed with Free_ReportDesc(). */ HIDDesc_t *Parse_ReportDesc(const unsigned char *ReportDesc, const int n) { int ret; HIDDesc_t *pDesc; HIDParser_t *parser; pDesc = calloc(1, sizeof(*pDesc)); if (!pDesc) { return NULL; } pDesc->item = calloc(MAX_REPORT, sizeof(*pDesc->item)); if (!pDesc->item) { Free_ReportDesc(pDesc); return NULL; } parser = calloc(1, sizeof(*parser)); if (!parser) { Free_ReportDesc(pDesc); return NULL; } parser->ReportDesc = ReportDesc; parser->ReportDescSize = n; for (pDesc->nitems = 0; pDesc->nitems < MAX_REPORT; pDesc->nitems += ret) { int id, max; ret = HIDParse(parser, &pDesc->item[pDesc->nitems]); if (ret < 0) { break; } id = pDesc->item[pDesc->nitems].ReportID; /* calculate bit range of this item within report */ max = pDesc->item[pDesc->nitems].Offset + pDesc->item[pDesc->nitems].Size; /* convert to bytes */ max = (max + 7) >> 3; /* update report length */ if (max > pDesc->replen[id]) { pDesc->replen[id] = max; } } /* Sanity check: are there remaining HID objects that can't * be processed? */ if ((pDesc->nitems == MAX_REPORT) && (parser->Pos < parser->ReportDescSize)) upslogx(LOG_ERR, "ERROR in %s: Too many HID objects", __func__); free(parser); if (pDesc->nitems == 0) { Free_ReportDesc(pDesc); return NULL; } pDesc->item = realloc(pDesc->item, pDesc->nitems * sizeof(*pDesc->item)); return pDesc; } /* free a parsed report descriptor, as allocated by Parse_ReportDesc() */ void Free_ReportDesc(HIDDesc_t *pDesc) { if (!pDesc) { return; } free(pDesc->item); free(pDesc); } nut-2.7.4/drivers/bestfortress.c0000644000175000017500000002737212640473702013642 00000000000000/* bestfortress.c - model specific routines for (very) old Best Power Fortress Copyright (C) 2002 Russell Kroll (skeleton) (C) 2002 Holger Dietze (C) 2009 Stuart D. Gathman 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 */ #include "main.h" #include "serial.h" #define UPSDELAY 50000 /* 50 ms delay required for reliable operation */ #define SER_WAIT_SEC 2 /* allow 2.0 sec for ser_get calls */ #define SER_WAIT_USEC 0 #define ENDCHAR '\r' #define IGNCHARS " \n" #if defined(__sgi) && ! defined(__GNUC__) #define inline __inline #endif #define DRIVER_NAME "Best Fortress UPS driver" #define DRIVER_VERSION "0.05" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Holger Dietze \n" "Stuart D. Gathman \n", DRV_EXPERIMENTAL, { NULL } }; static int instcmd (const char *cmdname, const char *extra); static int upsdrv_setvar (const char *varname, const char *val); /* rated VA load if known */ static int maxload = 0; void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "Best Power"); dstate_setinfo("ups.model", "Fortress"); dstate_setinfo("battery.voltage.nominal", "24"); /*dstate_setinfo ("alarm.overload", "0");*/ /* Flag */ /*dstate_setinfo ("alarm.temp", "0");*/ /* Flag */ if (maxload) dstate_setinfo("ups.load", "0"); dstate_setinfo("output.voltamps", "0"); dstate_setinfo("ups.delay.shutdown", "10"); /* write only */ /* tunable via front panel: (european voltage level) parameter factory default range INFO_LOWXFER 196 V p7=nnn 160-210 INFO_HIGHXFER 254 V p8=nnn 215-274 INFO_LOBATTIME 2 min p2=n 1-5 comm mode p6=0 dumb DONT USE (will lose access to parameter setting!) p6=1 B1200 p6=2 B2400 P6=3 B4800 p6=4 B9600 maybe cycle through speeds to autodetect? echo off e0 echo on e1 */ dstate_setinfo("input.transfer.low", "%s", ""); dstate_setflags("input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("input.transfer.low", 3); dstate_setinfo("input.transfer.high", "%s", ""); dstate_setflags("input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("input.transfer.high", 3); dstate_setinfo("battery.runtime.low", "%s", ""); dstate_setflags("battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("battery.runtime.low", 3); upsh.instcmd = instcmd; upsh.setvar = upsdrv_setvar; dstate_addcmd("shutdown.return"); dstate_addcmd("load.off"); } /* convert hex digit to int */ static inline int fromhex (char c) { return (c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : 0; } /* do checksumming on UPS response */ static int checksum (char * s) { int i; int sum; for (i = 40, sum = 0; s[0] && s[1] && i > 0; i--, s += 2) { sum += (fromhex (s[0]) << 4) + fromhex (s[1]); } return sum; } /* set info to integer value */ static inline int setinfo_int (const char *key, const char * s, size_t len) { char buf[10]; int val; if (len > sizeof(buf)) len = sizeof(buf)-1; strncpy (buf, s, len); buf[len] = 0; val = atoi(buf); dstate_setinfo (key, "%d", val); return val; } /* set info to integer value (for runtime remaining) value is expressed in minutes, but desired in seconds */ static inline void setinfo_int_minutes (const char *key, const char * s, size_t len) { char buf[10]; if (len > sizeof(buf)) len = sizeof(buf)-1; strncpy (buf, s, len); buf[len] = 0; dstate_setinfo (key, "%d", 60*atoi (buf)); } /* set info to float value */ static inline void setinfo_float (const char *key, const char * fmt, const char * s, size_t len, double factor) { char buf[10]; if (len > sizeof(buf)) len = sizeof(buf)-1; strncpy (buf, s, len); buf[len] = 0; dstate_setinfo (key, fmt, factor * (double)atoi (buf)); } static int upssend(const char *fmt,...) { int ret; char buf[1024], *p; va_list ap; unsigned int sent = 0; int d_usec = UPSDELAY; va_start(ap, fmt); ret = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(buf))) upslogx(LOG_WARNING, "ser_send_pace: vsnprintf needed more " "than %d bytes", (int)sizeof(buf)); for (p = buf; *p; p++) { if (write(upsfd, p, 1) != 1) return -1; if (d_usec) usleep(d_usec); sent++; } return sent; } static int upsrecv(char *buf,size_t bufsize,char ec,const char *ic) { return ser_get_line(upsfd, buf, bufsize - 1, ec, ic, SER_WAIT_SEC, SER_WAIT_USEC); } static int upsflushin(int f,int verbose,const char *ignset) { return ser_flush_in(upsfd, ignset, verbose); } /* read out UPS and store info */ void upsdrv_updateinfo(void) { char temp[256]; char *p; int loadva; int len, recv; int retry; char ch; int checksum_ok, is_online=1, is_off, low_batt, trimming, boosting; upsdebugx(1, "upsdrv_updateinfo"); for (retry = 0; retry < 5; ++retry) { upsflushin (0, 0, "\r "); upssend ("f\r"); while (ser_get_char(upsfd, &ch, 0, UPSDELAY) > 0 && ch != '\n'); /* response starts with \r\n */ temp[2] = 0; do { if ((recv = upsrecv (temp+2, sizeof temp - 2, ENDCHAR, IGNCHARS)) <= 0) { upsflushin (0, 0, "\r "); upssend ("f\r"); while (ser_get_char(upsfd, &ch, 0, UPSDELAY) > 0 && ch != '\n'); /* response starts with \r\n */ } } while (temp[2] == 0); upsdebugx(1, "upsdrv_updateinfo: received %i bytes (try %i)", recv, retry); upsdebug_hex(5, "buffer", temp, recv); /* syslog (LOG_DAEMON | LOG_NOTICE,"ups: got %d chars '%s'\n", recv, temp + 2); */ /* status example: 000000000001000000000000012201210000001200014500000280600000990025000000000301BE 000000000001000000000000012401230000001200014800000280600000990025000000000301B7 |Vi||Vo| |Io||Psou| |Vb||f| |tr||Ti| CS 000000000001000000000000023802370000000200004700000267500000990030000000000301BD 1 1 2 2 3 3 4 4 5 5 6 6 7 7 78 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 5 90 */ /* last bytes are a checksum: interpret response as hex string, sum of all bytes must be zero */ checksum_ok = (checksum (temp+2) & 0xff) == 0; /* setinfo (INFO_, ""); */ /* I can't figure out why this is missing the first two chars. But the first two chars are not used, so just set them to zero when missing. */ len = strlen(temp+2); temp[0] = '0'; temp[1] = '0'; p = temp+2; if (len == 78) p = temp; else if (len != 80) checksum_ok = 0; if (checksum_ok) break; sleep(SER_WAIT_SEC); } if (!checksum_ok) { upsdebugx(2, "checksum corruption"); upsdebug_hex(3, "buffer", temp, len); dstate_datastale(); return; } /* upslogx(LOG_INFO, "updateinfo: %s", p); */ setinfo_int ("input.voltage", p+24,4); setinfo_int ("output.voltage", p+28,4); setinfo_float ("battery.voltage", "%.1f", p+50,4, 0.1); setinfo_float ("output.current", "%.1f", p+36,4, 0.1); loadva = setinfo_int ("output.voltamps", p+40,6); if (maxload) dstate_setinfo ("ups.load", "%d", loadva * 100 / maxload); setinfo_float ("input.frequency", "%.1f", p+54,3, 0.1); setinfo_int_minutes ("battery.runtime", p+58,4); setinfo_int ("ups.temperature", p+62,4); is_online = p[17] == '0'; low_batt = fromhex(p[21]) & 8 || fromhex(p[20]) & 1; is_off = p[11] == '0'; trimming = p[33] == '1'; boosting = 0; /* FIXME, don't know which bit gets set (brownouts are very rare here and I can't simulate one) */ status_init(); if (low_batt) status_set("LB "); else if (trimming) status_set("TRIM"); else if (boosting) status_set("BOOST"); else status_set(is_online ? (is_off ? "OFF " : "OL ") : "OB "); /* setinfo(INFO_STATUS, "%s%s", * (util < lownorm) ? "BOOST ", "", * (util > highnorm) ? "TRIM ", "", * ((flags & TIOCM_CD) == 0) ? "" : "LB ", * ((flags & TIOCM_CTS) == TIOCM_CTS) ? "OB" : "OL"); */ status_commit(); dstate_dataok(); } /* Parameter setting */ /* all UPS tunable parameters are set with command 'p%d=%s' */ int setparam (int parameter, int dlen, const char * data) { char reply[80]; upssend ("p%d=%*s\r", parameter, dlen, data); if (upsrecv (reply, sizeof(reply), ENDCHAR, "") < 0) return 0; return strncmp (reply, "OK", 2) == 0; } /* ups_setsuper: set super-user access (allows setting variables) */ static void ups_setsuper (int super) { setparam (999, super ? 4 : 0, super ? "2639" : ""); } /* sets whether UPS will reapply power after it has shut down and line * power returns. */ static void autorestart (int restart) { ups_setsuper (1); setparam (1, 1, restart ? "1" : "0"); ups_setsuper (0); } /* set UPS parameters */ static int upsdrv_setvar (const char *var, const char * data) { int parameter; int len = strlen(data); upsdebugx(1, "Setvar: %s %s", var, data); if (strcmp("input.transfer.low", var) == 0) { parameter = 7; } else if (strcmp("input.transfer.high", var) == 0) { parameter = 8; } else if (strcmp("battery.runtime.low", var) == 0) { parameter = 2; } else { upslogx(LOG_INFO, "Setvar: unsettable variable %s", var); return STAT_SET_UNKNOWN; } ups_setsuper (1); if (setparam (parameter, len, data)) { dstate_setinfo (var, "%*s", len, data); } ups_setsuper (0); return STAT_SET_HANDLED; } void upsdrv_shutdown(void) { const char *grace; grace = dstate_getinfo("ups.delay.shutdown"); if (!grace) grace = "1"; /* apparently, OFF0 does not work */ printf ("shutdown in %s seconds\n", grace); /* make power return when utility power returns */ autorestart (1); upssend ("OFF%s\r", grace); /* I'm nearly dead, Jim */ /* OFF will powercycle when line power is available again */ } static int instcmd (const char *cmdname, const char *extra) { const char *p; if (!strcasecmp(cmdname, "load.off")) { printf ("powering off\n"); autorestart (0); upssend ("OFF1\r"); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "shutdown.return")) { p = dstate_getinfo ("ups.delay.shutdown"); if (!p) p = "1"; printf ("shutdown in %s seconds\n", p); autorestart (1); upssend ("OFF%s\r", p); return STAT_INSTCMD_HANDLED; } upslogx(LOG_INFO, "instcmd: unknown command %s", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar (VAR_VALUE, "baudrate", "serial line speed"); addvar (VAR_VALUE, "max_load", "rated VA load VA"); } struct { const char * val; speed_t speed; } speed_table[] = { {"1200", B1200}, {"2400", B2400}, {"4800", B4800}, {"9600", B9600}, {NULL, B1200}, }; void upsdrv_initups(void) { speed_t speed = B1200; char * speed_val = getval("baudrate"); char * max_load = getval("max_load"); if (max_load) maxload = atoi(max_load); if (speed_val) { int i; for (i=0; speed_table[i].val; i++) { if (strcmp (speed_val, speed_table[i].val) == 0) break; } speed = speed_table[i].speed; } upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, speed); /* TODO: probe ups type */ /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ } void upsdrv_cleanup(void) { } nut-2.7.4/drivers/skel.c0000644000175000017500000001016112640443572012041 00000000000000/* anything commented is optional anything else is mandatory for more information, refer to: * docs/developers.txt * docs/new-drivers.txt * docs/new-names.txt and possibly also to: * docs/hid-subdrivers.txt for USB/HID devices * or docs/snmp-subdrivers.txt for SNMP devices */ #include "main.h" /* #include "serial.h" */ /* #define ENDCHAR '\r' */ /* #define IGNCHARS "" */ #define DRIVER_NAME "Skeleton UPS driver" #define DRIVER_VERSION "0.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "John Doe ", DRV_STABLE, { NULL } }; void upsdrv_initinfo(void) { /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ...) * or fatalx(EXIT_FAILURE, ...) if it fails */ /* dstate_setinfo("ups.mfr", "skel manufacturer"); */ /* dstate_setinfo("ups.model", "longrun 15000"); */ /* note: for a transition period, these data are redundant! */ /* dstate_setinfo("device.mfr", "skel manufacturer"); */ /* dstate_setinfo("device.model", "longrun 15000"); */ /* upsh.instcmd = instcmd; */ } void upsdrv_updateinfo(void) { /* int flags; */ /* char temp[256]; */ /* ser_sendchar(upsfd, 'A'); */ /* ser_send(upsfd, "foo%d", 1234); */ /* ser_send_buf(upsfd, bincmd, 12); */ /* * ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS); * * if (ret < STATUS_LEN) { * upslogx(LOG_ERR, "Short read from UPS"); * dstate_datastale(); * return; * } */ /* dstate_setinfo("var.name", ""); */ /* if (ioctl(upsfd, TIOCMGET, &flags)) { * upslog_with_errno(LOG_ERR, "TIOCMGET"); * dstate_datastale(); * return; * } */ /* status_init(); * * if (ol) * status_set("OL"); * else * status_set("OB"); * ... * * status_commit(); * * dstate_dataok(); */ /* * poll_interval = 2; */ } void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ fatalx(EXIT_FAILURE, "shutdown not supported"); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ } /* static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } */ /* static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.test.interval")) { ser_send_buf(upsfd, ...); return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } */ void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { /* allow '-x xyzzy' */ /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ /* allow '-x foo=' */ /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ } void upsdrv_initups(void) { /* upsfd = ser_open(device_path); */ /* ser_set_speed(upsfd, device_path, B1200); */ /* probe ups type */ /* to get variables and flags from the command line, use this: * * first populate with upsdrv_makevartable() above, then... * * set flag foo : /bin/driver -x foo * set variable 'cable' to '1234' : /bin/driver -x cable=1234 * * to test flag foo in your code: * * if (testvar("foo")) * do_something(); * * to show the value of cable: * * if ((cable = getval("cable"))) * printf("cable is set to %s\n", cable); * else * printf("cable is not set!\n"); * * don't use NULL pointers - test the return result first! */ /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ /* don't try to detect the UPS here */ } void upsdrv_cleanup(void) { /* free(dynamic_mem); */ /* ser_close(upsfd, device_path); */ } nut-2.7.4/drivers/apcsmart.c0000644000175000017500000014322712640473702012725 00000000000000/* * apcsmart.c - driver for APC smart protocol units (originally "newapc") * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include /* strcasecmp() */ #include "main.h" #include "serial.h" #include "timehead.h" #include "apcsmart.h" #include "apcsmart_tabs.h" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" "Nigel Metheringham \n" "Michal Soltys ", DRV_STABLE, { &apc_tab_info, NULL } }; static int ups_status = 0; /* some forwards */ static int sdcmd_S(const void *); static int sdcmd_AT(const void *); static int sdcmd_K(const void *); static int sdcmd_Z(const void *); static int sdcmd_CS(const void *); /* * following table *must* match order defined in the man page, namely: * 0:: soft hibernate (*S*) * 1:: hard hibernate (*@*) * 2:: delayed poweroff (*K*) * 3:: instant poweroff (*Z*) * 4:: "force OB hack" (*CS*) */ static int (*sdlist[])(const void *) = { sdcmd_S, sdcmd_AT, sdcmd_K, sdcmd_Z, sdcmd_CS, }; #define SDIDX_AT 1 /* * note: both lookup functions MUST be used after variable detection is * completed - that is after deprecate_vars() call; the general reason for this * is 1:n and n:1 nut <-> apc mappings, which are not determined prior to the * detection */ static apc_vartab_t *vt_lookup_char(char cmdchar) { int i; for (i = 0; apc_vartab[i].name != NULL; i++) if ((apc_vartab[i].flags & APC_PRESENT) && apc_vartab[i].cmd == cmdchar) return &apc_vartab[i]; return NULL; } static apc_vartab_t *vt_lookup_name(const char *var) { int i; for (i = 0; apc_vartab[i].name != NULL; i++) if ((apc_vartab[i].flags & APC_PRESENT) && !strcasecmp(apc_vartab[i].name, var)) return &apc_vartab[i]; return NULL; } static const char *prtchr(char x) { static size_t curr = 24; static char info[32]; curr = (curr + 8) & 0x1F; snprintf(info + curr, 8, isprint(x) ? "%c" : "0x%02x", x); return info + curr; } static int rexhlp(const char *rex, const char *val) { static const char *empty = ""; int ret; regex_t mbuf; if (!rex || !*rex) return 1; if (!val) val = empty; regcomp(&mbuf, rex, REG_EXTENDED|REG_NOSUB); ret = regexec(&mbuf, val, 0, 0, 0); regfree(&mbuf); return !ret; } /* convert APC formatting to NUT formatting */ /* TODO: handle errors better */ static const char *convert_data(apc_vartab_t *vt, const char *upsval) { static char temp[APC_LBUF]; int tval; /* this should never happen */ if (strlen(upsval) >= sizeof(temp)) { logx(LOG_CRIT, "the length of [%s] is too big", vt->name); memcpy(temp, upsval, sizeof(temp) - 1); temp[sizeof(temp) - 1] = '\0'; return temp; } switch (vt->flags & APC_F_MASK) { case APC_F_PERCENT: case APC_F_VOLT: case APC_F_AMP: case APC_F_CELSIUS: case APC_F_HEX: case APC_F_DEC: case APC_F_SECONDS: case APC_F_LEAVE: /* no conversion for any of these */ strcpy(temp, upsval); return temp; case APC_F_HOURS: /* convert to seconds */ tval = 60 * 60 * strtol(upsval, NULL, 10); snprintf(temp, sizeof(temp), "%d", tval); return temp; case APC_F_MINUTES: /* Convert to seconds - NUT standard time measurement */ tval = 60 * strtol(upsval, NULL, 10); /* Ignore errors - there's not much we can do */ snprintf(temp, sizeof(temp), "%d", tval); return temp; case APC_F_REASON: switch (upsval[0]) { case 'R': return "unacceptable utility voltage rate of change"; case 'H': return "high utility voltage"; case 'L': return "low utility voltage"; case 'T': return "line voltage notch or spike"; case 'O': return "no transfers yet since turnon"; case 'S': return "simulated power failure or UPS test"; default: strcpy(temp, upsval); return temp; } } /* this should never happen */ logx(LOG_CRIT, "unable to convert [%s]", vt->name); strcpy(temp, upsval); return temp; } /* report differences if tcsetattr != tcgetattr, return otherwise */ /* * Aix compatible names */ #if defined(VWERSE) && !defined(VWERASE) #define VWERASE VWERSE #endif /* VWERSE && !VWERASE */ #if defined(VDISCRD) && !defined(VDISCARD) #define VDISCARD VDISCRD #endif /* VDISCRD && !VDISCARD */ static void apc_ser_diff(struct termios *tioset, struct termios *tioget) { size_t i; const char dir[] = { 's', 'g' }; struct termios *tio[] = { tioset, tioget }; struct cchar { const char *name; unsigned int sub; }; const struct cchar cchars1[] = { #ifdef VDISCARD { "discard", VDISCARD }, #endif #ifdef VDSUSP { "dsusp", VDSUSP }, #endif { "eof", VEOF }, { "eol", VEOL }, { "eol2", VEOL2 }, { "erase", VERASE }, #ifdef VINTR { "intr", VINTR }, #endif { "kill", VKILL }, { "lnext", VLNEXT }, { "min", VMIN }, { "quit", VQUIT }, #ifdef VREPRINT { "reprint", VREPRINT }, #endif { "start", VSTART }, #ifdef VSTATUS { "status", VSTATUS }, #endif { "stop", VSTOP }, { "susp", VSUSP }, { "time", VTIME }, { "werase", VWERASE }, { NULL }, }, *cp; /* clear status flags so that they don't affect our binary compare */ #if defined(PENDIN) || defined(FLUSHO) for (i = 0; i < sizeof(tio)/sizeof(tio[0]); i++) { #ifdef PENDIN tio[i]->c_lflag &= ~PENDIN; #endif #ifdef FLUSHO tio[i]->c_lflag &= ~FLUSHO; #endif } #endif /* defined(PENDIN) || defined(FLUSHO) */ if (!memcmp(tio[0], tio[1], sizeof(*tio[0]))) return; upslogx(LOG_NOTICE, "%s: device reports different attributes than requested", device_path); /* * According to the manual the most common problem is mis-matched * combinations of input and output baud rates. If the combination is * not supported then neither are changed. This should not be a * problem here since we set them both to the same extremely common * rate of 2400. */ for (i = 0; i < sizeof(tio)/sizeof(tio[0]); i++) { upsdebugx(1, "tc%cetattr(): gfmt1:cflag=%x:iflag=%x:lflag=%x:oflag=%x:", dir[i], (unsigned int) tio[i]->c_cflag, (unsigned int) tio[i]->c_iflag, (unsigned int) tio[i]->c_lflag, (unsigned int) tio[i]->c_oflag); for (cp = cchars1; cp->name; ++cp) upsdebugx(1, "\t%s=%x:", cp->name, tio[i]->c_cc[cp->sub]); upsdebugx(1, "\tispeed=%d:ospeed=%d", (int) cfgetispeed(tio[i]), (int) cfgetospeed(tio[i])); } } static void apc_ser_set(void) { struct termios tio, tio_chk; char *val; /* * this must be called before the rest, as ser_set_speed() performs * early initialization of the port, apart from changing speed */ ser_set_speed(upsfd, device_path, B2400); val = getval("cable"); if (val && !strcasecmp(val, ALT_CABLE_1)) { if (ser_set_dtr(upsfd, 1) == -1) fatx("ser_set_dtr(%s) failed", device_path); if (ser_set_rts(upsfd, 0) == -1) fatx("ser_set_rts(%s) failed", device_path); } /* * that's all if we want simple non canonical mode; this is meant as a * compatibility measure for windows systems and perhaps some * problematic serial cards/converters */ if ((val = getval("ttymode")) && !strcmp(val, "raw")) return; memset(&tio, 0, sizeof(tio)); errno = 0; if (tcgetattr(upsfd, &tio)) fate("tcgetattr(%s)", device_path); /* set port mode: common stuff, canonical processing */ tio.c_cflag |= (CS8 | CLOCAL | CREAD); tio.c_lflag |= ICANON; #ifdef NOKERNINFO tio.c_lflag |= NOKERNINFO; #endif tio.c_lflag &= ~(ISIG | IEXTEN); tio.c_iflag |= (IGNCR | IGNPAR); tio.c_iflag &= ~(IXON | IXOFF); tio.c_cc[VEOL] = '*'; /* specially handled in apc_read() */ #ifdef _POSIX_VDISABLE tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL2] = _POSIX_VDISABLE; #endif if (tcflush(upsfd, TCIOFLUSH)) fate("tcflush(%s)", device_path); /* * warn: * Note, that tcsetattr() returns success if /any/ of the requested * changes could be successfully carried out. Thus the more complicated * test. */ if (tcsetattr(upsfd, TCSANOW, &tio)) fate("tcsetattr(%s)", device_path); memset(&tio_chk, 0, sizeof(tio_chk)); if (tcgetattr(upsfd, &tio_chk)) fate("tcgetattr(%s)", device_path); apc_ser_diff(&tio, &tio_chk); } static void ups_status_set(void) { status_init(); if (ups_status & APC_STAT_CAL) status_set("CAL"); /* calibration */ if (ups_status & APC_STAT_TRIM) status_set("TRIM"); /* SmartTrim */ if (ups_status & APC_STAT_BOOST) status_set("BOOST"); /* SmartBoost */ if (ups_status & APC_STAT_OL) status_set("OL"); /* on line */ if (ups_status & APC_STAT_OB) status_set("OB"); /* on battery */ if (ups_status & APC_STAT_OVER) status_set("OVER"); /* overload */ if (ups_status & APC_STAT_LB) status_set("LB"); /* low battery */ if (ups_status & APC_STAT_RB) status_set("RB"); /* replace batt */ if (ups_status == 0) status_set("OFF"); status_commit(); } static void alert_handler(char ch) { switch (ch) { case '!': /* clear OL, set OB */ debx(1, "OB"); ups_status &= ~APC_STAT_OL; ups_status |= APC_STAT_OB; break; case '$': /* clear OB, set OL */ debx(1, "OL"); ups_status &= ~APC_STAT_OB; ups_status |= APC_STAT_OL; break; case '%': /* set LB */ debx(1, "LB"); ups_status |= APC_STAT_LB; break; case '+': /* clear LB */ debx(1, "not LB"); ups_status &= ~APC_STAT_LB; break; case '#': /* set RB */ debx(1, "RB"); ups_status |= APC_STAT_RB; break; case '?': /* set OVER */ debx(1, "OVER"); ups_status |= APC_STAT_OVER; break; case '=': /* clear OVER */ debx(1, "not OVER"); ups_status &= ~APC_STAT_OVER; break; default: debx(1, "got 0x%02x (unhandled)", ch); break; } ups_status_set(); } /* * we need a tiny bit different processing due to '*' and canonical mode; the * function is subtly different from generic ser_get_line_alert() */ #define apc_read(b, l, f) apc_read_i(b, l, f, __func__, __LINE__) static int apc_read_i(char *buf, size_t buflen, int flags, const char *fn, unsigned int ln) { const char *iset = IGN_CHARS, *aset = ""; size_t count = 0; int i, ret, sec = 3, usec = 0; char temp[APC_LBUF]; if (upsfd == -1) return 0; if (flags & SER_D0) { sec = 0; usec = 0; } if (flags & SER_DX) { sec = 0; usec = 200000; } if (flags & SER_D1) { sec = 1; usec = 500000; } if (flags & SER_D3) { sec = 3; usec = 0; } if (flags & SER_AA) { iset = IGN_AACHARS; aset = ALERT_CHARS; } if (flags & SER_CC) { iset = IGN_CCCHARS; aset = ""; sec = 6; usec = 0; } if (flags & SER_CS) { iset = IGN_CSCHARS; aset = ""; sec = 6; usec = 0; } memset(buf, '\0', buflen); while (count < buflen - 1) { errno = 0; ret = select_read(upsfd, temp, sizeof(temp), sec, usec); /* partial timeout (non-canon only paranoid check) */ if (ret == 0 && count) { ser_comm_fail("serial port partial timeout: %u(%s)", ln, fn); return -1; } /* error or no timeout allowed */ if (ret < 0 || (ret == 0 && !(flags & SER_TO))) { if (ret) ser_comm_fail("serial port read error: %u(%s): %s", ln, fn, strerror(errno)); else ser_comm_fail("serial port read timeout: %u(%s)", ln, fn); return ret; } /* ok, timeout is acceptable */ if (ret == 0 && (flags & SER_TO)) { /* * but it doesn't imply ser_comm_good * * for example we might be in comm_fail condition, * trying to "nudge" the UPS with some command * obviously expecting timeout if the comm is still * lost. This would result with filling logs with * confusing comm lost/comm re-established pairs due to * successful serial writes */ return 0; } /* parse input */ for (i = 0; i < ret; i++) { /* overflow read */ if (count == buflen - 1) { ser_comm_fail("serial port read overflow: %u(%s)", ln, fn); tcflush(upsfd, TCIFLUSH); return -1; } /* standard "line received" condition */ if (temp[i] == ENDCHAR) { ser_comm_good(); return count; } /* * '*' is set as a secondary EOL; convert to 'OK' only as a * reply to shutdown command in sdok(); otherwise next * select_read() will continue normally */ if ((flags & SER_HA) && temp[i] == '*') { /* * a bit paranoid, but remember '*' is not real EOL; * there could be some firmware in existence, that * would send both string: 'OK\n' and alert: '*'. * Just in case, try to flush the input with small 1 sec. * timeout */ memset(buf, '\0', buflen); errno = 0; ret = select_read(upsfd, temp, sizeof(temp), 1, 0); if (ret < 0) { ser_comm_fail("serial port read error: %u(%s): %s", ln, fn, strerror(errno)); return ret; } buf[0] = 'O'; buf[1] = 'K'; ser_comm_good(); return 2; } /* ignore set */ if (strchr(iset, temp[i]) || temp[i] == '*') { continue; } /* alert set */ if (strchr(aset, temp[i])) { alert_handler(temp[i]); continue; } buf[count++] = temp[i]; } } ser_comm_good(); return count; } #define apc_write(code) apc_write_i(code, __func__, __LINE__) static int apc_write_i(unsigned char code, const char *fn, unsigned int ln) { int ret; errno = 0; if (upsfd == -1) return 0; ret = ser_send_char(upsfd, code); /* * Formally any write() sould never return 0, if the count != 0. For * the sake of handling any obscure nonsense, we consider such return * as a failure - thus <= condition; either way, LE is pretty hard * condition hardly ever happening; */ if (ret <= 0) ser_comm_fail("serial port write error: %u(%s): %s", ln, fn, strerror(errno)); return ret; } /* * We have to watch out for NA, here; * This is generally safe, as otherwise we will just timeout. The reason we do * it, is that under certain conditions an ups might respond with NA for * something it would normally handle (e.g. calling @ while being in powered * off or hibernated state. If we keep sending the "arguments" after getting * NA, they will be interpreted as commands, which is quite a bug :) * Furthermore later flushes might not work properly, if the reply to those * commands are generated with some delay. * * We also set errno to something usable, so outside upslog calls don't output * confusing "success". */ #define apc_write_long(code) apc_write_long_i(code, __func__, __LINE__) static int apc_write_long_i(const char *code, const char *fn, unsigned int ln) { char temp[APC_LBUF]; int ret; ret = apc_write_i(*code, fn, ln); if (ret != 1) return ret; /* peek for the answer - anything at this point is failure */ ret = apc_read(temp, sizeof(temp), SER_DX|SER_TO); if (ret) { errno = ECANCELED; return -1; } ret = ser_send_pace(upsfd, 50000, "%s", code + 1); if (ret >= 0) ret++; /* see remark in plain apc_write() */ if (ret != (int)strlen(code)) ser_comm_fail("serial port write error: %u(%s): %s", ln, fn, strerror(errno)); return ret; } #define apc_write_rep(code) apc_write_rep_i(code, __func__, __LINE__) static int apc_write_rep_i(unsigned char code, const char *fn, unsigned int ln) { char temp[APC_LBUF]; int ret; ret = apc_write_i(code, fn, ln); if (ret != 1) return ret; /* peek for the answer - anything at this point is failure */ ret = apc_read(temp, sizeof(temp), SER_DX|SER_TO); if (ret) { errno = ECANCELED; return -1; } usleep(1300000); ret = apc_write_i(code, fn, ln); if (ret >= 0) ret++; return ret; } /* all flags other than SER_AA are ignored */ static void apc_flush(int flags) { char temp[APC_LBUF]; if (flags & SER_AA) { tcflush(upsfd, TCOFLUSH); /* TODO */ while(apc_read(temp, sizeof(temp), SER_D0|SER_TO|SER_AA) > 0); } else { tcflush(upsfd, TCIOFLUSH); /* tcflush(upsfd, TCIFLUSH); */ /* while(apc_read(temp, sizeof(temp), SER_D0|SER_TO)); */ } } /* apc specific wrappers around set/del info - to handle "packed" variables */ void apc_dstate_delinfo(apc_vartab_t *vt, int skip) { char name[vt->nlen0], *nidx; int c; /* standard not packed var */ if (!(vt->flags & APC_PACK)) { dstate_delinfo(vt->name); return; } strcpy(name, vt->name); nidx = strstr(name,".0.") + 1; for (c = skip; c < vt->cnt; c++) { *nidx = (char)('1' + c); dstate_delinfo(name); } vt->cnt = 0; } void apc_dstate_setinfo(apc_vartab_t *vt, const char *upsval) { char name[vt->nlen0], *nidx; char temp[strlen(upsval) + 1], *vidx[APC_PACK_MAX], *com, *curr; int c; /* standard not packed var */ if (!(vt->flags & APC_PACK)) { dstate_setinfo(vt->name, "%s", convert_data(vt, upsval)); return; } /* we have to set proper name for dstate_setinfo() calls */ strcpy(name, vt->name); nidx = strstr(name,".0.") + 1; /* split the value string */ strcpy(temp, upsval); curr = temp; c = 0; do { vidx[c] = curr; com = strchr(curr, ','); if (com) { curr = com + 1; *com = '\0'; } } while(++c < APC_PACK_MAX && com); /* * unlikely, but keep things tidy - remove leftover values, if * subsequent read returns less */ if (vt->cnt > c) apc_dstate_delinfo(vt, c); /* unlikely - warn user if we have more than APC_PACK_MAX fields */ if (c == APC_PACK_MAX && com) upslogx(LOG_WARNING, "packed variable %s [%s] longer than %d fields,\n" "ignoring remaining fields", vt->name, prtchr(vt->cmd), c); vt->cnt = c; while (c-- > 0) { *nidx = (char)('1' + c); if (*vidx[c]) dstate_setinfo(name, "%s", convert_data(vt, vidx[c])); else dstate_setinfo(name, "N/A"); } } static const char *preread_data(apc_vartab_t *vt) { int ret; static char temp[APC_LBUF]; debx(1, "%s [%s]", vt->name, prtchr(vt->cmd)); apc_flush(0); ret = apc_write(vt->cmd); if (ret != 1) return 0; ret = apc_read(temp, sizeof(temp), SER_TO); if (ret < 1 || !strcmp(temp, "NA")) { if (ret >= 0) logx(LOG_ERR, "%s [%s] timed out or not supported", vt->name, prtchr(vt->cmd)); return 0; } return temp; } static int poll_data(apc_vartab_t *vt) { char temp[APC_LBUF]; if (!(vt->flags & APC_PRESENT)) return 1; debx(1, "%s [%s]", vt->name, prtchr(vt->cmd)); apc_flush(SER_AA); if (apc_write(vt->cmd) != 1) return 0; if (apc_read(temp, sizeof(temp), SER_AA) < 1) return 0; /* automagically no longer supported by the hardware somehow */ if (!strcmp(temp, "NA")) { logx(LOG_WARNING, "verified variable %s [%s] returned NA, removing", vt->name, prtchr(vt->cmd)); vt->flags &= ~APC_PRESENT; apc_dstate_delinfo(vt, 0); } else apc_dstate_setinfo(vt, temp); return 1; } static int update_status(void) { int ret; char buf[APC_LBUF]; debx(1, "[%s]", prtchr(APC_STATUS)); apc_flush(SER_AA); if (apc_write(APC_STATUS) != 1) return 0; ret = apc_read(buf, sizeof(buf), SER_AA); if ((ret < 1) || (!strcmp(buf, "NA"))) { if (ret >= 0) logx(LOG_WARNING, "failed"); return 0; } ups_status = strtol(buf, 0, 16) & 0xff; ups_status_set(); return 1; } /* * two informative functions, to not redo the same thing in few places */ static inline void confirm_cv(unsigned char cmd, const char *tag, const char *name) { upsdebugx(1, "%s [%s] - %s supported", name, prtchr(cmd), tag); } static inline void warn_cv(unsigned char cmd, const char *tag, const char *name) { if (tag && name) upslogx(LOG_WARNING, "%s [%s] - %s invalid", name, prtchr(cmd), tag); else upslogx(LOG_WARNING, "[%s] unrecognized", prtchr(cmd)); } static void var_string_setup(apc_vartab_t *vt) { /* * handle special data for our two strings; note - STRING variables * cannot be PACK at the same time */ if (vt->flags & APC_STRING) { dstate_setflags(vt->name, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(vt->name, APC_STRLEN); vt->flags |= APC_RW; } } static int var_verify(apc_vartab_t *vt) { const char *temp; if (vt->flags & APC_MULTI) { /* APC_MULTI are handled by deprecate_vars() */ vt->flags |= APC_PRESENT; return -1; } temp = preread_data(vt); /* no conversion here, validator should operate on raw values */ if (!temp || !rexhlp(vt->regex, temp)) { warn_cv(vt->cmd, "variable", vt->name); return 0; } vt->flags |= APC_PRESENT; apc_dstate_setinfo(vt, temp); var_string_setup(vt); confirm_cv(vt->cmd, "variable", vt->name); return 1; } /* * This function iterates over vartab, deprecating nut<->apc 1:n and n:1 * variables. We prefer earliest present variable. All the other ones must be * marked as not present (which implies deprecation). * This pass is requried after completion of all protocol_verify() and/or * legacy_verify() calls. */ static void deprecate_vars(void) { int i, j; const char *temp; apc_vartab_t *vt, *vtn; for (i = 0; apc_vartab[i].name != NULL; i++) { vt = &apc_vartab[i]; if (!(vt->flags & APC_MULTI) || !(vt->flags & APC_PRESENT)) { /* * a) not interesting, or * b) not marked as present earlier, or already handled */ continue; } /* pre-read data, we have to verify it */ temp = preread_data(vt); /* no conversion here, validator should operate on raw values */ if (!temp || !rexhlp(vt->regex, temp)) { vt->flags &= ~APC_PRESENT; warn_cv(vt->cmd, "variable combination", vt->name); continue; } /* multi & present, deprecate all the remaining ones */ for (j = i + 1; apc_vartab[j].name != NULL; j++) { vtn = &apc_vartab[j]; if (strcmp(vtn->name, vt->name) && vtn->cmd != vt->cmd) continue; vtn->flags &= ~APC_PRESENT; } apc_dstate_setinfo(vt, temp); var_string_setup(vt); confirm_cv(vt->cmd, "variable combination", vt->name); } } static void apc_getcaps(int qco) { const char *ptr, *entptr; char upsloc, temp[APC_LBUF], cmd, loc, etmp[APC_SBUF], *endtemp; int nument, entlen, i, matrix, ret, valid; apc_vartab_t *vt; /* * If we can do caps, then we need the Firmware revision which has the * locale descriptor as the last character (ugh); this is valid for * both 'V' and 'b' commands. */ ptr = dstate_getinfo("ups.firmware"); if (ptr) upsloc = ptr[strlen(ptr) - 1]; else upsloc = 0; /* get capability string */ apc_flush(0); if (apc_write(APC_CAPS) != 1) return; /* * note - apc_read() needs larger timeout grace (not a problem w.r.t. * to nut's timing, as it's done only during setup) and different * ignore set due to certain characters like '#' being received */ ret = apc_read(temp, sizeof(temp), SER_CC|SER_TO); if ((ret < 1) || (!strcmp(temp, "NA"))) { /* * Early Smart-UPS not as smart as the later ones ... * this should never happen on properly functioning hardware - * as capability support was reported earlier */ if (ret >= 0) upslogx(LOG_WARNING, "APC cannot do capabilities but said it could !"); return; } /* recv always puts a \0 at the end, so this is safe */ /* however it assumes a zero byte cannot be embedded */ endtemp = &temp[0] + strlen(temp); if (temp[0] != '#') { upslogx(LOG_WARNING, "unknown capability start char [%c] !", temp[0]); upsdebugx(1, "please report this caps string: %s", temp); return; } if (temp[1] == '#') { /* Matrix-UPS */ ptr = &temp[0]; matrix = 1; } else { ptr = &temp[1]; matrix = 0; } /* command char, location, # of entries, entry length */ while (ptr[0] != '\0') { if (matrix) ptr += 2; /* jump over repeating ## */ /* check for idiocy */ if (ptr >= endtemp) { /* if we expected this, just ignore it */ if (qco) return; fatalx(EXIT_FAILURE, "capability string has overflowed, please report this error !"); } cmd = ptr[0]; loc = ptr[1]; nument = ptr[2] - 48; entlen = ptr[3] - 48; entptr = &ptr[4]; vt = vt_lookup_char(cmd); valid = vt && ((loc == upsloc) || (loc == '4')) && !(vt->flags & APC_PACK); /* mark this as writable */ if (valid) { upsdebugx(1, "%s [%s(%c)] - capability supported", vt->name, prtchr(cmd), loc); dstate_setflags(vt->name, ST_FLAG_RW); /* make sure setvar knows what this is */ vt->flags |= APC_RW | APC_ENUM; } else if (vt && (vt->flags & APC_PACK)) /* * Currently we assume - basing on the following * feedback: * http://www.mail-archive.com/nut-upsdev@lists.alioth.debian.org/msg03398.html * - that "packed" variables are not enumerable; if at * some point in the future it turns out to be false, * the handling will have to be a bit more complex */ upslogx(LOG_WARNING, "WARN: packed APC variable %s [%s] reported as enumerable,\n" "please report it on the mailing list", vt->name, prtchr(cmd)); for (i = 0; i < nument; i++) { if (valid) { snprintf(etmp, entlen + 1, "%s", entptr); dstate_addenum(vt->name, "%s", convert_data(vt, etmp)); } entptr += entlen; } ptr = entptr; } } static void legacy_verify(const char *var) { int i; /* * note: some NUT variables map onto multiple APC ones, e.g. firmware: * V,b -> ups.firmware; that's why we keep the loop, as it's over NUT * names */ for (i = 0; apc_vartab[i].name != NULL; i++) { if (strcmp(apc_vartab[i].name, var)) continue; var_verify(&apc_vartab[i]); } } static void protocol_verify(unsigned char cmd) { int i, found; apc_vartab_t *vt; apc_cmdtab_t *ct; /* don't bother with cmd/var we don't care about */ if (strchr(APC_UNR_CMDS, cmd)) return; /* * loop necessary for apc:nut 1:n cases (e.g. T -> device.uptime, * ambient.0.temperature) */ found = 0; for (i = 0; apc_vartab[i].name != NULL; i++) { vt = &apc_vartab[i]; if (vt->cmd != cmd) continue; var_verify(vt); found = 1; } if (found) return; /* * see if it's a command * loop necessary for apc:nut 1:n cases (e.g. D -> calibrate.start, * calibrate.stop) */ found = 0; for (i = 0; apc_cmdtab[i].name != NULL; i++) { ct = &apc_cmdtab[i]; if (ct->cmd != cmd) continue; ct->flags |= APC_PRESENT; dstate_addcmd(ct->name); confirm_cv(cmd, "command", ct->name); found = 1; } if (found) return; /* * epilogue - unrecognized command / variable not included * in APC_UNR_CMDS */ warn_cv(cmd, NULL, NULL); } static void oldapcsetup(void) { /* * note: battery.date and ups.id make little sense here, as * that would imply writability and this is an *old* apc psu */ legacy_verify("ups.temperature"); legacy_verify("ups.load"); legacy_verify("input.voltage"); legacy_verify("output.voltage"); legacy_verify("battery.charge"); legacy_verify("battery.voltage"); /* these will usually timeout */ legacy_verify("ups.model"); legacy_verify("ups.serial"); legacy_verify("ups.firmware"); legacy_verify("output.current"); deprecate_vars(); /* see if this might be an old Matrix-UPS instead */ if (vt_lookup_name("output.current")) dstate_setinfo("ups.model", "Matrix-UPS"); else { /* really old models don't support ups.model (apc: 0x01) */ if (!vt_lookup_name("ups.model")) /* force the model name */ dstate_setinfo("ups.model", "Smart-UPS"); } /* * If we have come down this path then we dont do capabilities and * other shiny features. */ } /* some hardware is a special case - hotwire the list of cmdchars */ static int firmware_table_lookup(void) { int ret; unsigned int i, j; char buf[APC_LBUF]; upsdebugx(1, "attempting firmware lookup using [%s]", prtchr(APC_FW_OLD)); apc_flush(0); if (apc_write(APC_FW_OLD) != 1) return 0; if ((ret = apc_read(buf, sizeof(buf), SER_TO)) < 0) return 0; /* * Some UPSes support both 'V' and 'b'. As 'b' doesn't always return * firmware version, we attempt that only if 'V' doesn't work. */ if (!ret || !strcmp(buf, "NA")) { upsdebugx(1, "attempting firmware lookup using [%s]", prtchr(APC_FW_NEW)); if (apc_write(APC_FW_NEW) != 1) return 0; if (apc_read(buf, sizeof(buf), SER_TO) < 1) return 0; } upsdebugx(1, "detected firmware version: %s", buf); /* this will be reworked if we get a lot of these things */ if (!strcmp(buf, "451.2.I")) { /* quirk_capability_overflow */ upsdebugx(1, "WARN: quirky firmware !"); return 2; } if (rexhlp("^[a-fA-F0-9]{2}$", buf)) { /* * certain old set of UPSes that return voltage above 255V * through 'b'; see: * http://article.gmane.org/gmane.comp.monitoring.nut.user/7762 */ strcpy(buf, "set\1"); } for (i = 0; apc_compattab[i].firmware != NULL; i++) { if (!strcmp(apc_compattab[i].firmware, buf)) { upsdebugx(1, "matched firmware: %s", apc_compattab[i].firmware); /* magic ? */ if (strspn(apc_compattab[i].firmware, "05")) { dstate_setinfo("ups.model", "Matrix-UPS"); } else { dstate_setinfo("ups.model", "Smart-UPS"); } /* matched - run the cmdchars from the table */ upsdebugx(1, "parsing out supported cmds and vars"); for (j = 0; j < strlen(apc_compattab[i].cmdchars); j++) protocol_verify(apc_compattab[i].cmdchars[j]); deprecate_vars(); return 1; /* matched */ } } return 0; } static int getbaseinfo(void) { unsigned int i; int ret, qco; char *cmds, *tail, temp[APC_LBUF]; /* * try firmware lookup first; we could start with 'a', but older models * sometimes return other things than a command set */ qco = firmware_table_lookup(); if (qco == 1) /* found compat */ return 1; upsdebugx(1, "attempting var/cmdset lookup using [%s]", prtchr(APC_CMDSET)); /* * Initially we ask the UPS what commands it takes. If this fails we are * going to need an alternate strategy - we can deal with that if it * happens */ apc_flush(0); if (apc_write(APC_CMDSET) != 1) return 0; if ((ret = apc_read(temp, sizeof(temp), SER_CS|SER_TO)) < 0) return 0; if (!ret || !strcmp(temp, "NA") || !rexhlp(APC_CMDSET_FMT, temp)) { /* We have an old dumb UPS - go to specific code for old stuff */ upslogx(LOG_NOTICE, "very old or unknown APC model, support will be limited"); oldapcsetup(); return 1; } upsdebugx(1, "parsing out supported cmds/vars"); /* * returned set is verified for validity above, so just extract * what's interesting for us * * the known format is: * ver.alerts.commands[.stuff] */ cmds = strchr(temp, '.'); cmds = strchr(cmds + 1, '.'); tail = strchr(++cmds, '.'); if (tail) *tail = 0; for (i = 0; i < strlen(cmds); i++) protocol_verify(cmds[i]); deprecate_vars(); /* if capabilities are supported, add them here */ if (strchr(cmds, APC_CAPS)) { upsdebugx(1, "parsing out caps"); apc_getcaps(qco); } return 1; } /* check for calibration status and either start or stop */ static int do_cal(int start) { char temp[APC_LBUF]; int tval, ret; apc_flush(SER_AA); ret = apc_write(APC_STATUS); if (ret != 1) { return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = apc_read(temp, sizeof(temp), SER_AA); /* if we can't check the current calibration status, bail out */ if ((ret < 1) || (!strcmp(temp, "NA"))) { upslogx(LOG_WARNING, "runtime calibration state undeterminable"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } tval = strtol(temp, 0, 16); if (tval & APC_STAT_CAL) { /* calibration currently happening */ if (start == 1) { /* requested start while calibration still running */ upslogx(LOG_NOTICE, "runtime calibration already in progress"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } /* stop requested */ upslogx(LOG_NOTICE, "stopping runtime calibration"); ret = apc_write(APC_CMD_CALTOGGLE); if (ret != 1) { return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { upslogx(LOG_WARNING, "stop calibration failed, cmd returned: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } return STAT_INSTCMD_HANDLED; /* FUTURE: success */ } /* calibration not happening */ if (start == 0) { /* stop requested */ upslogx(LOG_NOTICE, "runtime calibration not occurring"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } upslogx(LOG_NOTICE, "starting runtime calibration"); ret = apc_write(APC_CMD_CALTOGGLE); if (ret != 1) { return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { upslogx(LOG_WARNING, "start calibration failed, cmd returned: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } return STAT_INSTCMD_HANDLED; /* FUTURE: success */ } #if 0 /* get the UPS talking to us in smart mode */ static int smartmode(void) { int ret; char temp[APC_LBUF]; apc_flush(0); ret = apc_write(APC_GOSMART); if (ret != 1) { return 0; } ret = apc_read(temp, sizeof(temp), 0); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { upslogx(LOG_CRIT, "enabling smartmode failed !"); return 0; } return 1; } #endif /* * get the UPS talking to us in smart mode * note: this is weird overkill, but possibly excused due to some obscure * hardware/firmware combinations; simpler version commmented out above, for * now let's keep minimally adjusted old one */ static int smartmode(int cnt) { int ret, tries; char temp[APC_LBUF]; for (tries = 0; tries < cnt; tries++) { apc_flush(0); if (apc_write(APC_GOSMART) != 1) return 0; /* timeout here is intented */ ret = apc_read(temp, sizeof(temp), SER_TO|SER_D1); if (ret > 0 && !strcmp(temp, "SM")) return 1; /* success */ if (ret < 0) /* error, so we didn't timeout - wait a bit before retry */ sleep(1); if (apc_write(27) != 1) /* ESC */ return 0; /* eat the response (might be NA, might be something else) */ apc_read(temp, sizeof(temp), SER_TO|SER_D1); } return 0; /* failure */ } /* * all shutdown commands should respond with 'OK' or '*' * apc_read() handles conversion to 'OK' so we care only about that one * ign allows for timeout without assuming an error */ static int sdok(int ign) { int ret; char temp[APC_SBUF]; /* * older upses on failed commands might just timeout, we cut down * timeout grace though * furthermore, command 'Z' will not reply with anything */ ret = apc_read(temp, sizeof(temp), SER_HA|SER_D1|SER_TO); if (ret < 0) return STAT_INSTCMD_FAILED; debx(1, "got \"%s\"", temp); if ((!ret && ign) || !strcmp(temp, "OK")) { debx(1, "last shutdown cmd succeded"); return STAT_INSTCMD_HANDLED; } debx(1, "last shutdown cmd failed"); return STAT_INSTCMD_FAILED; } /* soft hibernate: S - working only when OB, otherwise ignored */ static int sdcmd_S(const void *foo) { apc_flush(0); if (!foo) debx(1, "issuing [%s]", prtchr(APC_CMD_SOFTDOWN)); if (apc_write(APC_CMD_SOFTDOWN) != 1) return STAT_INSTCMD_FAILED; return sdok(0); } /* soft hibernate, hack version for CS 350 & co. */ static int sdcmd_CS(const void *foo) { int ret; char temp[APC_SBUF]; debx(1, "issuing CS 'hack' [%s+%s]", prtchr(APC_CMD_SIMPWF), prtchr(APC_CMD_SOFTDOWN)); if (ups_status & APC_STAT_OL) { apc_flush(0); debx(1, "issuing [%s]", prtchr(APC_CMD_SIMPWF)); ret = apc_write(APC_CMD_SIMPWF); if (ret != 1) { return STAT_INSTCMD_FAILED; } /* eat response, allow timeout */ ret = apc_read(temp, sizeof(temp), SER_D1|SER_TO); if (ret < 0) { return STAT_INSTCMD_FAILED; } } /* continue with regular soft hibernate */ return sdcmd_S((void *)1); } /* * hard hibernate: @nnn / @nn * note: works differently for older and new models, see manual page for * thorough explanation */ static int sdcmd_AT(const void *str) { int ret, cnt, padto, i; const char *awd = str; char temp[APC_SBUF], *ptr; if (!awd) awd = "000"; cnt = strlen(awd); padto = cnt == 2 ? 2 : 3; temp[0] = APC_CMD_GRACEDOWN; ptr = temp + 1; for (i = cnt; i < padto ; i++) { *ptr++ = '0'; } strcpy(ptr, awd); debx(1, "issuing [%s] with %d minutes of additional wakeup delay", prtchr(APC_CMD_GRACEDOWN), (int)strtol(awd, NULL, 10)*6); apc_flush(0); ret = apc_write_long(temp); if (ret != padto + 1) { upslogx(LOG_ERR, "issuing [%s] with %d digits failed", prtchr(APC_CMD_GRACEDOWN), padto); return STAT_INSTCMD_FAILED; } ret = sdok(0); if (ret == STAT_INSTCMD_HANDLED || padto == 3) return ret; upslogx(LOG_ERR, "command [%s] with 2 digits doesn't work - try 3 digits", prtchr(APC_CMD_GRACEDOWN)); /* * "tricky" part - we tried @nn variation and it (unsurprisingly) * failed; we have to abort the sequence with something bogus to have * the clean state; newer upses will respond with 'NO', older will be * silent (YMMV); */ apc_write(APC_GOSMART); /* eat response, allow it to timeout */ apc_read(temp, sizeof(temp), SER_D1|SER_TO); return STAT_INSTCMD_FAILED; } /* shutdown: K - delayed poweroff */ static int sdcmd_K(const void *foo) { int ret; debx(1, "issuing [%s]", prtchr(APC_CMD_SHUTDOWN)); apc_flush(0); ret = apc_write_rep(APC_CMD_SHUTDOWN); if (ret != 2) return STAT_INSTCMD_FAILED; return sdok(0); } /* shutdown: Z - immediate poweroff */ static int sdcmd_Z(const void *foo) { int ret; debx(1, "issuing [%s]", prtchr(APC_CMD_OFF)); apc_flush(0); ret = apc_write_rep(APC_CMD_OFF); if (ret != 2) { return STAT_INSTCMD_FAILED; } /* note: ups will not reply anything after this command */ return sdok(1); } static void upsdrv_shutdown_simple(void) { unsigned int sdtype = 0; const char *val; if ((val = getval("sdtype"))) sdtype = strtol(val, NULL, 10); debx(1, "currently: %s, sdtype: %d", (ups_status & APC_STAT_OL) ? "on-line" : "on battery", sdtype); switch (sdtype) { case 5: /* hard hibernate */ sdcmd_AT(getval("awd")); break; case 4: /* special hack for CS 350 and similar models */ sdcmd_CS(0); break; case 3: /* delayed poweroff */ sdcmd_K(0); break; case 2: /* instant poweroff */ sdcmd_Z(0); break; case 1: /* * Send a combined set of shutdown commands which can work * better if the UPS gets power during shutdown process * Specifically it sends both the soft shutdown 'S' and the * hard hibernate '@nnn' commands */ /* S works only when OB */ if (!(ups_status & APC_STAT_OB) || sdcmd_S(0) != STAT_INSTCMD_HANDLED) sdcmd_AT(getval("awd")); break; default: /* * Send @nnn or S, depending on OB / OL status */ if (ups_status & APC_STAT_OL) /* on line */ sdcmd_AT(getval("awd")); else sdcmd_S(0); } } static void upsdrv_shutdown_advanced(void) { const void *arg; const char *val; size_t i, len; val = getval("advorder"); len = strlen(val); debx(1, "currently: %s, advorder: %s", (ups_status & APC_STAT_OL) ? "on-line" : "on battery", val); /* * try each method in the list with a little bit of handling in certain * cases */ for (i = 0; i < len; i++) { switch (val[i] - '0') { case SDIDX_AT: arg = getval("awd"); break; default: arg = NULL; } if (sdlist[val[i] - '0'](arg) == STAT_INSTCMD_HANDLED) break; /* finish if command succeeded */ } } /* power down the attached load immediately */ void upsdrv_shutdown(void) { char temp[APC_LBUF]; if (!smartmode(1)) logx(LOG_WARNING, "setting SmartMode failed !"); /* check the line status */ if (apc_write(APC_STATUS) == 1) { if (apc_read(temp, sizeof(temp), SER_D1) == 1) { ups_status = strtol(temp, 0, 16); } else { logx(LOG_WARNING, "status read failed, assuming LB+OB"); ups_status = APC_STAT_LB | APC_STAT_OB; } } else { logx(LOG_WARNING, "status write failed, assuming LB+OB"); ups_status = APC_STAT_LB | APC_STAT_OB; } if (testvar("advorder") && toupper(*getval("advorder")) != 'N') upsdrv_shutdown_advanced(); else upsdrv_shutdown_simple(); } static int update_info(int all) { int i; debx(1, "starting scan%s", all ? " (all vars)" : ""); for (i = 0; apc_vartab[i].name != NULL; i++) { if (!all && (apc_vartab[i].flags & APC_POLL) == 0) continue; if (!poll_data(&apc_vartab[i])) { debx(1, "aborting scan"); return 0; } } debx(1, "scan completed"); return 1; } static int setvar_enum(apc_vartab_t *vt, const char *val) { int i, ret; char orig[APC_LBUF], temp[APC_LBUF]; const char *ptr; apc_flush(SER_AA); if (apc_write(vt->cmd) != 1) return STAT_SET_FAILED; ret = apc_read(orig, sizeof(orig), SER_AA); if ((ret < 1) || (!strcmp(orig, "NA"))) return STAT_SET_FAILED; ptr = convert_data(vt, orig); /* suppress redundant changes - easier on the eeprom */ if (!strcmp(ptr, val)) { logx(LOG_INFO, "ignoring SET %s='%s' (unchanged value)", vt->name, val); return STAT_SET_HANDLED; /* FUTURE: no change */ } for (i = 0; i < 32; i++) { if (apc_write(APC_NEXTVAL) != 1) return STAT_SET_FAILED; /* this should return either OK (if rotated) or NO (if not) */ ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_SET_FAILED; /* sanity checks */ if (!strcmp(temp, "NO")) return STAT_SET_FAILED; if (strcmp(temp, "OK")) return STAT_SET_FAILED; /* see what it rotated onto */ if (apc_write(vt->cmd) != 1) return STAT_SET_FAILED; ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_SET_FAILED; ptr = convert_data(vt, temp); debx(1, "rotate - got [%s], want [%s]", ptr, val); if (!strcmp(ptr, val)) { /* got it */ logx(LOG_INFO, "SET %s='%s'", vt->name, val); /* refresh data from the hardware */ poll_data(vt); return STAT_SET_HANDLED; /* FUTURE: success */ } /* check for wraparound */ if (!strcmp(ptr, orig)) { logx(LOG_ERR, "variable %s wrapped", vt->name); return STAT_SET_FAILED; } } logx(LOG_ERR, "gave up after 6 tries for %s", vt->name); /* refresh data from the hardware */ poll_data(vt); return STAT_SET_FAILED; } static int setvar_string(apc_vartab_t *vt, const char *val) { unsigned int i; int ret; char temp[APC_LBUF], *ptr; /* sanitize length */ if (strlen(val) > APC_STRLEN) { logx(LOG_ERR, "value (%s) too long", val); return STAT_SET_FAILED; } apc_flush(SER_AA); if (apc_write(vt->cmd) != 1) return STAT_SET_FAILED; ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_SET_FAILED; /* suppress redundant changes - easier on the eeprom */ if (!strcmp(temp, val)) { logx(LOG_INFO, "ignoring SET %s='%s' (unchanged value)", vt->name, val); return STAT_SET_HANDLED; /* FUTURE: no change */ } /* length sanitized above */ temp[0] = APC_NEXTVAL; strcpy(temp + 1, val); ptr = temp + strlen(temp); for (i = strlen(val); i < APC_STRLEN; i++) *ptr++ = '\015'; /* pad with CRs */ *ptr = 0; if (apc_write_long(temp) != APC_STRLEN + 1) return STAT_SET_FAILED; ret = apc_read(temp, sizeof(temp), SER_AA); if (ret < 1) { logx(LOG_ERR, "short final read"); return STAT_SET_FAILED; } if (!strcmp(temp, "NO")) { logx(LOG_ERR, "got NO at final read"); return STAT_SET_FAILED; } /* refresh data from the hardware */ poll_data(vt); logx(LOG_INFO, "SET %s='%s'", vt->name, val); return STAT_SET_HANDLED; /* FUTURE: success */ } static int setvar(const char *varname, const char *val) { apc_vartab_t *vt; vt = vt_lookup_name(varname); if (!vt) return STAT_SET_UNKNOWN; if ((vt->flags & APC_RW) == 0) { logx(LOG_WARNING, "[%s] is not writable", varname); return STAT_SET_UNKNOWN; } if (vt->flags & APC_ENUM) return setvar_enum(vt, val); if (vt->flags & APC_STRING) return setvar_string(vt, val); logx(LOG_WARNING, "unknown type for [%s]", varname); return STAT_SET_UNKNOWN; } /* load on */ static int do_loadon(void) { apc_flush(0); debx(1, "issuing [%s]", prtchr(APC_CMD_ON)); if (apc_write_rep(APC_CMD_ON) != 2) return STAT_INSTCMD_FAILED; /* * ups will not reply anything after this command, but might * generate brief OVER condition (which will be corrected on * the next status update) */ debx(1, "[%s] completed", prtchr(APC_CMD_ON)); return STAT_INSTCMD_HANDLED; } /* actually send the instcmd's char to the ups */ static int do_cmd(const apc_cmdtab_t *ct) { int ret, c; char temp[APC_LBUF]; apc_flush(SER_AA); if (ct->flags & APC_REPEAT) { ret = apc_write_rep(ct->cmd); c = 2; } else { ret = apc_write(ct->cmd); c = 1; } if (ret != c) { return STAT_INSTCMD_FAILED; } ret = apc_read(temp, sizeof(temp), SER_AA); if (ret < 1) return STAT_INSTCMD_FAILED; if (strcmp(temp, "OK")) { logx(LOG_WARNING, "got [%s] after command [%s]", temp, ct->name); return STAT_INSTCMD_FAILED; } logx(LOG_INFO, "%s completed", ct->name); return STAT_INSTCMD_HANDLED; } /* some commands must be repeated in a window to execute */ static int instcmd_chktime(apc_cmdtab_t *ct, const char *ext) { double elapsed; time_t now; static time_t last = 0; time(&now); elapsed = difftime(now, last); last = now; /* you have to hit this in a small window or it fails */ if ((elapsed < MINCMDTIME) || (elapsed > MAXCMDTIME)) { debx(1, "outside window for [%s %s] (%2.0f)", ct->name, ext ? ext : "\b", elapsed); return 0; } return 1; } static int instcmd(const char *cmd, const char *ext) { int i; apc_cmdtab_t *ct = NULL; for (i = 0; apc_cmdtab[i].name != NULL; i++) { /* cmd must match */ if (strcasecmp(apc_cmdtab[i].name, cmd)) continue; /* if cmd specifies regex, ext must match */ if (apc_cmdtab[i].ext) { if (!rexhlp(apc_cmdtab[i].ext, ext)) continue; /* if cmd doesn't specify regex, ext must be NULL */ } else { if (ext) continue; } ct = &apc_cmdtab[i]; break; } if (!ct) { logx(LOG_WARNING, "unknown command [%s %s]", cmd, ext ? ext : "\b"); return STAT_INSTCMD_INVALID; } if (!(ct->flags & APC_PRESENT)) { logx(LOG_WARNING, "command [%s %s] recognized, but" " not supported by your UPS model", cmd, ext ? ext : "\b"); return STAT_INSTCMD_INVALID; } /* first verify if the command is "nasty" */ if ((ct->flags & APC_NASTY) && !instcmd_chktime(ct, ext)) return STAT_INSTCMD_HANDLED; /* future: again */ /* we're good to go, handle special stuff first, then generic cmd */ if (!strcasecmp(cmd, "calibrate.start")) return do_cal(1); if (!strcasecmp(cmd, "calibrate.stop")) return do_cal(0); if (!strcasecmp(cmd, "load.on")) return do_loadon(); if (!strcasecmp(cmd, "load.off")) return sdcmd_Z(0); if (!strcasecmp(cmd, "shutdown.stayoff")) return sdcmd_K(0); if (!strcasecmp(cmd, "shutdown.return")) { if (!ext || !*ext) return sdcmd_S(0); if (toupper(*ext) == 'A') return sdcmd_AT(ext + 3); if (toupper(*ext) == 'C') return sdcmd_CS(0); } /* nothing special here */ return do_cmd(ct); } /* install pointers to functions for msg handlers called from msgparse */ static void setuphandlers(void) { upsh.setvar = setvar; upsh.instcmd = instcmd; } /* ---- functions that interface with main.c ------------------------------- */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "ttymode", "tty discipline selection"); addvar(VAR_VALUE, "cable", "alternate cable (940-0095B) selection"); addvar(VAR_VALUE, "awd", "hard hibernate's additional wakeup delay"); addvar(VAR_VALUE, "sdtype", "simple shutdown method"); addvar(VAR_VALUE, "advorder", "advanced shutdown control"); } void upsdrv_help(void) { printf( "\nFor detailed information, please refer to:\n" " - apcsmart(8)\n" " - http://www.networkupstools.org/docs/man/apcsmart.html\n" ); } void upsdrv_initups(void) { char *val; apc_vartab_t *ptr; /* sanitize awd (additional waekup delay of '@' command) */ if ((val = getval("awd")) && !rexhlp(APC_AWDFMT, val)) { fatalx(EXIT_FAILURE, "invalid value (%s) for option 'awd'", val); } /* sanitize sdtype */ if ((val = getval("sdtype")) && !rexhlp(APC_SDFMT, val)) { fatalx(EXIT_FAILURE, "invalid value (%s) for option 'sdtype'", val); } /* sanitize advorder */ if ((val = getval("advorder")) && !rexhlp(APC_ADVFMT, val)) { fatalx(EXIT_FAILURE, "invalid value (%s) for option 'advorder'", val); } upsfd = extrafd = ser_open(device_path); apc_ser_set(); /* fill length values */ for (ptr = apc_vartab; ptr->name; ptr++) ptr->nlen0 = strlen(ptr->name) + 1; } void upsdrv_cleanup(void) { char temp[APC_LBUF]; if (upsfd == -1) return; apc_flush(0); /* try to bring the UPS out of smart mode */ apc_write(APC_GODUMB); apc_read(temp, sizeof(temp), SER_TO); ser_close(upsfd, device_path); } void upsdrv_initinfo(void) { const char *pmod, *pser; if (!smartmode(5)) { fatalx(EXIT_FAILURE, "unable to detect an APC Smart protocol UPS on port %s\n" "check the cabling, port name or model name and try again", device_path ); } if (!getbaseinfo()) { fatalx(EXIT_FAILURE, "Problems with communicating APC UPS on port %s\n", device_path ); } /* manufacturer ID - hardcoded in this particular module */ dstate_setinfo("ups.mfr", "APC"); if (!(pmod = dstate_getinfo("ups.model"))) pmod = "\"unknown model\""; if (!(pser = dstate_getinfo("ups.serial"))) pser = "unknown serial"; upsdebugx(1, "detected %s [%s] on %s", pmod, pser, device_path); setuphandlers(); /* * seems to be ok so far, it must be set so initial call of * upsdrv_updateinfo() doesn't begin with stale condition */ dstate_dataok(); } void upsdrv_updateinfo(void) { static int last_worked = 0; static time_t last_full = 0; int all; time_t now; /* try to wake up a dead ups once in awhile */ if (dstate_is_stale()) { if (!last_worked) debx(1, "comm lost"); /* reset this so a full update runs when the UPS returns */ last_full = 0; if (++last_worked < 10) return; /* become aggressive after a few tries */ debx(1, "nudging ups with 'Y', iteration #%d ...", last_worked); if (!smartmode(1)) return; last_worked = 0; } if (!update_status()) { dstate_datastale(); return; } time(&now); /* refresh all variables hourly */ /* does not catch measure-ups II insertion/removal */ if (difftime(now, last_full) > 3600) { last_full = now; all = 1; } else all = 0; if (update_info(all)) { dstate_dataok(); } else { dstate_datastale(); } } nut-2.7.4/drivers/powerware-mib.h0000644000175000017500000000027112640473702013667 00000000000000#ifndef POWERWARE_MIB_H #define POWERWARE_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t powerware; extern mib2nut_info_t pxgx_ups; #endif /* POWERWARE_MIB_H */ nut-2.7.4/drivers/etapro.c0000644000175000017500000002164312640443572012404 00000000000000/* etapro.c - model specific routines for ETA UPS Copyright (C) 2002 Marek Michalkiewicz 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 */ /* This driver is for the ETA UPS (http://www.eta.com.pl/) with the "PRO" option (available at small added cost, highly recommended). All units (even without that option) should also work in "dumb" mode with the genericups driver (type 7 or 10), but in that mode shutdown only works when running on battery. Tested with ETA mini+UPS 720 PRO. Thanks to ETA for help with protocol documentation, no free UPS though (but they still can send me another one if they like me ;-). Shutdown should work even when on line, so this should help avoid power races (system remaining in halted or "ATX standby" state, requiring manual intervention). Delay from power off to power on can be set in software, currently hardcoded to 15 seconds. Instant commands CMD_OFF and CMD_ON should work (not tested yet). Be careful with CMD_OFF - it turns off the load after one second. Known issues: - larger units (>= 1000VA) have a 24V battery, so the battery voltage reported should be multiplied by 2 if the model string indicates such a larger unit. - load percentage is only measured when running on battery, and is reported as 0 when on line. This seems to be a hardware limitation of the UPS, so we can't do much about it... - UPS does not provide any remaining battery charge (or time at current load) information, but we should be able to estimate it based on battery voltage, load percentage and UPS model. - error handling not tested (we assume that the UPS is always correctly connected to the serial port). */ #include "main.h" #include "serial.h" #define DRIVER_NAME "ETA PRO driver" #define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Marek Michalkiewicz ", DRV_STABLE, { NULL } }; static int etapro_get_response(const char *resp_type) { char tmp[256]; char *cp; unsigned int n, val; /* Read until a newline is found or there is no room in the buffer. Unlike ser_get_line(), don't discard the following characters because we have to handle multi-line responses. */ n = 0; while (ser_get_char(upsfd, (unsigned char *)&tmp[n], 1, 0) == 1) { if (n >= sizeof(tmp) - 1 || tmp[n] == '\n') break; n++; } tmp[n] = '\0'; if (n == 0) { upslogx(LOG_ERR, "no response from UPS"); return -1; } /* Search for start of response (skip any echoed back command). */ cp = strstr(tmp, resp_type); if (!cp || *cp == '\0' || cp[strlen(cp) - 1] != '\r') { upslogx(LOG_ERR, "bad response (%s)", tmp); return -1; } cp[strlen(cp) - 1] = '\0'; /* remove the CR */ switch (cp[1]) { /* Handle ASCII text responses directly here. */ case 'R': dstate_setinfo("ups.mfr", "%s", cp + 2); return 0; case 'S': dstate_setinfo("ups.model", "%s", cp + 2); return 0; case 'T': dstate_setinfo("ups.mfr.date", "%s", cp + 2); return 0; } /* Handle all other responses as hexadecimal numbers. */ val = 0; if (sscanf(cp + 2, "%x", &val) != 1) { upslogx(LOG_ERR, "bad response format (%s)", tmp); return -1; } return val; } static void etapro_set_on_timer(int seconds) { int x; if (seconds == 0) { /* cancel the running timer */ ser_send(upsfd, "RS\r"); x = etapro_get_response("SV"); if (x == 0x30) return; /* OK */ } else { if (seconds > 0x7fff) { /* minutes */ seconds = (seconds + 59) / 60; if (seconds > 0x7fff) seconds = 0x7fff; printf("UPS on in %d minutes\n", seconds); seconds |= 0x8000; } else { printf("UPS on in %d seconds\n", seconds); } ser_send(upsfd, "RN%04X\r", seconds); x = etapro_get_response("SV"); if (x == 0x20) return; /* OK */ } upslogx(LOG_ERR, "etapro_set_on_timer: error, status=0x%02x", x); } static void etapro_set_off_timer(int seconds) { int x; if (seconds == 0) { /* cancel the running timer */ ser_send(upsfd, "RR\r"); x = etapro_get_response("SV"); if (x == 0x10) return; /* OK */ } else { if (seconds > 0x7fff) { /* minutes */ seconds /= 60; if (seconds > 0x7fff) seconds = 0x7fff; printf("UPS off in %d minutes\n", seconds); seconds |= 0x8000; } else { printf("UPS off in %d seconds\n", seconds); } ser_send(upsfd, "RO%04X\r", seconds); x = etapro_get_response("SV"); if (x == 0) return; /* OK */ } upslogx(LOG_ERR, "etapro_set_off_timer: error, status=0x%02x", x); } static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "load.off")) { etapro_set_off_timer(1); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { etapro_set_on_timer(1); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { upsdrv_shutdown(); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_initinfo(void) { dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("shutdown.return"); /* First command after power on returns junk - ignore it. */ ser_send(upsfd, "RI\r"); sleep(1); upsdrv_updateinfo(); upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) { int x, flags; double utility, outvolt, battvolt, loadpct; ser_flush_in(upsfd, "", nut_debug_level); ser_send(upsfd, "RI\r"); /* identify */ x = etapro_get_response("SR"); /* manufacturer */ if (x < 0) { dstate_datastale(); return; } x = etapro_get_response("SS"); /* model */ if (x < 0) { dstate_datastale(); return; } x = etapro_get_response("ST"); /* mfr date */ if (x < 0) { dstate_datastale(); return; } x = etapro_get_response("SU"); /* UPS ident */ if (x < 0) { dstate_datastale(); return; } ser_send(upsfd, "RP\r"); /* read measurements */ x = etapro_get_response("SO"); /* status flags */ if (x < 0) { dstate_datastale(); return; } flags = x; x = etapro_get_response("SG"); /* input voltage, 0xFF = 270V */ if (x < 0) { dstate_datastale(); return; } utility = (270.0 / 255) * x; x = etapro_get_response("SH"); /* output voltage, 0xFF = 270V */ if (x < 0) { dstate_datastale(); return; } outvolt = (270.0 / 255) * x; x = etapro_get_response("SI"); /* battery voltage, 0xFF = 14V */ if (x < 0) { dstate_datastale(); return; } /* TODO: >= 1000VA models have a 24V battery (max 28V) - check the model string returned by the RI command. */ battvolt = (14.0 / 255) * x; x = etapro_get_response("SL"); /* load (on battery), 0xFF = 150% */ if (x < 0) { dstate_datastale(); return; } loadpct = (150.0 / 255) * x; x = etapro_get_response("SN"); /* time running on battery */ if (x < 0) { dstate_datastale(); return; } /* This is the time how long the UPS has been running on battery (in seconds, reset to zero after power returns), but there seems to be no variable defined for this yet... */ status_init(); if (!(flags & 0x02)) status_set("OFF"); else if (flags & 0x01) status_set("OL"); else status_set("OB"); if (!(flags & 0x04)) status_set("LB"); /* TODO bit 3: 1 = ok, 0 = fault */ if (flags & 0x10) status_set("BOOST"); if (loadpct > 100.0) status_set("OVER"); /* Battery voltage out of range (lower than LB, or too high). */ if (flags & 0x20) status_set("RB"); /* TODO bit 6: 1 = charging, 0 = full */ status_commit(); dstate_setinfo("input.voltage", "%03.1f", utility); dstate_setinfo("output.voltage", "%03.1f", outvolt); dstate_setinfo("battery.voltage", "%02.2f", battvolt); dstate_setinfo("ups.load", "%03.1f", loadpct); dstate_dataok(); } /* TODO: delays should be tunable, the UPS supports max 32767 minutes. */ /* Shutdown command to off delay in seconds. */ #define SHUTDOWN_GRACE_TIME 10 /* Shutdown to return delay in seconds. */ #define SHUTDOWN_TO_RETURN_TIME 15 void upsdrv_shutdown(void) { etapro_set_on_timer(SHUTDOWN_GRACE_TIME + SHUTDOWN_TO_RETURN_TIME); etapro_set_off_timer(SHUTDOWN_GRACE_TIME); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 1); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/mge-hid.h0000644000175000017500000000206112640443572012422 00000000000000/* mge-hid.h - data to monitor MGE UPS SYSTEMS HID (USB and serial) devices * * Copyright (C) 2003 - 2005 * Arnaud Quette * * Sponsored by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef MGE_HID_H #define MGE_HID_H #include "usbhid-ups.h" extern subdriver_t mge_subdriver; #endif /* MGE_HID_H */ nut-2.7.4/drivers/powercom-hid.c0000644000175000017500000005600712640473702013507 00000000000000/* powercom-hid.c - subdriver to monitor PowerCOM USB/HID devices with NUT * * Copyright (C) * 2003 - 2009 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * * 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 "main.h" /* for getval() */ #include "usbhid-ups.h" #include "powercom-hid.h" #include "usb-common.h" #define POWERCOM_HID_VERSION "PowerCOM HID 0.5" /* FIXME: experimental flag to be put in upsdrv_info */ /* PowerCOM */ #define POWERCOM_VENDORID 0x0d9f /* USB IDs device table */ static usb_device_id_t powercom_usb_device_table[] = { /* PowerCOM IMP - IMPERIAL Series */ { USB_DEVICE(POWERCOM_VENDORID, 0x00a2), NULL }, /* PowerCOM SKP - Smart KING Pro (all Smart series) */ { USB_DEVICE(POWERCOM_VENDORID, 0x00a3), NULL }, /* PowerCOM WOW */ { USB_DEVICE(POWERCOM_VENDORID, 0x00a4), NULL }, /* PowerCOM VGD - Vanguard */ { USB_DEVICE(POWERCOM_VENDORID, 0x00a5), NULL }, /* PowerCOM BNT - Black Knight Pro */ { USB_DEVICE(POWERCOM_VENDORID, 0x00a6), NULL }, /* PowerCOM Vanguard and BNT-xxxAP */ { USB_DEVICE(POWERCOM_VENDORID, 0x0004), NULL }, { USB_DEVICE(POWERCOM_VENDORID, 0x0001), NULL }, /* Terminating entry */ { -1, -1, NULL } }; static char powercom_scratch_buf[32]; static const char *powercom_startup_fun(double value) { uint16_t i = value; snprintf(powercom_scratch_buf, sizeof(powercom_scratch_buf), "%d", 60 * (((i & 0x00FF) << 8) + (i >> 8))); upsdebugx(3, "%s: value = %.0f, buf = %s", __func__, value, powercom_scratch_buf); return powercom_scratch_buf; } static double powercom_startup_nuf(const char *value) { const char *s = dstate_getinfo("ups.delay.start"); uint16_t val, command; val = atoi(value ? value : s) / 60; command = ((val << 8) + (val >> 8)); upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command); return command; } static info_lkp_t powercom_startup_info[] = { { 0, NULL, powercom_startup_fun, powercom_startup_nuf } }; static const char *powercom_shutdown_fun(double value) { uint16_t i = value; snprintf(powercom_scratch_buf, sizeof(powercom_scratch_buf), "%d", 60 * (i & 0x00FF) + (i >> 8)); upsdebugx(3, "%s: value = %.0f, buf = %s", __func__, value, powercom_scratch_buf); return powercom_scratch_buf; } static double powercom_shutdown_nuf(const char *value) { const char *s = dstate_getinfo("ups.delay.shutdown"); uint16_t val, command; val = atoi(value ? value : s); val = val ? val : 1; /* 0 sets the maximum delay */ command = ((val % 60) << 8) + (val / 60); command |= 0x4000; /* AC RESTART NORMAL ENABLE */ upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command); return command; } static info_lkp_t powercom_shutdown_info[] = { { 0, NULL, powercom_shutdown_fun, powercom_shutdown_nuf } }; static double powercom_stayoff_nuf(const char *value) { const char *s = dstate_getinfo("ups.delay.shutdown"); uint16_t val, command; val = atoi(value ? value : s); val = val ? val : 1; /* 0 sets the maximum delay */ command = ((val % 60) << 8) + (val / 60); command |= 0x8000; /* AC RESTART NORMAL DISABLE */ upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command); return command; } static info_lkp_t powercom_stayoff_info[] = { { 0, NULL, NULL, powercom_stayoff_nuf } }; static info_lkp_t powercom_beeper_info[] = { { 1, "enabled", NULL }, { 2, "disabled", NULL }, /* muted? */ { 0, NULL, NULL } }; static const char *powercom_voltage_conversion_fun(double value) { static char buf[20]; snprintf(buf, sizeof(buf), "%0.0f", value * 4); return buf; } static info_lkp_t powercom_voltage_conversion[] = { { 0, NULL, powercom_voltage_conversion_fun } }; static const char *powercom_upsfail_conversion_fun(double value) { if ((long)value & 0x0001) { return "fanfail"; } else { return "!fanfail"; } } static info_lkp_t powercom_upsfail_conversion[] = { { 0, NULL, powercom_upsfail_conversion_fun } }; static const char *powercom_replacebatt_conversion_fun(double value) { if ((long)value & 0x0002) { return "replacebatt"; } else { return "!replacebatt"; } } static info_lkp_t powercom_replacebatt_conversion[] = { { 0, NULL, powercom_replacebatt_conversion_fun } }; static const char *powercom_test_conversion_fun(double value) { if ((long)value & 0x0004) { return "cal"; } else { return "!cal"; } } static info_lkp_t powercom_test_conversion[] = { { 0, NULL, powercom_test_conversion_fun } }; static const char *powercom_shutdownimm_conversion_fun(double value) { if ((long)value & 0x0010) { return "shutdownimm"; } else { return "!shutdownimm"; } } static info_lkp_t powercom_shutdownimm_conversion[] = { { 0, NULL, powercom_shutdownimm_conversion_fun } }; static const char *powercom_online_conversion_fun(double value) { if ((long)value & 0x0001) { return "!online"; } else { return "online"; } } static info_lkp_t powercom_online_conversion[] = { { 0, NULL, powercom_online_conversion_fun } }; static const char *powercom_lowbatt_conversion_fun(double value) { if ((long)value & 0x0002) { return "lowbatt"; } else { return "!lowbatt"; } } static info_lkp_t powercom_lowbatt_conversion[] = { { 0, NULL, powercom_lowbatt_conversion_fun } }; static const char *powercom_trim_conversion_fun(double value) { if (((long)value & 0x0018) == 0x0008) { return "trim"; } else { return "!trim"; } } static info_lkp_t powercom_trim_conversion[] = { { 0, NULL, powercom_trim_conversion_fun } }; static const char *powercom_boost_conversion_fun(double value) { if (((long)value & 0x0018) == 0x0018) { return "boost"; } else { return "!boost"; } } static info_lkp_t powercom_boost_conversion[] = { { 0, NULL, powercom_boost_conversion_fun } }; static const char *powercom_overload_conversion_fun(double value) { if ((long)value & 0x0020) { return "overload"; } else { return "!overload"; } } static info_lkp_t powercom_overload_conversion[] = { { 0, NULL, powercom_overload_conversion_fun } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* POWERCOM usage table */ static usage_lkp_t powercom_usage_lkp[] = { { "PowercomUPS", 0x00020004 }, { "PowercomBatterySystem", 0x00020010 }, { "PowercomPowerConverter", 0x00020016 }, { "PowercomInput", 0x0002001a }, { "PowercomOutput", 0x0002001c }, { "PowercomVoltage", 0x00020030 }, { "PowercomFrequency", 0x00020032 }, { "PowercomPercentLoad", 0x00020035 }, { "PowercomTemperature", 0x00020036 }, { "PowercomDelayBeforeStartup", 0x00020056 }, { "PowercomDelayBeforeShutdown", 0x00020057 }, { "PowercomTest", 0x00020058 }, { "PowercomShutdownRequested", 0x00020068 }, { "PowercomInternalChargeController", 0x00020081 }, { "PowercomPrimaryBatterySupport", 0x00020082 }, { "PowercomDesignCapacity", 0x00020083 }, { "PowercomSpecificationInfo", 0x00020084 }, { "PowercomManufacturerDate", 0x00020085 }, { "PowercomSerialNumber", 0x00020086 }, { "PowercomManufacturerName", 0x00020087 }, { "POWERCOM1", 0x0084002f }, { "POWERCOM2", 0xff860060 }, { "POWERCOM3", 0xff860080 }, { "PCMDelayBeforeStartup", 0x00ff0056 }, { "PCMDelayBeforeShutdown", 0x00ff0057 }, { NULL, 0 } }; static usage_tables_t powercom_utab[] = { powercom_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ static hid_info_t powercom_hid2nut[] = { { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, 0, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, 0, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, 0, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.CommunicationLost", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, 0, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.POWERCOM3", NULL, "%.0f", 0, NULL }, */ /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownRequested", NULL, "%.0f", 0, NULL }, */ /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.VoltageNotRegulated", NULL, "%.0f", 0, NULL }, */ { "BOOL", 0, 0, "UPS.PresentStatus.ACPresent", NULL, NULL, 0, online_info }, { "BOOL", 0, 0, "UPS.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info }, { "BOOL", 0, 0, "UPS.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, 0, lowbatt_info }, { "BOOL", 0, 0, "UPS.PresentStatus.Boost", NULL, NULL, 0, boost_info }, { "BOOL", 0, 0, "UPS.PresentStatus.Buck", NULL, NULL, 0, trim_info }, { "BOOL", 0, 0, "UPS.PresentStatus.Charging", NULL, NULL, 0, charging_info }, { "BOOL", 0, 0, "UPS.PresentStatus.CommunicationLost", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PresentStatus.Discharging", NULL, NULL, 0, discharging_info }, { "BOOL", 0, 0, "UPS.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, { "BOOL", 0, 0, "UPS.PresentStatus.Overload", NULL, NULL, 0, overload_info }, { "BOOL", 0, 0, "UPS.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, { "BOOL", 0, 0, "UPS.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, /* { "BOOL", 0, 0, "UPS.PresentStatus.POWERCOM3", NULL, "%.0f", 0, NULL }, */ /* { "BOOL", 0, 0, "UPS.PresentStatus.ShutdownRequested", NULL, "%.0f", 0, NULL }, */ /* { "BOOL", 0, 0, "UPS.PresentStatus.Tested", NULL, "%.0f", 0, NULL }, */ /* { "BOOL", 0, 0, "UPS.PresentStatus.VoltageNotRegulated", NULL, "%.0f", 0, NULL }, */ /* * According to the HID PDC specifications, the below values should report battery.voltage(.nominal) * PowerCOM duplicates the output.voltage(.nominal) here, so we ignore them * { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL }, * { "battery.voltage", 0, 0, "UPS.Battery.Voltage", NULL, "%.2f", 0, NULL }, * { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, * { "battery.voltage.nominal", 0, 0, "UPS.Battery.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge", 0, 0, "UPS.Battery.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, /* { "unmapped.ups.battery.delaybeforestartup", 0, 0, "UPS.Battery.DelayBeforeStartup", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.battery.initialized", 0, 0, "UPS.Battery.Initialized", NULL, "%.0f", 0, NULL }, */ { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, powercom_beeper_info }, { "ups.beeper.status", 0, 0, "UPS.AudibleAlarmControl", NULL, "%s", 0, powercom_beeper_info }, { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.date", 0, 0, "UPS.PowerSummary.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion }, { "ups.test.result", 0, 0, "UPS.Battery.Test", NULL, "%s", 0, test_read_info }, /* { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ /* { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ /* { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ /* { "unmapped.ups.iname", 0, 0, "UPS.iName", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ /* { "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ /* The implementation of the HID path UPS.PowerSummary.DelayBeforeStartup is unconventional: * Read: * Byte 7, byte 8 (min) * Write: * Command 4, high byte min, low byte min */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.DelayBeforeStartup", NULL, "60", HU_FLAG_ABSENT, NULL }, { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, "%.0f", 0, powercom_startup_info }, /* The implementation of the HID path UPS.PowerSummary.DelayBeforeShutdown is unconventional: * Read: * Byte 13, Byte 14 (min, sec) * Write: * If Byte(sec), bit7=0 and bit6=0 Then * If Byte 9, bit0=1 Then command 185, 188, min, sec (OL -> shutdown.return) * If Byte 9, bit0=0 Then command 186, 188, min, sec (OB -> shutdown.stayoff) * If Byte(sec), bit7=0 and bit6=1 * Then command 185, 188, min, sec (shutdown.return) * If Byte(sec), bit7=1 and bit6=0 Then * Then command 186, 188, min, sec (shutdown.stayoff) * If Byte(sec), bit7=1 and bit6=1 Then * No actions */ { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, powercom_shutdown_info }, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, powercom_shutdown_info }, { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL }, { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%.1f", 0, NULL }, { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.1f", 0, NULL }, /* { "unmapped.ups.powercom1", 0, 0, "UPS.POWERCOM1", NULL, "%.0f", 0, NULL }, broken pipe */ /* { "unmapped.ups.powercom2", 0, 0, "UPS.POWERCOM2", NULL, "%.0f", 0, NULL }, broken pipe */ /* { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, */ /* { "unmapped.ups.shutdownimminent", 0, 0, "UPS.ShutdownImminent", NULL, "%.0f", 0, NULL }, */ /* instcmds */ { "beeper.toggle", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "0", HU_TYPE_CMD, NULL }, { "test.battery.start.quick", 0, 0, "UPS.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, NULL, HU_TYPE_CMD, powercom_startup_info }, { "shutdown.return", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info }, { "shutdown.stayoff", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info }, { "load.on", 0, 0, "UPS.PowerSummary.PCMDelayBeforeStartup", NULL, "0", HU_TYPE_CMD, powercom_startup_info }, { "load.off", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, powercom_stayoff_info }, { "shutdown.return", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info }, { "shutdown.stayoff", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info }, { "ups.serial", 0, 0, "PowercomUPS.PowercomSerialNumber", NULL, "%s", 0, stringid_conversion }, { "ups.mfr", 0, 0, "PowercomUPS.PowercomManufacturerName", NULL, "%s", 0, stringid_conversion }, /* { "UPS.DesignCapacity", 0, 0, "PowercomUPS.PowercomDesignCapacity", NULL, "%.0f", 0, NULL }, is always 255 */ { "ups.mfr.date", 0, 0, "PowercomUPS.PowercomManufacturerDate", NULL, "%s", 0, date_conversion }, { "battery.temperature", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomTemperature", NULL, "%.0f", 0, NULL }, { "battery.charge", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomVoltage", NULL, "%.0f", 0, NULL }, /* { "UPS.BatterySystem.SpecificationInfo", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomSpecificationInfo", NULL, "%.0f", 0, NULL }, */ { "input.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomFrequency", NULL, "%.0f", 0, NULL }, { "input.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion }, { "output.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion }, { "ups.load", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPercentLoad", NULL, "%.0f", 0, NULL }, /* flags: 4 - Testing, 8 - Probably mute (it's set on battery with muted beeper and sometimes during ups test) * bit 0 UPS fault (1 = FAILT) * bit 1 Battery status (1 = BAD, 0 = NORMAL) * bit 2 Test mode (1 = TEST, 0 = NORMAL) * bit 3 X * bit 4 Pre-SD count mode (1 = ACTIVE) * bit 5 Schedule count mode (1 = ACTIVE) * bit 6 Disable NO LOAD SHUTDOWN (1 = ACTIVE) * bit 7 0 */ /* { "UPS.PowerConverter.Output.InternalChargeController", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, "%.0f", 0, NULL }, */ { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_upsfail_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_replacebatt_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_test_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_shutdownimm_conversion }, /* flags: 1 - On battery, 2 - Low Battery, 8 - Trim, 8+16 - Boost * bit 0 is line fail (1 = INV, 0 = LINE) * bit 1 is low battery (1 = BAT_ LOW, 0 = NORMAL) * bit 2 X * bit 3 AVR status (1 = AVR, 0 = NO_AVR) * bit 4 AVR mode (1 = BOOST, 0 = BUCK) * bit 5 Load status (1 = OVER LOAD, 0 = NORMAL) * bit 6 X * bit 7 SD mode display */ /* { "UPS.PowerConverter.Output.PrimaryBatterySupport", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, "%.0f", 0, NULL }, */ { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_online_conversion }, /* Low battery status may not work */ { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_lowbatt_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_trim_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_boost_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_overload_conversion }, { "output.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomFrequency", NULL, "%.0f", 0, NULL }, { "ups.test.result", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomTest", NULL, "%s", 0, test_read_info }, /* { "UPS.PowerConverter.ShutdownRequested", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomShutdownRequested", NULL, "%.0f", 0, NULL }, */ { "ups.delay.shutdown", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "%.0f", 0, NULL }, { "ups.delay.start", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeStartup", NULL, "%.0f", 0, NULL }, { "load.off", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *powercom_format_model(HIDDevice_t *hd) { return hd->Product; } static const char *powercom_format_mfr(HIDDevice_t *hd) { return hd->Vendor ? hd->Vendor : "PowerCOM"; } static const char *powercom_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int powercom_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(powercom_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: if (hd->ProductID == 0x0002) { upsdebugx(0, "This Powercom device (%04x/%04x) is not supported by usbhid-ups.\n" "Please use the 'powercom' driver instead.\n", hd->VendorID, hd->ProductID); return 0; } /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("PowerCOM", hd); return 0; case SUPPORTED: if (hd->ProductID == 0x0001) { interrupt_only = 1; interrupt_size = 8; } return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t powercom_subdriver = { POWERCOM_HID_VERSION, powercom_claim, powercom_utab, powercom_hid2nut, powercom_format_model, powercom_format_mfr, powercom_format_serial, }; nut-2.7.4/drivers/delta_ups-mib.c0000644000175000017500000005534512667537407013657 00000000000000/* delta_ups-mib.c - subdriver to monitor delta_ups SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * * Note: this subdriver was initially generated as a "stub" by the * gen-snmp-subdriver.sh script. It must be customized! * * MIB reference: http://www.networkupstools.org/ups-protocols/snmp/DeltaUPSv4.mib * * 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 "delta_ups-mib.h" #define DELTA_UPS_MIB_VERSION "0.2" #define DELTA_UPS_SYSOID ".1.3.6.1.4.1.2254.2.4" /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { * { 1, "OB" }, * { 2, "OL" }, * { 0, NULL } * }; */ static info_lkp_t delta_ups_upstype_info[] = { { 1, "on-line" }, { 2, "off-line" }, { 3, "line-interactive" }, { 4, "3phase" }, { 5, "splite-phase" }, { 0, NULL } }; static info_lkp_t delta_ups_pwr_info[] = { { 0, "OL" }, /* normal */ { 1, "OB" }, /* battery */ { 2, "BYPASS" }, /* bypass */ { 3, "TRIM" }, /* reducing */ { 4, "BOOST" }, /* boosting */ { 5, "BYPASS" }, /* manualBypass */ /*{ 6, "NULL" },*/ /* other */ { 7, "OFF" }, /* none */ { 0, NULL } } ; /* DELTA_UPS Snmp2NUT lookup table */ static snmp_info_t delta_ups_mib[] = { /* Data format: * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo * info_len: length of strings if STR * cmd value if CMD, multiplier otherwise * OID: SNMP OID or NULL * dfl: default value * flags: snmp-ups internal flags (FIXME: ...) * oid2info: lookup table between OID and NUT values * setvar: variable to set for SU_FLAG_SETINT * * Example: * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, onbatt_info }, * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { * { 1, "OB" }, * { 2, "OL" }, * { 0, NULL } * }; */ /* dupsIdentManufacturer.0 = STRING: "Socomec" */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsIdentModel.0 = STRING: "NETYS RT 1/1 UPS" */ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsIdentAgentSoftwareVersion.0 = STRING: "2.0h " */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsIdentUPSSoftwareVersion.0 = STRING: "1.1" */ { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsType.0 = INTEGER: on-line(1) */ { "ups.type", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.19.0", NULL, SU_FLAG_OK, delta_ups_upstype_info }, /* dupsOutputLoad1.0 = INTEGER: 29 */ { "ups.load", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsRatingOutputVA.0 = INTEGER: 2200 */ { "ups.power", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsRatingOutputVoltage.0 = INTEGER: 230 */ { "output.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputVoltage1.0 = INTEGER: 2300 */ { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsRatingOutputFrequency.0 = INTEGER: 50 */ { "output.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputCurrent1.0 = INTEGER: 23 */ { "output.current", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsRatingInputVoltage.0 = INTEGER: 230 */ { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputVoltage1.0 = INTEGER: 2280 */ { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsRatingInputFrequency.0 = INTEGER: 50 */ { "input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.11.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputFrequency1.0 = INTEGER: 499 */ { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputSource.0 = INTEGER: normal(0) */ { "ups.status", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.1.0", NULL, SU_FLAG_OK, delta_ups_pwr_info }, /* Remaining unmapped variables. * Mostly the first field (string) is to be changed * Check docs/nut-names.txt for the right variable names */ #if 0 /* dupsIdentName.0 = "" */ { "unmapped.dupsIdentName", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsAttachedDevices.0 = "" */ { "unmapped.dupsAttachedDevices", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsRatingBatteryVoltage.0 = INTEGER: 0 */ { "unmapped.dupsRatingBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.12.0", NULL, SU_FLAG_OK, NULL }, /* dupsLowTransferVoltUpBound.0 = INTEGER: 0 Volt */ { "unmapped.dupsLowTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.13.0", NULL, SU_FLAG_OK, NULL }, /* dupsLowTransferVoltLowBound.0 = INTEGER: 0 Volt */ { "unmapped.dupsLowTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.14.0", NULL, SU_FLAG_OK, NULL }, /* dupsHighTransferVoltUpBound.0 = INTEGER: 0 Volt */ { "unmapped.dupsHighTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.15.0", NULL, SU_FLAG_OK, NULL }, /* dupsHighTransferVoltLowBound.0 = INTEGER: 0 Volt */ { "unmapped.dupsHighTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.16.0", NULL, SU_FLAG_OK, NULL }, /* dupsLowBattTime.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsLowBattTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.17.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutletRelays.0 = INTEGER: 2 */ { "unmapped.dupsOutletRelays", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.18.0", NULL, SU_FLAG_OK, NULL }, /* dupsShutdownType.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsShutdownType", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsAutoReboot.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAutoReboot", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsShutdownAction.0 = INTEGER: 0 */ { "unmapped.dupsShutdownAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsRestartAction.0 = INTEGER: 0 */ { "unmapped.dupsRestartAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsSetOutletRelay.0 = INTEGER: 1 */ { "unmapped.dupsSetOutletRelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsRelayOffDelay.0 = INTEGER: 0 */ { "unmapped.dupsRelayOffDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsRelayOnDelay.0 = INTEGER: 0 */ { "unmapped.dupsRelayOnDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigBuzzerAlarm.0 = INTEGER: alarm(1) */ { "unmapped.dupsConfigBuzzerAlarm", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigBuzzerState.0 = INTEGER: disable(2) */ { "unmapped.dupsConfigBuzzerState", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigSensitivity.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsConfigSensitivity", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigLowVoltageTransferPoint.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsConfigLowVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigHighVoltageTransferPoint.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsConfigHighVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigShutdownOSDelay.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsConfigShutdownOSDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigUPSBootDelay.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsConfigUPSBootDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsConfigExternalBatteryPack.0 = INTEGER: 0 */ { "unmapped.dupsConfigExternalBatteryPack", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputNumLines.0 = INTEGER: 1 */ { "unmapped.dupsInputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputCurrent1.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputFrequency2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputFrequency2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputVoltage2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputCurrent2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputFrequency3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputFrequency3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputVoltage3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsInputCurrent3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsInputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputFrequency.0 = INTEGER: 499 0.1 Hertz */ { "unmapped.dupsOutputFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputNumLines.0 = INTEGER: 1 */ { "unmapped.dupsOutputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputPower1.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputVoltage2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputCurrent2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputPower2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputLoad2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputLoad2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.11.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputVoltage3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.12.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputCurrent3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.13.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputPower3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.14.0", NULL, SU_FLAG_OK, NULL }, /* dupsOutputLoad3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsOutputLoad3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.15.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassFrequency.0 = INTEGER: 499 0.1 Hertz */ { "unmapped.dupsBypassFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassNumLines.0 = INTEGER: 1 */ { "unmapped.dupsBypassNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassVoltage1.0 = INTEGER: 2280 */ { "unmapped.dupsBypassVoltage1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassCurrent1.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassPower1.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassVoltage2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassCurrent2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassPower2.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassVoltage3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassCurrent3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypassPower3.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBypassPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.11.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypass.12.0 = NULL */ { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.12.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypass.13.0 = NULL */ { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.13.0", NULL, SU_FLAG_OK, NULL }, /* dupsBypass.14.0 = NULL */ { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.14.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryCondiction.0 = INTEGER: good(0) */ { "unmapped.dupsBatteryCondiction", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryStatus.0 = INTEGER: ok(0) */ { "unmapped.dupsBatteryStatus", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryCharge.0 = INTEGER: charging(1) */ { "unmapped.dupsBatteryCharge", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsSecondsOnBattery.0 = INTEGER: 0 seconds */ { "unmapped.dupsSecondsOnBattery", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryEstimatedTime.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBatteryEstimatedTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryVoltage.0 = INTEGER: 550 0.1 Volt DC */ { "unmapped.dupsBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryCurrent.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsBatteryCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsBatteryCapacity.0 = INTEGER: 100 percent */ { "unmapped.dupsBatteryCapacity", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsTemperature.0 = INTEGER: 32 degrees Centigrade */ { "unmapped.dupsTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsLastReplaceDate.0 = Wrong Type (should be OCTET STRING): NULL */ { "unmapped.dupsLastReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsNextReplaceDate.0 = Wrong Type (should be OCTET STRING): NULL */ { "unmapped.dupsNextReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.11.0", NULL, SU_FLAG_OK, NULL }, /* dupsTestType.0 = INTEGER: abort(0) */ { "unmapped.dupsTestType", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsTestResultsSummary.0 = INTEGER: noTestsInitiated(0) */ { "unmapped.dupsTestResultsSummary", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsTestResultsDetail.0 = Wrong Type (should be OCTET STRING): NULL */ { "unmapped.dupsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.8.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmDisconnect.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmDisconnect", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmPowerFail.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmPowerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmBatteryLow.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmBatteryLow", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmLoadWarning.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmLoadWarning", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmLoadSeverity.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmLoadSeverity", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmLoadOnBypass.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmLoadOnBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmUPSFault.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmUPSFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmBatteryGroundFault.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmBatteryGroundFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmTestInProgress.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmTestInProgress", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmBatteryTestFail.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmBatteryTestFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmFuseFailure.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmFuseFailure", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.11.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOutputOverload.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmOutputOverload", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.12.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOutputOverCurrent.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmOutputOverCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.13.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmInverterAbnormal.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmInverterAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.14.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmRectifierAbnormal.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmRectifierAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.15.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmReserveAbnormal.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmReserveAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.16.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmLoadOnReserve.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsAlarmLoadOnReserve", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.17.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOverTemperature.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmOverTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.18.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOutputBad.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmOutputBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.19.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmBypassBad.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmBypassBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.20.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmUPSOff.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmUPSOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.21.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmChargerFail.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmChargerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.22.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmFanFail.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmFanFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.23.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmEconomicMode.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmEconomicMode", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.24.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOutputOff.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmOutputOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.25.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmSmartShutdown.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmSmartShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.26.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmEmergencyPowerOff.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmEmergencyPowerOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.27.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmUPSShutdown.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmUPSShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.28.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvTemperature.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvHumidity.0 = Wrong Type (should be INTEGER): NULL */ { "unmapped.dupsEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.2.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvSetTemperatureLimit.0 = INTEGER: 40 degrees Centigrade */ { "unmapped.dupsEnvSetTemperatureLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.3.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvSetHumidityLimit.0 = INTEGER: 90 percentage */ { "unmapped.dupsEnvSetHumidityLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.4.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvSetEnvRelay1.0 = INTEGER: normalOpen(0) */ { "unmapped.dupsEnvSetEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.5.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvSetEnvRelay2.0 = INTEGER: normalOpen(0) */ { "unmapped.dupsEnvSetEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.6.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvSetEnvRelay3.0 = INTEGER: normalOpen(0) */ { "unmapped.dupsEnvSetEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.7.0", NULL, SU_FLAG_OK, NULL }, /* dupsEnvSetEnvRelay4.0 = INTEGER: normalOpen(0) */ { "unmapped.dupsEnvSetEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.8.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOverEnvTemperature.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmOverEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.9.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmOverEnvHumidity.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmOverEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.10.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmEnvRelay1.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.11.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmEnvRelay2.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.12.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmEnvRelay3.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.13.0", NULL, SU_FLAG_OK, NULL }, /* dupsAlarmEnvRelay4.0 = INTEGER: off(0) */ { "unmapped.dupsAlarmEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.14.0", NULL, SU_FLAG_OK, NULL }, #endif /* #if 0 */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t delta_ups = { "delta_ups", DELTA_UPS_MIB_VERSION, NULL, NULL, delta_ups_mib, DELTA_UPS_SYSOID }; nut-2.7.4/drivers/xppc-mib.h0000644000175000017500000000175212640473702012633 00000000000000/* xppc-mib.h - subdriver to monitor XPPC SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef XPPC_MIB_H #define XPPC_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t xppc; #endif /* XPPC_MIB_H */ nut-2.7.4/drivers/blazer_ser.c0000644000175000017500000001064412640473702013237 00000000000000/* * blazer_ser.c: support for Megatec/Q1 serial protocol based UPSes * * A document describing the protocol implemented by this driver can be * found online at "http://www.networkupstools.org/protocols/megatec.html". * * Copyright (C) 2008 - Arjen de Korte * * 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 "main.h" #include "serial.h" #include "blazer.h" #define DRIVER_NAME "Megatec/Q1 protocol serial driver" #define DRIVER_VERSION "1.57" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte ", DRV_BETA, { NULL } }; #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ /* * Generic command processing function. Send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on * success. */ int blazer_command(const char *cmd, char *buf, size_t buflen) { #ifndef TESTING int ret; ser_flush_io(upsfd); ret = ser_send(upsfd, "%s", cmd); if (ret <= 0) { upsdebugx(3, "send: %s", ret ? strerror(errno) : "timeout"); return ret; } upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd); ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0); if (ret <= 0) { upsdebugx(3, "read: %s", ret ? strerror(errno) : "timeout"); return ret; } upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf); return ret; #else const struct { const char *cmd; const char *answer; } testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 2.27 30.0 00101000\r" }, { "F\r", "#230.0 000 024.0 50.0\r" }, { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r" }, { NULL } }; int i; memset(buf, 0, buflen); for (i = 0; cmd && testing[i].cmd; i++) { if (strcasecmp(cmd, testing[i].cmd)) { continue; } return snprintf(buf, buflen, "%s", testing[i].answer); } return snprintf(buf, buflen, "%s", testing[i].cmd); #endif } void upsdrv_help(void) { printf("Read The Fine Manual ('man 8 blazer_ser')\n"); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "cablepower", "Set cable power for serial interface"); blazer_makevartable(); } void upsdrv_initups(void) { #ifndef TESTING const struct { const char *val; const int dtr; const int rts; } cablepower[] = { { "normal", 1, 0 }, /* default */ { "reverse", 0, 1 }, { "both", 1, 1 }, { "none", 0, 0 }, { NULL } }; int i; const char *val; struct termios tio; /* * Open and lock the serial port and set the speed to 2400 baud. */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* * Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = '\r'; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } val = getval("cablepower"); for (i = 0; val && cablepower[i].val; i++) { if (!strcasecmp(val, cablepower[i].val)) { break; } } if (!cablepower[i].val) { fatalx(EXIT_FAILURE, "Value '%s' not valid for 'cablepower'", val); } ser_set_dtr(upsfd, cablepower[i].dtr); ser_set_rts(upsfd, cablepower[i].rts); /* * Allow some time to settle for the cablepower */ usleep(100000); #endif blazer_initups(); } void upsdrv_initinfo(void) { blazer_initinfo(); } void upsdrv_cleanup(void) { #ifndef TESTING ser_set_dtr(upsfd, 0); ser_close(upsfd, device_path); #endif } nut-2.7.4/drivers/huawei-mib.c0000644000175000017500000002303412667537407013147 00000000000000/* huawei-mib.c - subdriver to monitor Huawei SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2015 Stuart Henderson * * 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 "huawei-mib.h" #define HUAWEI_MIB_VERSION "0.1" #define HUAWEI_SYSOID ".1.3.6.1.4.1.8072.3.2.10" #define HUAWEI_UPSMIB ".1.3.6.1.4.1.2011" /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { * { 1, "OB" }, * { 2, "OL" }, * { 0, NULL } * }; */ static info_lkp_t supplymethod_info[] = { { 1, "" }, /* no supply */ { 2, "OL BYPASS" }, { 3, "OL" }, { 4, "OB" }, { 5, "" }, /* combined */ { 6, "OL ECO" }, { 7, "OB ECO" }, { 0, NULL } }; static info_lkp_t battstate_info[] = { { 1, "" }, /* not connected */ { 2, "" }, /* not charging or discharging */ { 3, "" }, /* hibernation */ { 4, "" }, /* float */ { 5, "CHRG" }, /* equalized charging */ { 6, "DISCHRG" }, { 0, NULL } }; static info_lkp_t phase_info[] = { { 1, "1" }, { 2, "3" }, { 0, NULL } }; static info_lkp_t voltrating_info[] = { { 1, "200" }, { 2, "208" }, { 3, "220" }, { 4, "380" }, { 5, "400" }, { 6, "415" }, { 7, "480" }, { 8, "600" }, { 9, "690" }, { 0, NULL } }; static info_lkp_t freqrating_info[] = { { 1, "50" }, { 2, "60" }, { 0, NULL } }; static info_lkp_t pwrrating_info[] = { { 1, "80000" }, { 2, "100000" }, { 3, "120000" }, { 4, "160000" }, { 5, "200000" }, { 6, "30000" }, { 7, "40000" }, { 8, "60000" }, { 9, "2400000" }, { 10, "2500000" }, { 11, "2800000" }, { 12, "3000000" }, { 0, NULL } }; static info_lkp_t ietf_test_result_info[] = { { 1, "done and passed" }, { 2, "done and warning" }, { 3, "done and error" }, { 4, "aborted" }, { 5, "in progress" }, { 6, "no test initiated" }, { 0, NULL } }; /* HUAWEI Snmp2NUT lookup table */ static snmp_info_t huawei_mib[] = { /* Data format: * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo * info_len: length of strings if STR * cmd value if CMD, multiplier otherwise * OID: SNMP OID or NULL * dfl: default value * flags: snmp-ups internal flags (FIXME: ...) * oid2info: lookup table between OID and NUT values * setvar: variable to set for SU_FLAG_SETINT * * Example: * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, onbatt_info }, * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { * { 1, "OB" }, * { 2, "OL" }, * { 0, NULL } * }; */ /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Huawei", SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.2.1", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.1.1.2.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.time", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.11.1.0", NULL, SU_FLAG_OK, NULL }, /* seconds since epoch */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.3.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.5.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.1.1", NULL, SU_FLAG_OK, supplymethod_info }, { "ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.3.1", NULL, SU_STATUS_BATT | SU_FLAG_OK, battstate_info }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, ietf_test_result_info }, /* Input page */ /* hwUpsCtrlInputStandard listed in MIB but not present on tested UPS5000-E */ { "input.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.8", "3", SU_FLAG_ABSENT | SU_FLAG_OK, phase_info }, { "input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.1.1", NULL, SU_FLAG_OK, NULL }, { "input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.2.1", NULL, SU_FLAG_OK, NULL }, { "input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.3.1", NULL, SU_FLAG_OK, NULL }, { "input.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.4.1", NULL, SU_FLAG_OK, NULL }, { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.5.1", NULL, SU_FLAG_OK, NULL }, { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.6.1", NULL, SU_FLAG_OK, NULL }, { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.7.1", NULL, SU_FLAG_OK, NULL }, { "input.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.8.1", NULL, SU_FLAG_OK, NULL }, { "input.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.9.1", NULL, SU_FLAG_OK, NULL }, { "input.L3.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.10.1", NULL, SU_FLAG_OK, NULL }, { "input.bypass.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.1.1", NULL, SU_FLAG_OK, NULL }, { "input.bypass.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.2.1", NULL, SU_FLAG_OK, NULL }, { "input.bypass.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.3.1", NULL, SU_FLAG_OK, NULL }, { "input.bypass.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.4.1", NULL, SU_FLAG_OK, NULL }, /* Output page */ /* hwUpsCtrlOutputStandard listed in MIB but not present on tested UPS5000-E */ { "output.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.9", "3", SU_FLAG_ABSENT | SU_FLAG_OK, phase_info }, { "output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.1.1", NULL, SU_FLAG_OK, NULL }, { "output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.2.1", NULL, SU_FLAG_OK, NULL }, { "output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.3.1", NULL, SU_FLAG_OK, NULL }, { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.4.1", NULL, SU_FLAG_OK, NULL }, { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.5.1", NULL, SU_FLAG_OK, NULL }, { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.6.1", NULL, SU_FLAG_OK, NULL }, { "output.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.7.1", NULL, SU_FLAG_OK, NULL }, { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.8.1", NULL, SU_FLAG_OK, NULL }, { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.9.1", NULL, SU_FLAG_OK, NULL }, { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.10.1", NULL, SU_FLAG_OK, NULL }, { "output.L1.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.11.1", NULL, SU_FLAG_OK, NULL }, { "output.L2.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.12.1", NULL, SU_FLAG_OK, NULL }, { "output.L3.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.13.1", NULL, SU_FLAG_OK, NULL }, { "output.L1.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.14.1", NULL, SU_FLAG_OK, NULL }, { "output.L2.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.15.1", NULL, SU_FLAG_OK, NULL }, { "output.L3.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.16.1", NULL, SU_FLAG_OK, NULL }, { "output.voltage.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.17.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, voltrating_info }, { "output.frequency.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.18.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, freqrating_info }, { "output.power.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.6.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, pwrrating_info }, { "output.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.19.1", NULL, SU_FLAG_OK, NULL }, { "output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.20.1", NULL, SU_FLAG_OK, NULL }, { "output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.21.1", NULL, SU_FLAG_OK, NULL }, /* Battery page */ { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.1.1", NULL, SU_FLAG_OK, NULL }, { "battery.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.2.1", NULL, SU_FLAG_OK, NULL }, { "battery.charge", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.3.1", NULL, SU_FLAG_OK, NULL }, { "battery.runtime", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.4.1", NULL, SU_FLAG_OK, NULL }, /* { "unmapped.hwUpsBattTest", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.103.101.1.6.1", NULL, SU_FLAG_OK, NULL }, */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t huawei = { "huawei", HUAWEI_MIB_VERSION, NULL, NULL, huawei_mib, HUAWEI_SYSOID }; nut-2.7.4/drivers/nutdrv_qx_bestups.h0000644000175000017500000000176312640473702014715 00000000000000/* nutdrv_qx_bestups.h - Subdriver for Best Power/Sola Australia UPSes * * Copyright (C) * 2014 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_BESTUPS_H #define NUTDRV_QX_BESTUPS_H #include "nutdrv_qx.h" extern subdriver_t bestups_subdriver; #endif /* NUTDRV_QX_BESTUPS_H */ nut-2.7.4/drivers/rhino.c0000644000175000017500000004637512640443572012242 00000000000000/* rhino.c - driver for Microsol Rhino UPS hardware Copyright (C) 2004 Silvino B. Magalhães 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 2004/11/13 - Version 0.10 - Initial release 2005/07/07 - Version 0.20 - Initial rhino commands tests 2005/10/25 - Version 0.30 - Operational-1 release 2005/10/26 - Version 0.40 - Operational-2 release 2005/11/29 - Version 0.50 - rhino commands release http://www.microsol.com.br */ #include #include #include "main.h" #include "serial.h" #include "timehead.h" #define DRIVER_NAME "Microsol Rhino UPS driver" #define DRIVER_VERSION "0.51" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Silvino B. Magalhaes ", DRV_STABLE, { NULL } }; #define UPSDELAY 500 /* 0.5 ms delay */ typedef int bool_t; #define false 0 #define true 1 /* rhino commands */ #define CMD_INON 0x0001 #define CMD_INOFF 0x0002 #define CMD_SHUT 0x0004 #define CMD_OUTON 0x0003 #define CMD_OUTOFF 0x0004 #define CMD_PASSON 0x0005 #define CMD_PASSOFF 0x0006 #define CMD_UPSCONT 0x0053 /* xoff - xon protocol */ #define _SOH = 0x01; /* start of header */ #define _EOT = 0x04; /* end of transmission */ #define _ACK = 0x06; /* acknoledge (positive) */ #define _DLE = 0x10; /* data link escape */ #define _XOn = 0x11; /* transmit on */ #define _XOff = 0x13; /* transmit off */ #define _NAK = 0x15; /* negative acknoledge */ #define _SYN = 0x16; /* synchronous idle */ #define _CAN = 0x18; /* cancel */ static int const pacsize = 37; /* size of receive data package */ /* autonomy calcule */ static double const AmpH = 40; /* Amperes-hora da bateria */ static double const VbatMin = 126; /* Tensão mínina das baterias */ static double const VbatNom = 144; /* Tensão nominal das baterias */ static double const FM = 0.32; /* Fator multiplicativo de correção da autonomia */ static double const FA = -2; /* Fator aditivo de correção da autonomia */ static double const ConstInt = 250; /* Consumo interno sem o carregador */ static double const Vin = 220; /* Tensão de entrada */ static int Day, Month, Year; static int dian=0, mesn=0, anon=0, weekn=0; static int ihour,imin, isec; /* unsigned char DaysOnWeek; */ /* char seman[4]; */ /* int FExpansaoBateria; */ /* internal variables */ /* package handshake variables */ /* int ContadorEstouro; */ static bool_t detected; static bool_t SourceFail, Out110, RedeAnterior, OcorrenciaDeFalha; static bool_t RetornoDaRede, SuperAquecimento, SuperAquecimentoAnterior; static bool_t OverCharge, OldOverCharge, CriticBatt, OldCritBatt; static bool_t Flag_inversor, BypassOn, InputOn, OutputOn; static bool_t LowBatt, oldInversorOn; /* data vetor from received and configuration data package - not used yet unsigned char Dados[ 161 ]; */ /* identification group */ static int RhinoModel; /*, imodel; */ static int PotenciaNominal, PowerFactor; /* input group */ static double AppPowerIn, UtilPowerIn, InFreq, InCurrent; static double LimInfEntrada, LimSupEntrada, ValorNominalEntrada; static int FatorPotEntrada; /* output group */ static double OutVoltage, InVoltage, OutCurrent, AppPowerOut; static double UtilPowerOut, OutFreq, LimInfSaida, LimSupSaida, ValorNominalSaida; static int FatorPotSaida; /* battery group */ static int Autonomy, Waiting; static double BattVoltage, Temperature, LimInfBattSrc, LimSupBattSrc; static double LimInfBattInv, LimSupBattInv, BattNonValue; /* general group */ static int BoostVolt, Rendimento; /* status group */ static unsigned char StatusEntrada, StatusSaida, StatusBateria; /* events group */ static unsigned char EventosRede, EventosSaida, EventosBateria; /* Grupo de Programação */ /* Methods */ static void ScanReceivePack(void); static int AutonomyCalc( int ); static void CommReceive(const unsigned char*, int ); static void getbaseinfo(void); static void getupdateinfo(void); static unsigned char RecPack[37]; /* comment on english language */ /* #define PORTUGUESE */ /* The following Portuguese strings are in UTF-8. */ #ifdef PORTUGUESE #define M_UNKN "Modêlo rhino desconhecido\n" #define NO_RHINO "Rhino não detectado! abortando ...\n" #define UPS_DATE "Data no UPS %4d/%02d/%02d\n" #define SYS_DATE "Data do Sistema %4d/%02d/%02d dia da semana %s\n" #define ERR_PACK "Pacote errado\n" #define NO_EVENT "Não há eventos\n" #define UPS_TIME "Hora interna UPS %0d:%02d:%02d\n" #else #define M_UNKN "Unknown rhino model\n" #define NO_RHINO "Rhino not detected! aborting ...\n" #define UPS_DATE "UPS Date %4d/%02d/%02d\n" #define SYS_DATE "System Date %4d/%02d/%02d day of week %s\n" #define ERR_PACK "Wrong package\n" #define NO_EVENT "No events\n" #define UPS_TIME "UPS internal Time %0d:%02d:%02d\n" #endif static int AutonomyCalc( int ia ) /* all models */ { int result = 0; double auton, calc, currin; if( ia ) { if( ( BattVoltage == 0 ) ) result = 0; else { calc = ( OutVoltage * OutCurrent )* 1.0 / ( 0.08 * BattVoltage ); auton = pow( calc, 1.18 ); if( ( auton == 0 ) ) result = 0; else { auton = 1.0 / auton; auton = auton * 11.07; calc = ( BattVoltage * 1.0 / 10 ) - 168; result = (int) ( auton * calc * 2.5 ); } } } else { currin = ( UtilPowerOut + ConstInt ) *1.0 / Vin; auton = ( ( ( AmpH *1.0 / currin ) * 60 * ( ( BattVoltage - VbatMin ) * 1.0 /( VbatNom - VbatMin ) ) * FM ) + FA ); if( ( BattVoltage > 129 ) || ( BattVoltage < 144 ) ) result = 133; else result = (int) auton; } return result; } /* Treat received package */ static void ScanReceivePack( void ) { /* model independent data */ Year = RecPack[31] + ( RecPack[32] * 100 ); Month = RecPack[30]; Day = RecPack[29]; /* UPS internal time */ ihour = RecPack[26]; imin = RecPack[27]; isec = RecPack[28]; /* Flag1 */ /* SobreTemp = ( ( 0x01 & RecPack[33]) = 0x01 ); */ /* OutputOn = ( ( 0x02 & RecPack[33]) = 0x02 ); OutputOn */ /* InputOn = ( ( 0x04 & RecPack[33]) = 0x04 ); InputOn */ /* ByPassOn = ( ( 0x08 & RecPack[33]) = 0x08 ); BypassOn */ /* Auto_HAB = ( ( 0x10 & RecPack[33]) = 0x10 ); */ /* Timer_HAB = ( ( 0x20 & RecPack[33]) = 0x20 ); */ /* Boost_Ligado = ( ( 0x40 & RecPack[33]) = 0x40 ); */ /* Bateria_Desc = ( ( 0x80 & RecPack[33]) = 0x80 ); */ /* Flag2 */ /* Quad_Ant_Ent = ( ( 0x01 & RecPack[34]) = 0x01 ); */ /* Quadratura = ( ( 0x02 & RecPack[34]) = 0x02 ); */ /* Termino_XMODEM = ( ( 0x04 & RecPack[34]) = 0x04 ); */ /* Em_Sincronismo = ( ( 0x08 & RecPack[34]) = 0x08 ); */ /* Out110 = ( ( 0x10 & RecPack[34]) = 0x10 ); Out110 */ /* Exec_Beep = ( ( 0x20 & RecPack[34]) = 0x20 ); */ /* LowBatt = ( ( 0x40 & RecPack[34]) = 0x40 ); LowBatt */ /* Boost_Sobre = ( ( 0x80 & RecPack[34]) = 0x80 ); */ /* Flag3 */ /* OverCharge = ( ( 0x01 & RecPack[35]) = 0x01 ); OverCharge */ /* SourceFail = ( ( 0x02 & RecPack[35]) = 0x02 ); SourceFail */ /* RedeAnterior = ( ( 0x04 & RecPack[35]) = 0x04 ); */ /* Cmd_Executado = ( ( 0x08 & RecPack[35]) = 0x08 ); */ /* Exec_Autoteste = ( ( 0x10 & RecPack[35]) = 0x10 ); */ /* Quad_Ant_Sai = ( ( 0x20 & RecPack[35]) = 0x20 ); */ /* ComandoSerial = ( ( 0x40 & RecPack[35]) = 0x40 ); */ /* SobreTensao = ( ( 0x80 & RecPack[35]) = 0x80 ); */ OutputOn = ( ( 0x02 & RecPack[33] ) == 0x02 ); InputOn = ( ( 0x04 & RecPack[33] ) == 0x04 ); BypassOn = ( ( 0x08 & RecPack[33] ) == 0x08 ); Out110 = ( ( 0x10 & RecPack[34] ) == 0x10 ); LowBatt = ( ( 0x40 & RecPack[34] ) == 0x40 ); OverCharge = ( ( 0x01 & RecPack[35] ) == 0x01 ); SourceFail = ( ( 0x02 & RecPack[35] ) == 0x02 ); /* model dependent data read */ PowerFactor = 800; if( RecPack[0] ==0xC2 ) { LimInfBattSrc = 174; LimSupBattSrc = 192;/* 180????? carregador eh 180 (SCOPOS) */ LimInfBattInv = 174; LimSupBattInv = 192;/* 170????? (SCOPOS) */ } else { LimInfBattSrc = 138; LimSupBattSrc = 162;/* 180????? carregador eh 180 (SCOPOS) */ LimInfBattInv = 126; LimSupBattInv = 156;/* 170????? (SCOPOS) */ } BattNonValue = 144; /* VersaoInterna = "R10" + IntToStr( RecPack[1] ); */ InVoltage = RecPack[2]; InCurrent = RecPack[3]; UtilPowerIn = RecPack[4] + RecPack[5] * 256; AppPowerIn = RecPack[6] + RecPack[7] * 256; FatorPotEntrada = RecPack[8]; InFreq = ( RecPack[9] + RecPack[10] * 256 ) * 1.0 / 10; OutVoltage = RecPack[11]; OutCurrent = RecPack[12]; UtilPowerOut = RecPack[13] + RecPack[14] * 256; AppPowerOut = RecPack[15] + RecPack[16] * 256; FatorPotSaida = RecPack[17]; OutFreq = ( RecPack[18] + RecPack[19] * 256 ) * 1.0 / 10; BattVoltage = RecPack[20]; BoostVolt = RecPack[21] + RecPack[22] * 256; Temperature = ( 0x7F & RecPack[23] ); Rendimento = RecPack[24]; /* model independent data */ if( ( BattVoltage < LimInfBattInv ) ) CriticBatt = true; if( BypassOn ) OutVoltage = ( InVoltage * 1.0 / 2 ) + 5; if( SourceFail && RedeAnterior ) /* falha pela primeira vez */ OcorrenciaDeFalha = true; if( !( SourceFail ) && !( RedeAnterior ) ) /* retorno da rede */ RetornoDaRede = true; if( !( SourceFail ) == RedeAnterior ) { RetornoDaRede = false; OcorrenciaDeFalha = false; } RedeAnterior = !( SourceFail ); LimInfSaida = 75; LimSupSaida = 150; ValorNominalSaida = 110; LimInfEntrada = 190; LimSupEntrada = 250; ValorNominalEntrada = 220; if( SourceFail ) { StatusEntrada = 2; RecPack[8] = 200; /* ?????????????????????????????????? */ } else { StatusEntrada = 1; RecPack[8] = 99; /* ??????????????????????????????????? */ } if( OutputOn ) /* Output Status */ StatusSaida = 2; else StatusSaida = 1; if( OverCharge ) StatusSaida = 3; if( CriticBatt ) /* Battery Status */ StatusBateria = 4; else StatusBateria = 1; EventosRede = 0; if( OcorrenciaDeFalha ) EventosRede = 1; if( RetornoDaRede ) EventosRede = 2; /* verify InversorOn */ if( Flag_inversor ) { oldInversorOn = InputOn; Flag_inversor = false; } EventosSaida = 0; if( InputOn && !( oldInversorOn ) ) EventosSaida = 26; if( oldInversorOn && !( InputOn ) ) EventosSaida = 27; oldInversorOn = InputOn; if( SuperAquecimento && !( SuperAquecimentoAnterior ) ) EventosSaida = 12; if( SuperAquecimentoAnterior && !( SuperAquecimento ) ) EventosSaida = 13; SuperAquecimentoAnterior = SuperAquecimento; EventosBateria = 0; OldCritBatt = CriticBatt; if( OverCharge && !( OldOverCharge ) ) EventosSaida = 10; if( OldOverCharge && !( OverCharge ) ) EventosSaida = 11; OldOverCharge = OverCharge; /* autonomy calc. */ if( RecPack[ 0 ] ==0xC2 ) Autonomy = AutonomyCalc( 1 ); else Autonomy = AutonomyCalc( 0 ); } static void CommReceive(const unsigned char *bufptr, int size) { int i, i_end, CheckSum, chk; if( ( size==37 ) ) Waiting = 0; printf("CommReceive size = %d waiting = %d\n", size, Waiting ); switch( Waiting ) { /* normal package */ case 0: { if( size == 37 ) { i_end = 37; for( i = 0 ; i < i_end ; ++i ) { RecPack[i] = *bufptr; bufptr++; } /* CheckSum verify */ CheckSum = 0; i_end = 36; for( i = 0 ; i < i_end ; ++i ) { chk = RecPack[ i ]; CheckSum = CheckSum + chk; } CheckSum = CheckSum % 256; ser_flush_in(upsfd,"",0); /* clean port */ /* correct package */ if( ( RecPack[0] == 0xC0 || RecPack[0] == 0xC1 || RecPack[0] == 0xC2 || RecPack[0] == 0xC3 ) && ( RecPack[ 36 ] == CheckSum ) ) { if(!(detected)) { RhinoModel = RecPack[0]; detected = true; } switch( RhinoModel ) { case 0xC0: case 0xC1: case 0xC2: case 0xC3: { ScanReceivePack(); break; } default: { printf( M_UNKN ); break; } } } } break; } case 1: { /* dumping package, nothing to do yet */ Waiting = 0; break; } } Waiting =0; } static int send_command( int cmd ) { int i, chk, checksum = 0, iend = 18, sizes = 19, ret, kount; /*, j, uc; */ unsigned char ch, psend[sizes]; /* mounting buffer to send */ for(i = 0; i < iend; i++ ) { if ( i == 0 ) chk = 0x01; else { if( i == 1) chk = cmd; else chk = 0x00; /* 0x20; */ } ch = chk; psend[i] = ch; /* psend[0 - 17] */ if( i > 0 ) /* psend[0] not computed */ checksum = checksum + chk; } ch = checksum; ch = (~( ch) ); /* not ch */ psend[iend] = ch; /* send five times the command */ kount = 0; while ( kount < 5 ) { /* ret = ser_send_buf_pace(upsfd, UPSDELAY, psend, sizes ); */ /* optional delay */ for(i = 0 ; i < sizes ; i++) { ret = ser_send_char( upsfd, psend[i] ); /* usleep ( UPSDELAY ); sending without delay */ } usleep( UPSDELAY ); /* delay between sent command */ kount++; } return ret; } static void sendshut( void ) { int i; for(i=0; i < 30000; i++) usleep( UPSDELAY ); /* 15 seconds delay */ send_command( CMD_SHUT ); upslogx(LOG_NOTICE, "Ups shutdown command sent"); printf("Ups shutdown command sent\n"); } static void getbaseinfo(void) { unsigned char temp[256]; unsigned char Pacote[37]; int tam, i, j=0; time_t tmt; struct tm *now; const char *Model; time( &tmt ); now = localtime( &tmt ); dian = now->tm_mday; mesn = now->tm_mon+1; anon = now->tm_year+1900; weekn = now->tm_wday; /* trying detect rhino model */ while ( ( !detected ) && ( j < 10 ) ) { temp[0] = 0; /* flush temp buffer */ tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); if( tam == 37 ) { for( i = 0 ; i < tam ; i++ ) { Pacote[i] = temp[i]; } } j++; if( tam == 37) CommReceive(Pacote, tam); else CommReceive(temp, tam); } if( (!detected) ) { fatalx(EXIT_FAILURE, NO_RHINO ); } switch( RhinoModel ) { case 0xC0: { Model = "Rhino 20.0 kVA"; PotenciaNominal = 20000; break; } case 0xC1: { Model = "Rhino 10.0 kVA"; PotenciaNominal = 10000; break; } case 0xC2: { Model = "Rhino 6.0 kVA"; PotenciaNominal = 6000; break; } case 0xC3: { Model = "Rhino 7.5 kVA"; PotenciaNominal = 7500; break; } default: { Model = "Rhino unknown model"; PotenciaNominal = 0; break; } } /* manufacturer and model */ dstate_setinfo("ups.mfr", "%s", "Microsol"); dstate_setinfo("ups.model", "%s", Model); /* dstate_setinfo("input.transfer.low", "%03.1f", InDownLim); LimInfBattInv ? dstate_setinfo("input.transfer.high", "%03.1f", InUpLim); LimSupBattInv ? */ dstate_addcmd("shutdown.stayoff"); /* CMD_SHUT */ /* there is no reserved words for CMD_INON and CMD_INOFF yet */ /* dstate_addcmd("input.on"); */ /* CMD_INON = 1 */ /* dstate_addcmd("input.off"); */ /* CMD_INOFF = 2 */ dstate_addcmd("load.on"); /* CMD_OUTON = 3 */ dstate_addcmd("load.off"); /* CMD_OUTOFF = 4 */ dstate_addcmd("bypass.start"); /* CMD_PASSON = 5 */ dstate_addcmd("bypass.stop"); /* CMD_PASSOFF = 6 */ printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path); } static void getupdateinfo(void) { unsigned char temp[256]; int tam; temp[0] = 0; /* flush temp buffer */ tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); CommReceive(temp, tam); } static int instcmd(const char *cmdname, const char *extra) { int ret = 0; if (!strcasecmp(cmdname, "shutdown.stayoff")) { /* shutdown now (one way) */ /* send_command( CMD_SHUT ); */ sendshut(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { /* liga Saida */ ret = send_command( 3 ); if ( ret < 1 ) upslogx(LOG_ERR, "send_command 3 failed"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.off")) { /* desliga Saida */ ret = send_command( 4 ); if ( ret < 1 ) upslogx(LOG_ERR, "send_command 4 failed"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "bypass.start")) { /* liga Bypass */ ret = send_command( 5 ); if ( ret < 1 ) upslogx(LOG_ERR, "send_command 5 failed"); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "bypass.stop")) { /* desliga Bypass */ ret = send_command( 6 ); if ( ret < 1 ) upslogx(LOG_ERR, "send_command 6 failed"); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_initinfo(void) { getbaseinfo(); upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) { getupdateinfo(); /* new package for updates */ dstate_setinfo("output.voltage", "%03.1f", OutVoltage); dstate_setinfo("input.voltage", "%03.1f", InVoltage); dstate_setinfo("battery.voltage", "%02.1f", BattVoltage); /* output and bypass tests */ if( OutputOn ) dstate_setinfo("outlet.switchable", "%s", "yes"); else dstate_setinfo("outlet.switchable", "%s", "no"); if( BypassOn ) dstate_setinfo("outlet.1.switchable", "%s", "yes"); else dstate_setinfo("outlet.1.switchable", "%s", "no"); status_init(); if (!SourceFail ) status_set("OL"); /* on line */ else status_set("OB"); /* on battery */ if (Autonomy < 5 ) status_set("LB"); /* low battery */ status_commit(); dstate_setinfo("ups.temperature", "%2.2f", Temperature); dstate_setinfo("input.frequency", "%2.1f", InFreq); dstate_dataok(); } /* power down the attached load immediately */ void upsdrv_shutdown(void) { /* basic idea: find out line status and send appropriate command */ /* on line: send normal shutdown, ups will return by itself on utility */ /* on battery: send shutdown+return, ups will cycle and return soon */ if (!SourceFail) /* on line */ { printf("On line, forcing shutdown command...\n"); send_command( CMD_SHUT ); } else { printf("On battery, sending normal shutdown command...\n"); send_command( CMD_SHUT ); } } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "battext", "Battery Extension (0-80)min"); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B19200); /* dtr and rts setting */ ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/powercom-hid.h0000644000175000017500000000217612640443572013514 00000000000000/* powercom-hid.h - subdriver to monitor PowerCOM USB/HID devices with NUT * * Copyright (C) * 2003 - 2009 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef POWERCOM_HID_H #define POWERCOM_HID_H #include "usbhid-ups.h" extern subdriver_t powercom_subdriver; #endif /* POWERCOM_HID_H */ nut-2.7.4/drivers/powerp-bin.h0000644000175000017500000000210712640443572013173 00000000000000/* * powerp-bin.h - Model specific data/definitions for CyberPower text/binary * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2008 Arjen de Korte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef POWERP_BIN_H #define POWERP_BIN_H #include "powerpanel.h" extern subdriver_t powpan_binary; #endif /* POWERP_BIN_H */ nut-2.7.4/drivers/netvision-mib.c0000644000175000017500000002340012667537407013700 00000000000000/* netvision-mib.c - data to monitor Socomec Sicon UPS equipped * with Netvision WEB/SNMP card/external box with NUT * * Copyright (C) * 2004 Thanos Chatziathanassiou * 2012 Manuel Bouyer * 2015 Arnaud Quette * * 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 "netvision-mib.h" #define NETVISION_MIB_VERSION "0.4" #define NETVISION_SYSOID ".1.3.6.1.4.1.4555.1.1.1" /* SNMP OIDs set */ #define NETVISION_OID_UPS_MIB ".1.3.6.1.4.1.4555.1.1.1.1" #define NETVISION_OID_UPSIDENTMODEL ".1.3.6.1.4.1.4555.1.1.1.1.1.1.0" #define NETVISION_OID_UPSIDENTFWVERSION ".1.3.6.1.4.1.4555.1.1.1.1.1.2.0" #define NETVISION_OID_UPSIDENTAGENTSWVERSION ".1.3.6.1.4.1.4555.1.1.1.1.1.3.0" #define NETVISION_OID_UPSIDENTUPSSERIALNUMBER ".1.3.6.1.4.1.4555.1.1.1.1.1.4.0" /* UPS Battery */ #define NETVISION_OID_BATTERYSTATUS ".1.3.6.1.4.1.4555.1.1.1.1.2.1.0" static info_lkp_t netvision_batt_info[] = { { 2, "" }, /* battery normal */ { 3, "LB" }, /* battery low */ { 4, "LB" }, /* battery depleted */ { 5, "DISCHRG" }, /* battery discharging */ { 6, "RB" }, /* battery failure */ { 0, NULL } }; /* Battery status: upsAlarmOnBattery */ static info_lkp_t netvision_onbatt_info[] = { { 0, "OL" }, /* Online */ { 1, "OB" }, /* On battery */ { 0, NULL } }; #define NETVISION_OID_SECONDSONBATTERY ".1.3.6.1.4.1.4555.1.1.1.1.2.2.0" #define NETVISION_OID_BATT_RUNTIME_REMAINING ".1.3.6.1.4.1.4555.1.1.1.1.2.3.0" #define NETVISION_OID_BATT_CHARGE ".1.3.6.1.4.1.4555.1.1.1.1.2.4.0" #define NETVISION_OID_BATT_VOLTS ".1.3.6.1.4.1.4555.1.1.1.1.2.5.0" #define NETVISION_OID_INPUT_NUM_LINES ".1.3.6.1.4.1.4555.1.1.1.1.3.1.0" /* 1phase or 3phase UPS input */ #define NETVISION_OID_INPUT_FREQ ".1.3.6.1.4.1.4555.1.1.1.1.3.2.0" #define NETVISION_OID_OUTPUT_NUM_LINES ".1.3.6.1.4.1.4555.1.1.1.1.4.3.0" /* 1phase or 3phase UPS output */ #define NETVISION_OID_OUTPUT_FREQ ".1.3.6.1.4.1.4555.1.1.1.1.4.2.0" #define NETVISION_OID_BYPASS_FREQ ".1.3.6.1.4.1.4555.1.1.1.1.5.1.0" #define NETVISION_OID_BYPASS_NUM_LINES ".1.3.6.1.4.1.4555.1.1.1.1.5.2.0" /* 1phase or 3phase UPS input */ /* three phase ups provide input/output/load for each phase in case of one-phase output, only _P1 should be used */ #define NETVISION_OID_OUT_VOLTAGE_P1 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.2.1" #define NETVISION_OID_OUT_CURRENT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.3.1" #define NETVISION_OID_OUT_LOAD_PCT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.1" #define NETVISION_OID_IN_VOLTAGE_P1 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.5.1" #define NETVISION_OID_IN_CURRENT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.3.1" #define NETVISION_OID_BY_VOLTAGE_P1 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.2.1" #define NETVISION_OID_BY_CURRENT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.3.1" #define NETVISION_OID_OUT_VOLTAGE_P2 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.2.2" #define NETVISION_OID_OUT_CURRENT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.3.2" #define NETVISION_OID_OUT_LOAD_PCT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.2" #define NETVISION_OID_IN_VOLTAGE_P2 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.5.2" #define NETVISION_OID_IN_CURRENT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.3.2" #define NETVISION_OID_BY_VOLTAGE_P2 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.2.2" #define NETVISION_OID_BY_CURRENT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.3.2" #define NETVISION_OID_OUT_VOLTAGE_P3 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.2.3" #define NETVISION_OID_OUT_CURRENT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.3.3" #define NETVISION_OID_OUT_LOAD_PCT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.3" #define NETVISION_OID_IN_VOLTAGE_P3 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.5.3" #define NETVISION_OID_IN_CURRENT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.3.3" #define NETVISION_OID_BY_VOLTAGE_P3 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.2.3" #define NETVISION_OID_BY_CURRENT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.3.3" #define NETVISION_OID_OUTPUT_SOURCE ".1.3.6.1.4.1.4555.1.1.1.1.4.1.0" #define NETVISION_OID_CONTROL_STATUS ".1.3.6.1.4.1.4555.1.1.1.1.8.1" #define NETVISION_OID_CONTROL_SHUTDOWN_DELAY ".1.3.6.1.4.1.4555.1.1.1.1.8.2" static info_lkp_t netvision_output_info[] = { { 1, "" }, /* output source other */ { 2, "" }, /* output source none */ { 3, "OL" }, /* output source normal */ { 4, "OL BYPASS" }, /* output source bypass */ { 5, "OB" }, /* output source battery */ { 6, "OL BOOST" }, /* output source booster */ { 7, "OL TRIM" }, /* output source reducer */ { 8, "OL" }, /* output source standby */ { 9, "" }, /* output source ecomode */ { 0, NULL } }; /* Snmp2NUT lookup table */ static snmp_info_t netvision_mib[] = { { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTAGENTSWVERSION, "SOCOMEC SICON UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTMODEL, "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTUPSSERIALNUMBER, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTFWVERSION, "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_BATTERYSTATUS, "", SU_FLAG_OK | SU_STATUS_BATT, &netvision_batt_info[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_OUTPUT_SOURCE, "", SU_FLAG_OK | SU_STATUS_PWR, &netvision_output_info[0] }, /* upsAlarmOnBattery */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4555.1.1.1.1.6.3.2.0", "", SU_FLAG_OK | SU_STATUS_PWR, &netvision_onbatt_info[0] }, /* ups load */ { "ups.load", 0, 1, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_INPUT_1, NULL }, /*ups input,output voltage, output frquency phase 1 */ { "input.phases", 0, 1.0, NETVISION_OID_INPUT_NUM_LINES, 0, SU_FLAG_SETINT, NULL, &input_phases }, { "input.frequency", 0, 0.1, NETVISION_OID_INPUT_FREQ, 0, SU_FLAG_OK, NULL }, { "input.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, 0, SU_INPUT_1, NULL }, { "input.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, 0, SU_INPUT_1, NULL }, { "input.L1-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, 0, SU_INPUT_3, NULL }, { "input.L1.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, 0, SU_INPUT_3, NULL }, { "input.L2-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P2, 0, SU_INPUT_3, NULL }, { "input.L2.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P2, 0, SU_INPUT_3, NULL }, { "input.L3-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P3, 0, SU_INPUT_3, NULL }, { "input.L3.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P3, 0, SU_INPUT_3, NULL }, { "output.phases", 0, 1.0, NETVISION_OID_OUTPUT_NUM_LINES, 0, SU_FLAG_SETINT, NULL, &output_phases }, { "output.frequency", 0, 0.1, NETVISION_OID_OUTPUT_FREQ, 0, SU_FLAG_OK, NULL }, { "output.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, 0, SU_OUTPUT_1, NULL }, { "output.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, 0, SU_OUTPUT_1, NULL }, { "output.load", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_OUTPUT_1, NULL }, { "output.L1-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, 0, SU_OUTPUT_3, NULL }, { "output.L1.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, 0, SU_OUTPUT_3, NULL }, { "output.L1.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P2, 0, SU_OUTPUT_3, NULL }, { "output.L2.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P2, 0, SU_OUTPUT_3, NULL }, { "output.L2.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P2, 0, SU_OUTPUT_3, NULL }, { "output.L3-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P3, 0, SU_OUTPUT_3, NULL }, { "output.L3.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P3, 0, SU_OUTPUT_3, NULL }, { "output.L3.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P3, 0, SU_OUTPUT_3, NULL }, { "input.bypass.phases", 0, 1.0, NETVISION_OID_BYPASS_NUM_LINES, 0, SU_FLAG_SETINT, NULL, &bypass_phases }, { "input.bypass.frequency", 0, 0.1, NETVISION_OID_BYPASS_FREQ, 0, SU_FLAG_OK, NULL }, { "input.bypass.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, 0, SU_BYPASS_1, NULL }, { "input.bypass.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, 0, SU_BYPASS_1, NULL }, { "input.bypass.L1-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, 0, SU_BYPASS_3, NULL }, { "input.bypass.L1.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, 0, SU_BYPASS_3, NULL }, { "input.bypass.L2-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P2, 0, SU_BYPASS_3, NULL }, { "input.bypass.L2.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P2, 0, SU_BYPASS_3, NULL }, { "input.bypass.L3-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P3, 0, SU_BYPASS_3, NULL }, { "input.bypass.L3.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P3, 0, SU_BYPASS_3, NULL }, /* battery info */ { "battery.charge", 0, 1, NETVISION_OID_BATT_CHARGE, "", SU_FLAG_OK, NULL }, { "battery.voltage", 0, 0.1, NETVISION_OID_BATT_VOLTS, "", SU_FLAG_OK, NULL }, { "battery.runtime", 0, 60, NETVISION_OID_BATT_RUNTIME_REMAINING, "", SU_FLAG_OK, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t netvision = { "netvision", NETVISION_MIB_VERSION, NULL, NETVISION_OID_UPSIDENTMODEL, netvision_mib, NETVISION_SYSOID }; nut-2.7.4/drivers/usbhid-ups.c0000644000175000017500000012301112640473702013163 00000000000000/* usbhid-ups.c - Driver for USB and serial (MGE SHUT) HID UPS units * * Copyright (C) * 2003-2012 Arnaud Quette * 2005 John Stamp * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte * * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * * 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 * * TODO list: * - set ST_FLAG_RW according to HIDData_t->Attribute (ATTR_DATA_CST-ATTR_NVOL_VOL) */ #define DRIVER_NAME "Generic HID driver" #define DRIVER_VERSION "0.41" #include "main.h" #include "libhid.h" #include "usbhid-ups.h" #include "hidparser.h" #include "hidtypes.h" /* include all known subdrivers */ #include "mge-hid.h" #ifndef SHUT_MODE #include "explore-hid.h" #include "apc-hid.h" #include "belkin-hid.h" #include "cps-hid.h" #include "liebert-hid.h" #include "powercom-hid.h" #include "tripplite-hid.h" #include "idowell-hid.h" #include "openups-hid.h" #endif /* master list of avaiable subdrivers */ static subdriver_t *subdriver_list[] = { #ifndef SHUT_MODE &explore_subdriver, #endif &mge_subdriver, #ifndef SHUT_MODE &apc_subdriver, &belkin_subdriver, &cps_subdriver, &liebert_subdriver, &powercom_subdriver, &tripplite_subdriver, &idowell_subdriver, &openups_subdriver, #endif NULL }; upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette \n" \ "Peter Selinger \n" \ "Arjen de Korte \n" \ "John Stamp ", /*FIXME: link the subdrivers? do the same as for the mibs! */ #ifndef SHUT_MODE DRV_STABLE, #else DRV_EXPERIMENTAL, #endif { &comm_upsdrv_info, NULL } }; /* Data walk modes */ typedef enum { HU_WALKMODE_INIT = 0, HU_WALKMODE_QUICK_UPDATE, HU_WALKMODE_FULL_UPDATE } walkmode_t; /* pointer to the active subdriver object (changed in callback() function) */ static subdriver_t *subdriver = NULL; /* Global vars */ static HIDDevice_t *hd = NULL; static HIDDevice_t curDevice = { 0x0000, 0x0000, NULL, NULL, NULL, NULL }; static HIDDeviceMatcher_t *subdriver_matcher = NULL; #ifndef SHUT_MODE static HIDDeviceMatcher_t *exact_matcher = NULL; static HIDDeviceMatcher_t *regex_matcher = NULL; #endif static int pollfreq = DEFAULT_POLLFREQ; static int ups_status = 0; static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */ #ifndef SUN_LIBUSB bool_t use_interrupt_pipe = TRUE; #else bool_t use_interrupt_pipe = FALSE; #endif static time_t lastpoll; /* Timestamp the last polling */ hid_dev_handle_t udev; /* support functions */ static hid_info_t *find_nut_info(const char *varname); static hid_info_t *find_hid_info(const HIDData_t *hiddata); static const char *hu_find_infoval(info_lkp_t *hid2info, const double value); static long hu_find_valinfo(info_lkp_t *hid2info, const char* value); static void process_boolean_info(const char *nutvalue); static void ups_alarm_set(void); static void ups_status_set(void); static bool_t hid_ups_walk(walkmode_t mode); static int reconnect_ups(void); static int ups_infoval_set(hid_info_t *item, double value); static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen); #ifdef DEBUG static double interval(void); #endif /* global variables */ HIDDesc_t *pDesc = NULL; /* parsed Report Descriptor */ reportbuf_t *reportbuf = NULL; /* buffer for most recent reports */ /* ---------------------------------------------------------------------- */ /* data for processing boolean values from UPS */ #define STATUS(x) ((unsigned)1< should be fine for all Mfrs (TODO: validate it!) */ /* the purpose of the following status conversions is to collect information, not to interpret it. The function process_boolean_info() remembers these values by updating the global variable ups_status. Interpretation happens in ups_status_set, where they are converted to standard NUT status strings. Notice that the below conversions do not yield standard NUT status strings; this in indicated being in lower-case characters. The reason to separate the collection of information from its interpretation is that not each report received from the UPS may contain all the status flags, so they must be stored somewhere. Also, there can be more than one status flag triggering a certain condition (e.g. a certain UPS might have variables low_battery, shutdown_imminent, timelimit_exceeded, and each of these would trigger the NUT status LB. But we have to ensure that these variables don't unset each other, so they are remembered separately) */ info_lkp_t online_info[] = { { 1, "online", NULL }, { 0, "!online", NULL }, { 0, NULL, NULL } }; info_lkp_t discharging_info[] = { { 1, "dischrg", NULL }, { 0, "!dischrg", NULL }, { 0, NULL, NULL } }; info_lkp_t charging_info[] = { { 1, "chrg", NULL }, { 0, "!chrg", NULL }, { 0, NULL, NULL } }; info_lkp_t lowbatt_info[] = { { 1, "lowbatt", NULL }, { 0, "!lowbatt", NULL }, { 0, NULL, NULL } }; info_lkp_t overload_info[] = { { 1, "overload", NULL }, { 0, "!overload", NULL }, { 0, NULL, NULL } }; info_lkp_t replacebatt_info[] = { { 1, "replacebatt", NULL }, { 0, "!replacebatt", NULL }, { 0, NULL, NULL } }; info_lkp_t trim_info[] = { { 1, "trim", NULL }, { 0, "!trim", NULL }, { 0, NULL, NULL } }; info_lkp_t boost_info[] = { { 1, "boost", NULL }, { 0, "!boost", NULL }, { 0, NULL, NULL } }; info_lkp_t bypass_auto_info[] = { { 1, "bypassauto", NULL }, { 0, "!bypassauto", NULL }, { 0, NULL, NULL } }; info_lkp_t bypass_manual_info[] = { { 1, "bypassman", NULL }, { 0, "!bypassman", NULL }, { 0, NULL, NULL } }; /* note: this value is reverted (0=set, 1=not set). We report "being off" rather than "being on", so that devices that don't implement this variable are "on" by default */ info_lkp_t off_info[] = { { 0, "off", NULL }, { 1, "!off", NULL }, { 0, NULL, NULL } }; info_lkp_t calibration_info[] = { { 1, "cal", NULL }, { 0, "!cal", NULL }, { 0, NULL, NULL } }; /* note: this value is reverted (0=set, 1=not set). We report "battery not installed" rather than "battery installed", so that devices that don't implement this variable have a battery by default */ info_lkp_t nobattery_info[] = { { 1, "!nobattery", NULL }, { 0, "nobattery", NULL }, { 0, NULL, NULL } }; info_lkp_t fanfail_info[] = { { 1, "fanfail", NULL }, { 0, "!fanfail", NULL }, { 0, NULL, NULL } }; info_lkp_t shutdownimm_info[] = { { 1, "shutdownimm", NULL }, { 0, "!shutdownimm", NULL }, { 0, NULL, NULL } }; info_lkp_t overheat_info[] = { { 1, "overheat", NULL }, { 0, "!overheat", NULL }, { 0, NULL, NULL } }; info_lkp_t awaitingpower_info[] = { { 1, "awaitingpower", NULL }, { 0, "!awaitingpower", NULL }, { 0, NULL, NULL } }; info_lkp_t commfault_info[] = { { 1, "commfault", NULL }, { 0, "!commfault", NULL }, { 0, NULL, NULL } }; info_lkp_t timelimitexpired_info[] = { { 1, "timelimitexp", NULL }, { 0, "!timelimitexp", NULL }, { 0, NULL, NULL } }; info_lkp_t battvoltlo_info[] = { { 1, "battvoltlo", NULL }, { 0, "!battvoltlo", NULL }, { 0, NULL, NULL } }; info_lkp_t battvolthi_info[] = { { 1, "battvolthi", NULL }, { 0, "!battvolthi", NULL }, { 0, NULL, NULL } }; info_lkp_t chargerfail_info[] = { { 1, "chargerfail", NULL }, { 0, "!chargerfail", NULL }, { 0, NULL, NULL } }; info_lkp_t fullycharged_info[] = { /* used by CyberPower and TrippLite */ { 1, "fullycharged", NULL }, { 0, "!fullycharged", NULL }, { 0, NULL, NULL } }; info_lkp_t depleted_info[] = { { 1, "depleted", NULL }, { 0, "!depleted", NULL }, { 0, NULL, NULL } }; info_lkp_t vrange_info[] = { { 0, "!vrange", NULL }, { 1, "vrange", NULL }, { 0, NULL, NULL } }; info_lkp_t frange_info[] = { { 0, "!frange", NULL }, { 1, "frange", NULL }, { 0, NULL, NULL } }; info_lkp_t test_write_info[] = { { 0, "No test", NULL }, { 1, "Quick test", NULL }, { 2, "Deep test", NULL }, { 3, "Abort test", NULL }, { 0, NULL, NULL } }; info_lkp_t test_read_info[] = { { 1, "Done and passed", NULL }, { 2, "Done and warning", NULL }, { 3, "Done and error", NULL }, { 4, "Aborted", NULL }, { 5, "In progress", NULL }, { 6, "No test initiated", NULL }, { 7, "Test scheduled", NULL }, { 0, NULL, NULL } }; info_lkp_t beeper_info[] = { { 1, "disabled", NULL }, { 2, "enabled", NULL }, { 3, "muted", NULL }, { 0, NULL, NULL } }; info_lkp_t yes_no_info[] = { { 0, "no", NULL }, { 1, "yes", NULL }, { 0, NULL, NULL } }; info_lkp_t on_off_info[] = { { 0, "off", NULL }, { 1, "on", NULL }, { 0, NULL, NULL } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *date_conversion_fun(double value) { static char buf[20]; int year, month, day; if ((long)value == 0) { return "not set"; } year = 1980 + ((long)value >> 9); /* negative value represents pre-1980 date */ month = ((long)value >> 5) & 0x0f; day = (long)value & 0x1f; snprintf(buf, sizeof(buf), "%04d/%02d/%02d", year, month, day); return buf; } info_lkp_t date_conversion[] = { { 0, NULL, date_conversion_fun } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *hex_conversion_fun(double value) { static char buf[20]; snprintf(buf, sizeof(buf), "%08lx", (long)value); return buf; } info_lkp_t hex_conversion[] = { { 0, NULL, hex_conversion_fun } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *stringid_conversion_fun(double value) { static char buf[20]; return HIDGetIndexString(udev, (int)value, buf, sizeof(buf)); } info_lkp_t stringid_conversion[] = { { 0, NULL, stringid_conversion_fun } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *divide_by_10_conversion_fun(double value) { static char buf[20]; snprintf(buf, sizeof(buf), "%0.1f", value * 0.1); return buf; } info_lkp_t divide_by_10_conversion[] = { { 0, NULL, divide_by_10_conversion_fun } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *kelvin_celsius_conversion_fun(double value) { static char buf[20]; /* check if the value is in the Kelvin range, to * detect buggy value (already expressed in °C), as found * on some HP implementation */ if ((value >= 273) && (value <= 373)) { /* the value is indeed in °K */ snprintf(buf, sizeof(buf), "%.1f", value - 273.15); } else { /* else, this is actually °C, not °K! */ snprintf(buf, sizeof(buf), "%.1f", value); } return buf; } info_lkp_t kelvin_celsius_conversion[] = { { 0, NULL, kelvin_celsius_conversion_fun } }; /*! * subdriver matcher: only useful for USB mode * as SHUT is only supported by MGE UPS SYSTEMS units */ #ifndef SHUT_MODE static int match_function_subdriver(HIDDevice_t *d, void *privdata) { int i; for (i=0; subdriver_list[i] != NULL; i++) { if (subdriver_list[i]->claim(d)) { return 1; } } return 0; } static HIDDeviceMatcher_t subdriver_matcher_struct = { match_function_subdriver, NULL, NULL }; #endif /* --------------------------------------------- * driver functions implementations * --------------------------------------------- */ /* process instant command and take action. */ int instcmd(const char *cmdname, const char *extradata) { hid_info_t *hidups_item; const char *val; double value; if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.disable'"); return instcmd("beeper.disable", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } upsdebugx(1, "instcmd(%s, %s)", cmdname, extradata ? extradata : "[NULL]"); /* Retrieve and check netvar & item_path */ hidups_item = find_nut_info(cmdname); /* Check for fallback if not found */ if (hidups_item == NULL) { if (!strcasecmp(cmdname, "load.on")) { return instcmd("load.on.delay", "0"); } if (!strcasecmp(cmdname, "load.off")) { return instcmd("load.off.delay", "0"); } if (!strcasecmp(cmdname, "shutdown.return")) { int ret; /* Ensure "ups.start.auto" is set to "yes", if supported */ if (dstate_getinfo("ups.start.auto")) { setvar("ups.start.auto", "yes"); } ret = instcmd("load.on.delay", dstate_getinfo("ups.delay.start")); if (ret != STAT_INSTCMD_HANDLED) { return ret; } return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } if (!strcasecmp(cmdname, "shutdown.stayoff")) { int ret; /* Ensure "ups.start.auto" is set to "no", if supported */ if (dstate_getinfo("ups.start.auto")) { setvar("ups.start.auto", "no"); } ret = instcmd("load.on.delay", "-1"); if (ret != STAT_INSTCMD_HANDLED) { return ret; } return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } upsdebugx(2, "instcmd: info element unavailable %s\n", cmdname); return STAT_INSTCMD_INVALID; } /* Check if the item is an instant command */ if (!(hidups_item->hidflags & HU_TYPE_CMD)) { upsdebugx(2, "instcmd: %s is not an instant command\n", cmdname); return STAT_INSTCMD_INVALID; } /* If extradata is empty, use the default value from the HID-to-NUT table */ val = extradata ? extradata : hidups_item->dfl; /* Lookup the new value if needed */ if (hidups_item->hid2info != NULL) { value = hu_find_valinfo(hidups_item->hid2info, val); } else { value = atol(val); } /* Actual variable setting */ if (HIDSetDataValue(udev, hidups_item->hiddata, value) == 1) { upsdebugx(5, "instcmd: SUCCEED\n"); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_INSTCMD_HANDLED; } upsdebugx(3, "instcmd: FAILED\n"); /* TODO: HANDLED but FAILED, not UNKNOWN! */ return STAT_INSTCMD_FAILED; } /* set r/w variable to a value. */ int setvar(const char *varname, const char *val) { hid_info_t *hidups_item; double value; upsdebugx(1, "setvar(%s, %s)", varname, val); /* retrieve and check netvar & item_path */ hidups_item = find_nut_info(varname); if (hidups_item == NULL) { upsdebugx(2, "setvar: info element unavailable %s\n", varname); return STAT_SET_UNKNOWN; } /* Checking item writability and HID Path */ if (!(hidups_item->info_flags & ST_FLAG_RW)) { upsdebugx(2, "setvar: not writable %s\n", varname); return STAT_SET_UNKNOWN; } /* handle server side variable */ if (hidups_item->hidflags & HU_FLAG_ABSENT) { upsdebugx(2, "setvar: setting server side variable %s\n", varname); dstate_setinfo(hidups_item->info_type, "%s", val); return STAT_SET_HANDLED; } /* HU_FLAG_ABSENT is the only case of HID Path == NULL */ if (hidups_item->hidpath == NULL) { upsdebugx(2, "setvar: ID Path is NULL for %s\n", varname); return STAT_SET_UNKNOWN; } /* Lookup the new value if needed */ if (hidups_item->hid2info != NULL) { value = hu_find_valinfo(hidups_item->hid2info, val); } else { value = atol(val); } /* Actual variable setting */ if (HIDSetDataValue(udev, hidups_item->hiddata, value) == 1) { upsdebugx(5, "setvar: SUCCEED\n"); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_SET_HANDLED; } upsdebugx(3, "setvar: FAILED\n"); /* FIXME: HANDLED but FAILED, not UNKNOWN! */ return STAT_SET_UNKNOWN; } void upsdrv_shutdown(void) { upsdebugx(1, "upsdrv_shutdown..."); /* Try to shutdown with delay */ if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } /* If the above doesn't work, try shutdown.reboot */ if (instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } /* If the above doesn't work, try load.off.delay */ if (instcmd("load.off.delay", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } fatalx(EXIT_FAILURE, "Shutdown failed!"); } void upsdrv_help(void) { /* FIXME: to be completed */ } void upsdrv_makevartable(void) { char temp [MAX_STRING_SIZE]; upsdebugx(1, "upsdrv_makevartable..."); snprintf(temp, sizeof(temp), "Set low battery level, in %% (default=%s).", DEFAULT_LOWBATT); addvar (VAR_VALUE, HU_VAR_LOWBATT, temp); snprintf(temp, sizeof(temp), "Set shutdown delay, in seconds (default=%s)", DEFAULT_OFFDELAY); addvar(VAR_VALUE, HU_VAR_OFFDELAY, temp); snprintf(temp, sizeof(temp), "Set startup delay, in seconds (default=%s)", DEFAULT_ONDELAY); addvar(VAR_VALUE, HU_VAR_ONDELAY, temp); snprintf(temp, sizeof(temp), "Set polling frequency, in seconds, to reduce data flow (default=%d)", DEFAULT_POLLFREQ); addvar(VAR_VALUE, HU_VAR_POLLFREQ, temp); addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); #ifndef SHUT_MODE /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); addvar(VAR_FLAG, "explore", "Diagnostic matching of unsupported UPS"); addvar(VAR_FLAG, "maxreport", "Activate tweak for buggy APC Back-UPS firmware"); addvar(VAR_FLAG, "interruptonly", "Don't use polling, only use interrupt pipe"); addvar(VAR_VALUE, "interruptsize", "Number of bytes to read from interrupt pipe"); #else addvar(VAR_VALUE, "notification", "Set notification type, (ignored, only for backward compatibility)"); #endif } #define MAX_EVENT_NUM 32 void upsdrv_updateinfo(void) { hid_info_t *item; HIDData_t *event[MAX_EVENT_NUM], *found_data; int i, evtCount; double value; time_t now; upsdebugx(1, "upsdrv_updateinfo..."); time(&now); /* check for device availability to set datastale! */ if (hd == NULL) { /* don't flood reconnection attempts */ if (now < (int)(lastpoll + poll_interval)) { return; } upsdebugx(1, "Got to reconnect!\n"); if (!reconnect_ups()) { lastpoll = now; dstate_datastale(); return; } hd = &curDevice; if (hid_ups_walk(HU_WALKMODE_INIT) == FALSE) { hd = NULL; return; } } #ifdef DEBUG interval(); #endif /* Get HID notifications on Interrupt pipe first */ if (use_interrupt_pipe == TRUE) { evtCount = HIDGetEvents(udev, event, MAX_EVENT_NUM); switch (evtCount) { case -EBUSY: /* Device or resource busy */ upslog_with_errno(LOG_CRIT, "Got disconnected by another driver"); case -EPERM: /* Operation not permitted */ case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ /* Uh oh, got to reconnect! */ hd = NULL; return; default: upsdebugx(1, "Got %i HID objects...", (evtCount >= 0) ? evtCount : 0); break; } } else { evtCount = 0; upsdebugx(1, "Not using interrupt pipe..."); } /* Process pending events (HID notifications on Interrupt pipe) */ for (i = 0; i < evtCount; i++) { if (HIDGetDataValue(udev, event[i], &value, poll_interval) != 1) continue; if (nut_debug_level >= 2) { upsdebugx(2, "Path: %s, Type: %s, ReportID: 0x%02x, Offset: %i, Size: %i, Value: %g", HIDGetDataItem(event[i], subdriver->utab), HIDDataType(event[i]), event[i]->ReportID, event[i]->Offset, event[i]->Size, value); } /* Skip Input reports, if we don't use the Feature report */ found_data = FindObject_with_Path(pDesc, &(event[i]->Path), interrupt_only ? ITEM_INPUT:ITEM_FEATURE); if(!found_data && !interrupt_only) { found_data = FindObject_with_Path(pDesc, &(event[i]->Path), ITEM_INPUT); } if(!found_data) { upsdebugx(2, "Could not find event as either ITEM_INPUT or ITEM_FEATURE?"); continue; } item = find_hid_info(found_data); if (!item) { upsdebugx(3, "NUT doesn't use this HID object"); continue; } ups_infoval_set(item, value); } #ifdef DEBUG upsdebugx(1, "took %.3f seconds handling interrupt reports...\n", interval()); #endif /* clear status buffer before begining */ status_init(); /* Do a full update (polling) every pollfreq or upon data change (ie setvar/instcmd) */ if ((now > (lastpoll + pollfreq)) || (data_has_changed == TRUE)) { upsdebugx(1, "Full update..."); alarm_init(); if (hid_ups_walk(HU_WALKMODE_FULL_UPDATE) == FALSE) return; lastpoll = now; data_has_changed = FALSE; ups_alarm_set(); alarm_commit(); } else { upsdebugx(1, "Quick update..."); /* Quick poll data only to see if the UPS is still connected */ if (hid_ups_walk(HU_WALKMODE_QUICK_UPDATE) == FALSE) return; } ups_status_set(); status_commit(); dstate_dataok(); #ifdef DEBUG upsdebugx(1, "took %.3f seconds handling feature reports...\n", interval()); #endif } void upsdrv_initinfo(void) { char *val; upsdebugx(1, "upsdrv_initinfo..."); dstate_setinfo("driver.version.data", "%s", subdriver->name); /* init polling frequency */ val = getval(HU_VAR_POLLFREQ); if (val) { pollfreq = atoi(val); } dstate_setinfo("driver.parameter.pollfreq", "%d", pollfreq); /* ignore (broken) interrupt pipe */ if (testvar("pollonly")) { use_interrupt_pipe = FALSE; } time(&lastpoll); /* install handlers */ upsh.setvar = setvar; upsh.instcmd = instcmd; } void upsdrv_initups(void) { int ret; char *val; #ifdef SHUT_MODE /*! * SHUT is a serial protocol, so it needs * only the device path */ upsdebugx(1, "upsdrv_initups..."); subdriver_matcher = device_path; #else char *regex_array[6]; upsdebugx(1, "upsdrv_initups..."); subdriver_matcher = &subdriver_matcher_struct; /* enforce use of the "vendorid" option if "explore" is given */ if (testvar("explore") && getval("vendorid")==NULL) { fatalx(EXIT_FAILURE, "must specify \"vendorid\" when using \"explore\""); } /* Activate maxreport tweak */ if (testvar("maxreport")) { max_report_size = 1; } /* process the UPS selection options */ regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch(ret) { case 0: break; case -1: fatal_with_errno(EXIT_FAILURE, "HIDNewRegexMatcher()"); default: fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]); } /* link the matchers */ subdriver_matcher->next = regex_matcher; #endif /* SHUT_MODE */ /* Search for the first supported UPS matching the regular expression (USB) or device_path (SHUT) */ ret = comm_driver->open(&udev, &curDevice, subdriver_matcher, &callback); if (ret < 1) fatalx(EXIT_FAILURE, "No matching HID UPS found"); hd = &curDevice; upsdebugx(1, "Detected a UPS: %s/%s", hd->Vendor ? hd->Vendor : "unknown", hd->Product ? hd->Product : "unknown"); /* Activate Powercom tweaks */ if (testvar("interruptonly")) { interrupt_only = 1; } val = getval("interruptsize"); if (val) { interrupt_size = atoi(val); } if (hid_ups_walk(HU_WALKMODE_INIT) == FALSE) { fatalx(EXIT_FAILURE, "Can't initialize data from HID UPS"); } if (dstate_getinfo("battery.charge.low")) { /* Retrieve user defined battery settings */ val = getval(HU_VAR_LOWBATT); if (val) { dstate_setinfo("battery.charge.low", "%ld", strtol(val, NULL, 10)); } } if (dstate_getinfo("ups.delay.start")) { /* Retrieve user defined delay settings */ val = getval(HU_VAR_ONDELAY); if (val) { dstate_setinfo("ups.delay.start", "%ld", strtol(val, NULL, 10)); } } if (dstate_getinfo("ups.delay.shutdown")) { /* Retrieve user defined delay settings */ val = getval(HU_VAR_OFFDELAY); if (val) { dstate_setinfo("ups.delay.shutdown", "%ld", strtol(val, NULL, 10)); } } if (find_nut_info("load.off.delay")) { /* Adds default with a delay value of '0' (= immediate) */ dstate_addcmd("load.off"); } if (find_nut_info("load.on.delay")) { /* Adds default with a delay value of '0' (= immediate) */ dstate_addcmd("load.on"); } if (find_nut_info("load.off.delay") && find_nut_info("load.on.delay")) { /* Add composite instcmds (require setting multiple HID values) */ dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); } } void upsdrv_cleanup(void) { upsdebugx(1, "upsdrv_cleanup..."); comm_driver->close(udev); Free_ReportDesc(pDesc); free_report_buffer(reportbuf); #ifndef SHUT_MODE USBFreeExactMatcher(exact_matcher); USBFreeRegexMatcher(regex_matcher); free(curDevice.Vendor); free(curDevice.Product); free(curDevice.Serial); free(curDevice.Bus); #endif } /********************************************************************** * Support functions *********************************************************************/ void possibly_supported(const char *mfr, HIDDevice_t *hd) { upsdebugx(0, "This %s device (%04x:%04x) is not (or perhaps not yet) supported\n" "by usbhid-ups. Please make sure you have an up-to-date version of NUT. If\n" "this does not fix the problem, try running the driver with the\n" "'-x productid=%04x' option. Please report your results to the NUT user's\n" "mailing list .\n", mfr, hd->VendorID, hd->ProductID, hd->ProductID); } /* Update ups_status to remember this status item. Interpretation is done in ups_status_set(). */ static void process_boolean_info(const char *nutvalue) { status_lkp_t *status_item; int clear = 0; upsdebugx(5, "process_boolean_info: %s", nutvalue); if (*nutvalue == '!') { nutvalue++; clear = 1; } for (status_item = status_info; status_item->status_str != NULL ; status_item++) { if (strcasecmp(status_item->status_str, nutvalue)) continue; if (clear) { ups_status &= ~status_item->status_mask; } else { ups_status |= status_item->status_mask; } return; } upsdebugx(5, "Warning: %s not in list of known values", nutvalue); } static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen) { int i; const char *mfr = NULL, *model = NULL, *serial = NULL; #ifndef SHUT_MODE int ret; #endif upsdebugx(2, "Report Descriptor size = %d", rdlen); upsdebug_hex(3, "Report Descriptor", rdbuf, rdlen); /* Parse Report Descriptor */ Free_ReportDesc(pDesc); pDesc = Parse_ReportDesc(rdbuf, rdlen); if (!pDesc) { upsdebug_with_errno(1, "Failed to parse report descriptor!"); return 0; } /* prepare report buffer */ free_report_buffer(reportbuf); reportbuf = new_report_buffer(pDesc); if (!reportbuf) { upsdebug_with_errno(1, "Failed to allocate report buffer!"); Free_ReportDesc(pDesc); return 0; } /* select the subdriver for this device */ for (i=0; subdriver_list[i] != NULL; i++) { if (subdriver_list[i]->claim(hd)) { break; } } subdriver = subdriver_list[i]; if (!subdriver) { upsdebugx(1, "Manufacturer not supported!"); return 0; } upslogx(2, "Using subdriver: %s", subdriver->name); HIDDumpTree(udev, subdriver->utab); #ifndef SHUT_MODE /* create a new matcher for later matching */ USBFreeExactMatcher(exact_matcher); ret = USBNewExactMatcher(&exact_matcher, hd); if (ret) { upsdebug_with_errno(1, "USBNewExactMatcher()"); return 0; } regex_matcher->next = exact_matcher; #endif /* SHUT_MODE */ /* apply subdriver specific formatting */ mfr = subdriver->format_mfr(hd); model = subdriver->format_model(hd); serial = subdriver->format_serial(hd); if (mfr != NULL) { dstate_setinfo("ups.mfr", "%s", mfr); } else { dstate_delinfo("ups.mfr"); } if (model != NULL) { dstate_setinfo("ups.model", "%s", model); } else { dstate_delinfo("ups.model"); } if (serial != NULL) { dstate_setinfo("ups.serial", "%s", serial); } else { dstate_delinfo("ups.serial"); } dstate_setinfo("ups.vendorid", "%04x", hd->VendorID); dstate_setinfo("ups.productid", "%04x", hd->ProductID); return 1; } #ifdef DEBUG static double interval(void) { struct timeval now; static struct timeval last; double ret; gettimeofday(&now, NULL); ret = now.tv_sec - last.tv_sec + ((double)(now.tv_usec - last.tv_usec)) / 1000000; last = now; return ret; } #endif /* walk ups variables and set elements of the info array. */ static bool_t hid_ups_walk(walkmode_t mode) { hid_info_t *item; double value; int retcode; /* 3 modes: HU_WALKMODE_INIT, HU_WALKMODE_QUICK_UPDATE and HU_WALKMODE_FULL_UPDATE */ /* Device data walk ----------------------------- */ for (item = subdriver->hid2nut; item->info_type != NULL; item++) { #ifdef SHUT_MODE /* Check if we are asked to stop (reactivity++) in SHUT mode. * In USB mode, looping through this takes well under a second, * so any effort to improve reactivity here is wasted. */ if (exit_flag != 0) return TRUE; #endif /* filter data according to mode */ switch (mode) { /* Device capabilities enumeration */ case HU_WALKMODE_INIT: /* Apparently, we are reconnecting, so * NUT-to-HID translation is already good */ if (item->hiddata != NULL) break; /* Create the NUT-to-HID mapping */ item->hiddata = HIDGetItemData(item->hidpath, subdriver->utab); if (item->hiddata == NULL) continue; /* Special case for handling server side variables */ if (item->hidflags & HU_FLAG_ABSENT) { /* already set */ if (dstate_getinfo(item->info_type)) continue; dstate_setinfo(item->info_type, "%s", item->dfl); dstate_setflags(item->info_type, item->info_flags); /* Set max length for strings, if needed */ if (item->info_flags & ST_FLAG_STRING) dstate_setaux(item->info_type, item->info_len); continue; } /* Allow duplicates for these NUT variables... */ if (!strncmp(item->info_type, "ups.alarm", 9)) { break; } /* ...this one doesn't exist yet... */ if (dstate_getinfo(item->info_type) == NULL) { break; } /* ...but this one does, so don't use it! */ item->hiddata = NULL; continue; case HU_WALKMODE_QUICK_UPDATE: /* Quick update only deals with status and alarms! */ if (!(item->hidflags & HU_FLAG_QUICK_POLL)) continue; break; case HU_WALKMODE_FULL_UPDATE: /* These don't need polling after initinfo() */ if (item->hidflags & (HU_FLAG_ABSENT | HU_TYPE_CMD | HU_FLAG_STATIC)) continue; /* These need to be polled after user changes (setvar / instcmd) */ if ( (item->hidflags & HU_FLAG_SEMI_STATIC) && (data_has_changed == FALSE) ) continue; break; default: fatalx(EXIT_FAILURE, "hid_ups_walk: unknown update mode!"); } retcode = HIDGetDataValue(udev, item->hiddata, &value, poll_interval); switch (retcode) { case -EBUSY: /* Device or resource busy */ upslog_with_errno(LOG_CRIT, "Got disconnected by another driver"); case -EPERM: /* Operation not permitted */ case -ENODEV: /* No such device */ case -EACCES: /* Permission denied */ case -EIO: /* I/O error */ case -ENXIO: /* No such device or address */ case -ENOENT: /* No such file or directory */ /* Uh oh, got to reconnect! */ hd = NULL; return FALSE; case 1: break; /* Found! */ case 0: continue; case -ETIMEDOUT: /* Connection timed out */ case -EOVERFLOW: /* Value too large for defined data type */ #ifdef EPROTO case -EPROTO: /* Protocol error */ #endif case -EPIPE: /* Broken pipe */ default: /* Don't know what happened, try again later... */ continue; } upsdebugx(2, "Path: %s, Type: %s, ReportID: 0x%02x, Offset: %i, Size: %i, Value: %g", item->hidpath, HIDDataType(item->hiddata), item->hiddata->ReportID, item->hiddata->Offset, item->hiddata->Size, value); if (item->hidflags & HU_TYPE_CMD) { dstate_addcmd(item->info_type); continue; } /* Process the value we got back (set status bits and * set the value of other parameters) */ if (ups_infoval_set(item, value) != 1) continue; if (mode == HU_WALKMODE_INIT) { info_lkp_t *info_lkp; dstate_setflags(item->info_type, item->info_flags); /* Set max length for strings */ if (item->info_flags & ST_FLAG_STRING) { dstate_setaux(item->info_type, item->info_len); } /* Set enumerated values, only if the data has ST_FLAG_RW */ if (!(item->hidflags & HU_FLAG_ENUM) || !(item->info_flags & ST_FLAG_RW)) { continue; } /* Loop on all existing values */ for (info_lkp = item->hid2info; info_lkp != NULL && info_lkp->nut_value != NULL; info_lkp++) { /* Check if this value is supported */ if (hu_find_infoval(item->hid2info, info_lkp->hid_value) != NULL) { dstate_addenum(item->info_type, "%s", info_lkp->nut_value); } } } } return TRUE; } static int reconnect_ups(void) { int ret; upsdebugx(4, "=================================================="); upsdebugx(4, "= device has been disconnected, try to reconnect ="); upsdebugx(4, "=================================================="); ret = comm_driver->open(&udev, &curDevice, subdriver_matcher, NULL); if (ret > 0) { return 1; } return 0; } /* Convert the local status information to NUT format and set NUT alarms. */ static void ups_alarm_set(void) { if (ups_status & STATUS(REPLACEBATT)) { alarm_set("Replace battery!"); } if (ups_status & STATUS(SHUTDOWNIMM)) { alarm_set("Shutdown imminent!"); } if (ups_status & STATUS(FANFAIL)) { alarm_set("Fan failure!"); } if (ups_status & STATUS(NOBATTERY)) { alarm_set("No battery installed!"); } if (ups_status & STATUS(BATTVOLTLO)) { alarm_set("Battery voltage too low!"); } if (ups_status & STATUS(BATTVOLTHI)) { alarm_set("Battery voltage too high!"); } if (ups_status & STATUS(CHARGERFAIL)) { alarm_set("Battery charger fail!"); } if (ups_status & STATUS(OVERHEAT)) { alarm_set("Temperature too high!"); /* overheat; Belkin, TrippLite */ } if (ups_status & STATUS(COMMFAULT)) { alarm_set("Internal UPS fault!"); /* UPS fault; Belkin, TrippLite */ } if (ups_status & STATUS(AWAITINGPOWER)) { alarm_set("Awaiting power!"); /* awaiting power; Belkin, TrippLite */ } if (ups_status & STATUS(BYPASSAUTO)) { alarm_set("Automatic bypass mode!"); } if (ups_status & STATUS(BYPASSMAN)) { alarm_set("Manual bypass mode!"); } } /* Convert the local status information to NUT format and set NUT status. */ static void ups_status_set(void) { if (ups_status & STATUS(VRANGE)) { dstate_setinfo("input.transfer.reason", "input voltage out of range"); } else if (ups_status & STATUS(FRANGE)) { dstate_setinfo("input.transfer.reason", "input frequency out of range"); } else { dstate_delinfo("input.transfer.reason"); } if (ups_status & STATUS(ONLINE)) { status_set("OL"); /* on line */ } else { status_set("OB"); /* on battery */ } if ((ups_status & STATUS(DISCHRG)) && !(ups_status & STATUS(DEPLETED))) { status_set("DISCHRG"); /* discharging */ } if ((ups_status & STATUS(CHRG)) && !(ups_status & STATUS(FULLYCHARGED))) { status_set("CHRG"); /* charging */ } if (ups_status & (STATUS(LOWBATT) | STATUS(TIMELIMITEXP) | STATUS(SHUTDOWNIMM))) { status_set("LB"); /* low battery */ } if (ups_status & STATUS(OVERLOAD)) { status_set("OVER"); /* overload */ } if (ups_status & STATUS(REPLACEBATT)) { status_set("RB"); /* replace batt */ } if (ups_status & STATUS(TRIM)) { status_set("TRIM"); /* SmartTrim */ } if (ups_status & STATUS(BOOST)) { status_set("BOOST"); /* SmartBoost */ } if (ups_status & (STATUS(BYPASSAUTO) | STATUS(BYPASSMAN))) { status_set("BYPASS"); /* on bypass */ } if (ups_status & STATUS(OFF)) { status_set("OFF"); /* ups is off */ } if (ups_status & STATUS(CAL)) { status_set("CAL"); /* calibration */ } } /* find info element definition in info array * by NUT varname. */ static hid_info_t *find_nut_info(const char *varname) { hid_info_t *hidups_item; for (hidups_item = subdriver->hid2nut; hidups_item->info_type != NULL ; hidups_item++) { if (strcasecmp(hidups_item->info_type, varname)) continue; if (hidups_item->hiddata != NULL) return hidups_item; } upsdebugx(2, "find_nut_info: unknown info type: %s", varname); return NULL; } /* find info element definition in info array * by HID data pointer. */ static hid_info_t *find_hid_info(const HIDData_t *hiddata) { hid_info_t *hidups_item; if(!hiddata) { upsdebugx(2, "%s: hiddata == NULL", __func__); return NULL; } for (hidups_item = subdriver->hid2nut; hidups_item->info_type != NULL ; hidups_item++) { /* Skip server side vars */ if (hidups_item->hidflags & HU_FLAG_ABSENT) continue; if (hidups_item->hiddata == hiddata) return hidups_item; } return NULL; } /* find the HID Item value matching that NUT value */ /* useful for set with value lookup... */ static long hu_find_valinfo(info_lkp_t *hid2info, const char* value) { info_lkp_t *info_lkp; /* if a conversion function is defined, use 'value' as argument for it */ if (hid2info->nuf != NULL) { double hid_value; hid_value = hid2info->nuf(value); upsdebugx(5, "hu_find_valinfo: found %g (value: %s)", hid_value, value); return hid_value; } for (info_lkp = hid2info; info_lkp->nut_value != NULL; info_lkp++) { if (!(strcmp(info_lkp->nut_value, value))) { upsdebugx(5, "hu_find_valinfo: found %s (value: %ld)", info_lkp->nut_value, info_lkp->hid_value); return info_lkp->hid_value; } } upsdebugx(3, "hu_find_valinfo: no matching HID value for this INFO_* value (%s)", value); return -1; } /* find the NUT value matching that HID Item value */ static const char *hu_find_infoval(info_lkp_t *hid2info, const double value) { info_lkp_t *info_lkp; /* if a conversion function is defined, use 'value' as argument for it */ if (hid2info->fun != NULL) { return hid2info->fun(value); } /* use 'value' as an index for a lookup in an array */ for (info_lkp = hid2info; info_lkp->nut_value != NULL; info_lkp++) { if (info_lkp->hid_value == (long)value) { upsdebugx(5, "hu_find_infoval: found %s (value: %ld)", info_lkp->nut_value, (long)value); return info_lkp->nut_value; } } upsdebugx(3, "hu_find_infoval: no matching INFO_* value for this HID value (%g)", value); return NULL; } /* return -1 on failure, 0 for a status update and 1 in all other cases */ static int ups_infoval_set(hid_info_t *item, double value) { const char *nutvalue; /* need lookup'ed translation? */ if (item->hid2info != NULL){ if ((nutvalue = hu_find_infoval(item->hid2info, value)) == NULL) { upsdebugx(5, "Lookup [%g] failed for [%s]", value, item->info_type); return -1; } /* deal with boolean items */ if (!strncmp(item->info_type, "BOOL", 4)) { process_boolean_info(nutvalue); return 0; } /* deal with alarm items */ if (!strncmp(item->info_type, "ups.alarm", 9)) { alarm_set(nutvalue); return 0; } dstate_setinfo(item->info_type, "%s", nutvalue); } else { dstate_setinfo(item->info_type, item->dfl, value); } return 1; } nut-2.7.4/drivers/usbhid-ups.h0000644000175000017500000001571112640473702013177 00000000000000/* usbhid-ups.h - Driver for serial/USB HID UPS units * * Copyright (C) * 2003-2009 Arnaud Quette * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte * * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef USBHID_UPS_H #define USBHID_UPS_H #include #include #include #include #include "config.h" #include "libhid.h" extern hid_dev_handle_t udev; extern bool_t use_interrupt_pipe; /* Set to FALSE if interrupt reports should not be used */ /* Driver's parameters */ #define HU_VAR_LOWBATT "lowbatt" #define HU_VAR_ONDELAY "ondelay" #define HU_VAR_OFFDELAY "offdelay" #define HU_VAR_POLLFREQ "pollfreq" /* Parameters default values */ #define DEFAULT_LOWBATT "30" /* percentage of battery charge to consider the UPS in low battery state */ #define DEFAULT_ONDELAY "30" /* Delay between return of utility power */ /* and powering up of load, in seconds */ /* CAUTION: ondelay > offdelay */ #define DEFAULT_OFFDELAY "20" /* Delay before power off, in seconds */ #define DEFAULT_POLLFREQ 30 /* Polling interval, in seconds */ /* The driver will wait for Interrupt */ /* and do "light poll" in the meantime */ #define MAX_STRING_SIZE 128 /* --------------------------------------------------------------- */ /* Struct & data for lookup between HID and NUT values */ /* (From USB/HID, Power Devices Class standard) */ /* --------------------------------------------------------------- */ typedef struct { const long hid_value; /* HID value */ const char *nut_value; /* NUT value */ const char *(*fun)(double hid_value); /* optional HID to NUT mapping */ double (*nuf)(const char *nut_value); /* optional NUT to HID mapping */ } info_lkp_t; /* declarations of public lookup tables */ /* boolean status values from UPS */ extern info_lkp_t online_info[]; extern info_lkp_t discharging_info[]; extern info_lkp_t charging_info[]; extern info_lkp_t lowbatt_info[]; extern info_lkp_t overload_info[]; extern info_lkp_t replacebatt_info[]; extern info_lkp_t trim_info[]; extern info_lkp_t boost_info[]; extern info_lkp_t bypass_auto_info[]; extern info_lkp_t bypass_manual_info[]; extern info_lkp_t off_info[]; extern info_lkp_t calibration_info[]; extern info_lkp_t nobattery_info[]; extern info_lkp_t fanfail_info[]; extern info_lkp_t shutdownimm_info[]; extern info_lkp_t overheat_info[]; extern info_lkp_t awaitingpower_info[]; extern info_lkp_t commfault_info[]; extern info_lkp_t timelimitexpired_info[]; extern info_lkp_t battvoltlo_info[]; extern info_lkp_t battvolthi_info[]; extern info_lkp_t chargerfail_info[]; extern info_lkp_t emergency_stop_info[]; extern info_lkp_t fullycharged_info[]; extern info_lkp_t depleted_info[]; /* input.transfer.reason */ extern info_lkp_t vrange_info[]; extern info_lkp_t frange_info[]; /* non specific */ extern info_lkp_t test_write_info[]; extern info_lkp_t test_read_info[]; extern info_lkp_t beeper_info[]; extern info_lkp_t yes_no_info[]; extern info_lkp_t on_off_info[]; extern info_lkp_t date_conversion[]; extern info_lkp_t hex_conversion[]; extern info_lkp_t stringid_conversion[]; extern info_lkp_t divide_by_10_conversion[]; extern info_lkp_t kelvin_celsius_conversion[]; /* --------------------------------------------------------------- */ /* Structure containing information about how to get/set data */ /* from/to the UPS and convert these to/from NUT standard */ /* --------------------------------------------------------------- */ typedef struct { const char *info_type; /* NUT variable name */ int info_flags; /* NUT flags (to set in addinfo) */ int info_len; /* if ST_FLAG_STRING: length of the string */ /* if HU_TYPE_CMD: command value */ const char *hidpath; /* Full HID Object path (or NULL for server side vars) */ HIDData_t *hiddata; /* Full HID Object data (for caching purpose, filled at runtime) */ const char *dfl; /* if HU_FLAG_ABSENT: default value ; format otherwise */ unsigned long hidflags; /* driver's own flags */ info_lkp_t *hid2info; /* lookup table between HID and NUT values */ /* if HU_FLAG_ENUM is set, hid2info is also used * as enumerated values (dstate_addenum()) */ /* char *info_HID_format; *//* FFE: HID format for complex values */ /* interpreter interpret; *//* FFE: interpreter fct, NULL if not needed */ /* void *next; *//* next hid_info_t */ } hid_info_t; /* TODO: rework flags */ #define HU_FLAG_STATIC 2 /* retrieve info only once. */ #define HU_FLAG_SEMI_STATIC 4 /* retrieve info smartly */ #define HU_FLAG_ABSENT 8 /* data is absent in the device, */ /* use default value. */ #define HU_FLAG_QUICK_POLL 16 /* Mandatory vars */ #define HU_FLAG_STALE 32 /* data stale, don't try too often. */ #define HU_FLAG_ENUM 128 /* enum values exist */ /* hints for su_ups_set, applicable only to rw vars */ #define HU_TYPE_CMD 64 /* instant command */ #define HU_CMD_MASK 0x2000 #define HU_INFOSIZE 128 #define MAX_TRY 2 /* max number of GetItem retry */ /* --------------------------------------------------------------- */ /* Subdriver interface */ /* --------------------------------------------------------------- */ /* subdriver structure. Each subdriver is intended to support a * particular manufacturer (e.g. MGE, APC, Belkin), or a particular * range of models. */ typedef struct { const char *name; /* name of this subdriver */ int (*claim)(HIDDevice_t *hd); /* return 1 if device covered by * this subdriver */ usage_tables_t *utab; /* points to array of usage tables */ hid_info_t *hid2nut; /* main table of vars and instcmds */ const char *(*format_model)(HIDDevice_t *hd); /* driver-specific methods */ const char *(*format_mfr)(HIDDevice_t *hd); /* for preparing human- */ const char *(*format_serial)(HIDDevice_t *hd); /* readable information */ } subdriver_t; /* the following functions are exported for the benefit of subdrivers */ int instcmd(const char *cmdname, const char *extradata); int setvar(const char *varname, const char *val); void possibly_supported(const char *mfr, HIDDevice_t *hd); #endif /* USBHID_UPS_H */ nut-2.7.4/drivers/nut-libfreeipmi.c0000644000175000017500000007660512667761065014226 00000000000000/* nut-libfreeipmi.c - NUT IPMI backend, using FreeIPMI * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2011 - Albert Chu * * Based on the sample codes 'ipmi-fru-example.c', 'frulib.c' and * 'ipmimonitoring-sensors.c', from FreeIPMI * * 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 * TODO: * add power control support (ipmipower): seems OOB only! -n, --on Power on the target hosts. -f, --off Power off the target hosts. -c, --cycle Power cycle the target hosts. -r, --reset Reset the target hosts. -s, --stat Get power status of the target hosts. --pulse Send power diagnostic interrupt to target hosts. --soft Initiate a soft-shutdown of the OS via ACPI. --on-if-off Issue a power on command instead of a power cycle or hard reset command if the remote machine's power is currently off. --wait-until-off Regularly query the remote BMC and return only after the machine has powered off. --wait-until-on Regularly query the remote BMC and return only */ #include #include #include "timehead.h" #include "common.h" #include #include #if HAVE_FREEIPMI_MONITORING #include #endif #include "nut-ipmi.h" #include "dstate.h" /* FreeIPMI defines */ #define IPMI_FRU_STR_BUFLEN 1024 /* haven't seen a motherboard with more than 2-3 so far, * 64 should be more than enough */ #define IPMI_FRU_CUSTOM_FIELDS 64 /* FreeIPMI contexts and configuration*/ ipmi_ctx_t ipmi_ctx = NULL; ipmi_monitoring_ctx_t mon_ctx = NULL; struct ipmi_monitoring_ipmi_config ipmi_config; /* SDR management API has changed with 1.1.X and later */ #ifdef HAVE_FREEIPMI_11X_12X ipmi_sdr_ctx_t sdr_ctx = NULL; ipmi_fru_ctx_t fru_ctx = NULL; #define SDR_PARSE_CTX sdr_ctx #define NUT_IPMI_SDR_CACHE_DEFAULTS IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT #else ipmi_sdr_cache_ctx_t sdr_ctx = NULL; ipmi_sdr_parse_ctx_t sdr_parse_ctx = NULL; #define SDR_PARSE_CTX sdr_parse_ctx ipmi_fru_parse_ctx_t fru_ctx = NULL; /* Functions remapping */ #define ipmi_sdr_ctx_create ipmi_sdr_cache_ctx_create #define ipmi_sdr_ctx_destroy ipmi_sdr_cache_ctx_destroy #define ipmi_sdr_ctx_errnum ipmi_sdr_cache_ctx_errnum #define ipmi_sdr_ctx_errormsg ipmi_sdr_cache_ctx_errormsg #define ipmi_fru_ctx_create ipmi_fru_parse_ctx_create #define ipmi_fru_ctx_destroy ipmi_fru_parse_ctx_destroy #define ipmi_fru_ctx_set_flags ipmi_fru_parse_ctx_set_flags #define ipmi_fru_ctx_strerror ipmi_fru_parse_ctx_strerror #define ipmi_fru_ctx_errnum ipmi_fru_parse_ctx_errnum #define ipmi_fru_open_device_id ipmi_fru_parse_open_device_id #define ipmi_fru_close_device_id ipmi_fru_parse_close_device_id #define ipmi_fru_ctx_errormsg ipmi_fru_parse_ctx_errormsg #define ipmi_fru_read_data_area ipmi_fru_parse_read_data_area #define ipmi_fru_next ipmi_fru_parse_next #define ipmi_fru_type_length_field_to_string ipmi_fru_parse_type_length_field_to_string #define ipmi_fru_multirecord_power_supply_information ipmi_fru_parse_multirecord_power_supply_information #define ipmi_fru_board_info_area ipmi_fru_parse_board_info_area #define ipmi_fru_field_t ipmi_fru_parse_field_t /* Constants */ #define IPMI_SDR_MAX_RECORD_LENGTH IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH #define IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST IPMI_SDR_CACHE_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST #define IPMI_FRU_AREA_SIZE_MAX IPMI_FRU_PARSE_AREA_SIZE_MAX #define IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS #define IPMI_FRU_AREA_TYPE_BOARD_INFO_AREA IPMI_FRU_PARSE_AREA_TYPE_BOARD_INFO_AREA #define IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION #define IPMI_FRU_AREA_STRING_MAX IPMI_FRU_PARSE_AREA_STRING_MAX #define NUT_IPMI_SDR_CACHE_DEFAULTS IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, IPMI_SDR_CACHE_VALIDATION_FLAGS_DEFAULT #endif /* HAVE_FREEIPMI_11X_12X */ /* FIXME: freeipmi auto selects a cache based on the hostname you are * connecting too, but this is probably fine for you */ #define CACHE_LOCATION "/tmp/sdrcache" /* Support functions */ static const char* libfreeipmi_getfield (uint8_t language_code, ipmi_fru_field_t *field); static void libfreeipmi_cleanup(); static int libfreeipmi_get_psu_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev); static int libfreeipmi_get_board_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev); static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev); /******************************************************************************* * Implementation ******************************************************************************/ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev) { int ret = -1; uint8_t areabuf[IPMI_FRU_AREA_SIZE_MAX+1]; unsigned int area_type = 0; unsigned int area_length = 0; upsdebugx(1, "nut-libfreeipmi: nutipmi_open()..."); /* Initialize the FreeIPMI library. */ if (!(ipmi_ctx = ipmi_ctx_create ())) { /* we have to force cleanup, since exit handler is not yet installed */ libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_ctx_create"); } if ((ret = ipmi_ctx_find_inband (ipmi_ctx, NULL, 0, /* don't disable auto-probe */ 0, 0, NULL, 0, /* workaround flags, none by default */ 0 /* flags */ )) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_ctx_find_inband: %s", ipmi_ctx_errormsg (ipmi_ctx)); } if (!ret) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "could not find inband device"); } upsdebugx(1, "FreeIPMI initialized..."); /* Parse FRU information */ if (!(fru_ctx = ipmi_fru_ctx_create (ipmi_ctx))) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_fru_ctx_create()"); } /* lots of motherboards calculate checksums incorrectly */ if (ipmi_fru_ctx_set_flags (fru_ctx, IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_fru_ctx_set_flags: %s\n", ipmi_fru_ctx_strerror (ipmi_fru_ctx_errnum (fru_ctx))); } /* Now open the requested (local) PSU */ if (ipmi_fru_open_device_id (fru_ctx, ipmi_id) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_fru_open_device_id: %s\n", ipmi_fru_ctx_errormsg (fru_ctx)); } /* Set IPMI identifier */ ipmi_dev->ipmi_id = ipmi_id; do { /* clear fields */ area_type = 0; area_length = 0; memset (areabuf, '\0', IPMI_FRU_AREA_SIZE_MAX + 1); /* parse FRU buffer */ if (ipmi_fru_read_data_area (fru_ctx, &area_type, &area_length, areabuf, IPMI_FRU_AREA_SIZE_MAX) < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_fru_read_data_area: %s\n", ipmi_fru_ctx_errormsg (fru_ctx)); } if (area_length) { switch (area_type) { /* get generic board information */ case IPMI_FRU_AREA_TYPE_BOARD_INFO_AREA: if(libfreeipmi_get_board_info (areabuf, area_length, ipmi_dev) < 0) { upsdebugx(1, "Can't retrieve board information"); } break; /* get specific PSU information */ case IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION: if(libfreeipmi_get_psu_info (areabuf, area_length, ipmi_dev) < 0) { upsdebugx(1, "Can't retrieve PSU information"); } break; default: upsdebugx (5, "FRU: discarding FRU Area Type Read: %02Xh", area_type); break; } } } while ((ret = ipmi_fru_next (fru_ctx)) == 1); /* check for errors */ if (ret < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_fru_next: %s", ipmi_fru_ctx_errormsg (fru_ctx)); } else { /* Get all related sensors information */ libfreeipmi_get_sensors_info (ipmi_dev); } /* cleanup context */ libfreeipmi_cleanup(); return (0); } void nut_ipmi_close(void) { upsdebugx(1, "nutipmi_close..."); libfreeipmi_cleanup(); } static const char* libfreeipmi_getfield (uint8_t language_code, ipmi_fru_field_t *field) { static char strbuf[IPMI_FRU_AREA_STRING_MAX + 1]; unsigned int strbuflen = IPMI_FRU_AREA_STRING_MAX; if (!field->type_length_field_length) return NULL; memset (strbuf, '\0', IPMI_FRU_AREA_STRING_MAX + 1); if (ipmi_fru_type_length_field_to_string (fru_ctx, field->type_length_field, field->type_length_field_length, language_code, strbuf, &strbuflen) < 0) { upsdebugx (2, "ipmi_fru_type_length_field_to_string: %s", ipmi_fru_ctx_errormsg (fru_ctx)); return NULL; } if (strbuflen) return strbuf; return NULL; } /* Get voltage value from the IPMI voltage code */ static float libfreeipmi_get_voltage (uint8_t voltage_code) { if (voltage_code == IPMI_FRU_VOLTAGE_12V) return 12; else if (voltage_code == IPMI_FRU_VOLTAGE_MINUS12V) return -12; else if (voltage_code == IPMI_FRU_VOLTAGE_5V) return 5; else if (voltage_code == IPMI_FRU_VOLTAGE_3_3V) return 3.3; else return 0; } /* Cleanup IPMI contexts */ static void libfreeipmi_cleanup() { /* cleanup */ if (fru_ctx) { ipmi_fru_close_device_id (fru_ctx); ipmi_fru_ctx_destroy (fru_ctx); } if (sdr_ctx) { ipmi_sdr_ctx_destroy (sdr_ctx); } #ifndef HAVE_FREEIPMI_11X_12X if (sdr_parse_ctx) { ipmi_sdr_parse_ctx_destroy (sdr_parse_ctx); } #endif if (ipmi_ctx) { ipmi_ctx_close (ipmi_ctx); ipmi_ctx_destroy (ipmi_ctx); } if (mon_ctx) { ipmi_monitoring_ctx_destroy (mon_ctx); } } /* Get generic board information (manufacturer and model names, serial, ...) * from IPMI FRU */ static int libfreeipmi_get_psu_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev) { /* FIXME: directly use ipmi_dev fields */ unsigned int overall_capacity; int low_end_input_voltage_range_1; int high_end_input_voltage_range_1; unsigned int low_end_input_frequency_range; unsigned int high_end_input_frequency_range; unsigned int voltage_1; /* FIXME: check for the interest and capability to use these data */ unsigned int peak_va; unsigned int inrush_current; unsigned int inrush_interval; int low_end_input_voltage_range_2; int high_end_input_voltage_range_2; unsigned int ac_dropout_tolerance; unsigned int predictive_fail_support; unsigned int power_factor_correction; unsigned int autoswitch; unsigned int hot_swap_support; unsigned int tachometer_pulses_per_rotation_predictive_fail_polarity; unsigned int peak_capacity; unsigned int hold_up_time; unsigned int voltage_2; unsigned int total_combined_wattage; unsigned int predictive_fail_tachometer_lower_threshold; upsdebugx(1, "entering libfreeipmi_get_psu_info()"); if (ipmi_fru_multirecord_power_supply_information (fru_ctx, areabuf, area_length, &overall_capacity, &peak_va, &inrush_current, &inrush_interval, &low_end_input_voltage_range_1, &high_end_input_voltage_range_1, &low_end_input_voltage_range_2, &high_end_input_voltage_range_2, &low_end_input_frequency_range, &high_end_input_frequency_range, &ac_dropout_tolerance, &predictive_fail_support, &power_factor_correction, &autoswitch, &hot_swap_support, &tachometer_pulses_per_rotation_predictive_fail_polarity, &peak_capacity, &hold_up_time, &voltage_1, &voltage_2, &total_combined_wattage, &predictive_fail_tachometer_lower_threshold) < 0) { fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: %s", ipmi_fru_ctx_errormsg (fru_ctx)); } ipmi_dev->overall_capacity = overall_capacity; /* Voltages are in mV! */ ipmi_dev->input_minvoltage = low_end_input_voltage_range_1 / 1000; ipmi_dev->input_maxvoltage = high_end_input_voltage_range_1 / 1000; ipmi_dev->input_minfreq = low_end_input_frequency_range; ipmi_dev->input_maxfreq = high_end_input_frequency_range; ipmi_dev->voltage = libfreeipmi_get_voltage(voltage_1); upsdebugx(1, "libfreeipmi_get_psu_info() retrieved successfully"); return (0); } /* Get specific PSU information from IPMI FRU */ static int libfreeipmi_get_board_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev) { uint8_t language_code; uint32_t mfg_date_time; ipmi_fru_field_t board_manufacturer; ipmi_fru_field_t board_product_name; ipmi_fru_field_t board_serial_number; ipmi_fru_field_t board_part_number; ipmi_fru_field_t board_fru_file_id; ipmi_fru_field_t board_custom_fields[IPMI_FRU_CUSTOM_FIELDS]; const char *string = NULL; time_t timetmp; struct tm mfg_date_time_tm; char mfg_date_time_buf[IPMI_FRU_STR_BUFLEN + 1]; upsdebugx(1, "entering libfreeipmi_get_board_info()"); /* clear fields */ memset (&board_manufacturer, '\0', sizeof (ipmi_fru_field_t)); memset (&board_product_name, '\0', sizeof (ipmi_fru_field_t)); memset (&board_serial_number, '\0', sizeof (ipmi_fru_field_t)); memset (&board_fru_file_id, '\0', sizeof (ipmi_fru_field_t)); memset (&board_custom_fields[0], '\0', sizeof (ipmi_fru_field_t) * IPMI_FRU_CUSTOM_FIELDS); /* parse FRU buffer */ if (ipmi_fru_board_info_area (fru_ctx, areabuf, area_length, &language_code, &mfg_date_time, &board_manufacturer, &board_product_name, &board_serial_number, &board_part_number, &board_fru_file_id, board_custom_fields, IPMI_FRU_CUSTOM_FIELDS) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_fru_board_info_area: %s", ipmi_fru_ctx_errormsg (fru_ctx)); } if (IPMI_FRU_LANGUAGE_CODE_VALID (language_code)) { upsdebugx (5, "FRU Board Language: %s", ipmi_fru_language_codes[language_code]); } else { upsdebugx (5, "FRU Board Language Code: %02Xh", language_code); } /* Posix says individual calls need not clear/set all portions of * 'struct tm', thus passing 'struct tm' between functions could * have issues. So we need to memset */ memset (&mfg_date_time_tm, '\0', sizeof (struct tm)); timetmp = mfg_date_time; localtime_r (&timetmp, &mfg_date_time_tm); memset (mfg_date_time_buf, '\0', IPMI_FRU_STR_BUFLEN + 1); strftime (mfg_date_time_buf, IPMI_FRU_STR_BUFLEN, "%D - %T", &mfg_date_time_tm); /* Store values */ ipmi_dev->date = xstrdup(mfg_date_time_buf); upsdebugx(2, "FRU Board Manufacturing Date/Time: %s", ipmi_dev->date); if ((string = libfreeipmi_getfield (language_code, &board_manufacturer)) != NULL) ipmi_dev->manufacturer = xstrdup(string); else ipmi_dev->manufacturer = xstrdup("Generic IPMI manufacturer"); if ((string = libfreeipmi_getfield (language_code, &board_product_name)) != NULL) ipmi_dev->product = xstrdup(string); else ipmi_dev->product = xstrdup("Generic PSU"); if ((string = libfreeipmi_getfield (language_code, &board_serial_number)) != NULL) ipmi_dev->serial = xstrdup(string); else ipmi_dev->serial = NULL; if ((string = libfreeipmi_getfield (language_code, &board_part_number)) != NULL) ipmi_dev->part = xstrdup(string); else ipmi_dev->part = NULL; return (0); } /* Get the sensors list & values, specific to the given FRU ID * Return -1 on error, or the number of sensors found otherwise */ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) { uint8_t sdr_record[IPMI_SDR_MAX_RECORD_LENGTH]; uint8_t record_type, logical_physical_fru_device, logical_fru_device_device_slave_address; uint8_t tmp_entity_id, tmp_entity_instance; int sdr_record_len; uint16_t record_count; int found_device_id = 0; uint16_t record_id; uint8_t entity_id, entity_instance; int i; if (ipmi_ctx == NULL) return (-1); /* Clear the sensors list */ ipmi_dev->sensors_count = 0; memset(ipmi_dev->sensors_id_list, 0, sizeof(ipmi_dev->sensors_id_list)); if (!(sdr_ctx = ipmi_sdr_ctx_create ())) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_ctx_create()"); } #ifndef HAVE_FREEIPMI_11X_12X if (!(sdr_parse_ctx = ipmi_sdr_parse_ctx_create ())) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_parse_ctx_create()"); } #endif if (ipmi_sdr_cache_open (sdr_ctx, ipmi_ctx, CACHE_LOCATION) < 0) { if (ipmi_sdr_ctx_errnum (sdr_ctx) != IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", ipmi_sdr_ctx_errormsg (sdr_ctx)); } } if (ipmi_sdr_ctx_errnum (sdr_ctx) == IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { if (ipmi_sdr_cache_create (sdr_ctx, ipmi_ctx, CACHE_LOCATION, NUT_IPMI_SDR_CACHE_DEFAULTS, NULL, NULL) < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_create: %s", ipmi_sdr_ctx_errormsg (sdr_ctx)); } if (ipmi_sdr_cache_open (sdr_ctx, ipmi_ctx, CACHE_LOCATION) < 0) { if (ipmi_sdr_ctx_errnum (sdr_ctx) != IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", ipmi_sdr_ctx_errormsg (sdr_ctx)); } } } if (ipmi_sdr_cache_record_count (sdr_ctx, &record_count) < 0) { fprintf (stderr, "ipmi_sdr_cache_record_count: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx(3, "Found %i records in SDR cache", record_count); for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_ctx)) { memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH); if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_ctx, sdr_record, IPMI_SDR_MAX_RECORD_LENGTH)) < 0) { fprintf (stderr, "ipmi_sdr_cache_record_read: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } if (ipmi_sdr_parse_record_id_and_type (SDR_PARSE_CTX, sdr_record, sdr_record_len, NULL, &record_type) < 0) { fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx (5, "Checking record %i (/%i)", i, record_count); if (record_type != IPMI_SDR_FORMAT_FRU_DEVICE_LOCATOR_RECORD) { upsdebugx(1, "=======> not device locator (%i)!!", record_type); continue; } if (ipmi_sdr_parse_fru_device_locator_parameters (SDR_PARSE_CTX, sdr_record, sdr_record_len, NULL, &logical_fru_device_device_slave_address, NULL, NULL, &logical_physical_fru_device, NULL) < 0) { fprintf (stderr, "ipmi_sdr_parse_fru_device_locator_parameters: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx(2, "Checking device %i/%i", logical_physical_fru_device, logical_fru_device_device_slave_address); if (logical_physical_fru_device && logical_fru_device_device_slave_address == ipmi_dev->ipmi_id) { found_device_id++; if (ipmi_sdr_parse_fru_entity_id_and_instance (SDR_PARSE_CTX, sdr_record, sdr_record_len, &entity_id, &entity_instance) < 0) { fprintf (stderr, "ipmi_sdr_parse_fru_entity_id_and_instance: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } break; } } if (!found_device_id) { fprintf (stderr, "Couldn't find device id %d\n", ipmi_dev->ipmi_id); goto cleanup; } else upsdebugx(1, "Found device id %d", ipmi_dev->ipmi_id); if (ipmi_sdr_cache_first (sdr_ctx) < 0) { fprintf (stderr, "ipmi_sdr_cache_first: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_ctx)) { /* uint8_t sdr_record[IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH]; uint8_t record_type, tmp_entity_id, tmp_entity_instance; int sdr_record_len; */ memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH); if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_ctx, sdr_record, IPMI_SDR_MAX_RECORD_LENGTH)) < 0) { fprintf (stderr, "ipmi_sdr_cache_record_read: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } if (ipmi_sdr_parse_record_id_and_type (SDR_PARSE_CTX, sdr_record, sdr_record_len, &record_id, &record_type) < 0) { fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx (5, "Checking record %i (/%i)", record_id, record_count); if (record_type != IPMI_SDR_FORMAT_FULL_SENSOR_RECORD && record_type != IPMI_SDR_FORMAT_COMPACT_SENSOR_RECORD && record_type != IPMI_SDR_FORMAT_EVENT_ONLY_RECORD) { continue; } if (ipmi_sdr_parse_entity_id_instance_type (SDR_PARSE_CTX, sdr_record, sdr_record_len, &tmp_entity_id, &tmp_entity_instance, NULL) < 0) { fprintf (stderr, "ipmi_sdr_parse_entity_instance_type: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } if (tmp_entity_id == entity_id && tmp_entity_instance == entity_instance) { upsdebugx (1, "Found record id = %u for device id %u", record_id, ipmi_dev->ipmi_id); /* Add it to the tracked list */ ipmi_dev->sensors_id_list[ipmi_dev->sensors_count] = record_id; ipmi_dev->sensors_count++; } } cleanup: /* Cleanup */ if (sdr_ctx) { ipmi_sdr_ctx_destroy (sdr_ctx); } #ifndef HAVE_FREEIPMI_11X_12X if (sdr_parse_ctx) { ipmi_sdr_parse_ctx_destroy (sdr_parse_ctx); } #endif /* HAVE_FREEIPMI_11X_12X */ return ipmi_dev->sensors_count; } /* => Nominal conditions Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading, Sensor Units, Sensor Event/Reading Type Code, Sensor Event Bitmask, Sensor Event String 52, Presence, 84, Entity Presence, Nominal, N/A, N/A, 6Fh, 1h, 'Entity Present' 57, Status, 100, Power Supply, Nominal, N/A, N/A, 6Fh, 1h, 'Presence detected' 116, Current, 148, Current, Nominal, 0.20, A, 1h, C0h, 'OK' 118, Voltage, 150, Voltage, Nominal, 236.00, V, 1h, C0h, 'OK' => Power failure conditions Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading, Sensor Units, Sensor Event/Reading Type Code, Sensor Event Bitmask, Sensor Event String 52, Presence, 84, Entity Presence, Nominal, N/A, N/A, 6Fh, 1h, 'Entity Present' 57, Status, 100, Power Supply, Critical, N/A, N/A, 6Fh, 9h, 'Presence detected' 'Power Supply input lost (AC/DC)' => PSU removed Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading, Sensor Units, Sensor Event/Reading Type Code, Sensor Event Bitmask, Sensor Event String 52, Presence, 84, Entity Presence, Critical, N/A, N/A, 6Fh, 2h, 'Entity Absent' 57, Status, 100, Power Supply, Critical, N/A, N/A, 6Fh, 8h, 'Power Supply input lost (AC/DC)' */ int nut_ipmi_monitoring_init() { int errnum; if (ipmi_monitoring_init (0, &errnum) < 0) { upsdebugx (1, "ipmi_monitoring_init() error: %s", ipmi_monitoring_ctx_strerror (errnum)); return -1; } if (!(mon_ctx = ipmi_monitoring_ctx_create ())) { upsdebugx (1, "ipmi_monitoring_ctx_create() failed"); return -1; } #if HAVE_FREEIPMI_MONITORING /* FIXME: replace "/tmp" by a proper place, using mkdtemp() or similar */ if (ipmi_monitoring_ctx_sdr_cache_directory (mon_ctx, "/tmp") < 0) { upsdebugx (1, "ipmi_monitoring_ctx_sdr_cache_directory() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); return -1; } if (ipmi_monitoring_ctx_sensor_config_file (mon_ctx, NULL) < 0) { upsdebugx (1, "ipmi_monitoring_ctx_sensor_config_file() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); return -1; } #endif /* HAVE_FREEIPMI_MONITORING */ return 0; } int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) { int retval = -1; #if HAVE_FREEIPMI_MONITORING /* It seems we don't need more! */ unsigned int sensor_reading_flags = IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_NON_INTERPRETABLE_SENSORS; int sensor_count, i, str_count; int psu_status = PSU_STATUS_UNKNOWN; if (mon_ctx == NULL) { upsdebugx (1, "Monitoring context not initialized!"); return -1; } /* Monitor only the list of sensors found previously */ if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (mon_ctx, NULL, /* hostname is NULL for In-band communication */ NULL, /* FIXME: needed? ipmi_config */ sensor_reading_flags, ipmi_dev->sensors_id_list, ipmi_dev->sensors_count, NULL, NULL)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_readings_by_record_id() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); return -1; } upsdebugx (1, "nut_ipmi_get_sensors_status: %i sensors to check", sensor_count); for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next (mon_ctx)) { int record_id, sensor_type; int sensor_bitmask_type = -1; /* int sensor_reading_type, sensor_state; */ char **sensor_bitmask_strings = NULL; void *sensor_reading = NULL; if ((record_id = ipmi_monitoring_sensor_read_record_id (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_record_id() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } if ((sensor_type = ipmi_monitoring_sensor_read_sensor_type (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_type() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } upsdebugx (1, "checking sensor #%i, type %i", record_id, sensor_type); /* should we consider this for ALARM? * IPMI_MONITORING_STATE_NOMINAL * IPMI_MONITORING_STATE_WARNING * IPMI_MONITORING_STATE_CRITICAL * if ((sensor_state = ipmi_monitoring_sensor_read_sensor_state (mon_ctx)) < 0) * ... */ if ((sensor_reading = ipmi_monitoring_sensor_read_sensor_reading (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_reading() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); } /* This can be needed to interpret sensor_reading format! if ((sensor_reading_type = ipmi_monitoring_sensor_read_sensor_reading_type (ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_reading_type() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); } */ if ((sensor_bitmask_type = ipmi_monitoring_sensor_read_sensor_bitmask_type (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_bitmask_type() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } if ((sensor_bitmask_strings = ipmi_monitoring_sensor_read_sensor_bitmask_strings (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_bitmask_strings() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } /* Only the few possibly interesting sensors are considered */ switch (sensor_type) { case IPMI_MONITORING_SENSOR_TYPE_TEMPERATURE: ipmi_dev->temperature = *((double *)sensor_reading); upsdebugx (3, "Temperature: %.2f", *((double *)sensor_reading)); dstate_setinfo("ambient.temperature", "%.2f", *((double *)sensor_reading)); retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_VOLTAGE: ipmi_dev->voltage = *((double *)sensor_reading); upsdebugx (3, "Voltage: %.2f", *((double *)sensor_reading)); dstate_setinfo("input.voltage", "%.2f", *((double *)sensor_reading)); retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_CURRENT: ipmi_dev->input_current = *((double *)sensor_reading); upsdebugx (3, "Current: %.2f", *((double *)sensor_reading)); dstate_setinfo("input.current", "%.2f", *((double *)sensor_reading)); retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_POWER_SUPPLY: /* Possible values: * 'Presence detected' * 'Power Supply input lost (AC/DC)' => maps to status:OFF */ upsdebugx (3, "Power Supply: status string"); if (sensor_bitmask_type == IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) { upsdebugx(3, "No status string"); } str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); if (!strncmp("Power Supply input lost (AC/DC)", sensor_bitmask_strings[str_count], strlen("Power Supply input lost (AC/DC)"))) { /* Don't override PSU absence! */ if (psu_status != PSU_ABSENT) { psu_status = PSU_POWER_FAILURE; /* = status OFF */ } } str_count++; } break; case IPMI_MONITORING_SENSOR_TYPE_ENTITY_PRESENCE: /* Possible values: * 'Entity Present' => maps to status:OL * 'Entity Absent' (PSU has been removed!) => declare staleness */ upsdebugx (3, "Entity Presence: status string"); if (sensor_bitmask_type == IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) { upsdebugx(3, "No status string"); } str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); if (!strncmp("Entity Present", sensor_bitmask_strings[str_count], strlen("Entity Present"))) { psu_status = PSU_PRESENT; } else if (!strncmp("Entity Absent", sensor_bitmask_strings[str_count], strlen("Entity Absent"))) { psu_status = PSU_ABSENT; } str_count++; } break; /* Not sure of the values of these, so get as much as possible... */ case IPMI_MONITORING_SENSOR_TYPE_POWER_UNIT: upsdebugx (3, "Power Unit: status string"); str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); str_count++; } break; case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_ACPI_POWER_STATE: upsdebugx (3, "System ACPI Power State: status string"); str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); str_count++; } break; case IPMI_MONITORING_SENSOR_TYPE_BATTERY: upsdebugx (3, "Battery: status string"); str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); str_count++; } break; } } /* Process status if needed */ if (psu_status != PSU_STATUS_UNKNOWN) { status_init(); switch (psu_status) { case PSU_PRESENT: status_set("OL"); retval = 0; break; case PSU_ABSENT: status_set("OFF"); /* Declare stale */ retval = -1; break; case PSU_POWER_FAILURE: status_set("OFF"); retval = 0; break; } status_commit(); } #endif /* HAVE_FREEIPMI_MONITORING */ return retval; } /* --chassis-control=CONTROL Control the chassis. This command provides power-up, power-down, and reset control. Supported values: POWER-DOWN, POWER-UP, POWER-CYCLE, HARD-RESET, DIAGNOS†TIC-INTERRUPT, SOFT-SHUTDOWN. */ nut-2.7.4/drivers/nut-ipmi.h0000644000175000017500000000420512640443572012654 00000000000000/* nut-ipmi.h - Abstract IPMI interface, to allow using different IPMI backends * * Copyright (C) 2011 - Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUT_IPMI_H #define NUT_IPMI_H typedef enum { PSU_STATUS_UNKNOWN = 1, PSU_PRESENT, /* = status OL */ PSU_ABSENT, /* = status stale */ PSU_POWER_FAILURE /* = status OFF */ } psu_status_t; /* Abstract structure to store information */ typedef struct IPMIDevice_s { int ipmi_id; /* FRU ID */ char* manufacturer; /* Manufacturer Name */ char* product; /* Product Name */ char* serial; /* Product serial number */ char* part; /* Part Number */ char* date; /* Manufacturing Date/Time */ int overall_capacity; /* realpower.nominal? */ int input_minvoltage; int input_maxvoltage; int input_minfreq; int input_maxfreq; int voltage; /* psu.voltage or device.voltage */ unsigned int sensors_count; /* number of sensors IDs in sensors_id_list */ unsigned int sensors_id_list[20]; /* ID of sensors linked to this FRU */ /* measurements... */ int status; /* values from psu_status_t */ double input_voltage; double input_current; double temperature; } IPMIDevice_t; /* Generic functions, to implement in the backends */ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev); void nut_ipmi_close(void); int nut_ipmi_monitoring_init(); int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev); #endif /* NUT_IPMI_H */ nut-2.7.4/drivers/idowell-hid.h0000644000175000017500000000217012640443572013312 00000000000000/* idowell-hid.h - subdriver to monitor iDowell USB/HID devices with NUT * * Copyright (C) * 2003 - 2009 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef IDOWELL_HID_H #define IDOWELL_HID_H #include "usbhid-ups.h" extern subdriver_t idowell_subdriver; #endif /* IDOWELL_HID_H */ nut-2.7.4/drivers/nutdrv_qx_zinto.h0000644000175000017500000000174412640473702014372 00000000000000/* nutdrv_qx_zinto.h - Subdriver for Zinto protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_ZINTO_H #define NUTDRV_QX_ZINTO_H #include "nutdrv_qx.h" extern subdriver_t zinto_subdriver; #endif /* NUTDRV_QX_ZINTO_H */ nut-2.7.4/drivers/nutdrv_qx_q1.c0000644000175000017500000001406512640473702013543 00000000000000/* nutdrv_qx_q1.c - Subdriver for Q1 protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 * * NOTE: * This subdriver implements the same protocol as the one used by the 'megatec' subdriver minus the vendor (I) and ratings (F) queries. * In the claim function: * - it doesn't even try to get 'vendor' informations (I) * - it checks only status (Q1), through 'input.voltage' variable * Therefore it should be able to work even if the UPS doesn't support vendor/ratings *and* the user doesn't use the 'novendor'/'norating' flags, as long as: * - the UPS replies a Q1-compliant answer (i.e. not necessary filled with all of the Q1-required data, but at least of the right length and with not available data filled with some replacement character) * - the UPS reports a valid input.voltage (used in the claim function) * - the UPS reports valid status bits (1st, 2nd, 3rd, 6th, 7th are the mandatory ones) * */ #include "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_q1.h" #define Q1_VERSION "Q1 0.07" /* qx2nut lookup table */ static item_t q1_qx2nut[] = { /* * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* Testing table */ #ifdef TESTING static testing_t q1_testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "Q\r", "", -1 }, { "S03\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "", -1 }, { "T04\r", "", -1 }, { "TL\r", "", -1 }, { "T\r", "", -1 }, { "CT\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* Subdriver-specific initups */ static void q1_initups(void) { blazer_initups_light(q1_qx2nut); } /* Subdriver interface */ subdriver_t q1_subdriver = { Q1_VERSION, blazer_claim_light, q1_qx2nut, q1_initups, NULL, blazer_makevartable_light, "ACK", NULL, #ifdef TESTING q1_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/everups.c0000644000175000017500000001037012640443572012576 00000000000000/* everups.c - support for Ever UPS models Copyright (C) 2001 Bartek Szady 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 "main.h" #include "serial.h" #define DRIVER_NAME "Ever UPS driver" #define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Bartek Szady ", DRV_STABLE, { NULL } }; static unsigned char upstype = 0; static void init_serial(void) { ser_set_dtr(upsfd, 0); ser_set_rts(upsfd, 0); } static int Code(int tries) { unsigned char cRecv; do { ser_send_char(upsfd, 208); ser_get_char(upsfd, &cRecv, 3, 0); if (cRecv==208) return 1; } while (--tries>0); return 0; } static int InitUpsType(void) { if (Code(1)) { ser_send_char(upsfd, 173); ser_get_char(upsfd, &upstype, 3, 0); return 1; } else return 0; } static const char *GetTypeUpsName(void) { switch(upstype) { case 67: return "NET 500-DPC"; case 68: return "NET 700-DPC"; case 69: return "NET 1000-DPC"; case 70: return "NET 1400-DPC"; case 71: return "NET 2200-DPC"; case 73: return "NET 700-DPC (new)"; case 74: return "NET 1000-DPC (new)"; case 75: return "NET 1400-DPC (new)"; case 76: return "NET 500-DPC (new)"; case 81: return "AP 450-PRO"; case 82: return "AP 650-PRO"; default: return "Unknown"; } } void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "Ever"); dstate_setinfo("ups.model", "%s", GetTypeUpsName()); } void upsdrv_updateinfo(void) { int battery=0,standby=0; unsigned char recBuf[2]; unsigned long acuV; unsigned long lineV; double fVal; if (!Code(2)) { upslog_with_errno(LOG_INFO, "Code failed"); dstate_datastale(); return; } /*Line status*/ ser_send_char(upsfd, 175); ser_get_char(upsfd, recBuf, 3, 0); if ((recBuf[0] & 1) !=0) standby=1; else battery=(recBuf[0] &4) !=0; if (Code(1)) { /*Accumulator voltage value*/ ser_send_char(upsfd, 189); ser_get_char(upsfd, recBuf, 3, 0); acuV=((unsigned long)recBuf[0])*150; acuV/=255; } else { upslog_with_errno(LOG_INFO, "Code failed"); dstate_datastale(); return; } if (Code(1)) { /*Line voltage*/ ser_send_char(upsfd, 245); ser_get_buf_len(upsfd, recBuf, 2, 3, 0); if ( upstype > 72 && upstype < 77) lineV=(recBuf[0]*100+recBuf[1]*25600)/352; else lineV=(recBuf[0]*100+recBuf[1]*25600)/372; } else { upslog_with_errno(LOG_INFO, "Code failed"); dstate_datastale(); return; } status_init(); if (battery && acuV<105) status_set("LB"); /* low battery */ if (battery) status_set("OB"); /* on battery */ else status_set("OL"); /* on line */ status_commit(); dstate_setinfo("input.voltage", "%03ld", lineV); dstate_setinfo("battery.voltage", "%03.2f", (double)acuV /10.0); fVal=((double)acuV-95.0)*100.0; if (standby) fVal/=(135.5-95.0); else fVal/=(124.5-95.0); if (fVal>100) fVal=100; else if (fVal<0) fVal=0; dstate_setinfo("battery.charge", "%03.1f", fVal); dstate_dataok(); } void upsdrv_shutdown(void) { if (!Code(2)) { upslog_with_errno(LOG_INFO, "Code failed"); return; } ser_send_char(upsfd, 28); ser_send_char(upsfd, 1); /* 1.28 sec */ if (!Code(1)) { upslog_with_errno(LOG_INFO, "Code failed"); return; } ser_send_char(upsfd, 13); ser_send_char(upsfd, 8); } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B300); init_serial(); InitUpsType(); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/apc-mib.c0000644000175000017500000004553412667537407012441 00000000000000/* apc-mib.c - data to monitor APC SNMP devices (Powernet MIB) with NUT * * Copyright (C) * 2002-2003 - Dmitry Frolov * 2002-2012 - Arnaud Quette * 2012 - Chew Hong Gunn (high precision values) * * Sponsored by MGE UPS SYSTEMS * * 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 "apc-mib.h" #define APCC_MIB_VERSION "1.2" /* Other APC sysOID: * * examples found on the Net and other sources: * 'enterprises.apc.products.system.smartUPS.smartUPS700' * - from fence agents * '.1.3.6.1.4.1.318.1.3.4.5': ApcRPDU, * '.1.3.6.1.4.1.318.1.3.4.4': ApcMSP * - from Bill Seligman * .1.3.6.1.4.1.318.1.3.2.11 * .1.3.6.1.4.1.318.1.3.2.12 * .1.3.6.1.4.1.318.1.3.27 * .1.3.6.1.4.1.318.1.3.2.7 * .1.3.6.1.4.1.318.1.3.2.8 */ /* TODO: find the right sysOID for this MIB * Ie ".1.3.6.1.4.1.318.1.1.1" or ".1.3.6.1.4.1.318" or? */ /* .1.3.6.1.4.1.318.1.1.1 * enterprise^ * apc ---------^ * products ------^ * hardware --------^ * ups ---------------^ * ref: ftp://ftp.apc.com/apc/public/software/pnetmib/mib/404/powernet404.mib */ /* info elements */ #define APCC_OID_BATT_STATUS ".1.3.6.1.4.1.318.1.1.1.2.1.1.0" /* Defines for APCC_OID_BATT_STATUS */ static info_lkp_t apcc_batt_info[] = { { 1, "" }, /* unknown */ { 2, "" }, /* batteryNormal */ { 3, "LB" }, /* batteryLow */ { 0, NULL } } ; #define APCC_OID_POWER_STATUS ".1.3.6.1.4.1.318.1.1.1.4.1.1.0" /* Defines for APCC_OID_POWER_STATUS */ static info_lkp_t apcc_pwr_info[] = { { 1, "" }, /* unknown */ { 2, "OL" }, /* onLine */ { 3, "OB" }, /* onBattery */ { 4, "OL BOOST" }, /* onSmartBoost */ { 5, "OFF" }, /* timedSleeping */ { 6, "OFF" }, /* softwareBypass */ { 7, "OFF" }, /* off */ { 8, "" }, /* rebooting */ { 9, "BYPASS" }, /* switchedBypass */ { 10, "BYPASS" }, /* hardwareFailureBypass */ { 11, "OFF" }, /* sleepingUntilPowerReturn */ { 12, "OL TRIM" }, /* onSmartTrim */ { 0, NULL } } ; #define APCC_OID_CAL_RESULTS ".1.3.6.1.4.1.318.1.1.1.7.2.6.0" static info_lkp_t apcc_cal_info[] = { { 1, "" }, /* Calibration Successful */ { 2, "" }, /* Calibration not done, battery capacity below 100% */ { 3, "CAL" }, /* Calibration in progress */ { 0, NULL } }; #define APCC_OID_NEEDREPLBATT ".1.3.6.1.4.1.318.1.1.1.2.2.4.0" static info_lkp_t apcc_battrepl_info[] = { { 1, "" }, /* No battery needs replacing */ { 2, "RB" }, /* Batteries need to be replaced */ { 0, NULL } }; #define APCC_OID_TESTDIAGRESULTS ".1.3.6.1.4.1.318.1.1.1.7.2.3.0" static info_lkp_t apcc_testdiag_results[] = { { 1, "Ok" }, { 2, "Failed" }, { 3, "InvalidTest" }, { 4, "TestInProgress"}, { 0, NULL } }; #define APCC_OID_SENSITIVITY ".1.3.6.1.4.1.318.1.1.1.5.2.7.0" static info_lkp_t apcc_sensitivity_modes[] = { { 1, "auto" }, { 2, "low" }, { 3, "medium" }, { 4, "high" }, { 0, NULL } }; #define APCC_OID_TRANSFERREASON "1.3.6.1.4.1.318.1.1.1.3.2.5.0" static info_lkp_t apcc_transfer_reasons[] = { { 1, "noTransfer" }, { 2, "highLineVoltage" }, { 3, "brownout" }, { 4, "blackout" }, { 5, "smallMomentarySag" }, { 6, "deepMomentarySag" }, { 7, "smallMomentarySpike" }, { 8, "largeMomentarySpike" }, { 9, "selfTest" }, { 10, "rateOfVoltageChange" } }; /* --- */ /* commands */ #define APCC_OID_REBOOT ".1.3.6.1.4.1.318.1.1.1.6.2.2" #define APCC_REBOOT_DO 2 #define APCC_REBOOT_GRACEFUL 3 #if 0 /* not used. */ #define APCC_OID_SLEEP ".1.3.6.1.4.1.318.1.1.1.6.2.3" #define APCC_SLEEP_ON "2" #define APCC_SLEEP_GRACEFUL "3" #endif static snmp_info_t apcc_mib[] = { /* info elements. */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", "Generic Powernet SNMP device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.3.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.1.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, { "input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.2.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, { "input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.3.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, { "input.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", "", SU_FLAG_OK, NULL }, { "input.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.2.0", "", SU_FLAG_OK, NULL }, { "input.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.3.0", "", SU_FLAG_OK, NULL }, { "input.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "input.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3-L1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L1-L2.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2-L3.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3-L1.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L1-L2.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2-L3.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3-L1.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L1.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L1.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "input.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.4.0", "", SU_FLAG_OK, NULL }, { "input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.3.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "input.transfer.reason", ST_FLAG_STRING, 1, APCC_OID_TRANSFERREASON, "", SU_TYPE_INT | SU_FLAG_OK, apcc_transfer_reasons }, { "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, APCC_OID_SENSITIVITY, "", SU_TYPE_INT | SU_FLAG_OK, apcc_sensitivity_modes }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_POWER_STATUS, "OFF", SU_FLAG_OK | SU_STATUS_PWR, apcc_pwr_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_BATT_STATUS, "", SU_FLAG_OK | SU_STATUS_BATT, apcc_batt_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_CAL_RESULTS, "", SU_FLAG_OK | SU_STATUS_CAL, apcc_cal_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_NEEDREPLBATT, "", SU_FLAG_OK | SU_STATUS_RB, apcc_battrepl_info }, { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.2.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL }, { "ups.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", "", SU_FLAG_OK, NULL }, { "ups.load", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.3.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "ups.load", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.3.0", "", SU_FLAG_OK, NULL }, { "ups.firmware", ST_FLAG_STRING, 16, ".1.3.6.1.4.1.318.1.1.1.1.2.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.10.0", "", SU_FLAG_OK, NULL }, { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.9.0", "", SU_FLAG_OK, NULL }, { "battery.charge", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.1.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "battery.charge", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", "", SU_FLAG_OK, NULL }, { "battery.charge.restart", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.6.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "battery.runtime", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", "", SU_FLAG_OK, NULL }, { "battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.8.0", "", SU_FLAG_OK, NULL }, { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "battery.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.8.0", "", SU_FLAG_OK, NULL }, { "battery.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.7.0", "", SU_FLAG_OK, NULL }, { "battery.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.5.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL }, { "battery.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.9.0", "", SU_FLAG_OK, NULL }, { "battery.current.total", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.6.0", "", SU_FLAG_OK, NULL }, { "battery.packs", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.5.0", "", SU_FLAG_OK, NULL }, { "battery.packs.bad", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.6.0", "", SU_FLAG_OK, NULL }, { "battery.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.2.1.3.0", "", SU_FLAG_OK | SU_FLAG_STATIC | SU_TYPE_STRING, NULL }, { "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.1.1.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC | SU_TYPE_STRING, NULL }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_TESTDIAGRESULTS, "", SU_FLAG_OK, apcc_testdiag_results }, { "ups.test.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.7.2.4.0", "", SU_FLAG_OK | SU_FLAG_STATIC | SU_TYPE_STRING, NULL }, { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.1.0", "", SU_FLAG_OK | SU_FLAG_UNIQUE, NULL }, { "output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.1.0", "", SU_FLAG_OK, NULL }, { "output.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.2.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.2.0", "", SU_FLAG_OK, NULL }, { "output.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "output.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", "", SU_FLAG_OK, NULL }, { "output.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3-L1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L1.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L3.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.voltage.nominal", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, /* Measure-UPS ambient variables */ /* Environmental sensors (AP9612TH and others) */ { "ambient.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.1.0", "", SU_FLAG_OK, NULL }, { "ambient.1.temperature.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.3.1", "", SU_FLAG_OK, NULL }, { "ambient.1.temperature.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.4.1", "", SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.2.0", "", SU_FLAG_OK, NULL }, { "ambient.1.humidity.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL }, { "ambient.1.humidity.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL }, /* IEM ambient variables */ /* IEM: integrated environment monitor probe */ #define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" #define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" #define APCC_IEM_FAHRENHEIT 2 #define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" { "ambient.temperature", 0, 1, APCC_OID_IEM_TEMP, "", SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 1, APCC_OID_IEM_HUMID, "", SU_FLAG_OK, NULL }, /* instant commands. */ { "load.off", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "load.on", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.6.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "shutdown.stayoff", 0, 3, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* { CMD_SDRET, 0, APCC_REBOOT_GRACEFUL, APCC_OID_REBOOT, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ { "shutdown.return", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.1.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "test.failure.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.4.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "test.panel.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.5.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "bypass.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "bypass.stop", 0, 3, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "test.battery.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.7.2.2.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "calibrate.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "calibrate.stop", 0, 3, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, { "reset.input.minmax", 0, 2, ".1.3.6.1.4.1.318.1.1.1.9.1.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; mib2nut_info_t apc = { "apcc", APCC_MIB_VERSION, APCC_OID_POWER_STATUS, ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", apcc_mib }; /* vim:ts=4:sw=4:et: */ nut-2.7.4/drivers/nutdrv_qx_voltronic-qs-hex.c0000644000175000017500000003773212640473702016452 00000000000000/* nutdrv_qx_voltronic-qs-hex.c - Subdriver for Voltronic Power UPSes with QS-Hex protocol * * Copyright (C) * 2014 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_voltronic-qs-hex.h" #define VOLTRONIC_QS_HEX_VERSION "Voltronic-QS-Hex 0.10" /* Support functions */ static int voltronic_qs_hex_claim(void); static void voltronic_qs_hex_initups(void); /* Answer preprocess functions */ static int voltronic_qs_hex_preprocess_qs_answer(item_t *item, const int len); static int voltronic_qs_hex_char_to_binary(const unsigned char value); /* Preprocess functions */ static int voltronic_qs_hex_protocol(item_t *item, char *value, const size_t valuelen); static int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, const size_t valuelen); static int voltronic_qs_hex_load(item_t *item, char *value, const size_t valuelen); static int voltronic_qs_hex_frequency(item_t *item, char *value, const size_t valuelen); static int voltronic_qs_hex_battery_voltage(item_t *item, char *value, const size_t valuelen); static int voltronic_qs_hex_process_ratings_bits(item_t *item, char *value, const size_t valuelen); /* == Ranges == */ /* Range for ups.delay.start */ static info_rw_t voltronic_qs_hex_r_ondelay[] = { { "60", 0 }, { "599940", 0 }, { "", 0 } }; /* Range for ups.delay.shutdown */ static info_rw_t voltronic_qs_hex_r_offdelay[] = { { "12", 0 }, { "540", 0 }, { "", 0 } }; /* == qx2nut lookup table == */ static item_t voltronic_qs_hex_qx2nut[] = { /* Query UPS for protocol * > [M\r] * < [P\r] * 01 * 0 */ { "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "PM-%s", QX_FLAG_STATIC, NULL, NULL, voltronic_qs_hex_protocol }, /* Query UPS for status * > [QS\r] * < [#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001\r] ('P' protocol, after being preprocessed) * < [#6901 6C 6802 6C 00 5FD7 12C000 E4 1E 00001001 00000010\r] ('T' protocol, after being preprocessed) * 01234567890123456789012345678901234567890123456789012345 * 0 1 2 3 4 5 */ { "input.voltage", 0, NULL, "QS\r", "", 47, '#', "", 1, 7, "%.1f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_input_output_voltage }, { "output.voltage", 0, NULL, "QS\r", "", 47, '#', "", 9, 15, "%.1f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_input_output_voltage }, { "ups.load", 0, NULL, "QS\r", "", 47, '#', "", 17, 18, "%d", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_load }, { "output.frequency", 0, NULL, "QS\r", "", 47, '#', "", 20, 30, "%.1f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_frequency }, { "battery.voltage", 0, NULL, "QS\r", "", 47, '#', "", 32, 36, "%.2f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_battery_voltage }, /* Status bits */ { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "QS\r", "", 47, '#', "", 41, 41, NULL, 0, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "QS\r", "", 47, '#', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "QS\r", "", 47, '#', "", 45, 45, "%s", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Beeper status */ /* Ratings bits */ { "output.frequency.nominal", 0, NULL, "QS\r", "", 56, '#', "", 47, 47, "%.1f", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, { "battery.voltage.nominal", 0, NULL, "QS\r", "", 56, '#', "", 48, 49, "%.1f", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, /* { "reserved.1", 0, NULL, "QS\r", "", 56, '#', "", 50, 50, "%s", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, *//* Reserved */ /* { "reserved.2", 0, NULL, "QS\r", "", 56, '#', "", 51, 51, "%s", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, *//* Reserved */ { "output.voltage.nominal", 0, NULL, "QS\r", "", 56, '#', "", 52, 54, "%.1f", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, voltronic_qs_hex_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_hex_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* == Testing table == */ #ifdef TESTING static testing_t voltronic_qs_hex_testing[] = { { "QS\r", "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r", 27 }, { "M\r", "P\r", -1 }, { "Q\r", "", -1 }, { "S00R0000\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "N\r", -1 }, { "T\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* == Support functions == */ /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ static int voltronic_qs_hex_claim(void) { /* We need at least M and QS to run this subdriver */ /* UPS Protocol */ item_t *item = find_nut_info("ups.firmware.aux", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value/Protocol not supported */ if (ups_infoval_set(item) != 1) return 0; item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) { dstate_delinfo("ups.firmware.aux"); return 0; } /* No reply/Unable to get value */ if (qx_process(item, NULL)) { dstate_delinfo("ups.firmware.aux"); return 0; } /* Unable to process value */ if (ups_infoval_set(item) != 1) { dstate_delinfo("ups.firmware.aux"); return 0; } return 1; } /* Subdriver-specific initups */ static void voltronic_qs_hex_initups(void) { blazer_initups_light(voltronic_qs_hex_qx2nut); } /* == Answer preprocess functions == */ /* Preprocess the answer we got back from the UPS when queried with 'QS\r' */ static int voltronic_qs_hex_preprocess_qs_answer(item_t *item, const int len) { int i, token; char refined[SMALLBUF] = ""; if (len <= 0) return len; if (item->answer[0] != '#') { upsdebugx(4, "%s: wrong leading character [%s: 0x%0x]", __func__, item->info_type, item->answer[0]); return -1; } snprintf(refined, sizeof(refined), "%s", "#"); /* e.g.: item->answer = "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r" */ upsdebug_hex(4, "read", item->answer, len); for (i = 1, token = 1; i < len; i++) { /* New token */ if (item->answer[i] == 0x20) { snprintfcat(refined, sizeof(refined), "%s", " "); token++; continue; } /* 'Unescape' raw data */ if (item->answer[i] == 0x28 && i < len) { switch (item->answer[i + 1]) { case 0x00: /* Escaped because: CR */ snprintfcat(refined, sizeof(refined), "%02x", 0x0D); break; case 0x01: /* Escaped because: XON */ snprintfcat(refined, sizeof(refined), "%02x", 0x11); break; case 0x02: /* Escaped because: XOFF */ snprintfcat(refined, sizeof(refined), "%02x", 0x13); break; case 0x03: /* Escaped because: LF */ snprintfcat(refined, sizeof(refined), "%02x", 0x0A); break; case 0x04: /* Escaped because: space */ snprintfcat(refined, sizeof(refined), "%02x", 0x20); break; default: if (token != 10 && token != 11) snprintfcat(refined, sizeof(refined), "%02x", ((unsigned char *)item->answer)[i]); else snprintfcat(refined, sizeof(refined), "%08d", voltronic_qs_hex_char_to_binary(((unsigned char *)item->answer)[i])); continue; } i++; continue; } /* Trailing CR */ if (item->answer[i] == 0x0D) break; if (token != 10 && token != 11) snprintfcat(refined, sizeof(refined), "%02x", ((unsigned char *)item->answer)[i]); else snprintfcat(refined, sizeof(refined), "%08d", voltronic_qs_hex_char_to_binary(((unsigned char *)item->answer)[i])); } if ( token < 10 || token > 11 || (token == 10 && strlen(refined) != 46) || (token == 11 && strlen(refined) != 55) ) { upsdebugx(2, "noncompliant reply: %s", refined); return -1; } upsdebugx(4, "read: %s", refined); /* e.g.: item->answer = "#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001" */ return snprintf(item->answer, sizeof(item->answer), "%s\r", refined); } /* Transform a char into its binary form (as an int) */ static int voltronic_qs_hex_char_to_binary(const unsigned char value) { unsigned char remainder = value; int ret = 0, power = 1; while (remainder) { if (remainder & 1) ret += power; power *= 10; remainder >>= 1; } return ret; } /* == Preprocess functions == */ /* Protocol used by the UPS */ static int voltronic_qs_hex_protocol(item_t *item, char *value, const size_t valuelen) { item_t *unskip; int i; const struct { const char *info_type; /* info_type of the item to be unskipped */ const unsigned long flags; /* qxflags that have to be set in the item */ const unsigned long noflags; /* qxflags that have to be absent in the item */ } items_to_be_unskipped[] = { { "test.battery.start.quick", QX_FLAG_CMD, 0 }, { "output.frequency.nominal", 0, 0 }, { "battery.voltage.nominal", 0, 0 }, { "output.voltage.nominal", 0, 0 }, { NULL, 0, 0 } }; if (strcasecmp(item->value, "P") && strcasecmp(item->value, "T")) { upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value); return -1; } snprintf(value, valuelen, item->dfl, item->value); /* Unskip items supported only by devices that implement 'T' protocol */ if (!strcasecmp(item->value, "P")) return 0; for (i = 0; items_to_be_unskipped[i].info_type; i++) { unskip = find_nut_info(items_to_be_unskipped[i].info_type, items_to_be_unskipped[i].flags, items_to_be_unskipped[i].noflags); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } return 0; } /* Input/Output voltage */ static int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, const size_t valuelen) { int val; double ret; char *str_end; if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } val = strtol(item->value, &str_end, 16) * strtol(str_end, NULL, 16) / 51; ret = val / 256.0; snprintf(value, valuelen, item->dfl, ret); return 0; } /* Device load */ static int voltronic_qs_hex_load(item_t *item, char *value, const size_t valuelen) { if (strspn(item->value, "0123456789ABCDEFabcdef") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } snprintf(value, valuelen, item->dfl, strtol(item->value, NULL, 16)); return 0; } /* Output frequency */ static int voltronic_qs_hex_frequency(item_t *item, char *value, const size_t valuelen) { double val1, val2, ret; char *str_end; if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } val1 = strtol(item->value, &str_end, 16); val2 = strtol(str_end, NULL, 16); ret = val2 / val1; ret = ret > 99.9 ? 99.9 : ret; snprintf(value, valuelen, item->dfl, ret); return 0; } /* Battery voltage */ static int voltronic_qs_hex_battery_voltage(item_t *item, char *value, const size_t valuelen) { int val1, val2; char *str_end; if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } val1 = strtol(item->value, &str_end, 16); val2 = strtol(str_end, NULL, 16); snprintf(value, valuelen, item->dfl, (val1 * val2) / 510.0); return 0; } /* Ratings bits */ static int voltronic_qs_hex_process_ratings_bits(item_t *item, char *value, const size_t valuelen) { int val; double ret; if (strspn(item->value, "01") != strlen(item->value)) { upsdebugx(3, "%s: unexpected value %s@%d->%s", __func__, item->info_type, item->from, item->value); return -1; } val = strtol(item->value, NULL, 10); switch (item->from) { case 47: /* Nominal output frequency */ if (val == 0) /* 0 -> 50 Hz */ ret = 50; else /* 1 -> 60 Hz */ ret = 60; break; case 48: /* Nominal battery voltage */ if (val == 0) /* 0 -> 12 V */ ret = 12; else if (val == 1) /* 1 -> 24 V */ ret = 24; else if (val == 10) /* 10 -> 36 V */ ret = 36; else /* 11 -> 48 V */ ret = 48; break; /* case 50: *//* Reserved */ /* break;*/ /* case 51: *//* Reserved */ /* break;*/ case 52: /* Nominal output voltage */ switch (val) { case 0: ret = 110; break; case 1: ret = 120; break; case 10: ret = 220; break; case 11: ret = 230; break; case 100: ret = 240; break; default: /* Unknown */ return -1; } break; default: /* Don't know what happened */ return -1; } snprintf(value, valuelen, item->dfl, ret); return 0; } /* == Subdriver interface == */ subdriver_t voltronic_qs_hex_subdriver = { VOLTRONIC_QS_HEX_VERSION, voltronic_qs_hex_claim, voltronic_qs_hex_qx2nut, voltronic_qs_hex_initups, NULL, blazer_makevartable_light, NULL, "N\r", #ifdef TESTING voltronic_qs_hex_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/clone.c0000644000175000017500000002672512640473702012216 00000000000000/* * clone.c: UPS driver clone * * Copyright (C) 2009 - Arjen de Korte * * 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 "main.h" #include "parseconf.h" #include #include #include #define DRIVER_NAME "Clone UPS driver" #define DRIVER_VERSION "0.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte ", DRV_EXPERIMENTAL, { NULL } }; static struct { struct { int start; int shutdown; } timer; char status[ST_MAX_VALUE_LEN]; } ups = { { -1, -1 }, "WAIT" }; static struct { struct { double act; double low; } charge; struct { double act; double low; } runtime; } battery = { { 0, 0 }, { 0, 0 } }; static int dumpdone = 0, online = 1, outlet = 1; static int offdelay = 120, ondelay = 30; static PCONF_CTX_t sock_ctx; static time_t last_poll = 0, last_heard = 0, last_ping = 0, last_connfail = 0; static int instcmd(const char *cmdname, const char *extra); static int parse_args(int numargs, char **arg) { if (numargs < 1) { return 0; } if (!strcasecmp(arg[0], "PONG")) { upsdebugx(3, "Got PONG from UPS"); return 1; } if (!strcasecmp(arg[0], "DUMPDONE")) { upsdebugx(3, "UPS: dump is done"); dumpdone = 1; return 1; } if (!strcasecmp(arg[0], "DATASTALE")) { dstate_datastale(); return 1; } if (!strcasecmp(arg[0], "DATAOK")) { dstate_dataok(); return 1; } if (numargs < 2) { return 0; } /* DELINFO */ if (!strcasecmp(arg[0], "DELINFO")) { dstate_delinfo(arg[1]); return 1; } if (numargs < 3) { return 0; } /* SETINFO */ if (!strcasecmp(arg[0], "SETINFO")) { if (!strncasecmp(arg[1], "driver.", 7) || !strcasecmp(arg[1], "battery.charge.low") || !strcasecmp(arg[1], "battery.runtime.low") || !strncasecmp(arg[1], "ups.delay.", 10) || !strncasecmp(arg[1], "ups.timer.", 10)) { /* don't pass on upstream driver settings */ return 1; } if (!strcasecmp(arg[1], "ups.status")) { snprintf(ups.status, sizeof(ups.status), "%s", arg[2]); online = strstr(ups.status, "OL") ? 1 : 0; if (ups.timer.shutdown > 0) { dstate_setinfo("ups.status", "FSD %s", ups.status); return 1; } } if (!strcasecmp(arg[1], "battery.charge")) { battery.charge.act = strtod(arg[2], NULL); dstate_setinfo("battery.charge.low", "%g", battery.charge.low); dstate_setflags("battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.charge.low", 3); } if (!strcasecmp(arg[1], "battery.runtime")) { battery.runtime.act = strtod(arg[2], NULL); dstate_setinfo("battery.runtime.low", "%g", battery.runtime.low); dstate_setflags("battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.runtime.low", 4); } dstate_setinfo(arg[1], "%s", arg[2]); return 1; } return 0; } static int sstate_connect(void) { int ret, fd; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", device_path); return -1; } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); if (ret < 0) { time_t now; close(fd); /* rate-limit complaints - don't spam the syslog */ time(&now); if (difftime(now, last_connfail) < 60) { return -1; } last_connfail = now; upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); return -1; } ret = fcntl(fd, F_GETFL, 0); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", device_path); close(fd); return -1; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", device_path); close(fd); return -1; } /* get a dump started so we have a fresh set of data */ ret = write(fd, dumpcmd, strlen(dumpcmd)); if (ret != (int)strlen(dumpcmd)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); close(fd); return -1; } pconf_init(&sock_ctx, NULL); time(&last_heard); dumpdone = 0; /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ dstate_setinfo("ups.status", "WAIT"); upslogx(LOG_INFO, "Connected to UPS [%s]", device_path); return fd; } static void sstate_disconnect(void) { if (upsfd < 0) { return; } pconf_finish(&sock_ctx); close(upsfd); upsfd = -1; } static int sstate_sendline(const char *buf) { int ret; if (upsfd < 0) { return -1; /* failed */ } ret = write(upsfd, buf, strlen(buf)); if (ret == (int)strlen(buf)) { return 0; } upslog_with_errno(LOG_NOTICE, "Send to UPS [%s] failed", device_path); return -1; /* failed */ } static int sstate_readline(void) { int i, ret; char buf[SMALLBUF]; if (upsfd < 0) { return -1; /* failed */ } ret = read(upsfd, buf, sizeof(buf)); if (ret < 0) { switch(errno) { case EINTR: case EAGAIN: return 0; default: upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path); return -1; } } for (i = 0; i < ret; i++) { switch (pconf_char(&sock_ctx, buf[i])) { case 1: if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) { time(&last_heard); } continue; case 0: continue; /* haven't gotten a line yet */ default: /* parse error */ upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg); return -1; } } return 0; } static int sstate_dead(int maxage) { time_t now; double elapsed; /* an unconnected ups is always dead */ if (upsfd < 0) { upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", device_path); return -1; /* dead */ } time(&now); /* ignore DATAOK/DATASTALE unless the dump is done */ if (dumpdone && dstate_is_stale()) { upsdebugx(3, "sstate_dead: driver for UPS [%s] says data is stale", device_path); return -1; /* dead */ } elapsed = difftime(now, last_heard); /* somewhere beyond a third of the maximum time - prod it to make it talk */ if ((elapsed > (maxage / 3)) && (difftime(now, last_ping) > (maxage / 3))) { upsdebugx(3, "Send PING to UPS"); sstate_sendline("PING\n"); last_ping = now; } if (elapsed > maxage) { upsdebugx(3, "sstate_dead: didn't hear from driver for UPS [%s] for %g seconds (max %d)", device_path, elapsed, maxage); return -1; /* dead */ } return 0; } static int instcmd(const char *cmdname, const char *extra) { const char *val; val = dstate_getinfo(getval("load.status")); if (val) { if (!strcasecmp(val, "off") || !strcasecmp(val, "no")) { outlet = 0; } if (!strcasecmp(val, "on") || !strcasecmp(val, "yes")) { outlet = 1; } } if (!strcasecmp(cmdname, "shutdown.return")) { if (outlet && (ups.timer.shutdown < 0)) { ups.timer.shutdown = offdelay; dstate_setinfo("ups.status", "FSD %s", ups.status); } ups.timer.start = ondelay; return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stayoff")) { if (outlet && (ups.timer.shutdown < 0)) { ups.timer.shutdown = offdelay; dstate_setinfo("ups.status", "FSD %s", ups.status); } ups.timer.start = -1; return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "battery.charge.low")) { battery.charge.low = strtod(val, NULL); dstate_setinfo("battery.charge.low", "%g", battery.charge.low); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "battery.runtime.low")) { battery.runtime.low = strtod(val, NULL); dstate_setinfo("battery.runtime.low", "%g", battery.runtime.low); return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); return STAT_SET_UNKNOWN; } void upsdrv_initinfo(void) { const char *val; time(&last_poll); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); val = getval("offdelay"); if (val) { offdelay = strtol(val, NULL, 10); } val = getval("ondelay"); if (val) { ondelay = strtol(val, NULL, 10); } val = getval("mincharge"); if (val) { battery.charge.low = strtod(val, NULL); } val = getval("minruntime"); if (val) { battery.runtime.low = strtod(val, NULL); } dstate_setinfo("ups.delay.shutdown", "%d", offdelay); dstate_setinfo("ups.delay.start", "%d", ondelay); dstate_setinfo("ups.timer.shutdown", "%d", ups.timer.shutdown); dstate_setinfo("ups.timer.start", "%d", ups.timer.start); upsh.instcmd = instcmd; upsh.setvar = setvar; } void upsdrv_updateinfo(void) { time_t now = time(NULL); if (sstate_dead(15)) { sstate_disconnect(); extrafd = upsfd = sstate_connect(); return; } if (sstate_readline()) { sstate_disconnect(); return; } if (ups.timer.shutdown >= 0) { ups.timer.shutdown -= difftime(now, last_poll); if (ups.timer.shutdown < 0) { const char *val; ups.timer.shutdown = -1; outlet = 0; val = getval("load.off"); if (val) { char buf[SMALLBUF]; snprintf(buf, sizeof(buf), "INSTCMD %s\n", val); sstate_sendline(buf); } } } else if (ups.timer.start >= 0) { if (online) { ups.timer.start -= difftime(now, last_poll); } else { ups.timer.start = ondelay; } if (ups.timer.start < 0) { const char *val; ups.timer.start = -1; outlet = 1; val = getval("load.on"); if (val) { char buf[SMALLBUF]; snprintf(buf, sizeof(buf), "INSTCMD %s\n", val); sstate_sendline(buf); } dstate_setinfo("ups.status", "%s", ups.status); } } else if (!online && outlet) { if (battery.charge.act < battery.charge.low) { upslogx(LOG_INFO, "Battery charge low"); instcmd("shutdown.return", NULL); } else if (battery.runtime.act < battery.runtime.low) { upslogx(LOG_INFO, "Battery runtime low"); instcmd("shutdown.return", NULL); } } dstate_setinfo("ups.timer.shutdown", "%d", ups.timer.shutdown); dstate_setinfo("ups.timer.start", "%d", ups.timer.start); last_poll = now; } void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "offdelay", "Delay before outlet shutdown (seconds)"); addvar(VAR_VALUE, "ondelay", "Delay before outlet startup (seconds)"); addvar(VAR_VALUE, "mincharge", "Remaining battery level when UPS switches to LB (percent)"); addvar(VAR_VALUE, "minruntime", "Remaining battery runtime when UPS switches to LB (seconds)"); addvar(VAR_VALUE, "load.off", "Command to switch off outlet"); addvar(VAR_VALUE, "load.on", "Command to switch on outlet"); addvar(VAR_VALUE, "load.status", "Variable that indicates outlet is on/off"); } void upsdrv_initups(void) { extrafd = upsfd = sstate_connect(); } void upsdrv_cleanup(void) { sstate_disconnect(); } nut-2.7.4/drivers/baytech-mib.c0000644000175000017500000001037012667537407013303 00000000000000/* baytech-mib.c - data to monitor BayTech PDUs * * Copyright (C) 2009 * Opengear * Arnaud Quette * * 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 "baytech-mib.h" #define BAYTECH_MIB_VERSION "4032" /* Baytech MIB */ #define BAYTECH_OID_MIB ".1.3.6.1.4.1.4779" #define BAYTECH_OID_MODEL_NAME ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1" static info_lkp_t outlet_status_info[] = { { -1, "error" }, { 0, "off" }, { 1, "on" }, { 2, "cycling" }, /* transitional status */ { 0, NULL } }; /* Snmp2NUT lookup table for BayTech MIBs */ static snmp_info_t baytech_mib[] = { /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "BayTech", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Baytech", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.3.0", "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.10.2.1", NULL, 0, NULL, NULL }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.count", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.2.1.15.1", "0", 0, NULL }, { "outlet.current", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.6.2.1", NULL, 0, NULL, NULL }, { "outlet.voltage", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.8.2.1", NULL, 0, NULL, NULL }, /* outlet template definition */ { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_OUTLET, &outlet_status_info[0], NULL }, { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.4.1.%i", NULL, SU_OUTLET, NULL, NULL }, { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.6.1.3.2.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_OUTLET | SU_FLAG_OK, NULL, NULL }, { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL, NULL }, /* instant commands. */ { "outlet.%i.load.off", 0, 0, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } }; mib2nut_info_t baytech = { "baytech", BAYTECH_MIB_VERSION, NULL, BAYTECH_OID_MODEL_NAME, baytech_mib }; nut-2.7.4/drivers/nutdrv_qx_mecer.c0000644000175000017500000002430712640473702014315 00000000000000/* nutdrv_qx_mecer.c - Subdriver for Mecer/Voltronic Power P98 UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_mecer.h" #define MECER_VERSION "Mecer 0.07" /* Support functions */ static int mecer_claim(void); static void mecer_initups(void); /* Preprocess functions */ static int voltronic_p98_protocol(item_t *item, char *value, const size_t valuelen); static int mecer_process_test_battery(item_t *item, char *value, const size_t valuelen); /* == qx2nut lookup table == */ static item_t mecer_qx2nut[] = { /* Query UPS for protocol (Voltronic Power UPSes) * > [QPI\r] * < [(PI98\r] * 012345 * 0 */ { "ups.firmware.aux", 0, NULL, "QPI\r", "", 6, '(', "", 1, 4, "%s", QX_FLAG_STATIC, NULL, NULL, voltronic_p98_protocol }, /* * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* * > [I\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Instant commands * The UPS will reply '(ACK\r' in case of success, '(NAK\r' if the command is rejected or invalid */ { "beeper.toggle", 0, NULL, "Q\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, mecer_process_test_battery }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* == Testing table == */ #ifdef TESTING static testing_t mecer_testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "QPI\r", "(PI98\r", -1 }, { "F\r", "#230.0 000 024.0 50.0\r", -1 }, { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, { "Q\r", "(ACK\r", -1 }, { "S03\r", "(NAK\r", -1 }, { "C\r", "(NAK\r", -1 }, { "S02R0005\r", "(ACK\r", -1 }, { "S.5R0000\r", "(ACK\r", -1 }, { "T04\r", "(NAK\r", -1 }, { "TL\r", "(ACK\r", -1 }, { "T\r", "(NAK\r", -1 }, { "CT\r", "(ACK\r", -1 }, { NULL } }; #endif /* TESTING */ /* == Support functions == */ /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ static int mecer_claim(void) { /* Apart from status (Q1), try to identify protocol (QPI, for Voltronic Power P98 units) or whether the UPS uses '(ACK\r'/'(NAK\r' replies */ item_t *item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value */ if (ups_infoval_set(item) != 1) return 0; /* UPS Protocol */ item = find_nut_info("ups.firmware.aux", 0, 0); /* Don't know what happened */ if (!item) { dstate_delinfo("input.voltage"); return 0; } /* No reply/Unable to get value/Command rejected */ if (qx_process(item, NULL)) { /* No reply/Command echoed back or rejected with something other than '(NAK\r' -> Not a '(ACK/(NAK' unit */ if (!strlen(item->answer) || strcasecmp(item->answer, "(NAK\r")) { dstate_delinfo("input.voltage"); return 0; } /* Command rejected with '(NAK\r' -> '(ACK/(NAK' unit */ /* Skip protocol query from now on */ item->qxflags |= QX_FLAG_SKIP; } else { /* Unable to process value/Command echoed back or rejected with something other than '(NAK\r'/Protocol not supported */ if (ups_infoval_set(item) != 1) { dstate_delinfo("input.voltage"); return 0; } /* Voltronic Power P98 unit */ } return 1; } /* Subdriver-specific initups */ static void mecer_initups(void) { blazer_initups(mecer_qx2nut); } /* == Preprocess functions == */ /* Protocol used by the UPS */ static int voltronic_p98_protocol(item_t *item, char *value, const size_t valuelen) { if (strcasecmp(item->value, "PI98")) { upslogx(LOG_ERR, "Protocol [%s] is not supported by this driver", item->value); return -1; } snprintf(value, valuelen, item->dfl, "Voltronic Power P98"); return 0; } /* *CMD* Preprocess 'test.battery.start' instant command */ static int mecer_process_test_battery(item_t *item, char *value, const size_t valuelen) { const char *protocol = dstate_getinfo("ups.firmware.aux"); char buf[SMALLBUF] = ""; int min, test_time; /* Voltronic P98 units -> Accepted values for test time: .2 -> .9 (.2=12sec ..), 01 -> 99 (minutes) -> range = [12..5940] */ if (protocol && !strcasecmp(protocol, "Voltronic Power P98")) min = 12; /* Other units: 01 -> 99 (minutes) -> [60..5940] */ else min = 60; if (strlen(value) != strspn(value, "0123456789")) { upslogx(LOG_ERR, "%s: non numerical value [%s]", item->info_type, value); return -1; } test_time = strlen(value) > 0 ? strtol(value, NULL, 10) : 600; if ((test_time < min) || (test_time > 5940)) { upslogx(LOG_ERR, "%s: battery test time '%d' out of range [%d..5940] seconds", item->info_type, test_time, min); return -1; } /* test time < 1 minute */ if (test_time < 60) { test_time = test_time / 6; snprintf(buf, sizeof(buf), ".%d", test_time); /* test time > 1 minute */ } else { test_time = test_time / 60; snprintf(buf, sizeof(buf), "%02d", test_time); } snprintf(value, valuelen, item->command, buf); return 0; } /* == Subdriver interface == */ subdriver_t mecer_subdriver = { MECER_VERSION, mecer_claim, mecer_qx2nut, mecer_initups, NULL, blazer_makevartable, "ACK", "(NAK\r", #ifdef TESTING mecer_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/hidtypes.h0000644000175000017500000001043612640473702012744 00000000000000/* * types.h: HID Parser types definitions * * This file is part of the MGE UPS SYSTEMS HID Parser * * Copyright (C) * 1998-2003 MGE UPS SYSTEMS, Luc Descotils * 2015 Eaton, Arnaud Quette (Update MAX_REPORT) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * -------------------------------------------------------------------------- */ #ifndef HIDTYPES_H #define HIDTYPES_H #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /* __cplusplus */ #include #include "nut_stdint.h" /* * Constants * -------------------------------------------------------------------------- */ #define PATH_SIZE 10 /* Deep max for Path */ #define USAGE_TAB_SIZE 50 /* Size of usage stack */ #define MAX_REPORT 500 /* Including FEATURE, INPUT and OUTPUT */ #define REPORT_DSC_SIZE 6144 /* Size max of Report Descriptor */ #define MAX_REPORT_TS 3 /* Max time validity of a report */ /* * Items * -------------------------------------------------------------------------- */ #define SIZE_0 0x00 #define SIZE_1 0x01 #define SIZE_2 0x02 #define SIZE_4 0x03 #define SIZE_MASK 0x03 #define TYPE_MAIN 0x00 #define TYPE_GLOBAL 0x04 #define TYPE_LOCAL 0x08 #define TYPE_MASK 0x0C /* Main items */ #define ITEM_COLLECTION 0xA0 #define ITEM_END_COLLECTION 0xC0 #define ITEM_FEATURE 0xB0 #define ITEM_INPUT 0x80 #define ITEM_OUTPUT 0x90 /* Global items */ #define ITEM_UPAGE 0x04 #define ITEM_LOG_MIN 0x14 #define ITEM_LOG_MAX 0x24 #define ITEM_PHY_MIN 0x34 #define ITEM_PHY_MAX 0x44 #define ITEM_UNIT_EXP 0x54 #define ITEM_UNIT 0x64 #define ITEM_REP_SIZE 0x74 #define ITEM_REP_ID 0x84 #define ITEM_REP_COUNT 0x94 /* Local items */ #define ITEM_USAGE 0x08 #define ITEM_STRING 0x78 /* Long item */ #define ITEM_LONG 0xFC #define ITEM_MASK 0xFC /* Attribute Flags */ #define ATTR_DATA_CST 0x01 #define ATTR_NVOL_VOL 0x80 /* * HIDNode_t struct * * Describe a HID Path point: Usage = bits 0..15, UPage = bits 16..31 * -------------------------------------------------------------------------- */ typedef uint32_t HIDNode_t; /* * HIDPath struct * * Describe a HID Path * -------------------------------------------------------------------------- */ typedef struct { uint8_t Size; /* HID Path size */ HIDNode_t Node[PATH_SIZE]; /* HID Path */ } HIDPath_t; /* * HIDData struct * * Describe a HID Data with its location in report * -------------------------------------------------------------------------- */ typedef struct { HIDPath_t Path; /* HID Path */ uint8_t ReportID; /* Report ID */ uint8_t Offset; /* Offset of data in report */ uint8_t Size; /* Size of data in bit */ uint8_t Type; /* Type : FEATURE / INPUT / OUTPUT */ uint8_t Attribute; /* Report field attribute */ long Unit; /* HID Unit */ int8_t UnitExp; /* Unit exponent */ long LogMin; /* Logical Min */ long LogMax; /* Logical Max */ long PhyMin; /* Physical Min */ long PhyMax; /* Physical Max */ int8_t have_PhyMin; /* Physical Min defined? */ int8_t have_PhyMax; /* Physical Max defined? */ } HIDData_t; /* * HIDDesc struct * * Holds a parsed report descriptor * -------------------------------------------------------------------------- */ typedef struct { int nitems; /* number of items in descriptor */ HIDData_t *item; /* list of items */ int replen[256]; /* list of report lengths, in byte */ } HIDDesc_t; #ifdef __cplusplus /* *INDENT-OFF* */ } /* extern "C" */ /* *INDENT-ON* */ #endif /* __cplusplus */ #endif /* HIDTYPES_H */ nut-2.7.4/drivers/snmp-ups.c0000644000175000017500000020125012670005601012654 00000000000000/* snmp-ups.c - NUT Meta SNMP driver (support different MIBS) * * Based on NetSNMP API (Simple Network Management Protocol V1-2) * * Copyright (C) * 2002 - 2014 Arnaud Quette * 2015 - 2016 Arnaud Quette * 2002 - 2006 Dmitry Frolov * J.W. Hoogervorst * Niels Baggesen * 2009 - 2010 Arjen de Korte * * Sponsored by Eaton * and originally by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include /* for isprint() */ /* NUT SNMP common functions */ #include "main.h" #include "snmp-ups.h" #include "parseconf.h" /* include all known mib2nut lookup tables */ #include "apc-mib.h" #include "mge-mib.h" #include "netvision-mib.h" #include "powerware-mib.h" #include "eaton-mib.h" #include "raritan-pdu-mib.h" #include "baytech-mib.h" #include "compaq-mib.h" #include "bestpower-mib.h" #include "cyberpower-mib.h" #include "delta_ups-mib.h" #include "huawei-mib.h" #include "ietf-mib.h" #include "xppc-mib.h" #include "eaton-ats-mib.h" #include "apc-ats-mib.h" /* Address API change */ #ifndef usmAESPrivProtocol #define usmAESPrivProtocol usmAES128PrivProtocol #endif static mib2nut_info_t *mib2nut[] = { &apc, &mge, &netvision, &powerware, &pxgx_ups, &aphel_genesisII, &aphel_revelation, &eaton_marlin, &pulizzi_switched1, &pulizzi_switched2, &raritan, &baytech, &compaq, &bestpower, &cyberpower, &delta_ups, &xppc, &huawei, &tripplite_ietf, &eaton_ats, &apc_ats, /* * Prepend vendor specific MIB mappings before IETF, so that * if a device supports both IETF and vendor specific MIB, * the vendor specific one takes precedence (when mib=auto) */ &ietf, /* end of structure. */ NULL }; struct snmp_session g_snmp_sess, *g_snmp_sess_p; const char *OID_pwr_status; int g_pwr_battery; int pollfreq; /* polling frequency */ int input_phases, output_phases, bypass_phases; /* pointer to the Snmp2Nut lookup table */ mib2nut_info_t *mib2nut_info; /* FIXME: to be trashed */ snmp_info_t *snmp_info; alarms_info_t *alarms_info; const char *mibname; const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" #define DRIVER_VERSION "0.97" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette \n" \ "Arnaud Quette \n" \ "Dmitry Frolov \n" \ "J.W. Hoogervorst \n" \ "Niels Baggesen \n" \ "Arjen de Korte ", DRV_STABLE, { NULL } }; /* FIXME: integrate MIBs info? do the same as for usbhid-ups! */ time_t lastpoll = 0; /* template OIDs index start with 0 or 1 (estimated stable for a MIB), * automatically guessed at the first pass */ int template_index_base = -1; /* sysOID location */ #define SYSOID_OID ".1.3.6.1.2.1.1.2.0" /* Forward functions declarations */ static void disable_transfer_oids(void); bool_t get_and_process_data(int mode, snmp_info_t *su_info_p); int extract_template_number(int template_type, const char* varname); /* --------------------------------------------- * driver functions implementations * --------------------------------------------- */ void upsdrv_initinfo(void) { snmp_info_t *su_info_p; upsdebugx(1, "SNMP UPS driver: entering %s()", __func__); dstate_setinfo("driver.version.data", "%s MIB %s", mibname, mibvers); /* add instant commands to the info database. * outlet (and groups) commands are processed later, during initial walk */ for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) { su_info_p->flags |= SU_FLAG_OK; if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && !(su_info_p->flags & SU_OUTLET) && !(su_info_p->flags & SU_OUTLET_GROUP)) { /* first check that this OID actually exists */ if (nut_snmp_get(su_info_p->OID) != NULL) { dstate_addcmd(su_info_p->info_type); upsdebugx(1, "upsdrv_initinfo(): adding command '%s'", su_info_p->info_type); } } } if (testvar("notransferoids")) disable_transfer_oids(); /* initialize all other INFO_ fields from list */ if (snmp_ups_walk(SU_WALKMODE_INIT) == TRUE) dstate_dataok(); else dstate_datastale(); /* setup handlers for instcmd and setvar functions */ upsh.setvar = su_setvar; upsh.instcmd = su_instcmd; } void upsdrv_updateinfo(void) { upsdebugx(1,"SNMP UPS driver: entering %s()", __func__); /* only update every pollfreq */ /* FIXME: only update status (SU_STATUS_*), à la usbhid-ups, in between */ if (time(NULL) > (lastpoll + pollfreq)) { alarm_init(); status_init(); /* update all dynamic info fields */ if (snmp_ups_walk(SU_WALKMODE_UPDATE)) dstate_dataok(); else dstate_datastale(); alarm_commit(); status_commit(); /* store timestamp */ lastpoll = time(NULL); } } void upsdrv_shutdown(void) { /* This driver will probably never support this. In order to be any use, the driver should be called near the end of the system halt script. By that time we in all likelyhood we won't have network capabilities anymore, so we could never send this command to the UPS. This is not an error, but a limitation of the interface used. */ upsdebugx(1, "%s...", __func__); /* Try to shutdown with delay */ if (su_instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } /* If the above doesn't work, try shutdown.reboot */ if (su_instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } /* If the above doesn't work, try load.off.delay */ if (su_instcmd("load.off.delay", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } fatalx(EXIT_FAILURE, "Shutdown failed!"); } void upsdrv_help(void) { upsdebugx(1, "entering %s", __func__); } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { upsdebugx(1, "entering %s()", __func__); addvar(VAR_VALUE, SU_VAR_MIBS, "Set MIB compliance (default=ietf, allowed: mge,apcc,netvision,pw,cpqpower,...)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_COMMUNITY, "Set community name (default=public)"); addvar(VAR_VALUE, SU_VAR_VERSION, "Set SNMP version (default=v1, allowed v2c)"); addvar(VAR_VALUE, SU_VAR_POLLFREQ, "Set polling frequency in seconds, to reduce network flow (default=30)"); addvar(VAR_VALUE, SU_VAR_RETRIES, "Specifies the number of Net-SNMP retries to be used in the requests (default=5)"); addvar(VAR_VALUE, SU_VAR_TIMEOUT, "Specifies the Net-SNMP timeout in seconds between retries (default=1)"); addvar(VAR_FLAG, "notransferoids", "Disable transfer OIDs (use on APCC Symmetras)"); addvar(VAR_VALUE, SU_VAR_SECLEVEL, "Set the securityLevel used for SNMPv3 messages (default=noAuthNoPriv, allowed: authNoPriv,authPriv)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_SECNAME, "Set the securityName used for authenticated SNMPv3 messages (no default)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_AUTHPASSWD, "Set the authentication pass phrase used for authenticated SNMPv3 messages (no default)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_PRIVPASSWD, "Set the privacy pass phrase used for encrypted SNMPv3 messages (no default)"); addvar(VAR_VALUE, SU_VAR_AUTHPROT, "Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5)"); addvar(VAR_VALUE, SU_VAR_PRIVPROT, "Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES)"); } void upsdrv_initups(void) { snmp_info_t *su_info_p; char model[SU_INFOSIZE]; bool_t status= FALSE; const char *mibs; upsdebugx(1, "SNMP UPS driver: entering %s()", __func__); /* Retrieve user's parameters */ mibs = testvar(SU_VAR_MIBS) ? getval(SU_VAR_MIBS) : "auto"; /* init SNMP library, etc... */ nut_snmp_init(progname, device_path); /* FIXME: first test if the device is reachable to avoid timeouts! */ /* Load the SNMP to NUT translation data */ load_mib2nut(mibs); /* init polling frequency */ if (getval(SU_VAR_POLLFREQ)) pollfreq = atoi(getval(SU_VAR_POLLFREQ)); else pollfreq = DEFAULT_POLLFREQ; /* Get UPS Model node to see if there's a MIB */ su_info_p = su_find_info("ups.model"); /* Try to get device.model if ups.model is not available */ if (su_info_p == NULL) su_info_p = su_find_info("device.model"); if (su_info_p != NULL) status = nut_snmp_get_str(su_info_p->OID, model, sizeof(model), NULL); if (status == TRUE) upslogx(0, "Detected %s on host %s (mib: %s %s)", model, device_path, mibname, mibvers); else fatalx(EXIT_FAILURE, "%s MIB wasn't found on %s", mibs, g_snmp_sess.peername); /* FIXME: "No supported device detected" */ if (su_find_info("load.off.delay")) { /* Adds default with a delay value of '0' (= immediate) */ dstate_addcmd("load.off"); } if (su_find_info("load.on.delay")) { /* Adds default with a delay value of '0' (= immediate) */ dstate_addcmd("load.on"); } if (su_find_info("load.off.delay") && su_find_info("load.on.delay")) { /* Add composite instcmds (require setting multiple OID values) */ dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); } } void upsdrv_cleanup(void) { nut_snmp_cleanup(); } /* ----------------------------------------------------------- * SNMP functions. * ----------------------------------------------------------- */ void nut_snmp_init(const char *type, const char *hostname) { char *ns_options = NULL; const char *community, *version; const char *secLevel = NULL, *authPassword, *privPassword; const char *authProtocol, *privProtocol; int snmp_retries = DEFAULT_NETSNMP_RETRIES; long snmp_timeout = DEFAULT_NETSNMP_TIMEOUT; upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, type); /* Force numeric OIDs resolution (ie, do not resolve to textual names) * This is mostly for the convenience of debug output */ ns_options = snmp_out_toggle_options("n"); if (ns_options != NULL) { upsdebugx(2, "Failed to enable numeric OIDs resolution"); } /* Initialize the SNMP library */ init_snmp(type); /* Initialize session */ snmp_sess_init(&g_snmp_sess); g_snmp_sess.peername = xstrdup(hostname); /* Net-SNMP timeout and retries */ if (testvar(SU_VAR_RETRIES)) { snmp_retries = atoi(getval(SU_VAR_RETRIES)); } g_snmp_sess.retries = snmp_retries; upsdebugx(2, "Setting SNMP retries to %i", snmp_retries); if (testvar(SU_VAR_TIMEOUT)) { snmp_timeout = atol(getval(SU_VAR_TIMEOUT)); } /* We have to convert from seconds to microseconds */ g_snmp_sess.timeout = snmp_timeout * ONE_SEC; upsdebugx(2, "Setting SNMP timeout to %ld second(s)", snmp_timeout); /* Retrieve user parameters */ version = testvar(SU_VAR_VERSION) ? getval(SU_VAR_VERSION) : "v1"; if ((strcmp(version, "v1") == 0) || (strcmp(version, "v2c") == 0)) { g_snmp_sess.version = (strcmp(version, "v1") == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c; community = testvar(SU_VAR_COMMUNITY) ? getval(SU_VAR_COMMUNITY) : "public"; g_snmp_sess.community = (unsigned char *)xstrdup(community); g_snmp_sess.community_len = strlen(community); } else if (strcmp(version, "v3") == 0) { /* SNMP v3 related init */ g_snmp_sess.version = SNMP_VERSION_3; /* Security level */ if (testvar(SU_VAR_SECLEVEL)) { secLevel = getval(SU_VAR_SECLEVEL); if (strcmp(secLevel, "noAuthNoPriv") == 0) g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH; else if (strcmp(secLevel, "authNoPriv") == 0) g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; else if (strcmp(secLevel, "authPriv") == 0) g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; else fatalx(EXIT_FAILURE, "Bad SNMPv3 securityLevel: %s", secLevel); } else g_snmp_sess.securityLevel = SNMP_SEC_LEVEL_NOAUTH; /* Security name */ if (testvar(SU_VAR_SECNAME)) { g_snmp_sess.securityName = xstrdup(getval(SU_VAR_SECNAME)); g_snmp_sess.securityNameLen = strlen(g_snmp_sess.securityName); } else fatalx(EXIT_FAILURE, "securityName is required for SNMPv3"); /* Process mandatory fields, based on the security level */ authPassword = testvar(SU_VAR_AUTHPASSWD) ? getval(SU_VAR_AUTHPASSWD) : NULL; privPassword = testvar(SU_VAR_PRIVPASSWD) ? getval(SU_VAR_PRIVPASSWD) : NULL; switch (g_snmp_sess.securityLevel) { case SNMP_SEC_LEVEL_AUTHNOPRIV: if (authPassword == NULL) fatalx(EXIT_FAILURE, "authPassword is required for SNMPv3 in %s mode", secLevel); break; case SNMP_SEC_LEVEL_AUTHPRIV: if ((authPassword == NULL) || (privPassword == NULL)) fatalx(EXIT_FAILURE, "authPassword and privPassword are required for SNMPv3 in %s mode", secLevel); break; default: case SNMP_SEC_LEVEL_NOAUTH: /* nothing else needed */ break; } /* Process authentication protocol and key */ g_snmp_sess.securityAuthKeyLen = USM_AUTH_KU_LEN; authProtocol = testvar(SU_VAR_AUTHPROT) ? getval(SU_VAR_AUTHPROT) : "MD5"; if (strcmp(authProtocol, "MD5") == 0) { g_snmp_sess.securityAuthProto = usmHMACMD5AuthProtocol; g_snmp_sess.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid); } else if (strcmp(authProtocol, "SHA") == 0) { g_snmp_sess.securityAuthProto = usmHMACSHA1AuthProtocol; g_snmp_sess.securityAuthProtoLen = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid); } else fatalx(EXIT_FAILURE, "Bad SNMPv3 authProtocol: %s", authProtocol); /* set the authentication key to a MD5/SHA1 hashed version of our * passphrase (must be at least 8 characters long) */ if(g_snmp_sess.securityLevel != SNMP_SEC_LEVEL_NOAUTH) { if (generate_Ku(g_snmp_sess.securityAuthProto, g_snmp_sess.securityAuthProtoLen, (u_char *) authPassword, strlen(authPassword), g_snmp_sess.securityAuthKey, &g_snmp_sess.securityAuthKeyLen) != SNMPERR_SUCCESS) { fatalx(EXIT_FAILURE, "Error generating Ku from authentication pass phrase"); } } privProtocol = testvar(SU_VAR_PRIVPROT) ? getval(SU_VAR_PRIVPROT) : "DES"; if (strcmp(privProtocol, "DES") == 0) { g_snmp_sess.securityPrivProto = usmDESPrivProtocol; g_snmp_sess.securityPrivProtoLen = sizeof(usmDESPrivProtocol)/sizeof(oid); } else if (strcmp(privProtocol, "AES") == 0) { g_snmp_sess.securityPrivProto = usmAESPrivProtocol; g_snmp_sess.securityPrivProtoLen = sizeof(usmAESPrivProtocol)/sizeof(oid); } else fatalx(EXIT_FAILURE, "Bad SNMPv3 authProtocol: %s", authProtocol); /* set the privacy key to a MD5/SHA1 hashed version of our * passphrase (must be at least 8 characters long) */ if(g_snmp_sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) { g_snmp_sess.securityPrivKeyLen = USM_PRIV_KU_LEN; if (generate_Ku(g_snmp_sess.securityAuthProto, g_snmp_sess.securityAuthProtoLen, (u_char *) privPassword, strlen(privPassword), g_snmp_sess.securityPrivKey, &g_snmp_sess.securityPrivKeyLen) != SNMPERR_SUCCESS) { fatalx(EXIT_FAILURE, "Error generating Ku from privacy pass phrase"); } } } else fatalx(EXIT_FAILURE, "Bad SNMP version: %s", version); /* Open the session */ SOCK_STARTUP; /* MS Windows wrapper, not really needed on Unix! */ g_snmp_sess_p = snmp_open(&g_snmp_sess); /* establish the session */ if (g_snmp_sess_p == NULL) { nut_snmp_perror(&g_snmp_sess, 0, NULL, "nut_snmp_init: snmp_open"); fatalx(EXIT_FAILURE, "Unable to establish communication"); } } void nut_snmp_cleanup(void) { /* close snmp session. */ if (g_snmp_sess_p) { snmp_close(g_snmp_sess_p); g_snmp_sess_p = NULL; } SOCK_CLEANUP; /* wrapper not needed on Unix! */ } /* Free a struct snmp_pdu * returned by nut_snmp_walk */ void nut_snmp_free(struct snmp_pdu ** array_to_free) { struct snmp_pdu ** current_element; current_element = array_to_free; while (*current_element != NULL) { snmp_free_pdu(*current_element); current_element++; } free( array_to_free ); } /* Return a NULL terminated array of snmp_pdu * */ struct snmp_pdu **nut_snmp_walk(const char *OID, int max_iteration) { int status; struct snmp_pdu *pdu, *response = NULL; oid name[MAX_OID_LEN]; size_t name_len = MAX_OID_LEN; oid * current_name; size_t current_name_len; static unsigned int numerr = 0; int nb_iteration = 0; struct snmp_pdu ** ret_array = NULL; int type = SNMP_MSG_GET; upsdebugx(3, "%s(%s)", __func__, OID); upsdebugx(4, "%s: max. iteration = %i", __func__, max_iteration); /* create and send request. */ if (!snmp_parse_oid(OID, name, &name_len)) { upsdebugx(2, "[%s] %s: %s: %s", upsname?upsname:device_name, __func__, OID, snmp_api_errstring(snmp_errno)); return NULL; } current_name = name; current_name_len = name_len; while( nb_iteration < max_iteration ) { /* Going to a shorter OID means we are outside our sub-tree */ if( current_name_len < name_len ) { break; } pdu = snmp_pdu_create(type); if (pdu == NULL) { fatalx(EXIT_FAILURE, "Not enough memory"); } snmp_add_null_var(pdu, current_name, current_name_len); status = snmp_synch_response(g_snmp_sess_p, pdu, &response); if (!response) { break; } if (!((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))) { if (mibname == NULL) { /* We are probing for proper mib - ignore errors */ snmp_free_pdu(response); return NULL; } numerr++; if ((numerr == SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) { upslogx(LOG_WARNING, "[%s] Warning: excessive poll " "failures, limiting error reporting (OID = %s)", upsname?upsname:device_name, OID); } if ((numerr < SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) { if (type == SNMP_MSG_GETNEXT) { upsdebugx(2, "=> No more OID, walk complete"); } else { nut_snmp_perror(g_snmp_sess_p, status, response, "%s: %s", __func__, OID); } } snmp_free_pdu(response); break; } else { numerr = 0; } nb_iteration++; /* +1 is for the terminating NULL */ ret_array = realloc(ret_array,sizeof(struct snmp_pdu*)*(nb_iteration+1)); ret_array[nb_iteration-1] = response; ret_array[nb_iteration]=NULL; current_name = response->variables->name; current_name_len = response->variables->name_length; type = SNMP_MSG_GETNEXT; } return ret_array; } struct snmp_pdu *nut_snmp_get(const char *OID) { struct snmp_pdu ** pdu_array; struct snmp_pdu * ret_pdu; if (OID == NULL) return NULL; upsdebugx(3, "%s(%s)", __func__, OID); pdu_array = nut_snmp_walk(OID,1); if(pdu_array == NULL) { return NULL; } ret_pdu = snmp_clone_pdu(*pdu_array); nut_snmp_free(pdu_array); return ret_pdu; } static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_lkp_t *oid2info) { size_t len = 0; char tmp_buf[SU_LARGEBUF]; /* zero out buffer. */ memset(buf, 0, buf_len); switch (pdu->variables->type) { case ASN_OCTET_STR: case ASN_OPAQUE: len = pdu->variables->val_len > buf_len - 1 ? buf_len - 1 : pdu->variables->val_len; if (len > 0) { /* Test for hexadecimal values */ if (!isprint(pdu->variables->val.string[0])) snprint_hexstring(buf, buf_len, pdu->variables->val.string, pdu->variables->val_len); else { memcpy(buf, pdu->variables->val.string, len); buf[len] = '\0'; } } break; case ASN_INTEGER: case ASN_COUNTER: case ASN_GAUGE: if(oid2info) { const char *str; if((str=su_find_infoval(oid2info, *pdu->variables->val.integer))) { strncpy(buf, str, buf_len-1); } else { strncpy(buf, "UNKNOWN", buf_len-1); } buf[buf_len-1]='\0'; } else { len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer); } break; case ASN_TIMETICKS: /* convert timeticks to seconds */ len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer / 100); break; case ASN_OBJECT_ID: snprint_objid (tmp_buf, sizeof(tmp_buf), pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid)); upsdebugx(2, "Received an OID value: %s", tmp_buf); /* Try to get the value of the pointed OID */ if (nut_snmp_get_str(tmp_buf, buf, buf_len, oid2info) == FALSE) { upsdebugx(3, "Failed to retrieve OID value, using fallback"); /* Otherwise return the last part of the returned OID (ex: 1.2.3 => 3) */ char *oid_leaf = strrchr(tmp_buf, '.'); snprintf(buf, buf_len, "%s", oid_leaf+1); upsdebugx(3, "Fallback value: %s", buf); } else snprintf(buf, buf_len, "%s", tmp_buf); break; default: return FALSE; } return TRUE; } bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info) { struct snmp_pdu *pdu; bool_t ret; upsdebugx(3, "Entering %s()", __func__); pdu = nut_snmp_get(OID); if (pdu == NULL) return FALSE; ret = decode_str(pdu,buf,buf_len,oid2info); if(ret == FALSE) { upsdebugx(2, "[%s] unhandled ASN 0x%x received from %s", upsname?upsname:device_name, pdu->variables->type, OID); } snmp_free_pdu(pdu); return ret; } static bool_t decode_oid(struct snmp_pdu *pdu, char *buf, size_t buf_len) { /* zero out buffer. */ memset(buf, 0, buf_len); switch (pdu->variables->type) { case ASN_OBJECT_ID: snprint_objid (buf, buf_len, pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid)); upsdebugx(2, "OID value: %s", buf); break; default: return FALSE; } return TRUE; } /* Return the value stored in OID, which is an OID (sysOID for example) * and don't try to get the value pointed by this OID (no follow). * To achieve the latter behavior, use standard nut_snmp_get_{str,int}() */ bool_t nut_snmp_get_oid(const char *OID, char *buf, size_t buf_len) { struct snmp_pdu *pdu; bool_t ret = FALSE; /* zero out buffer. */ memset(buf, 0, buf_len); upsdebugx(3, "Entering %s()", __func__); pdu = nut_snmp_get(OID); if (pdu == NULL) return FALSE; ret = decode_oid(pdu, buf, buf_len); if(ret == FALSE) { upsdebugx(2, "[%s] unhandled ASN 0x%x received from %s", upsname?upsname:device_name, pdu->variables->type, OID); } snmp_free_pdu(pdu); return ret; } bool_t nut_snmp_get_int(const char *OID, long *pval) { char tmp_buf[SU_LARGEBUF]; struct snmp_pdu *pdu; long value; char *buf; pdu = nut_snmp_get(OID); if (pdu == NULL) return FALSE; switch (pdu->variables->type) { case ASN_OCTET_STR: case ASN_OPAQUE: buf = xmalloc(pdu->variables->val_len + 1); memcpy(buf, pdu->variables->val.string, pdu->variables->val_len); buf[pdu->variables->val_len] = '\0'; value = strtol(buf, NULL, 0); free(buf); break; case ASN_INTEGER: case ASN_COUNTER: case ASN_GAUGE: value = *pdu->variables->val.integer; break; case ASN_TIMETICKS: /* convert timeticks to seconds */ value = *pdu->variables->val.integer / 100; break; case ASN_OBJECT_ID: snprint_objid (tmp_buf, sizeof(tmp_buf), pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid)); upsdebugx(2, "Received an OID value: %s", tmp_buf); /* Try to get the value of the pointed OID */ if (nut_snmp_get_int(tmp_buf, &value) == FALSE) { upsdebugx(3, "Failed to retrieve OID value, using fallback"); /* Otherwise return the last part of the returned OID (ex: 1.2.3 => 3) */ char *oid_leaf = strrchr(tmp_buf, '.'); value = strtol(oid_leaf+1, NULL, 0); upsdebugx(3, "Fallback value: %ld", value); } break; default: upslogx(LOG_ERR, "[%s] unhandled ASN 0x%x received from %s", upsname?upsname:device_name, pdu->variables->type, OID); return FALSE; } snmp_free_pdu(pdu); if (pval != NULL) *pval = value; return TRUE; } bool_t nut_snmp_set(const char *OID, char type, const char *value) { int status; bool_t ret = FALSE; struct snmp_pdu *pdu, *response = NULL; oid name[MAX_OID_LEN]; size_t name_len = MAX_OID_LEN; upsdebugx(1, "entering %s(%s, %c, %s)", __func__, OID, type, value); if (!snmp_parse_oid(OID, name, &name_len)) { upslogx(LOG_ERR, "[%s] %s: %s: %s", upsname?upsname:device_name, __func__, OID, snmp_api_errstring(snmp_errno)); return FALSE; } pdu = snmp_pdu_create(SNMP_MSG_SET); if (pdu == NULL) fatalx(EXIT_FAILURE, "Not enough memory"); if (snmp_add_var(pdu, name, name_len, type, value)) { upslogx(LOG_ERR, "[%s] %s: %s: %s", upsname?upsname:device_name, __func__, OID, snmp_api_errstring(snmp_errno)); return FALSE; } status = snmp_synch_response(g_snmp_sess_p, pdu, &response); if ((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR)) ret = TRUE; else nut_snmp_perror(g_snmp_sess_p, status, response, "%s: can't set %s", __func__, OID); snmp_free_pdu(response); return ret; } bool_t nut_snmp_set_str(const char *OID, const char *value) { return nut_snmp_set(OID, 's', value); } bool_t nut_snmp_set_int(const char *OID, long value) { char buf[SU_BUFSIZE]; snprintf(buf, sizeof(buf), "%ld", value); return nut_snmp_set(OID, 'i', buf); } bool_t nut_snmp_set_time(const char *OID, long value) { char buf[SU_BUFSIZE]; snprintf(buf, SU_BUFSIZE, "%ld", value * 100); return nut_snmp_set(OID, 't', buf); } /* log descriptive SNMP error message. */ void nut_snmp_perror(struct snmp_session *sess, int status, struct snmp_pdu *response, const char *fmt, ...) { va_list va; int cliberr, snmperr; char *snmperrstr; char buf[SU_LARGEBUF]; va_start(va, fmt); vsnprintf(buf, sizeof(buf), fmt, va); va_end(va); if (response == NULL) { snmp_error(sess, &cliberr, &snmperr, &snmperrstr); upslogx(LOG_ERR, "[%s] %s: %s", upsname?upsname:device_name, buf, snmperrstr); free(snmperrstr); } else if (status == STAT_SUCCESS) { switch (response->errstat) { case SNMP_ERR_NOERROR: break; case SNMP_ERR_NOSUCHNAME: /* harmless */ upsdebugx(2, "[%s] %s: %s", upsname?upsname:device_name, buf, snmp_errstring(response->errstat)); break; default: upslogx(LOG_ERR, "[%s] %s: Error in packet: %s", upsname?upsname:device_name, buf, snmp_errstring(response->errstat)); break; } } else if (status == STAT_TIMEOUT) { upslogx(LOG_ERR, "[%s] %s: Timeout: no response from %s", upsname?upsname:device_name, buf, sess->peername); } else { snmp_sess_error(sess, &cliberr, &snmperr, &snmperrstr); upslogx(LOG_ERR, "[%s] %s: %s", upsname?upsname:device_name, buf, snmperrstr); free(snmperrstr); } } /* ----------------------------------------------------------- * utility functions. * ----------------------------------------------------------- */ /* deal with APCC weirdness on Symmetras */ static void disable_transfer_oids(void) { snmp_info_t *su_info_p; upslogx(LOG_INFO, "Disabling transfer OIDs"); for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) { if (!strcasecmp(su_info_p->info_type, "input.transfer.low")) { su_info_p->flags &= ~SU_FLAG_OK; continue; } if (!strcasecmp(su_info_p->info_type, "input.transfer.high")) { su_info_p->flags &= ~SU_FLAG_OK; continue; } } } /* universal function to add or update info element. */ void su_setinfo(snmp_info_t *su_info_p, const char *value) { info_lkp_t *info_lkp; upsdebugx(1, "entering %s(%s)", __func__, su_info_p->info_type); if (SU_TYPE(su_info_p) == SU_TYPE_CMD) return; /* ups.status and {ups, Lx, outlet, outlet.group}.alarm have special * handling, not here! */ if ((strcasecmp(su_info_p->info_type, "ups.status")) && (strcasecmp(strrchr(su_info_p->info_type, '.'), ".alarm"))) { if (value != NULL) dstate_setinfo(su_info_p->info_type, "%s", value); else dstate_setinfo(su_info_p->info_type, "%s", su_info_p->dfl); dstate_setflags(su_info_p->info_type, su_info_p->info_flags); dstate_setaux(su_info_p->info_type, su_info_p->info_len); /* Set enumerated values, only if the data has ST_FLAG_RW and there * are lookup values */ if ((su_info_p->info_flags & ST_FLAG_RW) && su_info_p->oid2info) { upsdebugx(3, "%s: adding enumerated values", __func__); /* Loop on all existing values */ for (info_lkp = su_info_p->oid2info; info_lkp != NULL && info_lkp->info_value != NULL; info_lkp++) { dstate_addenum(su_info_p->info_type, "%s", info_lkp->info_value); } } /* Commit the current value, to avoid staleness with huge * data collections on slow devices */ dstate_dataok(); } } void su_status_set(snmp_info_t *su_info_p, long value) { const char *info_value = NULL; upsdebugx(2, "SNMP UPS driver: entering %s()", __func__); if ((info_value = su_find_infoval(su_info_p->oid2info, value)) != NULL) { if (strcmp(info_value, "")) { status_set(info_value); } } /* TODO: else */ } void su_alarm_set(snmp_info_t *su_info_p, long value) { const char *info_value = NULL; char alarm_info_value[SU_LARGEBUF]; /* number of the outlet or phase */ int item_number = -1; upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, su_info_p->info_type); if ((info_value = su_find_infoval(su_info_p->oid2info, value)) != NULL && info_value[0] != 0) { /* Special handling for outlet & outlet groups alarms */ if ((su_info_p->flags & SU_OUTLET) || (su_info_p->flags & SU_OUTLET_GROUP)) { /* Extract template number */ item_number = extract_template_number(su_info_p->flags, su_info_p->info_type); /* Inject in the alarm string */ snprintf(alarm_info_value, sizeof(alarm_info_value), "outlet%s %i %s", (su_info_p->flags & SU_OUTLET_GROUP) ? " group" : "", item_number, info_value); info_value = &alarm_info_value[0]; } /* Special handling for phase alarms * Note that SU_*PHASE flags are cleared, so match the 'Lx' * start of path */ if (su_info_p->info_type[0] == 'L') { /* Extract phase number */ item_number = atoi(su_info_p->info_type+1); /* Inject in the alarm string */ snprintf(alarm_info_value, sizeof(alarm_info_value), "phase L%i %s", item_number, info_value); info_value = &alarm_info_value[0]; } /* Set the alarm value */ alarm_set(info_value); } /* TODO: else */ } /* find info element definition in my info array. */ snmp_info_t *su_find_info(const char *type) { snmp_info_t *su_info_p; for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) if (!strcasecmp(su_info_p->info_type, type)) { upsdebugx(3, "%s: \"%s\" found", __func__, type); return su_info_p; } upsdebugx(3, "%s: unknown info type (%s)", __func__, type); return NULL; } /* Try to find the MIB using sysOID matching. * Return a pointer to a mib2nut definition if found, NULL otherwise */ mib2nut_info_t *match_sysoid() { snmp_info_t *su_info_p; char sysOID_buf[LARGEBUF]; char testOID_buf[LARGEBUF]; oid device_sysOID[MAX_OID_LEN]; size_t device_sysOID_len = MAX_OID_LEN; oid mib2nut_sysOID[MAX_OID_LEN]; size_t mib2nut_sysOID_len = MAX_OID_LEN; int i; /* Retrieve sysOID value of this device */ if (nut_snmp_get_oid(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf)) == TRUE) { upsdebugx(1, "%s: device sysOID value = %s", __func__, sysOID_buf); /* Build OIDs for comparison */ if (!read_objid(sysOID_buf, device_sysOID, &device_sysOID_len)) { upsdebugx(2, "%s: can't build device_sysOID %s: %s", __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); return FALSE; } /* Now, iterate on mib2nut definitions */ for (i = 0; mib2nut[i] != NULL; i++) { upsdebugx(1, "%s: checking MIB %s", __func__, mib2nut[i]->mib_name); if (mib2nut[i]->sysOID == NULL) continue; /* Clear variables */ memset(mib2nut_sysOID, 0, MAX_OID_LEN); mib2nut_sysOID_len = MAX_OID_LEN; if (!read_objid(mib2nut[i]->sysOID, mib2nut_sysOID, &mib2nut_sysOID_len)) { upsdebugx(2, "%s: can't build OID %s: %s", __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); /* Try to continue anyway! */ continue; } /* Now compare these */ upsdebugx(1, "%s: comparing %s with %s", __func__, sysOID_buf, mib2nut[i]->sysOID); if (!netsnmp_oid_equals(device_sysOID, device_sysOID_len, mib2nut_sysOID, mib2nut_sysOID_len)) { upsdebugx(2, "%s: sysOID matches MIB '%s'!", __func__, mib2nut[i]->mib_name); /* Counter verify, using {ups,device}.model */ snmp_info = mib2nut[i]->snmp_info; su_info_p = su_find_info("ups.model"); /* Try to get device.model if ups.model is not available */ if (su_info_p == NULL) su_info_p = su_find_info("device.model"); if (su_info_p != NULL) { upsdebugx(2, "Testing %s using OID %s", su_info_p->info_type, su_info_p->OID); if (nut_snmp_get_str(su_info_p->OID, testOID_buf, LARGEBUF, NULL) != TRUE) { upsdebugx(2, "%s: testOID provided and doesn't match MIB '%s'!", __func__, mib2nut[i]->mib_name); snmp_info = NULL; continue; } else upsdebugx(2, "%s: testOID provided and matches MIB '%s'!", __func__, mib2nut[i]->mib_name); } return mib2nut[i]; } } /* Yell all to call for user report */ upslogx(LOG_ERR, "No matching MIB found for sysOID '%s'!\n" \ "Please report it to NUT developers, with an 'upsc' output for your device.\n" \ "Going back to the classic MIB detection method.", sysOID_buf); } else upsdebugx(2, "Can't get sysOID value"); return NULL; } /* Load the right snmp_info_t structure matching mib parameter */ bool_t load_mib2nut(const char *mib) { int i; char buf[LARGEBUF]; snmp_info_t *su_info_p; mib2nut_info_t *m2n = NULL; upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, mib); /* First, try to match against sysOID, if no MIB was provided. * This should speed up init stage * (Note: sysOID points the device main MIB entry point) */ if (!strcmp(mib, "auto")) { upsdebugx(1, "trying the new match_sysoid() method"); m2n = match_sysoid(); } /* Otherwise, revert to the classic method */ if (m2n == NULL) { for (i = 0; mib2nut[i] != NULL; i++) { /* Is there already a MIB name provided? */ if (strcmp(mib, "auto") && strcmp(mib, mib2nut[i]->mib_name)) { continue; } upsdebugx(1, "load_mib2nut: trying classic method with '%s' mib", mib2nut[i]->mib_name); /* Classic method: test an OID specific to this MIB */ snmp_info = mib2nut[i]->snmp_info; su_info_p = su_find_info("ups.model"); /* Try to get device.model if ups.model is not available */ if (su_info_p == NULL) su_info_p = su_find_info("device.model"); if (su_info_p != NULL) { upsdebugx(2, "Testing %s using OID %s", su_info_p->info_type, su_info_p->OID); if (nut_snmp_get_str(su_info_p->OID, buf, LARGEBUF, NULL) != TRUE) { upsdebugx(2, "%s: testOID provided and doesn't match MIB '%s'!", __func__, mib2nut[i]->mib_name); snmp_info = NULL; continue; } else upsdebugx(2, "%s: testOID provided and matches MIB '%s'!", __func__, mib2nut[i]->mib_name); } /* MIB found */ m2n = mib2nut[i]; break; } } /* Store the result, if any */ if (m2n != NULL) { snmp_info = m2n->snmp_info; OID_pwr_status = m2n->oid_pwr_status; mibname = m2n->mib_name; mibvers = m2n->mib_version; alarms_info = m2n->alarms_info; upsdebugx(1, "load_mib2nut: using %s mib", mibname); return TRUE; } /* Did we find something or is it really an unknown mib */ if (strcmp(mib, "auto") != 0) { fatalx(EXIT_FAILURE, "Unknown mibs value: %s", mib); } else { fatalx(EXIT_FAILURE, "No supported device detected"); } } /* find the OID value matching that INFO_* value */ long su_find_valinfo(info_lkp_t *oid2info, const char* value) { info_lkp_t *info_lkp; for (info_lkp = oid2info; (info_lkp != NULL) && (strcmp(info_lkp->info_value, "NULL")); info_lkp++) { if (!(strcmp(info_lkp->info_value, value))) { upsdebugx(1, "%s: found %s (value: %s)", __func__, info_lkp->info_value, value); return info_lkp->oid_value; } } upsdebugx(1, "%s: no matching INFO_* value for this OID value (%s)", __func__, value); return -1; } /* find the INFO_* value matching that OID value */ const char *su_find_infoval(info_lkp_t *oid2info, long value) { info_lkp_t *info_lkp; for (info_lkp = oid2info; (info_lkp != NULL) && (info_lkp->info_value != NULL) && (strcmp(info_lkp->info_value, "NULL")); info_lkp++) { if (info_lkp->oid_value == value) { upsdebugx(1, "%s: found %s (value: %ld)", __func__, info_lkp->info_value, value); return info_lkp->info_value; } } upsdebugx(1, "%s: no matching INFO_* value for this OID value (%ld)", __func__, value); return NULL; } /* FIXME: doesn't work with templates! */ static void disable_competition(snmp_info_t *entry) { snmp_info_t *p; for(p=snmp_info; p->info_type!=NULL; p++) { if(p!=entry && !strcmp(p->info_type, entry->info_type)) { upsdebugx(2, "%s: disabling %s %s", __func__, p->info_type, p->OID); p->flags &= ~SU_FLAG_OK; } } } /*********************************************************************** * Template handling functions **********************************************************************/ /* Instantiate an snmp_info_t from a template. * Useful for outlet and outlet.group templates. * Note: remember to adapt info_type, OID and optionaly dfl */ snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) { upsdebugx(1, "%s(%s)", __func__, info_template->info_type); /* sanity check */ if (info_template == NULL) return NULL; if (new_instance == NULL) new_instance = (snmp_info_t *)xmalloc(sizeof(snmp_info_t)); new_instance->info_type = (char *)xmalloc(SU_INFOSIZE); if (info_template->OID != NULL) new_instance->OID = (char *)xmalloc(SU_INFOSIZE); else new_instance->OID = NULL; new_instance->info_flags = info_template->info_flags; new_instance->info_len = info_template->info_len; /* FIXME: check if we need to adapt this one... */ new_instance->dfl = info_template->dfl; new_instance->flags = info_template->flags; new_instance->oid2info = info_template->oid2info; new_instance->setvar = info_template->setvar; upsdebugx(2, "instantiate_info: template instantiated"); return new_instance; } /* Free a dynamically allocated snmp_info_t. * Useful for outlet and outlet.group templates */ void free_info(snmp_info_t *su_info_p) { /* sanity check */ if (su_info_p == NULL) return; if (su_info_p->info_type != NULL) free ((char *)su_info_p->info_type); if (su_info_p->OID != NULL) free ((char *)su_info_p->OID); free (su_info_p); } /* return the base SNMP index (0 or 1) to start template iteration on * the MIB, based on a test using a template OID */ int base_snmp_template_index(const char *OID_template) { int base_index = template_index_base; char test_OID[SU_INFOSIZE]; if (template_index_base == -1) { /* not initialised yet */ for (base_index = 0 ; base_index < 2 ; base_index++) { snprintf(test_OID, sizeof(test_OID), OID_template, base_index); if (nut_snmp_get(test_OID) != NULL) break; } template_index_base = base_index; } upsdebugx(3, "%s: %i", __func__, template_index_base); return base_index; } /* return the NUT offset (increment) based on template_index_base * ie (template_index_base == 0) => increment +1 * (template_index_base == 1) => increment +0 */ int base_nut_template_offset(void) { return (template_index_base==0)?1:0; } /* Try to determine the number of items (outlets, outlet groups, ...), * using a template definition. Walk through the template until we can't * get anymore values. I.e., if we can iterate up to 8 item, return 8 */ static int guestimate_template_count(const char *OID_template) { int base_index = 0; char test_OID[SU_INFOSIZE]; int base_count; upsdebugx(1, "%s(%s)", __func__, OID_template); /* Determine if OID index starts from 0 or 1? */ snprintf(test_OID, sizeof(test_OID), OID_template, base_index); if (nut_snmp_get(test_OID) == NULL) base_index++; /* Now, actually iterate */ for (base_count = 0 ; ; base_count++) { snprintf(test_OID, sizeof(test_OID), OID_template, base_index + base_count); if (nut_snmp_get(test_OID) == NULL) break; } upsdebugx(3, "%s: %i", __func__, base_count); return base_count; } /* Process template definition, instantiate and get data or register * command * type: outlet, outlet.group */ bool_t process_template(int mode, const char* type, snmp_info_t *su_info_p) { /* Default to TRUE, and leave to get_and_process_data() to set * to FALSE when actually getting data from devices, to avoid false * negative with server side data */ bool_t status = TRUE; int cur_template_number = 1; int cur_nut_index = 0; int template_count = 0; snmp_info_t cur_info_p; char template_count_var[SU_BUFSIZE]; upsdebugx(1, "%s template definition found (%s)...", type, su_info_p->info_type); snprintf(template_count_var, sizeof(template_count_var), "%s.count", type); if(dstate_getinfo(template_count_var) == NULL) { /* FIXME: should we disable it? * su_info_p->flags &= ~SU_FLAG_OK; * or rely on guestimation? */ template_count = guestimate_template_count(su_info_p->OID); /* Publish the count estimation */ dstate_setinfo(template_count_var, "%i", template_count); } else { template_count = atoi(dstate_getinfo(template_count_var)); } /* Only instantiate templates if needed! */ if (template_count > 0) { /* general init of data using the template */ instantiate_info(su_info_p, &cur_info_p); for (cur_template_number = base_snmp_template_index(su_info_p->OID) ; cur_template_number < (template_count + base_snmp_template_index(su_info_p->OID)) ; cur_template_number++) { cur_nut_index = cur_template_number + base_nut_template_offset(); snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, su_info_p->info_type, cur_nut_index); /* check if default value is also a template */ if ((cur_info_p.dfl != NULL) && (strstr(su_info_p->dfl, "%i") != NULL)) { cur_info_p.dfl = (char *)xmalloc(SU_INFOSIZE); snprintf((char *)cur_info_p.dfl, SU_INFOSIZE, su_info_p->dfl, cur_nut_index); } if (cur_info_p.OID != NULL) { snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, cur_template_number); /* add instant commands to the info database. */ if (SU_TYPE(su_info_p) == SU_TYPE_CMD) { upsdebugx(1, "Adding template command %s", cur_info_p.info_type); /* FIXME: only add if "su_ups_get(cur_info_p) == TRUE" */ if (mode == SU_WALKMODE_INIT) dstate_addcmd(cur_info_p.info_type); } else /* get and process this data */ status = get_and_process_data(mode, &cur_info_p); } else { /* server side (ABSENT) data */ su_setinfo(&cur_info_p, NULL); } /* set back the flag */ su_info_p->flags = cur_info_p.flags; } free((char*)cur_info_p.info_type); if (cur_info_p.OID != NULL) free((char*)cur_info_p.OID); if ((cur_info_p.dfl != NULL) && (strstr(su_info_p->dfl, "%i") != NULL)) free((char*)cur_info_p.dfl); } else { upsdebugx(1, "No %s present, discarding template definition...", type); } return status; } /* Return the type of template, according to a variable name. * Return: SU_OUTLET_GROUP, SU_OUTLET or 0 if not a template */ int get_template_type(const char* varname) { /* Check if it is outlet / outlet.group */ if (!strncmp(varname, "outlet.group", 12)) { return SU_OUTLET_GROUP; } else if (!strncmp(varname, "outlet", 6)) { return SU_OUTLET_GROUP; } else { upsdebugx(2, "Unknown template type: %s", varname); return 0; } } /* Extract the id number of an instantiated template. * Example: return '1' for type = 'outlet.1.desc', -1 if unknown */ int extract_template_number(int template_type, const char* varname) { const char* item_number_ptr = NULL; int item_number = -1; if (template_type & SU_OUTLET_GROUP) item_number_ptr = &varname[12]; else if (template_type & SU_OUTLET) item_number_ptr = &varname[6]; else return -1; item_number = atoi(++item_number_ptr); upsdebugx(3, "%s: item %i", __func__, item_number); return item_number; } /* Extract the id number of a template from a variable name. * Example: return '1' for type = 'outlet.1.desc' */ int extract_template_number_from_snmp_info_t(const char* varname) { return extract_template_number(get_template_type(varname), varname); } /* end of template functions */ /* process a single data from a walk */ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) { bool_t status = FALSE; upsdebugx(1, "getting data: %s (%s)", su_info_p->info_type, su_info_p->OID); /* ok, update this element. */ status = su_ups_get(su_info_p); /* set stale flag if data is stale, clear if not. */ if (status == TRUE) { if (su_info_p->flags & SU_FLAG_STALE) { upslogx(LOG_INFO, "[%s] snmp_ups_walk: data resumed for %s", upsname?upsname:device_name, su_info_p->info_type); su_info_p->flags &= ~SU_FLAG_STALE; } if(su_info_p->flags & SU_FLAG_UNIQUE) { /* We should be the only provider of this */ disable_competition(su_info_p); su_info_p->flags &= ~SU_FLAG_UNIQUE; } dstate_dataok(); } else { if (mode == SU_WALKMODE_INIT) { /* handle unsupported vars */ su_info_p->flags &= ~SU_FLAG_OK; } else { if (!(su_info_p->flags & SU_FLAG_STALE)) { upslogx(LOG_INFO, "[%s] snmp_ups_walk: data stale for %s", upsname?upsname:device_name, su_info_p->info_type); su_info_p->flags |= SU_FLAG_STALE; } dstate_datastale(); } } return status; } /* walk ups variables and set elements of the info array. */ bool_t snmp_ups_walk(int mode) { static unsigned long iterations = 0; snmp_info_t *su_info_p; bool_t status = FALSE; for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) return TRUE; /* skip instcmd, not linked to outlets */ if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && !(su_info_p->flags & SU_OUTLET) && !(su_info_p->flags & SU_OUTLET_GROUP)) { upsdebugx(1, "SU_CMD_MASK => %s", su_info_p->OID); continue; } /* skip elements we shouldn't show */ if (!(su_info_p->flags & SU_FLAG_OK)) continue; /* skip static elements in update mode */ if (mode == SU_WALKMODE_UPDATE && su_info_p->flags & SU_FLAG_STATIC) continue; /* Set default value if we cannot fetch it */ /* and set static flag on this element. * Not applicable to outlets (need SU_FLAG_STATIC tagging) */ if ((su_info_p->flags & SU_FLAG_ABSENT) && !(su_info_p->flags & SU_OUTLET) && !(su_info_p->flags & SU_OUTLET_GROUP)) { if (mode == SU_WALKMODE_INIT) { if (su_info_p->dfl) { /* Set default value if we cannot fetch it from ups. */ su_setinfo(su_info_p, NULL); } su_info_p->flags |= SU_FLAG_STATIC; } continue; } /* check stale elements only on each PN_STALE_RETRY iteration. */ /* if ((su_info_p->flags & SU_FLAG_STALE) && (iterations % SU_STALE_RETRY) != 0) continue; */ /* Filter 1-phase Vs 3-phase according to {input,output}.phase. * Non matching items are disabled, and flags are cleared at * init time */ if (su_info_p->flags & SU_INPHASES) { upsdebugx(1, "Check input_phases (%i)", input_phases); if (input_phases == 0) { /* FIXME: to get from input.phases * this would avoid the use of the SU_FLAG_SETINT flag * and potential human-error to not declare the right way. * It would also free the slot for SU_OUTLET_GROUP */ continue; } if (su_info_p->flags & SU_INPUT_1) { if (input_phases == 1) { upsdebugx(1, "input_phases is 1"); su_info_p->flags &= ~SU_INPHASES; } else { upsdebugx(1, "input_phases is not 1"); su_info_p->flags &= ~SU_FLAG_OK; continue; } } else if (su_info_p->flags & SU_INPUT_3) { if (input_phases == 3) { upsdebugx(1, "input_phases is 3"); su_info_p->flags &= ~SU_INPHASES; } else { upsdebugx(1, "input_phases is not 3"); su_info_p->flags &= ~SU_FLAG_OK; continue; } } else { upsdebugx(1, "input_phases is %d", input_phases); } } if (su_info_p->flags & SU_OUTPHASES) { upsdebugx(1, "Check output_phases"); if (output_phases == 0) { /* FIXME: same as for input_phases */ continue; } if (su_info_p->flags & SU_OUTPUT_1) { if (output_phases == 1) { upsdebugx(1, "output_phases is 1"); su_info_p->flags &= ~SU_OUTPHASES; } else { upsdebugx(1, "output_phases is not 1"); su_info_p->flags &= ~SU_FLAG_OK; continue; } } else if (su_info_p->flags & SU_OUTPUT_3) { if (output_phases == 3) { upsdebugx(1, "output_phases is 3"); su_info_p->flags &= ~SU_OUTPHASES; } else { upsdebugx(1, "output_phases is not 3"); su_info_p->flags &= ~SU_FLAG_OK; continue; } } else { upsdebugx(1, "output_phases is %d", output_phases); } } if (su_info_p->flags & SU_BYPPHASES) { upsdebugx(1, "Check bypass_phases"); if (bypass_phases == 0) { /* FIXME: same as for input_phases */ continue; } if (su_info_p->flags & SU_BYPASS_1) { if (bypass_phases == 1) { upsdebugx(1, "bypass_phases is 1"); su_info_p->flags &= ~SU_BYPPHASES; } else { upsdebugx(1, "bypass_phases is not 1"); su_info_p->flags &= ~SU_FLAG_OK; continue; } } else if (su_info_p->flags & SU_BYPASS_3) { if (input_phases == 3) { upsdebugx(1, "bypass_phases is 3"); su_info_p->flags &= ~SU_BYPPHASES; } else { upsdebugx(1, "bypass_phases is not 3"); su_info_p->flags &= ~SU_FLAG_OK; continue; } } else { upsdebugx(1, "bypass_phases is %d", bypass_phases); } } /* process outlet template definition */ if (su_info_p->flags & SU_OUTLET) { /* Skip commands after init */ if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) continue; else status = process_template(mode, "outlet", su_info_p); } else if (su_info_p->flags & SU_OUTLET_GROUP) { /* Skip commands after init */ if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) continue; else status = process_template(mode, "outlet.group", su_info_p); } else { /* get and process this data */ status = get_and_process_data(mode, su_info_p); } } /* for (su_info_p... */ iterations++; return status; } bool_t su_ups_get(snmp_info_t *su_info_p) { static char buf[SU_INFOSIZE]; bool_t status; long value; const char *strValue = NULL; struct snmp_pdu ** pdu_array; struct snmp_pdu * current_pdu; alarms_info_t * alarms; int index = 0; upsdebugx(2, "%s: %s %s", __func__, su_info_p->info_type, su_info_p->OID); if (!strcasecmp(su_info_p->info_type, "ups.status")) { status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { su_status_set(su_info_p, value); upsdebugx(2, "=> value: %ld", value); } else upsdebugx(2, "=> Failed"); return status; } /* Handle 'ups.alarm', 'outlet.n.alarm' and 3phase 'Lx.alarm', * nothing else! */ if (!strcmp(strrchr(su_info_p->info_type, '.'), ".alarm")) { upsdebugx(2, "Processing alarm: %s", su_info_p->info_type); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { su_alarm_set(su_info_p, value); upsdebugx(2, "=> value: %ld", value); } else upsdebugx(2, "=> Failed"); return status; } /* Walk a subtree (array) of alarms, composed of OID references. * The object referenced should not be accessible, but rather when * present, this means that the alarm condition is TRUE. * Only present in powerware-mib.c for now */ if (!strcasecmp(su_info_p->info_type, "ups.alarms")) { status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { upsdebugx(2, "=> %ld alarms present", value); if( value > 0 ) { pdu_array = nut_snmp_walk(su_info_p->OID, INT_MAX); if(pdu_array == NULL) { upsdebugx(2, "=> Walk failed"); return FALSE; } current_pdu = pdu_array[index]; while(current_pdu) { /* Retrieve the OID name, for comparison */ if (decode_oid(current_pdu, buf, sizeof(buf)) == TRUE) { alarms = alarms_info; while( alarms->OID ) { if(!strcmp(buf, alarms->OID)) { upsdebugx(3, "Alarm OID found => %s", alarms->OID); /* Check for ups.status value */ if (alarms->status_value) { upsdebugx(3, "Alarm value (status) found => %s", alarms->status_value); status_set(alarms->status_value); } /* Check for ups.alarm value */ if (alarms->alarm_value) { upsdebugx(3, "Alarm value (alarm) found => %s", alarms->alarm_value); alarm_set(alarms->alarm_value); } break; } alarms++; } } index++; current_pdu = pdu_array[index]; } nut_snmp_free(pdu_array); } } else { upsdebugx(2, "=> Failed"); } return status; } /* another special case */ if (!strcasecmp(su_info_p->info_type, "ambient.temperature")) { float temp=0; status = nut_snmp_get_int(su_info_p->OID, &value); if(status != TRUE) { return status; } /* only do this if using the IEM sensor */ if (!strcmp(su_info_p->OID, APCC_OID_IEM_TEMP)) { int su; long units; su = nut_snmp_get_int(APCC_OID_IEM_TEMP_UNIT, &units); /* no response, or units == F */ if ((su == FALSE) || (units == APCC_IEM_FAHRENHEIT)) temp = (value - 32) / 1.8; else temp = value; } else { temp = value * su_info_p->info_len; } snprintf(buf, sizeof(buf), "%.1f", temp); su_setinfo(su_info_p, buf); return TRUE; } if (su_info_p->info_flags & ST_FLAG_STRING) { status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info); } else { status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { if (su_info_p->flags&SU_FLAG_NEGINVALID && value<0) { su_info_p->flags &= ~SU_FLAG_OK; if(su_info_p->flags&SU_FLAG_UNIQUE) { disable_competition(su_info_p); su_info_p->flags &= ~SU_FLAG_UNIQUE; } return FALSE; } if (su_info_p->flags & SU_FLAG_SETINT) { upsdebugx(1, "setvar %s", su_info_p->OID); *su_info_p->setvar = value; } /* Check if there is a value to be looked up */ if ((strValue = su_find_infoval(su_info_p->oid2info, value)) != NULL) snprintf(buf, sizeof(buf), "%s", strValue); else snprintf(buf, sizeof(buf), "%.2f", value * su_info_p->info_len); } } if (status == TRUE) { su_setinfo(su_info_p, buf); upsdebugx(2, "=> value: %s", buf); } else upsdebugx(2, "=> Failed"); return status; } /* set r/w INFO_ element to a value. */ int su_setvar(const char *varname, const char *val) { snmp_info_t *su_info_p = NULL; bool_t status; int retval = STAT_SET_FAILED; long value = -1; /* normal (default), outlet, or outlet group variable */ int vartype = get_template_type(varname); upsdebugx(2, "entering %s(%s, %s)", __func__, varname, val); /* Check if it is outlet / outlet.group */ if (strncmp(varname, "outlet", 6)) su_info_p = su_find_info(varname); else { snmp_info_t *tmp_info_p; /* Point the outlet or outlet group number in the string */ const char *item_number_ptr = NULL; /* Store the target outlet or group number */ int item_number = extract_template_number_from_snmp_info_t(varname); /* Store the total number of outlets or outlet groups */ int total_items = -1; /* Check if it is outlet / outlet.group */ if (vartype == SU_OUTLET_GROUP) { total_items = atoi(dstate_getinfo("outlet.group.count")); item_number_ptr = &varname[12]; } else { total_items = atoi(dstate_getinfo("outlet.count")); item_number_ptr = &varname[6]; } item_number = atoi(++item_number_ptr); upsdebugx(3, "%s: item %i / %i", __func__, item_number, total_items); /* ensure the item number is supported (filtered upstream though)! */ if (item_number > total_items) { /* out of bound item number */ upsdebugx(2, "%s: item is out of bound (%i / %i)", __func__, item_number, total_items); return STAT_SET_INVALID; } /* find back the item template */ char *item_varname = (char *)xmalloc(SU_INFOSIZE); snprintf(item_varname, SU_INFOSIZE, "%s.%s%s", (vartype == SU_OUTLET)?"outlet":"outlet.group", "%i", strchr(item_number_ptr++, '.')); upsdebugx(3, "%s: searching for template\"%s\"", __func__, item_varname); tmp_info_p = su_find_info(item_varname); free(item_varname); /* for an snmp_info_t instance */ su_info_p = instantiate_info(tmp_info_p, su_info_p); /* check if default value is also a template */ if ((su_info_p->dfl != NULL) && (strstr(tmp_info_p->dfl, "%i") != NULL)) { su_info_p->dfl = (char *)xmalloc(SU_INFOSIZE); snprintf((char *)su_info_p->dfl, sizeof(su_info_p->dfl), tmp_info_p->dfl, item_number - base_nut_template_offset()); } /* adapt the OID */ if (su_info_p->OID != NULL) { snprintf((char *)su_info_p->OID, sizeof(su_info_p->OID), tmp_info_p->OID, item_number - base_nut_template_offset()); } /* else, don't return STAT_SET_INVALID since we can be setting * a server side variable! */ /* adapt info_type */ if (su_info_p->info_type != NULL) snprintf((char *)su_info_p->info_type, sizeof(su_info_p->info_type), "%s", varname); } if (!su_info_p || !su_info_p->info_type || !(su_info_p->flags & SU_FLAG_OK)) { upsdebugx(2, "%s: info element unavailable %s", __func__, varname); /* Free template (outlet and outlet.group) */ if (vartype != 0) free_info(su_info_p); return STAT_SET_UNKNOWN; } if (!(su_info_p->info_flags & ST_FLAG_RW) || su_info_p->OID == NULL) { upsdebugx(2, "%s: not writable %s", __func__, varname); /* Free template (outlet and outlet.group) */ if (vartype != 0) free_info(su_info_p); return STAT_SET_INVALID; } /* set value into the device */ if (su_info_p->info_flags & ST_FLAG_STRING) { status = nut_snmp_set_str(su_info_p->OID, val); } else { /* non string data may imply a value lookup */ if (su_info_p->oid2info) { value = su_find_valinfo(su_info_p->oid2info, val); } else { /* Convert value and apply multiplier */ value = atof(val) / su_info_p->info_len; } /* Actually apply the new value */ status = nut_snmp_set_int(su_info_p->OID, value); } if (status == FALSE) upsdebugx(1, "%s: cannot set value %s for %s", __func__, val, su_info_p->OID); else { retval = STAT_SET_HANDLED; upsdebugx(1, "%s: successfully set %s to \"%s\"", __func__, varname, val); /* update info array * FIXME: we'd better call su_ups_get() to refresh! */ su_setinfo(su_info_p, val); } /* Free template (outlet and outlet.group) */ if (vartype != 0) free_info(su_info_p); return retval; } /* process instant command and take action. */ int su_instcmd(const char *cmdname, const char *extradata) { snmp_info_t *su_info_p = NULL; int status; int retval = STAT_INSTCMD_FAILED; int cmd_offset = 0; /* normal (default), outlet, or outlet group variable */ int vartype = get_template_type(cmdname); upsdebugx(2, "entering %s(%s, %s)", __func__, cmdname, extradata); /* FIXME: this should only apply if strchr(%)! */ if (strncmp(cmdname, "outlet", 6)) { su_info_p = su_find_info(cmdname); } else { /* FIXME: common with su_setvar(), apart from upsdebugx */ snmp_info_t *tmp_info_p; /* Point the outlet or outlet group number in the string */ const char *item_number_ptr = NULL; /* Store the target outlet or group number */ int item_number = extract_template_number_from_snmp_info_t(cmdname); /* Store the total number of outlets or outlet groups */ int total_items = -1; /* Check if it is outlet / outlet.group */ if (vartype == SU_OUTLET_GROUP) { total_items = atoi(dstate_getinfo("outlet.group.count")); item_number_ptr = &cmdname[12]; } else { total_items = atoi(dstate_getinfo("outlet.count")); item_number_ptr = &cmdname[6]; } item_number = atoi(++item_number_ptr); upsdebugx(3, "%s: item %i / %i", __func__, item_number, total_items); /* ensure the item number is supported (filtered upstream though)! */ if (item_number > total_items) { /* out of bound item number */ upsdebugx(2, "%s: item is out of bound (%i / %i)", __func__, item_number, total_items); return STAT_SET_INVALID; } /* find back the item template */ char *item_varname = (char *)xmalloc(SU_INFOSIZE); snprintf(item_varname, SU_INFOSIZE, "%s.%s%s", (vartype == SU_OUTLET)?"outlet":"outlet.group", "%i", strchr(item_number_ptr++, '.')); upsdebugx(3, "%s: searching for template\"%s\"", __func__, item_varname); tmp_info_p = su_find_info(item_varname); free(item_varname); /* for an snmp_info_t instance */ su_info_p = instantiate_info(tmp_info_p, su_info_p); /* check if default value is also a template */ if ((su_info_p->dfl != NULL) && (strstr(tmp_info_p->dfl, "%i") != NULL)) { su_info_p->dfl = (char *)xmalloc(SU_INFOSIZE); snprintf((char *)su_info_p->dfl, sizeof(su_info_p->dfl), tmp_info_p->dfl, item_number - base_nut_template_offset()); } /* FIXME: common with su_setvar(), apart from upsdebugx */ /* adapt the OID */ if (su_info_p->OID != NULL) { /* Workaround buggy Eaton Pulizzi implementation * which have different offsets index for data & commands! */ if (su_info_p->flags & SU_CMD_OFFSET) { upsdebugx(3, "Adding command offset"); cmd_offset++; } snprintf((char *)su_info_p->OID, sizeof(su_info_p->OID), tmp_info_p->OID, item_number - base_nut_template_offset() + cmd_offset); } else { free_info(su_info_p); return STAT_INSTCMD_UNKNOWN; } } /* Sanity check */ if (!su_info_p || !su_info_p->info_type || !(su_info_p->flags & SU_FLAG_OK)) { /* Check for composite commands */ if (!strcasecmp(cmdname, "load.on")) { return su_instcmd("load.on.delay", "0"); } if (!strcasecmp(cmdname, "load.off")) { return su_instcmd("load.off.delay", "0"); } if (!strcasecmp(cmdname, "shutdown.return")) { int ret; /* Ensure "ups.start.auto" is set to "yes", if supported */ if (dstate_getinfo("ups.start.auto")) { su_setvar("ups.start.auto", "yes"); } ret = su_instcmd("load.on.delay", dstate_getinfo("ups.delay.start")); if (ret != STAT_INSTCMD_HANDLED) { return ret; } return su_instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } if (!strcasecmp(cmdname, "shutdown.stayoff")) { int ret; /* Ensure "ups.start.auto" is set to "no", if supported */ if (dstate_getinfo("ups.start.auto")) { su_setvar("ups.start.auto", "no"); } ret = su_instcmd("load.on.delay", "-1"); if (ret != STAT_INSTCMD_HANDLED) { return ret; } return su_instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } upsdebugx(2, "%s: %s unavailable", __func__, cmdname); if (!strncmp(cmdname, "outlet", 6)) free_info(su_info_p); return STAT_INSTCMD_UNKNOWN; } /* set value, using the provided one, or the default one otherwise */ if (su_info_p->info_flags & ST_FLAG_STRING) { status = nut_snmp_set_str(su_info_p->OID, extradata ? extradata : su_info_p->dfl); } else { status = nut_snmp_set_int(su_info_p->OID, extradata ? atoi(extradata) : su_info_p->info_len); } if (status == FALSE) upsdebugx(1, "%s: cannot set value for %s", __func__, cmdname); else { retval = STAT_INSTCMD_HANDLED; upsdebugx(1, "%s: successfully sent command %s", __func__, cmdname); } if (!strncmp(cmdname, "outlet", 6)) free_info(su_info_p); return retval; } /* FIXME: the below functions can be removed since these were for loading * the mib2nut information from a file instead of the .h definitions... */ /* return 1 if usable, 0 if not */ static int parse_mibconf_args(int numargs, char **arg) { bool_t ret; /* everything below here uses up through arg[1] */ if (numargs < 6) return 0; /* */ /* special case for setting some OIDs value at driver startup */ if (!strcmp(arg[0], "init")) { /* set value. */ if (!strcmp(arg[1], "str")) { ret = nut_snmp_set_str(arg[3], arg[4]); } else { ret = nut_snmp_set_int(arg[3], strtol(arg[4], NULL, 0)); } if (ret == FALSE) upslogx(LOG_ERR, "%s: cannot set value %s for %s", __func__, arg[4], arg[3]); else upsdebugx(1, "%s: successfully set %s to \"%s\"", __func__, arg[0], arg[4]); return 1; } /* TODO: create the lookup table */ upsdebugx(2, "%s, %s, %s, %s, %s, %s", arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]); return 1; } /* called for fatal errors in parseconf like malloc failures */ static void mibconf_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf (*mib.conf): %s", errmsg); } /* load *mib.conf into an snmp_info_t structure */ void read_mibconf(char *mib) { char fn[SMALLBUF]; PCONF_CTX_t ctx; upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, mib); snprintf(fn, sizeof(fn), "%s/snmp/%s.conf", CONFPATH, mib); pconf_init(&ctx, mibconf_err); if (!pconf_file_begin(&ctx, fn)) fatalx(EXIT_FAILURE, "%s", ctx.errmsg); while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); continue; } if (ctx.numargs < 1) continue; if (!parse_mibconf_args(ctx.numargs, ctx.arglist)) { unsigned int i; char errmsg[SMALLBUF]; snprintf(errmsg, sizeof(errmsg), "mib.conf: invalid directive"); for (i = 0; i < ctx.numargs; i++) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); upslogx(LOG_WARNING, "%s", errmsg); } } pconf_finish(&ctx); } nut-2.7.4/drivers/eaton-ats-mib.h0000644000175000017500000000207012667537407013562 00000000000000/* eaton_ats-mib.h - subdriver to monitor eaton_ats SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2016 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef EATON_ATS_MIB_H #define EATON_ATS_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t eaton_ats; #endif /* EATON_ATS_MIB_H */ nut-2.7.4/drivers/mge-xml.h0000644000175000017500000000345712640444140012460 00000000000000/* mge-xml.h Model specific data for MGE XML protocol UPSes Copyright (C) 2008 Arjen de Korte This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef MGE_XML_H #define MGE_XML_H #include "netxml-ups.h" extern subdriver_t mge_xml_subdriver; /** * \brief Convert NUT variable name to MGE XML * * \param name NUT variable name * * \return MGE XML variable name */ const char *vname_nut2mge_xml(const char *name); /** * \brief Convert MGE XML variable name to NUT * * \param name MGE XML variable name * * \return NUT variable name */ const char * vname_mge_xml2nut(const char *name); /** * \brief Convert MGE XML variable value to NUT value * * The function produces a newly created C-string that should * be destroyed using \c free. * * \param name NUT variable name * \param value MGE XML variable value * \param len MGE XML variable value length (in characters) * * \return NUT variable value */ char *vvalue_mge_xml2nut(const char *name, const char *value, size_t len); /** * \brief Register set of R/W variables */ void vname_register_rw(void); #endif /* MGE_XML_H */ nut-2.7.4/drivers/bestpower-mib.c0000644000175000017500000000577512667537407013713 00000000000000/* bestpower-mib.c - data to monitor Eaton Best Power Ferrups * using earlier version of the ConnectUPS * * Copyright (C) 2010 - Arnaud Quette * * 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 "bestpower-mib.h" #define BESTPOWER_MIB_VERSION "0.1" #define BESTPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.2947.1.1.2.0" /* * http://powerquality.eaton.com/Support/Software-Drivers/Downloads/connectivity-firmware/bestpwr2.mib */ /* TODO: find the right sysOID for this MIB * #define BESTPOWER_SYSOID ".1.3.6.1.4.1.2947???" */ static info_lkp_t bestpower_power_status[] = { { 1, "OL" }, { 2, "OB" }, { 0, NULL } } ; /* Snmp2NUT lookup table for Best Power MIB */ static snmp_info_t bestpower_mib[] = { /* Device page */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, /*.1.3.6.1.4.1.2947.1.1.1.0 = STRING: "Ferrups" .1.3.6.1.4.1.2947.1.1.2.0 = STRING: "FE850VA"*/ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, BESTPOWER_OID_MODEL_NAME, "Best Ferrups", SU_FLAG_STATIC, NULL, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.5.0", "", SU_FLAG_STATIC, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.7.0", "", SU_FLAG_STATIC, NULL }, { "ups.power", 0, 1, ".1.3.6.1.4.1.2947.1.1.3.0", "", 0, NULL }, { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.8.0", "", 0, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.2.1.0", "", 0 /*SU_STATUS_PWR*/, &bestpower_power_status[0] }, /* Battery runtime is expressed in minutes */ { "battery.runtime", 0, 60.0, ".1.3.6.1.4.1.2947.1.2.3.0", "", 0, NULL }, /* The elapsed time in seconds since the * UPS has switched to battery power */ { "battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.2947.1.2.2.0", "", 0, NULL }, { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2947.1.2.4.0", "", 0, NULL }, { "battery.current", 0, 0.1, ".1.3.6.1.4.1.2947.1.2.5.0", "", 0, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } } ; mib2nut_info_t bestpower = { "bestpower", BESTPOWER_MIB_VERSION, NULL, BESTPOWER_OID_MODEL_NAME, bestpower_mib }; nut-2.7.4/drivers/safenet.h0000644000175000017500000000424612640443572012544 00000000000000/* * safenet.h - defines/macros for the safenet driver * * Copyright (C) 2003 Arjen de Korte * * 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 */ /* * The following commands where traced on the serial port. From these, the * COM_POLL_STAT command is just an example of how this command looks. * Inside the driver, we'll overwrite the default with specially crafted * 'random' data (see the comments). */ #define COM_INITIALIZE "ZCADLIOPERJD\r" #define COM_MAINS_TEST "ZFSDERBTRFGY\r" #define COM_BATT_TEST "ZAVLEJFICOPR\r" #define COM_STOP_TEST "ZGWLEJFICOPR\r" #define COM_TOGGLE_BEEP "ZELWSABPMBEQ\r" #define COM_POLL_STAT "ZHDGFGDJELBC\r" /* * The following command is "ZBASdddWLPGE\r", where 'ddd' equals the number of * seconds delay before the UPS switches off. Value must be greater than or * equal to 1. Mapping of the numerals is 0=A, 1=B, 2=C, etc. */ #define SHUTDOWN_RETURN "ZBASAAAWLPGE\r" /* shutdown in 1 second */ /* * The following commands are "ZAFdddRrrrrO\r", where 'ddd' equals the number * of seconds delay before the UPS switches off and 'rrrr' the number of * minutes before it restarts. Both values must be greater than or equal to 1. * Mapping of the numerals is 0=A, 1=B, 2=C, etc. */ #define SHUTDOWN_REBOOT "ZAFAAARAAAAO\r" /* shutdown in 1 second, return after 1 minute */ struct safenet { char onbattery; char dunno_02; char batterylow; char overload; char dunno_05; char silenced; char batteryfail; char systemfail; char systemtest; char dunno_10; }; nut-2.7.4/drivers/idowell-hid.c0000644000175000017500000001706212640443572013313 00000000000000/* idowell-hid.c - subdriver to monitor iDowell USB/HID devices with NUT * * Copyright (C) * 2003 - 2009 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * * Note: this subdriver was initially generated as a "stub" by the * gen-usbhid-subdriver script. It must be customized. * * 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 "usbhid-ups.h" #include "idowell-hid.h" #include "main.h" /* for getval() */ #include "usb-common.h" #define IDOWELL_HID_VERSION "iDowell HID 0.1" /* FIXME: experimental flag to be put in upsdrv_info */ /* iDowell */ #define IDOWELL_VENDORID 0x075d /* USB IDs device table */ static usb_device_id_t idowell_usb_device_table[] = { /* iDowell */ { USB_DEVICE(IDOWELL_VENDORID, 0x0300), NULL }, /* Terminating entry */ { -1, -1, NULL } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* IDOWELL usage table */ static usage_lkp_t idowell_usage_lkp[] = { { NULL, 0 } }; static usage_tables_t idowell_utab[] = { idowell_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ static hid_info_t idowell_hid2nut[] = { #ifdef DEBUG { "unmapped.ups.flow.[4].flowid", 0, 0, "UPS.Flow.[4].FlowID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powerconverter.output.outputid", 0, 0, "UPS.PowerConverter.Output.OutputID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powerconverter.powerconverterid", 0, 0, "UPS.PowerConverter.PowerConverterID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.flowid", 0, 0, "UPS.PowerSummary.FlowID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.presentstatus.undefined", 0, 0, "UPS.PowerSummary.PresentStatus.Undefined", NULL, "%.0f", 0, NULL }, #endif /* DEBUG */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.CommunicationLost", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, 0, off_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, /* battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_STATIC , NULL }, /* Read only */ { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, /* UPS page */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.power.nominal", 0, 0, "UPS.Flow.[4].ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* input page */ { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", 0, 0, "UPS.Flow.[4].ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "output.frequency.nominal", 0, 0, "UPS.Flow.[4].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* instant commands */ { "load.off.delay", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *idowell_format_model(HIDDevice_t *hd) { return hd->Product; } static const char *idowell_format_mfr(HIDDevice_t *hd) { return hd->Vendor ? hd->Vendor : "iDowell"; } static const char *idowell_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int idowell_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(idowell_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("iDowell", hd); return 0; case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t idowell_subdriver = { IDOWELL_HID_VERSION, idowell_claim, idowell_utab, idowell_hid2nut, idowell_format_model, idowell_format_mfr, idowell_format_serial, }; nut-2.7.4/drivers/mge-shut.c0000644000175000017500000011211412640473702012633 00000000000000/* mge-shut.c - monitor MGE UPS for NUT with SHUT protocol * * Copyright (C) 2002 - 2012 * Arnaud Quette * * Sponsored by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include "config.h" #include "main.h" #include "serial.h" #include "timehead.h" #include "mge-shut.h" #include "hidparser.h" #include "hidtypes.h" #include "common.h" /* for upsdebugx() etc */ /* --------------------------------------------------------------- */ /* Define "technical" constants */ /* --------------------------------------------------------------- */ #define DRIVER_NAME "Eaton / SHUT driver" #define DRIVER_VERSION "0.70" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette ", DRV_STABLE, { NULL } }; #define MAX_TRY 4 /* global variables */ int commstatus = 0; int lowbatt = -1; int ondelay = DEFAULT_ONDELAY; int offdelay = DEFAULT_OFFDELAY; int notification = DEFAULT_NOTIFICATION; #define SD_RETURN 0 #define SD_STAYOFF 1 int sdtype = SD_RETURN; #define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8)) /* realign packet data according to Endianess */ static void align_request(hid_packet_t *sd) { #if WORDS_BIGENDIAN /* Sparc/Mips/... are big endian, USB/SHUT little endian */ (*sd).wValue = BYTESWAP((*sd).wValue); (*sd).wIndex = BYTESWAP((*sd).wIndex); (*sd).wLength = BYTESWAP((*sd).wLength); #endif } /* --------------------------------------------------------------- */ /* Global structures */ /* --------------------------------------------------------------- */ hid_desc_data_t hid_descriptor; device_desc_data_t device_descriptor; static long hValue; static HIDDesc_t *pDesc = NULL; /* parsed Report Descriptor */ u_char raw_buf[4096]; /* --------------------------------------------------------------- */ /* Function prototypes */ /* --------------------------------------------------------------- */ float expo(int a, int b); extern long FormatValue(long Value, u_char Size); static const char *hu_find_infoval(info_lkp_t *hid2info, long value); /* --------------------------------------------------------------- */ /* UPS Driver Functions */ /* --------------------------------------------------------------- */ void upsdrv_initinfo (void) { mge_info_item_t *item; upsdebugx(2, "entering initinfo()\n"); /* Get complete Model information */ shut_identify_ups (); printf("Detected %s [%s] on %s\n", dstate_getinfo("ups.model"), dstate_getinfo("ups.serial"), device_path); /* Device capabilities enumeration ----------------------------- */ for ( item = mge_info ; item->type != NULL ; item++ ) { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) return; /* avoid redundancy when multiple defines (RO/RW) */ if (dstate_getinfo(item->type) != NULL) continue; /* Special case for handling server side variables */ if (item->shut_flags & SHUT_FLAG_ABSENT) { /* Check if exists (if necessary) before creation */ if (item->item_path != NULL) { if (hid_get_value(item->item_path) != 1 ) continue; } else { /* Simply set the default value */ dstate_setinfo(item->type, "%s", item->dfl); dstate_setflags(item->type, item->flags); continue; } dstate_setinfo(item->type, "%s", item->dfl); dstate_setflags(item->type, item->flags); /* Set max length for strings, if needed */ if (item->flags & ST_FLAG_STRING) dstate_setaux(item->type, item->length); /* disable reading now item->shut_flags &= ~SHUT_FLAG_OK;*/ } else { if (hid_get_value(item->item_path) != 0 ) { item->shut_flags &= SHUT_FLAG_OK; dstate_setinfo(item->type, item->fmt, hValue); dstate_setflags(item->type, item->flags); /* Set max length for strings */ if (item->flags & ST_FLAG_STRING) dstate_setaux(item->type, item->length); } else { item->shut_flags &= ~SHUT_FLAG_OK; } } } /* commands ----------------------------------------------- */ dstate_addcmd("load.off"); dstate_addcmd("load.on"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); /* install handlers */ upsh.setvar = hid_set_value; /* setvar; */ upsh.instcmd = instcmd; /* check if low battery level has been given to set it */ if (lowbatt != -1) { hid_set_value("battery.charge.low", getval ("lowbatt")); } } /* --------------------------------------------------------------- */ void upsdrv_updateinfo (void) { mge_info_item_t *item; const char *nutvalue; upsdebugx(2, "entering upsdrv_updateinfo()"); if (commstatus == 0) { if (shut_ups_start () != 0) { upsdebugx(2, "No communication with UPS, retrying"); dstate_datastale(); return; } else { upsdebugx(2, "Communication with UPS established"); } } shut_ups_status(); /* Device data walk ----------------------------- */ for ( item = mge_info ; item->type != NULL; item++ ) { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) return; if (item->shut_flags & SHUT_FLAG_ABSENT) continue; if (item->shut_flags & SHUT_FLAG_OK) { if(hid_get_value(item->item_path) != 0 ) { upsdebugx(3, "%s: hValue = %ld", item->item_path, hValue); /* upsdebugx(3, "%s: hValue = %ld (%ld)", item->item_path, hValue, hData.LogMax); */ /* need lookup'ed translation */ if (item->hid2info != NULL) { nutvalue = hu_find_infoval(item->hid2info, (long)hValue); if (nutvalue != NULL) dstate_setinfo(item->type, "%s", nutvalue); else dstate_setinfo(item->type, item->fmt, hValue); } else dstate_setinfo(item->type, item->fmt, hValue); dstate_dataok(); } else { if (shut_ups_start () != 0) dstate_datastale(); } } } } /* --------------------------------------------------------------- */ void upsdrv_shutdown (void) { char val[5]; if (sdtype == SD_RETURN) { /* set DelayBeforeStartup */ snprintf(val, sizeof(val), "%d", ondelay); hid_set_value("ups.timer.start", val); } /* set DelayBeforeShutdown */ snprintf(val, sizeof(val), "%d", offdelay); hid_set_value("ups.timer.shutdown", val); } /* --------------------------------------------------------------- */ void upsdrv_help (void) { upsdebugx(2, "entering upsdrv_help"); } /* --------------------------------------------------------------- */ /* list flags and values that you want to receive via -x */ void upsdrv_makevartable (void) { char msg[MAX_STRING]; upsdebugx (2, "entering upsdrv_makevartable()"); snprintf(msg, sizeof(msg), "Set low battery level, in %% (default=%d).", DEFAULT_LOWBATT); addvar (VAR_VALUE, "lowbatt", msg); snprintf(msg, sizeof(msg), "Set shutdown delay, in seconds (default=%d).", DEFAULT_OFFDELAY); addvar (VAR_VALUE, "offdelay", msg); snprintf(msg, sizeof(msg), "Set startup delay, in ten seconds units (default=%d).", DEFAULT_ONDELAY); addvar (VAR_VALUE, "ondelay", msg); snprintf(msg, sizeof(msg), "Set notification type, 1 = no, 2 = light, 3 = yes (default=%d).", DEFAULT_NOTIFICATION); addvar (VAR_VALUE, "notification", msg); } /* --------------------------------------------------------------- */ void upsdrv_initups (void) { upsdebugx(2, "entering upsdrv_initups()"); /* initialize serial port */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); setline (1); /* get battery lowlevel */ if (getval ("lowbatt")) lowbatt = atoi (getval ("lowbatt")); /* on delay */ if (getval ("ondelay")) ondelay = atoi (getval ("ondelay")); /* shutdown delay */ if (getval ("offdelay")) offdelay = atoi (getval ("offdelay")); /* notification type */ if (getval ("notification")) notification = atoi (getval ("notification")); /* initialise communication */ if (shut_ups_start () != 0) fatalx(EXIT_FAILURE, "No communication with UPS"); else upsdebugx(2, "Communication with UPS established"); /* initialise HID communication */ if(hid_init_device() != 0) fatalx(EXIT_FAILURE, "Can't initialise HID device"); } /* --------------------------------------------------------------- */ void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } /* --------------------------------------------------------------- */ int instcmd(const char *cmdname, const char *extra) { /* Shutdown UPS and return when power is restored */ if (!strcasecmp(cmdname, "shutdown.return")) { sdtype = SD_RETURN; upsdrv_shutdown(); return STAT_INSTCMD_HANDLED; } /* Shutdown UPS and stay off when power is restored */ if (!strcasecmp(cmdname, "shutdown.stayoff")) { sdtype = SD_STAYOFF; upsdrv_shutdown(); return STAT_INSTCMD_HANDLED; } /* Power off the load immediatly */ if (!strcasecmp(cmdname, "load.off")) { /* set DelayBeforeShutdown to 0 */ hid_set_value("ups.timer.shutdown", "0"); return STAT_INSTCMD_HANDLED; } /* Power on the load immediatly */ if (!strcasecmp(cmdname, "load.on")) { /* set DelayBeforeStartup to 0 */ hid_set_value("ups.timer.start", "0"); return STAT_INSTCMD_HANDLED; } /* Start battery test */ if (!strcasecmp(cmdname, "test.battery.start")) { /* set Test to 1 (Quick test) */ hid_set_value("ups.test.result", "1"); return STAT_INSTCMD_HANDLED; } /* Stop battery test */ if (!strcasecmp(cmdname, "test.battery.stop")) { /* set Test to 3 (Abort test) */ hid_set_value("ups.test.result", "3"); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } /***************************************************************************** * shut_ups_start () * * initiate communication with the UPS * * return 0 on success, -1 on failure * *****************************************************************************/ int shut_ups_start () { u_char c = SHUT_SYNC, r[1]; int try; upsdebugx (2, "entering shut_ups_start()\n"); r[0] = '\0'; switch (notification) { case OFF_NOTIFICATION: c = SHUT_SYNC_OFF; break; case LIGHT_NOTIFICATION: c = SHUT_SYNC_LIGHT; break; default: case COMPLETE_NOTIFICATION: c = SHUT_SYNC; break; } /* Sync with the UPS using Complete, Off or light notification */ for (try = 0; try < MAX_TRY; try++) { if ((shut_token_send(c)) == -1) { upsdebugx (3, "Communication error while writing to port"); return -1; } serial_read (1000, &r[0]); if (r[0] == c) { commstatus = 1; upsdebugx (3, "Syncing and notification setting done"); return 0; } } commstatus = 0; return -1; } /********************************************************************** * shut_identify_ups () * * Get SHUT device complete name * * return 0 on success, -1 on failure * *********************************************************************/ int shut_identify_ups () { char string[MAX_STRING]; char model[MAX_STRING]; const char *finalname = NULL; int retcode, tries=MAX_TRY; if (commstatus == 0) return -1; upsdebugx (2, "entering shut_identify_ups(0x%04x, 0x%04x)\n", device_descriptor.dev_desc.iManufacturer, device_descriptor.dev_desc.iProduct); /* Get strings iModel and iProduct */ while (tries > 0) { if (shut_get_string(device_descriptor.dev_desc.iProduct, string, 0x25) > 0) { strcpy(model, string); if(hid_get_value("UPS.PowerSummary.iModel") != 0 ) { if((shut_get_string(hValue, string, 0x25)) > 0) { finalname = get_model_name(model, string); upsdebugx (2, "iModel = %s", string); tries = 0; } } else { /* Try with "UPS.Flow.[4].ConfigApparentPower" */ if(hid_get_value("UPS.Flow.[4].ConfigApparentPower") != 0 ) { snprintf(string, sizeof(string), "%i", (int)hValue); finalname = get_model_name(model, string); } else finalname = get_model_name(model, NULL); tries = 0; } dstate_setinfo("ups.model", "%s", finalname); } else tries--; } /* Get strings iSerialNumber */ if (((retcode = shut_get_string(device_descriptor.dev_desc.iSerialNumber, string, 0x25)) > 0) && strcmp(string, "") && string[0] != '\t') { dstate_setinfo("ups.serial", "%s", string); } else dstate_setinfo("ups.serial", "unknown"); /* all went fine */ return 1; } /********************************************************************** * shut_wait_ack() * * wait for an ACK packet * * returns 0 on success, -1 on error, -2 on NACK, -3 on NOTIFICATION * *********************************************************************/ int shut_wait_ack (void) { u_char c[1]; c[0] = '\0'; serial_read (DEFAULT_TIMEOUT, &c[0]); if (c[0] == SHUT_OK) { upsdebugx (2, "shut_wait_ack(): ACK received"); return 0; } else if (c[0] == SHUT_NOK) { upsdebugx (2, "shut_wait_ack(): NACK received"); return -2; } else if ((c[0] & 0x0f) == SHUT_TYPE_NOTIFY) { upsdebugx (2, "shut_wait_ack(): NOTIFY received"); return -3; } upsdebugx (2, "shut_wait_ack(): Nothing received"); return -1; } /********************************************************************** * char_read (char *bytes, int size, int read_timeout) * * reads size bytes from the serial port * * bytes - buffer to store the data * size - size of the data to get * read_timeout - serial timeout (in milliseconds) * * return -1 on error, -2 on timeout, nb_bytes_readen on success * *********************************************************************/ static int char_read (char *bytes, int size, int read_timeout) { struct timeval serial_timeout; fd_set readfs; int readen = 0; int rc = 0; FD_ZERO (&readfs); FD_SET (upsfd, &readfs); serial_timeout.tv_usec = (read_timeout % 1000) * 1000; serial_timeout.tv_sec = (read_timeout / 1000); rc = select (upsfd + 1, &readfs, NULL, NULL, &serial_timeout); if (0 == rc) return -2; /* timeout */ if (FD_ISSET (upsfd, &readfs)) { int now = read (upsfd, bytes, size - readen); if (now < 0) { return -1; } else { bytes += now; readen += now; } } else { return -1; } return readen; } /********************************************************************** * serial_read (int read_timeout) * * return data one byte at a time * * read_timeout - serial timeout (in milliseconds) * * returns 0 on success, -1 on error, -2 on timeout * **********************************************************************/ int serial_read (int read_timeout, u_char *readbuf) { static u_char cache[512]; static u_char *cachep = cache; static u_char *cachee = cache; int recv; *readbuf = '\0'; /* if still data in cache, get it */ if (cachep < cachee) { *readbuf = *cachep++; return 0; /* return (int) *cachep++; */ } recv = char_read ((char *)cache, 1, read_timeout); if ((recv == -1) || (recv == -2)) return recv; cachep = cache; cachee = cache + recv; cachep = cache; cachee = cache + recv; if (recv) { upsdebugx(5,"received: %02x", *cachep); *readbuf = *cachep++; return 0; } return -1; } /********************************************************************** * serial_send (char *buf, int len) * * write the content of buf to the serial port * * buf - data to send * len - lenght of data to send * * returns number of bytes written on success, -1 on error * **********************************************************************/ int serial_send (u_char *buf, int len) { tcflush (upsfd, TCIFLUSH); upsdebug_hex (3, "sent", (u_char *)buf, len); return write (upsfd, buf, len); } /* * Serial HID UPS Transfer (SHUT) functions *********************************************************************/ /* Get and parse UPS status */ void shut_ups_status(void) { int try = 0, retcode = 0; /* clear status buffer before begining */ status_init(); /* Ensure to have at least basic status */ while (try < MAX_TRY) { if((retcode = hid_get_value("UPS.PowerSummary.PresentStatus.ACPresent")) != 0 ) { try = MAX_TRY; if(hValue == 1){ status_set("OL"); } else { status_set("OB"); } } else { /* retry to get data */ try++; } } if(hid_get_value("UPS.PowerSummary.PresentStatus.Discharging") != 0 ) { if(hValue == 1) status_set("DISCHRG"); } if(hid_get_value("UPS.PowerSummary.PresentStatus.Charging") != 0 ) { if(hValue == 1) status_set("CHRG"); } if(hid_get_value("UPS.PowerSummary.PresentStatus.ShutdownImminent") != 0 ) { if(hValue == 1) status_set("LB"); } if(hid_get_value("UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit") != 0 ) { if(hValue == 1) status_set("LB"); } if(hid_get_value("UPS.PowerSummary.PresentStatus.Overload") != 0 ) { if(hValue == 1) status_set("OVER"); } if(hid_get_value("UPS.PowerSummary.PresentStatus.NeedReplacement") != 0 ) { if(hValue == 1) status_set("RB"); } if(hid_get_value("UPS.PowerSummary.PresentStatus.Good") != 0 ) { if(hValue == 0) status_set("OFF"); } /* FIXME: extend ups.status for BYPASS: */ /* Manual bypass */ if(hid_get_value("UPS.PowerConverter.Input.[4].PresentStatus.Used") != 0 ) { if(hValue == 1) status_set("BYPASS"); } /* Automatic bypass */ if(hid_get_value("UPS.PowerConverter.Input.[2].PresentStatus.Used") != 0 ) { if(hValue == 1) status_set("BYPASS"); } status_commit(); } /* Calculate the SHUT checksum for the packet "buf" */ u_char shut_checksum(const u_char *buf, int bufsize) { int i; u_char chk=0; for(i=0; i0 && Retry>0) { Size=(datalen>=8) ? 8 : datalen; /* Packets need only to be sent once * NACK handling should take care of the rest */ if (Retry == 1) { /* Forge SHUT Frame */ SHUTRequest.bType = SHUT_TYPE_REQUEST + token; SHUTRequest.bLength = (Size<<4) + Size; SHUTRequest.data = *hdata; /* memcpy(&SHUTRequest.data.raw_pkt, hdata->raw_pkt, Size); */ sdata.shut_pkt = SHUTRequest; sdata.raw_pkt[(Size+3) - 1] = shut_checksum(sdata.shut_pkt.data.raw_pkt, Size); upsdebugx (4, "shut_checksum = %2x", sdata.raw_pkt[(Size+3)-1]); serial_send (sdata.raw_pkt, Size+3); } i = shut_wait_ack (); if (i == 0) { datalen-=Size; Retry=5; upsdebugx (4, "received ACK"); break; } else if ((i == -1) || (i == -3)) { /* retry a finite number of times if something wrong happened while * sending like a notification or a NACK */ if (Retry >= MAX_TRY) { upsdebugx(2, "Max tries reached while waiting for ACK, still getting errors"); return i; } else { upsdebugx(4, "Retry = %i", Retry); /* Send a NACK to get a resend from the UPS */ shut_token_send(SHUT_NOK); Retry++; } } } return (datalen==0); } int shut_packet_recv (u_char *Buf, int datalen) { u_char Start[2]; u_char Frame[8]; u_char Chk[1]; u_short Size=8; u_short Pos=0; u_char Retry=0; int recv; upsdebugx (4, "entering shut_packet_recv (%i)", datalen); while(datalen>0 && Retry<3) { if(serial_read (DEFAULT_TIMEOUT, &Start[0]) >= 0) { if(Start[0]==SHUT_SYNC) { upsdebugx (4, "received SYNC token"); memcpy(Buf, Start, 1); return 1; } else { /* if(((Start[1] = serial_read (DEFAULT_TIMEOUT)) >= 0) && */ if((serial_read (DEFAULT_TIMEOUT, &Start[1]) >= 0) && ((Start[1]>>4)==(Start[1]&0x0F))) { upsdebug_hex(3, "Receive", Start, 2); Size=Start[1]&0x0F; for(recv=0;recv OK", Chk[0]); memcpy(Buf, Frame, Size); datalen-=Size; Buf+=Size; Pos+=Size; Retry=0; shut_token_send(SHUT_OK); /* Check if there are more data to receive */ if((Start[0] & 0xf0) == SHUT_PKT_LAST) { /* Check if it's a notification */ if ((Start[0] & 0x0f) == SHUT_TYPE_NOTIFY) { /* TODO: process notification (dropped for now) */ upsdebugx (4, "=> notification"); datalen+=Pos; Pos=0; } else return Pos; } else upsdebugx (4, "need more data (%i)!", datalen); } else { upsdebugx (4, "shut_checksum: %02x => NOK", Chk[0]); shut_token_send(SHUT_NOK); Retry++; } } else return 0; } } else Retry++; } /* while */ return 0; } /* * Human Interface Device (HID) functions *********************************************************************/ /********************************************************************** * shut_get_descriptor(int desctype, u_char *pkt) * * get descriptor specified by DescType and return it in Buf * * desctype - from shutdataType * pkt - where to store the report received * * return 0 on success, -1 on failure, -2 on NACK * *********************************************************************/ int shut_get_descriptor(int desctype, u_char *pkt, int reportlen) { hid_packet_t HIDRequest; hid_data_t data; int retcode; upsdebugx (2, "entering shut_get_descriptor(n %02x, %i)", desctype, reportlen); HIDRequest.bmRequestType = REQUEST_TYPE_USB+(desctype>=HID_DESCRIPTOR?1:0); HIDRequest.bRequest = 0x06; HIDRequest.wValue = (desctype<<8); HIDRequest.wIndex = 0x0000; HIDRequest.wLength = reportlen; align_request(&HIDRequest); data.hid_pkt = HIDRequest; /* if((retcode = shut_packet_send (&data, sizeof(data), SHUT_PKT_LAST)) > 0) */ if((retcode = shut_packet_send (&data, 8, SHUT_PKT_LAST)) > 0) { if((retcode = shut_packet_recv (pkt, reportlen)) > 0) { upsdebug_hex(3, "shut_get_descriptor", pkt, retcode); return retcode; } else return retcode; } return retcode; } /********************************************************************** * shut_get_string(int index, u_char *pkt, int reportlen) * * get descriptor specified by DescType and return it in Buf * * index - from shutdataType * string - where to store the string received * strlen - length of string * * return string size on success, -1 on failure, -2 on NACK * *********************************************************************/ int shut_get_string(int strindex, char *string, int stringlen) { hid_packet_t HIDRequest; hid_data_t data; int retcode; u_char buf[MAX_STRING]; upsdebugx (2, "entering shut_get_string(%02x)", strindex); HIDRequest.bmRequestType = REQUEST_TYPE_USB; HIDRequest.bRequest = 0x06; HIDRequest.wValue = strindex+(STRING_DESCRIPTOR<<8); HIDRequest.wIndex = 0x0000; HIDRequest.wLength = (stringlen<<8); /* (reportlen&0xFF)&(reportlen>>8); */ align_request(&HIDRequest); data.hid_pkt = HIDRequest; if((retcode = shut_packet_send (&data, 8, SHUT_PKT_LAST)) >0) { upsdebug_hex(3, "shut_get_string", data.raw_pkt, 8); if((retcode = shut_packet_recv (buf, stringlen)) > 0) { upsdebug_hex(3, "shut_get_string", buf, retcode); make_string(buf, retcode, string); upsdebugx(2, "string: %s", string); return strlen(string); } else return retcode; } return 0; } /********************************************************************** * shut_get_report(int id, u_char *pkt, int reportlen) * * get report specified by id and return it in pkt * * id - from shutdataType * pkt - where to store the string received * strlen - length of string * * return report size on success, -1 on failure, -2 on NACK * *********************************************************************/ int shut_get_report(int id, u_char *pkt, int reportlen) { hid_packet_t HIDRequest; hid_data_t data; int retcode; upsdebugx (2, "entering shut_get_report(id: %02x, len: %02x)", id, reportlen); HIDRequest.bmRequestType = REQUEST_TYPE_GET_REPORT; HIDRequest.bRequest = 0x01; HIDRequest.wValue = id+(HID_REPORT_TYPE_FEATURE<<8); HIDRequest.wIndex = 0x0000; HIDRequest.wLength = reportlen; align_request(&HIDRequest); data.hid_pkt = HIDRequest; /* if((retcode = shut_packet_send (&data, sizeof(data), SHUT_PKT_LAST)) > 0) */ if((retcode = shut_packet_send (&data, 8, SHUT_PKT_LAST)) > 0) { if((retcode = shut_packet_recv (pkt, reportlen)) > 0) { upsdebug_hex(3, "shut_get_report", pkt, retcode); return retcode; } else return retcode; } return retcode; } /********************************************************************** * shut_set_report(int id, u_char *pkt, int reportlen) * * set report specified by id using pkt as value * * id - from shutdataType * pkt - what to put in report * strlen - length of report * * return string size on success, -1 on failure, -2 on NACK * *********************************************************************/ int shut_set_report(int id, u_char *pkt, int reportlen) { hid_packet_t HIDRequest; hid_data_t data; int retcode; upsdebugx (2, "entering shut_set_report(id: %02x, len: %02x)", id, reportlen); HIDRequest.bmRequestType = REQUEST_TYPE_SET_REPORT; HIDRequest.bRequest = 0x09; HIDRequest.wValue = id+(HID_REPORT_TYPE_FEATURE<<8); HIDRequest.wIndex = 0x0000; HIDRequest.wLength = reportlen; align_request(&HIDRequest); data.hid_pkt = HIDRequest; /* first packet to instruct a set command */ if((retcode = shut_packet_send (&data, sizeof(data), 0x0)) > 0) { /* second packet to give the actual data */ memcpy(&data.raw_pkt, pkt, reportlen); upsdebug_hex(3, "Set2", pkt, reportlen); retcode = shut_packet_send (&data, reportlen, SHUT_PKT_LAST); } return retcode; } /********************************************************************** * hid_init_device() * * Get Device/HID/Report descriptors from device and initialise * HID Parser for further actions * * return 0 on success, -1 on failure * *********************************************************************/ int hid_init_device() { int retcode; /* Get HID descriptor */ if((retcode = shut_get_descriptor(HID_DESCRIPTOR, hid_descriptor.raw_desc, 0x09)) > 0) { upsdebug_hex(3, "shut_get_descriptor(hid)", hid_descriptor.raw_desc, retcode); /* WORKAROUND: need to be fixed */ hid_descriptor.hid_desc.wDescriptorLength = hid_descriptor.raw_desc[7] + (hid_descriptor.raw_desc[8]<<8); upsdebugx(3, "HID Descriptor: \nbLength: \t\t0x%02x\nbDescriptorType: \t0x%02x\n", hid_descriptor.hid_desc.bLength, hid_descriptor.hid_desc.bDescriptorType); upsdebugx(3, "bcdHID: \t\t0x%04x\nbCountryCode: \t\t0x%02x\nbNumDescriptors: \t0x%02x\n", hid_descriptor.hid_desc.bcdHID, hid_descriptor.hid_desc.bCountryCode, hid_descriptor.hid_desc.bNumDescriptors); upsdebugx(3, "bReportDescriptorType: \t0x%02x\nwDescriptorLength: \t0x%04x", hid_descriptor.hid_desc.bReportDescriptorType, hid_descriptor.hid_desc.wDescriptorLength); /* Get Device descriptor */ if((retcode = shut_get_descriptor(DEVICE_DESCRIPTOR, device_descriptor.raw_desc, 0x12)) > 0) { upsdebug_hex(3, "shut_get_descriptor(device)", device_descriptor.raw_desc, retcode); upsdebugx(2, "Device Descriptor: \nbLength: \t\t0x%02x\nbDescriptorType:\ \t0x%02x\nbcdUSB: \t\t0x%04x\nbDeviceClass: \t\t0x%02x\nbDeviceSubClass:\ \t0x%02x\nbDeviceProtocol: \t0x%02x\nbMaxPacketSize0:\ \t0x%02x\nidVendor: \t\t0x%04x\nidProduct: \t\t0x%04x\nbcdDevice:\ \t\t0x%04x\niManufacturer: \t\t0x%02x\niProduct:\ \t\t0x%02x\niSerialNumber: \t\t0x%02x\nbNumConfigurations: \t0x%02x\n", device_descriptor.dev_desc.bLength, device_descriptor.dev_desc.bDescriptorType, device_descriptor.dev_desc.bcdUSB, device_descriptor.dev_desc.bDeviceClass, device_descriptor.dev_desc.bDeviceSubClass, device_descriptor.dev_desc.bDeviceProtocol, device_descriptor.dev_desc.bMaxPacketSize0, device_descriptor.dev_desc.idVendor, device_descriptor.dev_desc.idProduct, device_descriptor.dev_desc.bcdDevice, device_descriptor.dev_desc.iManufacturer, device_descriptor.dev_desc.iProduct, device_descriptor.dev_desc.iSerialNumber, device_descriptor.dev_desc.bNumConfigurations); /* Get Report descriptor */ if((retcode = shut_get_descriptor(REPORT_DESCRIPTOR, raw_buf, hid_descriptor.hid_desc.wDescriptorLength)) > 0) { upsdebug_hex(3, "shut_get_descriptor(report)", raw_buf, retcode); /* Parse Report Descriptor */ Free_ReportDesc(pDesc); pDesc = Parse_ReportDesc(raw_buf, retcode); if (!pDesc) { fatalx(EXIT_FAILURE, "Failed to parse report descriptor: %s", strerror(errno)); } } else fatalx(EXIT_FAILURE, "Unable to get Report Descriptor"); } else fatalx(EXIT_FAILURE, "Unable to get Device Descriptor"); } else fatalx(EXIT_FAILURE, "Unable to get HID Descriptor"); return 0; } /* translate HID string path to numeric path and return path depth */ ushort lookup_path(const char *HIDpath, HIDData_t *data) { ushort i = 0, cond = 1; int cur_usage; char buf[MAX_STRING]; char *start, *end; strncpy(buf, HIDpath, strlen(HIDpath)); buf[strlen(HIDpath)] = '\0'; start = end = buf; upsdebugx(3, "entering lookup_path(%s)", buf); while (cond) { if ((end = strchr(start, '.')) == NULL) { cond = 0; } else *end = '\0'; upsdebugx(4, "parsing %s", start); /* lookup code */ if ((cur_usage = hid_lookup_usage(start)) == -1) { upsdebugx(4, "%s wasn't found", start); return 0; } else { data->Path.Node[i] = cur_usage; i++; } if(cond) start = end +1 ; } data->Path.Size = i; return i; } /* Lookup this usage name to find its code (page + index) */ int hid_lookup_usage(char *name) { int i; upsdebugx(4, "Looking up %s", name); if (name[0] == '[') /* manage indexed collection */ return (0x00FF0000 + atoi(&name[1])); else { for (i = 0; (usage_lkp[i].usage_code != 0x0); i++) { if (!strcmp(usage_lkp[i].usage_name, name)) { upsdebugx(4, "hid_lookup_usage: found %04x", usage_lkp[i].usage_code); return usage_lkp[i].usage_code; } } } return -1; } /* Get an item value from a HID path */ int hid_get_value(const char *item_path) { int i, retcode; HIDData_t hData; upsdebugx(3, "entering hid_get_value(%s)", item_path); /* Prepare path of HID object */ hData.Type = ITEM_FEATURE; hData.ReportID = 0; if((retcode = lookup_path(item_path, &hData)) > 0) { upsdebugx(3, "Path depth = %i\n", retcode); for (i = 0; i 0) { GetValue((const u_char *) raw_buf, &hData, &hValue); upsdebug_hex(3, "Object's report", raw_buf, 10); upsdebugx(3, "Value = %ld", hValue); return 1; } else shut_ups_start(); } else { upsdebugx(3, "Can't find object"); return 0; } } else { upsdebugx(3, "Can't lookup object's path"); return 0; } return 0; } /* * Internal functions ****************************************************************************/ /* * Filter and reformat HID strings (suppress space * between each letter) * Note: string format in HID String Descriptor is * Byte1: Size of descriptor(=>string) * Byte2: String descriptor type (always 0x03) * Byte3 to byteN: UNICODE string (in US: xx 00 for a letter) */ void make_string(u_char *buf, int datalen, char *string) { int i, /* Skip size and type */ j=0; upsdebugx(4, "String descriptor: size = 0x%02x, type = 0x%02x", buf[0], buf[1]); /* TODO: add clean support for UNICODE */ for(i=2;i0) return (float) a * expo(a,b-1); if (b<0) return (float)((float)(1/(float)a) * (float) expo(a,b+1)); /* not reached */ return -1; } /* Format model names */ const char *get_model_name(char *iProduct, char *iModel) { models_name_t *model = NULL; upsdebugx(2, "get_model_name(%s, %s)\n", iProduct, iModel); /* Search for formatting rules */ for ( model = models_names ; model->iProduct != NULL ; model++ ) { upsdebugx(2, "comparing with: %s", model->finalname); if ( (!strncmp(iProduct, model->iProduct, strlen(model->iProduct))) && (!strncmp(iModel, model->iModel, strlen(model->iModel))) ) { upsdebugx(2, "Found %s\n", model->finalname); break; } } /* FIXME: if we end up with model->iProduct == NULL * then process name in a generic way (not yet supported models!) */ return model->finalname; } /* set r/w INFO_ element to a value. */ int hid_set_value(const char *varname, const char *val) { int retcode, i, replen; mge_info_item_t *shut_info_p; HIDData_t hData; upsdebugx(2, "============== entering hid_set_value(%s, %s) ==============", varname, val); /* 1) retrieve and check netvar & item_path */ shut_info_p = shut_find_info(varname); if (shut_info_p == NULL || shut_info_p->type == NULL || !(shut_info_p->flags & SHUT_FLAG_OK)) { upsdebugx(2, "hid_ups_set: info element unavailable %s", varname); return STAT_SET_UNKNOWN; } /* Checking item writability and HID Path */ if (!shut_info_p->flags & ST_FLAG_RW) { upsdebugx(2, "hid_ups_set: not writable %s", varname); return STAT_SET_UNKNOWN; } /* handle server side variable */ if (shut_info_p->shut_flags & SHUT_FLAG_ABSENT) { upsdebugx(2, "hid_ups_set: setting server side variable %s", varname); dstate_setinfo(shut_info_p->type, "%s", val); return STAT_SET_HANDLED; } else { /* SHUT_FLAG_ABSENT is the only case of HID Path == NULL */ if (shut_info_p->item_path == NULL) { upsdebugx(2, "hid_ups_set: ID Path is NULL for %s", varname); return STAT_SET_UNKNOWN; } } /* Prepare path of HID object */ hData.Type = ITEM_FEATURE; hData.ReportID = 0; if((retcode = lookup_path(shut_info_p->item_path, &hData)) > 0) { upsdebugx(3, "Path depth = %i\n", retcode); for (i = 0; i disabled for now if (shut_get_report(hData.ReportID, raw_buf, MAX_REPORT_SIZE) > 0) { GetValue((const u_char *) raw_buf, &hData, &hValue); upsdebugx(3, "Value = %d", hValue); if (hValue != atol(val)) upsdebugx(3, "FAILED"); else upsdebugx(3, "SUCCEED"); } else upsdebugx(3, "FAILED"); */ return STAT_SET_HANDLED; } else upsdebugx(3, "Object is constant"); } else upsdebugx(3, "Can't find object"); } else upsdebugx(3, "Can't lookup object's path"); return STAT_SET_UNKNOWN; } /* find info element definition in my info array. */ mge_info_item_t *shut_find_info(const char *varname) { mge_info_item_t *shut_info_p; for (shut_info_p = &mge_info[0]; shut_info_p->type != NULL; shut_info_p++) if (!strcasecmp(shut_info_p->type, varname)) return shut_info_p; fatalx(EXIT_FAILURE, "shut_find_info: unknown info type: %s", varname); return NULL; } /* find the NUT value matching that HID Item value */ static const char *hu_find_infoval(info_lkp_t *hid2info, long value) { info_lkp_t *info_lkp; upsdebugx(3, "hu_find_infoval: searching for value = %ld\n", value); for (info_lkp = hid2info; (info_lkp != NULL) && (strcmp(info_lkp->nut_value, "NULL")); info_lkp++) { if (info_lkp->hid_value == value) { upsdebugx(3, "hu_find_infoval: found %s (value: %ld)\n", info_lkp->nut_value, value); return info_lkp->nut_value; } } upsdebugx(3, "hu_find_infoval: no matching INFO_* value for this HID value (%ld)\n", value); return NULL; } nut-2.7.4/drivers/genericups.h0000644000175000017500000001660112640444140013251 00000000000000/* genericups.h - contact closure UPS line status definitions Copyright (C) 1999, 2000 Russell Kroll 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 */ struct { const char *mfr; /* value for INFO_MFR */ const char *model; /* value for INFO_MODEL */ const char *desc; /* used in -h listing */ int line_norm; int line_ol, val_ol; int line_bl, val_bl; int line_sd; } upstab[] = { /* Type 0 */ { "UPSONIC", "LAN Saver 600", "UPSONIC LAN Saver 600", TIOCM_DTR | TIOCM_RTS, /* cable power: DTR + RTS */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ TIOCM_RTS /* shutdown: RTS */ }, /* Type 1 */ { "APC", "Back-UPS", "APC Back-UPS (940-0095A/C cable)", TIOCM_DTR, /* cable power: DTR */ TIOCM_RNG, 0, /* online: RNG off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ TIOCM_RTS /* shutdown: RTS */ }, /* Type 2 */ { "APC", "Back-UPS", "APC Back-UPS (940-0020B/C cable)", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ TIOCM_DTR|TIOCM_RTS /* shutdown: DTR + RTS */ }, /* Type 3 */ { "PowerTech", "Comp1000", "PowerTech Comp1000 with DTR as cable power", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ TIOCM_DTR | TIOCM_RTS /* shutdown: DTR + RTS */ }, /* Type 4 */ { "Generic", "Generic RUPS model", "Generic RUPS model", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ 0 /* shutdown: none */ }, /* Type 5 */ { "TrippLite", "Internet Office Series", "Tripp Lite UPS with Lan2.2 interface (black 73-0844 cable)", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_DTR | TIOCM_RTS /* shutdown: DTR + RTS */ }, /* Type 6 */ { "Best", "Patriot", "Best Patriot (INT51 cable)", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_RTS /* shutdown: set RTS */ }, /* Type 7 */ { "CyberPower", "Power99", "CyberPower Power99", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_DTR /* shutdown: set DTR */ }, /* Type 8 */ { "Nitram", "Elite UPS", "Nitram Elite 500", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ -1 /* shutdown: unknown */ }, /* Type 9 */ { "APC", "Back-UPS", "APC Back-UPS (940-0023A cable)", 0, /* cable power: none */ TIOCM_CD, 0, /* online: CD off */ TIOCM_CTS, TIOCM_CTS, /* low battery: CTS on */ TIOCM_RTS /* shutdown: RTS */ }, /* Type 10 (duplicate from 7) */ { "Victron", "Lite", "Victron Lite (crack cable)", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_DTR /* shutdown: DTR */ }, /* Type 11 */ { "Powerware", "3115", "Powerware 3115", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_ST /* shutdown: ST */ }, /* Type 12 */ { "APC", "Back-UPS Office", "APC Back-UPS Office (940-0119A cable)", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ TIOCM_DTR /* shutdown: raise DTR */ }, /* Type 13 */ { "RPT", "Repoteck", "Repoteck RPT-800A, RPT-162A", TIOCM_DTR | TIOCM_RTS, /* cable power: DTR + RTS */ TIOCM_CD, TIOCM_CD, /* On-line : DCD on */ TIOCM_CTS, 0, /* Battery low: CTS off */ TIOCM_ST /* shutdown: TX BREA */ }, /* Type 14 */ { "Online", "P250, P500, P750, P1250", "Online P-series", TIOCM_DTR, /* cable power: DTR */ TIOCM_CD, TIOCM_CD, /* online: CD on */ TIOCM_CTS, 0, /* low battery: CTS off */ TIOCM_RTS /* shutdown: raise RTS */ }, /* Type 15 */ { "Powerware", "5119", "Powerware 5119", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_ST /* shutdown: ST (break) */ }, /* Type 16 */ { "Nitram", "Elite UPS", "Nitram Elite 2002", TIOCM_DTR | TIOCM_RTS, /* cable power: DTR + RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ -1 /* shutdown: unknown */ }, /* Type 17 (duplicate from 8) */ { "PowerKinetics", "9001", "PowerKinetics 9001", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ -1 /* shutdown: unknown */ }, /* Type 18 */ { "TrippLite", "Omni 450LAN", "TrippLite UPS with Martin's cabling", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, TIOCM_CD, /* low battery: CAR on */ -1 /* shutdown: none */ }, /* Type 19 (duplicate from 6) */ { "Fideltronik", "Ares Series", "Fideltronik Ares Series", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: DCD off */ TIOCM_RTS /* shutdown: set RTS */ }, /* Type 20 */ /* docs/cables/powerware.txt */ { "Powerware", "5119 RM", "Powerware 5119 RM", TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ TIOCM_ST /* shutdown: ST (break) */ }, /* Type 21 */ /* http://lists.exploits.org/upsdev/Oct2004/00004.html */ { "Generic", "Generic RUPS 2000", "Generic RUPS 2000 (Megatec M2501 cable)", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_RTS | TIOCM_DTR /* shutdown: RTS+DTR */ }, /* Type 22 (duplicate from 7)*/ { "Gamatronic Electronic Industries", "Generic Alarm UPS", "Gamatronic UPSs with alarm interface", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ TIOCM_DTR /* shutdown: DTR */ }, /* add any new entries directly above this line */ { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0 } }; nut-2.7.4/drivers/nutdrv_qx_zinto.c0000644000175000017500000001467512640473702014374 00000000000000/* nutdrv_qx_zinto.c - Subdriver for Zinto protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_zinto.h" #define ZINTO_VERSION "Zinto 0.06" /* qx2nut lookup table */ static item_t zinto_qx2nut[] = { /* * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* * > [FW?\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ { "device.mfr", 0, NULL, "FW?\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "device.model", 0, NULL, "FW?\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.firmware", 0, NULL, "FW?\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* Testing table */ #ifdef TESTING static testing_t zinto_testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "F\r", "#230.0 000 024.0 50.0\r", -1 }, { "FW?\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, { "Q\r", "", -1 }, { "S03\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "", -1 }, { "T04\r", "", -1 }, { "TL\r", "", -1 }, { "T\r", "", -1 }, { "CT\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* Subdriver-specific initups */ static void zinto_initups(void) { blazer_initups(zinto_qx2nut); } /* Subdriver interface */ subdriver_t zinto_subdriver = { ZINTO_VERSION, blazer_claim, zinto_qx2nut, zinto_initups, NULL, blazer_makevartable, "ACK", NULL, #ifdef TESTING zinto_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/bcmxcp_usb.c0000644000175000017500000003261012667537407013245 00000000000000#include "main.h" #include "bcmxcp.h" #include "bcmxcp_io.h" #include "common.h" #include "usb-common.h" #include "timehead.h" #include "nut_stdint.h" /* for uint16_t */ #include #include #include #include #include #define SUBDRIVER_NAME "USB communication subdriver" #define SUBDRIVER_VERSION "0.22" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { SUBDRIVER_NAME, SUBDRIVER_VERSION, NULL, 0, { NULL } }; #define MAX_TRY 4 /* Powerware */ #define POWERWARE 0x0592 /* Phoenixtec Power Co., Ltd */ #define PHOENIXTEC 0x06da /* Hewlett Packard */ #define HP_VENDORID 0x03f0 /* USB functions */ usb_dev_handle *nutusb_open(const char *port); int nutusb_close(usb_dev_handle *dev_h, const char *port); /* unified failure reporting: call these often */ void nutusb_comm_fail(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); void nutusb_comm_good(void); /* function pointer, set depending on which device is used */ int (*usb_set_descriptor)(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, int size); /* usb_set_descriptor() for Powerware devices */ static int usb_set_powerware(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, int size) { return usb_control_msg(udev, USB_ENDPOINT_OUT, USB_REQ_SET_DESCRIPTOR, (type << 8) + index, 0, buf, size, 1000); } static void *powerware_ups(USBDevice_t *device) { usb_set_descriptor = &usb_set_powerware; return NULL; } /* usb_set_descriptor() for Phoenixtec devices */ static int usb_set_phoenixtec(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, int size) { return usb_control_msg(udev, 0x42, 0x0d, (0x00 << 8) + 0x0, 0, buf, size, 1000); } static void *phoenixtec_ups(USBDevice_t *device) { usb_set_descriptor = &usb_set_phoenixtec; return NULL; } /* USB IDs device table */ static usb_device_id_t pw_usb_device_table[] = { /* various models */ { USB_DEVICE(POWERWARE, 0x0002), &powerware_ups }, /* various models */ { USB_DEVICE(PHOENIXTEC, 0x0002), &phoenixtec_ups }, /* T500 */ { USB_DEVICE(HP_VENDORID, 0x1f01), &phoenixtec_ups }, /* T750 */ { USB_DEVICE(HP_VENDORID, 0x1f02), &phoenixtec_ups }, /* Terminating entry */ { -1, -1, NULL } }; /* limit the amount of spew that goes in the syslog when we lose the UPS */ #define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define USB_ERR_RATE 10 /* then only print every 10th error */ #define XCP_USB_TIMEOUT 5000 /* global variables */ usb_dev_handle *upsdev = NULL; extern int exit_flag; static unsigned int comm_failures = 0; /* Functions implementations */ void send_read_command(unsigned char command) { unsigned char buf[4]; if (upsdev) { buf[0] = PW_COMMAND_START_BYTE; buf[1] = 0x01; /* data length */ buf[2] = command; /* command to send */ buf[3] = calc_checksum(buf); /* checksum */ upsdebug_hex (3, "send_read_command", buf, 4); usb_set_descriptor(upsdev, USB_DT_STRING, 4, buf, 4); /* FIXME: Ignore error */ } } void send_write_command(unsigned char *command, int command_length) { unsigned char sbuf[128]; if (upsdev) { /* Prepare the send buffer */ sbuf[0] = PW_COMMAND_START_BYTE; sbuf[1] = (unsigned char)(command_length); memcpy(sbuf+2, command, command_length); command_length += 2; /* Add checksum */ sbuf[command_length] = calc_checksum(sbuf); command_length += 1; upsdebug_hex (3, "send_write_command", sbuf, command_length); usb_set_descriptor(upsdev, USB_DT_STRING, 4, sbuf, command_length); /* FIXME: Ignore error */ } } #define PW_HEADER_SIZE (PW_HEADER_LENGTH + 1) #define PW_CMD_BUFSIZE 256 /* get the answer of a command from the ups. And check that the answer is for this command */ int get_answer(unsigned char *data, unsigned char command) { unsigned char buf[PW_CMD_BUFSIZE], *my_buf = buf; int length, end_length, res, endblock, bytes_read, ellapsed_time, need_data; int tail; unsigned char block_number, sequence, seq_num; struct timeval start_time, now; if (upsdev == NULL) return -1; need_data = PW_HEADER_SIZE; /* 4 - cmd response header length, 1 for csum */ end_length = 0; /* total length of sequence(s), not counting header(s) */ endblock = 0; /* signal the last sequence in the block */ bytes_read = 0; /* total length of data read, including XCP header */ res = 0; ellapsed_time = 0; seq_num = 1; /* current theoric sequence */ upsdebugx(1, "entering get_answer(%x)", command); /* Store current time */ gettimeofday(&start_time, NULL); memset(&buf, 0x0, PW_CMD_BUFSIZE); while ( (!endblock) && ((XCP_USB_TIMEOUT - ellapsed_time) > 0) ) { /* Get (more) data if needed */ if (need_data > 0) { res = usb_interrupt_read(upsdev, 0x81, (char *) buf + bytes_read, 128, (XCP_USB_TIMEOUT - ellapsed_time)); /* Update time */ gettimeofday(&now, NULL); ellapsed_time = (now.tv_sec - start_time.tv_sec)*1000 + (now.tv_usec - start_time.tv_usec)/1000; /* Check libusb return value */ if (res < 0) { /* Clear any possible endpoint stalls */ usb_clear_halt(upsdev, 0x81); /* continue; */ /* FIXME: seems a break would be better! */ break; } /* this seems to occur on XSlot USB card */ if (res == 0) { /* FIXME: */ continue; } /* Else, we got some input bytes */ bytes_read += res; need_data -= res; upsdebug_hex(1, "get_answer", buf, bytes_read); } if (need_data > 0) /* We need more data */ continue; /* Now validate XCP frame */ /* Check header */ if ( my_buf[0] != PW_COMMAND_START_BYTE ) { upsdebugx(2, "get_answer: wrong header 0xab vs %02x", my_buf[0]); /* Sometime we read something wrong. bad cables? bad ports? */ my_buf = memchr(my_buf, PW_COMMAND_START_BYTE, bytes_read); if (!my_buf) return -1; } /* Read block number byte */ block_number = my_buf[1]; upsdebugx(1, "get_answer: block_number = %x", block_number); /* Check data length byte (remove the header length) */ length = my_buf[2]; upsdebugx(3, "get_answer: data length = %d", length); if (bytes_read - (length + PW_HEADER_SIZE) < 0) { if (need_data < 0) --need_data; /* count zerro byte too */ need_data += length + 1; /* packet lenght + checksum */ upsdebugx(2, "get_answer: need to read %d more data", need_data); continue; } /* Check if Length conforms to XCP (121 for normal, 140 for Test mode) */ /* Use the more generous length for testing */ if (length > 140) { upsdebugx(2, "get_answer: bad length"); return -1; } /* Test the Sequence # */ sequence = my_buf[3]; if ((sequence & PW_SEQ_MASK) != seq_num) { nutusb_comm_fail("get_answer: not the right sequence received %x!!!\n", (sequence & PW_SEQ_MASK)); return -1; } else { upsdebugx(2, "get_answer: sequence number (%x) is ok", (sequence & PW_SEQ_MASK)); } /* Validate checksum */ if (!checksum_test(my_buf)) { nutusb_comm_fail("get_answer: checksum error! "); return -1; } else { upsdebugx(2, "get_answer: checksum is ok"); } /* Check if it's the last sequence */ if (sequence & PW_LAST_SEQ) { /* we're done receiving data */ upsdebugx(2, "get_answer: all data received"); endblock = 1; } else { seq_num++; upsdebugx(2, "get_answer: next sequence is %d", seq_num); } /* copy the current valid XCP frame back */ memcpy(data+end_length, my_buf + 4, length); /* increment pointers to process the next sequence */ end_length += length; tail = bytes_read - (length + PW_HEADER_SIZE); if (tail > 0) my_buf = memmove(&buf[0], my_buf + length + PW_HEADER_SIZE, tail); else if (tail == 0) my_buf = &buf[0]; bytes_read = tail; } upsdebug_hex (5, "get_answer", data, end_length); return end_length; } /* Sends a single command (length=1). and get the answer */ int command_read_sequence(unsigned char command, unsigned char *data) { int bytes_read = 0; int retry = 0; while ((bytes_read < 1) && (retry < 5)) { send_read_command(command); bytes_read = get_answer(data, command); retry++; } if (bytes_read < 1) { nutusb_comm_fail("Error executing command"); dstate_datastale(); return -1; } return bytes_read; } /* Sends a setup command (length > 1) */ int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer) { int bytes_read = 0; int retry = 0; while ((bytes_read < 1) && (retry < 5)) { send_write_command(command, command_length); sleep(PW_SLEEP); bytes_read = get_answer(answer, command[0]); retry ++; } if (bytes_read < 1) { nutusb_comm_fail("Error executing command"); dstate_datastale(); return -1; } return bytes_read; } void upsdrv_comm_good(void) { nutusb_comm_good(); } void upsdrv_initups(void) { upsdev = nutusb_open("USB"); } void upsdrv_cleanup(void) { upslogx(LOG_ERR, "CLOSING\n"); nutusb_close(upsdev, "USB"); } void upsdrv_reconnect(void) { upsdebugx(4, "=================================================="); upsdebugx(4, "= device has been disconnected, try to reconnect ="); upsdebugx(4, "=================================================="); nutusb_close(upsdev, "USB"); upsdev = NULL; upsdrv_initups(); } /* USB functions */ static void nutusb_open_error(const char *port) { printf("Unable to find POWERWARE UPS device on USB bus (%s)\n\n", port); printf("Things to try:\n\n"); printf(" - Connect UPS device to USB bus\n\n"); printf(" - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n"); printf(" See upsdrvctl(8) and ups.conf(5).\n\n"); fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } /* FIXME: this part of the opening can go into common... */ static usb_dev_handle *open_powerware_usb(void) { struct usb_bus *busses = usb_get_busses(); struct usb_bus *bus; USBDevice_t curDevice; for (bus = busses; bus; bus = bus->next) { struct usb_device *dev; for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.bDeviceClass != USB_CLASS_PER_INTERFACE) { continue; } curDevice.VendorID = dev->descriptor.idVendor; curDevice.ProductID = dev->descriptor.idProduct; curDevice.Bus = strdup(bus->dirname); /* FIXME: we should also retrieve * dev->descriptor.iManufacturer * dev->descriptor.iProduct * dev->descriptor.iSerialNumber * as in libusb.c->libusb_open() * This is part of the things to put in common... */ if (is_usb_device_supported(pw_usb_device_table, &curDevice) == SUPPORTED) { return usb_open(dev); } } } return 0; } usb_dev_handle *nutusb_open(const char *port) { int dev_claimed = 0; usb_dev_handle *dev_h = NULL; int retry, errout = 0; upsdebugx(1, "entering nutusb_open()"); /* Initialize Libusb */ usb_init(); usb_find_busses(); usb_find_devices(); for (retry = 0; retry < MAX_TRY ; retry++) { dev_h = open_powerware_usb(); if (!dev_h) { upsdebugx(1, "Can't open POWERWARE USB device"); errout = 1; } else { upsdebugx(1, "device %s opened successfully", usb_device(dev_h)->filename); errout = 0; if (usb_claim_interface(dev_h, 0) < 0) { upsdebugx(1, "Can't claim POWERWARE USB interface: %s", usb_strerror()); errout = 1; } else { dev_claimed = 1; errout = 0; } /* FIXME: the above part of the opening can go into common... up to here at least */ if (usb_clear_halt(dev_h, 0x81) < 0) { upsdebugx(1, "Can't reset POWERWARE USB endpoint: %s", usb_strerror()); if (dev_claimed) usb_release_interface(dev_h, 0); usb_reset(dev_h); sleep(5); /* Wait reconnect */ errout = 1; } else errout = 0; } /* Test if we succeeded */ if ( (dev_h != NULL) && dev_claimed && (errout == 0) ) break; else { /* Clear errors, and try again */ errout = 0; } } if (!dev_h && !dev_claimed && retry == MAX_TRY) errout = 1; else return dev_h; if (dev_h && dev_claimed) usb_release_interface(dev_h, 0); if (dev_h) usb_close(dev_h); if (errout == 1) nutusb_open_error(port); return NULL; } /* FIXME: this part can go into common... */ int nutusb_close(usb_dev_handle *dev_h, const char *port) { if (dev_h) { usb_release_interface(dev_h, 0); return usb_close(dev_h); } return 0; } void nutusb_comm_fail(const char *fmt, ...) { int ret; char why[SMALLBUF]; va_list ap; /* this means we're probably here because select was interrupted */ if (exit_flag != 0) return; /* ignored, since we're about to exit anyway */ comm_failures++; if ((comm_failures == USB_ERR_LIMIT) || ((comm_failures % USB_ERR_RATE) == 0)) { upslogx(LOG_WARNING, "Warning: excessive comm failures, " "limiting error reporting"); } /* once it's past the limit, only log once every USB_ERR_LIMIT calls */ if ((comm_failures > USB_ERR_LIMIT) && ((comm_failures % USB_ERR_LIMIT) != 0)) { /* Try reconnection */ upsdebugx(1, "Got to reconnect!\n"); upsdrv_reconnect(); return; } /* generic message if the caller hasn't elaborated */ if (!fmt) { upslogx(LOG_WARNING, "Communications with UPS lost - check cabling"); return; } va_start(ap, fmt); ret = vsnprintf(why, sizeof(why), fmt, ap); va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(why))) upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed " "more than %d bytes", (int)sizeof(why)); upslogx(LOG_WARNING, "Communications with UPS lost: %s", why); } void nutusb_comm_good(void) { if (comm_failures == 0) return; upslogx(LOG_NOTICE, "Communications with UPS re-established"); comm_failures = 0; } nut-2.7.4/drivers/solis.c0000644000175000017500000007125612667537407012262 00000000000000/* solis.c - driver for Microsol Solis UPS hardware Copyright (C) 2004 Silvino B. Magalhães 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 2004/10/10 - Version 0.10 - Initial release 2004/10/20 - Version 0.20 - add Battery information in driver 2004/10/26 - Version 0.30 - add commands and test shutdown 2004/10/30 - Version 0.40 - add model data structs 2005/06/30 - Version 0.41 - patch for solaris compability 2005/07/01 - Version 0.50 - add internal e external shutdown programming 2005/08/18 - Version 0.60 - save external shutdown programming to ups, and support new cables for solis 3 2015/09/19 - Version 0.65 - patch for correct reading for Microsol Back-Ups BZ1200-BR (see the version control logs for more recent updates) Microsol contributed with UPS Solis 1.5 HS 1.5 KVA for my tests. http://www.microsol.com.br */ #include #include #include #include "main.h" #include "serial.h" #include "solis.h" #include "timehead.h" #define DRIVER_NAME "Microsol Solis UPS driver" #define DRIVER_VERSION "0.65" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Silvino B. Magalhães ", DRV_STABLE, { NULL } }; #define false 0 #define true 1 #define RESP_END 0xFE #define ENDCHAR 13 /* replies end with CR */ /* solis commands */ #define CMD_UPSCONT 0xCC #define CMD_SHUT 0xDD #define CMD_SHUTRET 0xDE #define CMD_EVENT 0xCE #define CMD_DUMP 0xCD /* comment on english language */ /* #define PORTUGUESE */ /* The following Portuguese strings are in UTF-8. */ #ifdef PORTUGUESE #define M_UNKN "Modêlo solis desconhecido\n" #define NO_SOLIS "Solis não detectado! abortando ...\n" #define UPS_DATE "Data no UPS %4d/%02d/%02d\n" #define SYS_DATE "Data do Sistema %4d/%02d/%02d dia da semana %s\n" #define ERR_PACK "Pacote errado\n" #define NO_EVENT "Não há eventos\n" #define UPS_TIME "Hora interna UPS %0d:%02d:%02d\n" #define PRG_DAYS "Shutdown Programavel Dom Seg Ter Qua Qui Sex Sab\n" #define PRG_ONON "Programação shutdown ativa externa\n" #define PRG_ONOU "Programação shutdown ativa interna\n" #define TIME_OFF "UPS Hora desligar %02d:%02d\n" #define TIME_ON "UPS Hora ligar %02d:%02d\n" #define PRG_ONOF "Programação shutdown desativada\n" #define TODAY_DD "Desligamento hoje as %02d:%02d\n" #define SHUT_NOW "Shutdown iminente!\n" #else #define M_UNKN "Unknown solis model\n" #define NO_SOLIS "Solis not detected! aborting ...\n" #define UPS_DATE "UPS Date %4d/%02d/%02d\n" #define SYS_DATE "System Date %4d/%02d/%02d day of week %s\n" #define ERR_PACK "Wrong package\n" #define NO_EVENT "No events\n" #define UPS_TIME "UPS internal Time %0d:%02d:%02d\n" #define PRG_DAYS "Programming Shutdown Sun Mon Tue Wed Thu Fri Sat\n" #define PRG_ONON "External shutdown programming active\n" #define PRG_ONOU "Internal shutdown programming atcive\n" #define TIME_OFF "UPS Time power off %02d:%02d\n" #define TIME_ON "UPS Time power on %02d:%02d\n" #define PRG_ONOF "Shutdown programming not activated\n" #define TODAY_DD "Shutdown today at %02d:%02d\n" #define SHUT_NOW "Shutdown now!\n" #endif #define FMT_DAYS " %d %d %d %d %d %d %d\n" /* convert standard days string to firmware format */ static char* convdays( char *cop ) { char *stra; char alt[8]; int i, ish, fim, iw; iw = weekn; if ( iw == 6) ish = 0; else ish = 1 + iw; fim = 7 - ish; /* rotate left only 7 bits */ for(i=0; i < fim; i++) { alt[i] = cop[i+ish]; } if ( ish > 0 ) { for(i=0; i < ish; i++) { alt[i+fim] = cop[i]; } } alt[7] = 0; /* string terminator */ stra = strdup( alt ); return stra; } static int IsBinary(char ch ) { if( ch == '1' || ch == '0' ) return 1; else return 0; } /* convert string to binary */ static int Binary( char *nome ) { char ch, cc; int cont=0, nint = 1, tobin=0; int ex, nbin; while( *nome && ( cont < 7 ) ) { ch = *nome; if( !(IsBinary( ch ) ) ) nint = 0; else { if( ch == '1') { cc = 1; ex = (6 - cont); nbin = cc< 0 ) { /* this is the string to binary standard */ sunday = ( ( dweek & 0x40 ) == 0x40 ); monday = ( ( dweek & 0x20 ) == 0x20 ); tuesday = ( ( dweek & 0x10 ) == 0x10 ); wednesday = ( ( dweek & 0x08 ) == 0x08 ); thursday = ( ( dweek & 0x04 ) == 0x04 ); friday = ( ( dweek & 0x02 ) == 0x02 ); saturday = ( ( dweek & 0x01 ) == 0x01 ); if( prgups == 3) printf( PRG_ONOU ); else printf( PRG_ONON ); printf( TIME_ON, lhour, lmin); printf( TIME_OFF, dhour, dmin); printf( PRG_DAYS ); printf( FMT_DAYS, sunday, monday, tuesday, wednesday, thursday, friday, saturday); } else printf( PRG_ONOF ); } /* is today shutdown day ? */ static int IsToday( unsigned char dweek, int nweek) { switch ( nweek ) { case 0: /* sunday */ return ( ( ( dweek & 0x40 ) == 0x40 ) ); case 1: return ( ( ( dweek & 0x20 ) == 0x20 ) ); case 2: return ( ( ( dweek & 0x10 ) == 0x10 ) ); case 3: return ( ( ( dweek & 0x08 ) == 0x08 ) ); case 4: return ( ( ( dweek & 0x04 ) == 0x04 ) ); case 5: return ( ( ( dweek & 0x02 ) == 0x02 ) ); case 6: /* saturday */ return ( ( ( dweek & 0x01 ) == 0x01 ) ); } return 0; } static void AutonomyCalc( int iauto ) /* all models */ { int indice, indd, lim, min, max, inf, sup, indc, bx, ipo =0; bx = bext[iauto]; indice = RecPack[3]; indd = indice - 139; if( UtilPower > 20 ) ipo = ( UtilPower - 51 ) / 100; indc = auton[iauto].maxi; if( ipo > indc ) return; min = auton[iauto].minc[ipo]; inf = min - 1; max = auton[iauto].maxc[ipo]; lim = max - 139; sup = max + 1; if( UtilPower <= 20 ) { Autonomy = 170; maxauto = 170; } else { maxauto = auton[iauto].mm[ipo][lim]; if( indice > inf && indice < sup ) { Autonomy = auton[iauto].mm[ipo][indd]; } else { if( indice > max ) Autonomy = maxauto; if( indice < min ) Autonomy = 0; } } if( BattExtension > 0 && iauto < 4 ) Autonomy = ( Autonomy * ( BattExtension + bx ) * 1.0 / bx ); } static void ScanReceivePack( void ) { int aux, im, ov = 0; /* model independent data */ Year = ( RecPack[ 19 ] & 0x0F ) + BASE_YEAR; Month = ( RecPack[ 19 ] & 0xF0 ) >> 4; Day = ( RecPack[ 18 ] & 0x1F ); DaysOnWeek = RecPack[17]; /* Days of week if in UPS shutdown programming mode */ if( prgups == 3 ) { DaysStd = revertdays( DaysOnWeek ); /* time for programming UPS off */ dhour = RecPack[15]; dmin = RecPack[16]; /* time for programming UPS on */ lhour = RecPack[13]; lmin = RecPack[14]; } /* UPS internal time */ ihour = RecPack[11]; imin = RecPack[10]; isec = RecPack[9]; if( ( ( 0x01 & RecPack[ 20 ] ) == 0x01 ) ) Out220 = 1; CriticBatt = ( ( 0x04 & RecPack[ 20 ] ) == 0x04 ); InversorOn = ( ( 0x08 & RecPack[ 20 ] ) == 0x08 ); SuperHeat = ( ( 0x10 & RecPack[ 20 ] ) == 0x10 ); SourceFail = ( ( 0x20 & RecPack[ 20 ] ) == 0x20 ); OverCharge = ( ( 0x80 & RecPack[ 20 ] ) == 0x80 ); if( ( ( 0x40 & RecPack[ 20 ] ) == 0x40 ) ) InputValue = 1; else InputValue = 0; Temperature = ( 0x7F & RecPack[ 4 ]); if( ( ( 0x80 & RecPack[ 4 ] ) == 0x80 ) ) Temperature = Temperature - 128; /* model dependent data */ im = inds[imodel]; ov = Out220; if (SolisModel != 16) { if( RecPack[ 6 ] >= 194 ) InVoltage = RecPack[ 6 ] * ctab[imodel].m_involt194[0] + ctab[imodel].m_involt194[1]; else InVoltage = RecPack[ 6 ] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1]; } else { /* Code InVoltage for STAY1200_USB */ if ((RecPack[20] & 0x1) == 0) { //IsOutVoltage 220 InVoltage = RecPack[2] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1]; } else { InVoltage = RecPack[2] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1] - 3.0; } } BattVoltage = RecPack[ 3 ] * ctab[imodel].m_battvolt[0] + ctab[imodel].m_battvolt[1]; NominalPower = nompow[im]; if( SourceFail ) { OutVoltage = RecPack[ 1 ] * ctab[imodel].m_outvolt_i[ov][0] + ctab[imodel].m_outvolt_i[ov][1]; OutCurrent = RecPack[ 5 ] * ctab[imodel].m_outcurr_i[ov][0] + ctab[imodel].m_outcurr_i[ov][1]; AppPower = ( RecPack[ 5 ] * RecPack[ 1 ] ) * ctab[imodel].m_appp_i[ov][0] + ctab[imodel].m_appp_i[ov][1]; UtilPower = ( RecPack[ 7 ] + RecPack[ 8 ] * 256 ) * ctab[imodel].m_utilp_i[ov][0] + ctab[imodel].m_utilp_i[ov][1]; InCurrent = 0; } else { OutVoltage = RecPack[ 1 ] * ctab[imodel].m_outvolt_s[ov][0] + ctab[imodel].m_outvolt_s[ov][1]; OutCurrent = RecPack[ 5 ] * ctab[imodel].m_outcurr_s[ov][0] + ctab[imodel].m_outcurr_s[ov][1]; AppPower = ( RecPack[ 5 ] * RecPack[ 1 ] ) * ctab[imodel].m_appp_s[ov][0] + ctab[imodel].m_appp_s[ov][1]; UtilPower = ( RecPack[ 7 ] + RecPack[ 8 ] * 256 ) * ctab[imodel].m_utilp_s[ov][0] + ctab[imodel].m_utilp_s[ov][1]; InCurrent = ( ctab[imodel].m_incurr[0] * 1.0 / BattVoltage ) - ( AppPower * 1.0 / ctab[imodel].m_incurr[1] ) + OutCurrent *( OutVoltage * 1.0 / InVoltage ); } if (SolisModel == 16) { int configRelay = (RecPack[6] & 0x38) >> 3; double TENSAO_SAIDA_F1_MR[8] = { 1.1549, 1.0925, 0.0, 0.0, 1.0929, 1.0885, 0.0, 0.8654262224145391 }; double TENSAO_SAIDA_F2_MR[8] = { -6.9157, 11.026, 10.43, 0.0, -0.6109, 12.18, 0.0, 13.677}; const double TENSAO_SAIDA_F2_MI[8] ={ 5.59, 9.47, 13.7, 0.0, 0.0, 0.0, 0.0, 0.0 }; const double TENSAO_SAIDA_F1_MI[8] = { 7.9, 9.1, 17.6, 0.0, 0.0, 0.0, 0.0, 0.0 }; const double corrente_saida_F1_MR = 0.12970000389100012; const double corrente_saida_F2_MR = 0.5387060281204546; /* double corrente_saida_F1_MI = 0.1372; double corrente_saida_F2_MI = 0.3456; */ if (SourceFail) { if (RecPack[20] == 0) { double a = RecPack[1] * 2; a /= 128.0; // a = double sqrt(a); OutVoltage = RecPack[1] * a * TENSAO_SAIDA_F1_MI[configRelay] + TENSAO_SAIDA_F2_MI[configRelay]; } } else { OutCurrent = (float)(corrente_saida_F1_MR * RecPack[5] + corrente_saida_F2_MR); OutVoltage = RecPack[1] * TENSAO_SAIDA_F1_MR[configRelay] + TENSAO_SAIDA_F2_MR[configRelay]; AppPower = OutCurrent * OutVoltage; double RealPower = (RecPack[7] + RecPack[8] * 256); double potVA1 = 5.968 * AppPower - 284.36; double potVA2 = 7.149 * AppPower - 567.18; double potLin = 0.1664 * RealPower + 49.182; double potRe = 0.1519 * RealPower + 32.644; if (fabs(potVA1 - RealPower) < fabs(potVA2 - RealPower)) { RealPower = potLin; } else { RealPower = potRe; } if (OutCurrent < 0.7) { RealPower = AppPower; } if (AppPower < RealPower) { double f = AppPower; AppPower = RealPower; RealPower = f; } } } aux = ( RecPack[ 21 ] + RecPack[ 22 ] * 256 ); if( aux > 0 ) InFreq = ctab[imodel].m_infreq * 1.0 / aux; /* Specific for STAY1200_USB */ if (SolisModel == 16) { InFreq = ((float)(0.37 * (257 - (aux >> 8)))); } else InFreq = 0; /* input voltage offset */ if( InVoltage < InVolt_offset ) { /* all is equal 30 */ InFreq = 0; InVoltage = 0; InCurrent = 0; } /* app power offset */ if( AppPower < ctab[imodel].m_appp_offset ) { AppPower = 0; UtilPower = 0; ChargePowerFactor = 0; OutCurrent = 0; } if( im < 3 ) AutonomyCalc( im ); else { if( BattExtension == 80 ) AutonomyCalc( im + 1 ); else AutonomyCalc( im ); } /* model independent data */ batcharge = ( Autonomy / maxauto ) * 100.0; upscharge = ( AppPower / NominalPower ) * 100.0; if (batcharge > 100.0) batcharge = 100.0; OutFreq = 60; if( !( InversorOn ) ) { OutVoltage = 0; OutFreq = 0; } if( ( !( SourceFail ) && InversorOn ) ) OutFreq = InFreq; if( AppPower <= 0 ) /* charge pf */ ChargePowerFactor = 0; else { if( AppPower == 0 ) ChargePowerFactor = 100; else ChargePowerFactor = (( UtilPower / AppPower) * 100 ); if( ChargePowerFactor > 100 ) ChargePowerFactor = 100; } if( SourceFail && SourceLast ) /* first time failure */ FailureFlag = true; /* source return */ if( !( SourceFail ) && !( SourceLast ) ) { SourceReturn = true; ser_flush_in(upsfd,"",0); /* clean port */ } if( !( SourceFail ) == SourceLast ) { SourceReturn = false; FailureFlag = false; } SourceLast = !( SourceFail ); /* Autonomy */ if( Autonomy < 5 ) LowBatt = true; else LowBatt = false; UpsPowerFactor = 700; /* input 110V or 220v */ if( InputValue == 0 ) { InDownLim = 75; InUpLim = 150; NomInVolt = 110; } else { InDownLim = 150; InUpLim = 300; NomInVolt = 220; } /* output volage 220V or 110V */ if( Out220 ) { OutDownLim = 190; OutUpLim = 250; NomOutVolt = 220; } else { OutDownLim = 100; OutUpLim = 140; NomOutVolt = 110; } if( SourceFail ) /* source status */ InputStatus = 2; else InputStatus = 1; if( InversorOn ) /* output status */ OutputStatus = 1; else OutputStatus = 2; if( OverCharge ) OutputStatus = 3; if( CriticBatt ) /* battery status */ BattStatus = 4; else BattStatus = 1; SourceEvents = 0; if( FailureFlag ) SourceEvents = 1; if( SourceReturn ) SourceEvents = 2; /* verify Inversor */ if( Flag_inversor ) { InversorOnLast = InversorOn; Flag_inversor = false; } OutputEvents = 0; if( InversorOn && !( InversorOnLast ) ) OutputEvents = 26; if( InversorOnLast && !( InversorOn ) ) OutputEvents = 27; InversorOnLast = InversorOn; if( SuperHeat && !( SuperHeatLast ) ) OutputEvents = 12; if( SuperHeatLast && !( SuperHeat ) ) OutputEvents = 13; SuperHeatLast = SuperHeat; if( OverCharge && !( OverChargeLast ) ) OutputEvents = 10; if( OverChargeLast && !( OverCharge ) ) OutputEvents = 11; OverChargeLast = OverCharge; BattEvents = 0; CriticBattLast = CriticBatt; } static void CommReceive(const char *bufptr, int size) { int i, CheckSum, i_end; if( size == 25 ) { i_end = 25; for( i = 0 ; i < i_end ; ++i ) { RecPack[i] = *bufptr; bufptr++; } if(nut_debug_level >= 3) { upsdebug_hex(3, "CommReceive: RecPack", RecPack, size); } /* CheckSum verify */ CheckSum = 0; i_end = 23; for( i = 0 ; i < i_end ; ++i ) CheckSum = RecPack[ i ] + CheckSum; CheckSum = CheckSum % 256; upsdebugx(4, "%s: calculated checksum = 0x%02x, RecPack[23] = 0x%02x", __func__, CheckSum, RecPack[23]); ser_flush_in(upsfd,"",0); /* clean port */ /* RecPack[0] identify the model number below. * SOLIS = 1; RHINO = 2; STAY = 3; SOLIS_LI_700 = 169; SOLIS_M11 = 171; SOLIS_M15 = 175; SOLIS_M14 = 174; SOLIS_M13 = 173; SOLISDC_M14 = 201; SOLISDC_M13 = 206; SOLISDC_M15 = 207; CABECALHO_RHINO = 194; PS800 = 185; STAY1200_USB = 186; PS350_CII = 184; PS2200 = 187; PS2200_22 = 188; STAY700_USB = 189; BZ1500 = 190; */ if( ( ( (RecPack[0] & 0xF0) == 0xA0 ) || (RecPack[0] & 0xF0) == 0xB0) && ( RecPack[ 24 ] == 254 ) && ( RecPack[ 23 ] == CheckSum ) ) { if(!(detected)) { if (RecPack[0] == 186) { SolisModel = 16; } else { SolisModel = (int) (RecPack[0] & 0x0F); } if( SolisModel < 13 ) imodel = SolisModel - 10; /* 10 = 0, 11 = 1 */ else imodel = SolisModel - 11; /* 13 = 2, 14 = 3, 15 = 4 */ detected = true; } switch( SolisModel ) { case 10: case 11: case 12: case 13: case 14: case 15: { ScanReceivePack(); break; } case 16: // STAY1200_USB model { ScanReceivePack(); break; } default: { printf( M_UNKN ); ScanReceivePack(); // Scan anyway. break; } } } } } static void getbaseinfo(void) { unsigned char tmp; #ifdef PORTUGUESE const char diassemana[7][4]={"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"}; #else const char DaysOfWeek[7][4]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; #endif char *str1, *str2, *str3, *str4, *strx; unsigned char Pacote[25]; int i, i1=0, i2=0, tam; const int tpac=25; time_t tmt; struct tm *now; time( &tmt ); now = localtime( &tmt ); dian = now->tm_mday; mesn = now->tm_mon+1; anon = now->tm_year+1900; ihour = now->tm_hour; imin = now->tm_min; isec = now->tm_sec; weekn = now->tm_wday; #ifdef PORTUGUESE strcpy( seman, diassemana[weekn] ); #else strcpy( seman, DaysOfWeek[weekn] ); #endif if( testvar("battext")) BattExtension = atoi(getval("battext")); if( testvar("prgshut")) prgups = atoi(getval("prgshut")); if( prgups > 0 && prgups < 3 ) { if( testvar("daysweek") ) { strx = getval("daysweek"); str1 = convdays( strx ); DaysOnWeek = Binary( str1 ); } if( testvar("daysoff") ) { strx = getval("daysoff"); str2 = convdays( strx ); DaysStd = Binary ( strx ); DaysOffWeek = Binary( str2 ); } if( testvar("houron") ) { str3 = getval("houron"); i1 = IsHour( str3, 0 ); } if( testvar("houroff") ) { str4 = getval("houroff"); i2 = IsHour( str4, 1 ); } if( i1 == 1 && i2 == 1 && ( DaysOnWeek > 0 ) ) { isprogram = 1; /* prgups == 1 ou 2 */ if( prgups == 2 ) confups(); /* save ups config */ } else { if( (i2 == 1) && ( DaysOffWeek > 0 ) ) { isprogram = 1; if( DaysOnWeek != DaysOffWeek ) DaysOnWeek = DaysOffWeek; } } } /* end prgups 1 - 2 */ /* dummy read attempt to sync - throw it out */ upsdebugx(3, "%s: sending CMD_UPSCONT and ENDCHAR to sync", __func__); ser_send(upsfd, "%c%c", CMD_UPSCONT, ENDCHAR); /* Read until end-of-response character (0xFE): */ for(i=0; i 0 && nut_debug_level >= 4) { upsdebug_hex(4, "received from ser_get_buf_len()", Pacote, tam); } CommReceive((char *)Pacote, tam); } if( (!detected) ) { fatalx(EXIT_FAILURE, NO_SOLIS ); } switch( SolisModel ) { case 10: case 11: case 12: { Model = "Solis 1.0"; break; } case 13: { Model = "Solis 1.5"; break; } case 14: { Model = "Solis 2.0"; break; } case 15: { Model = "Solis 3.0"; break; } case 16: Model = "Microsol Back-Ups BZ1200-BR"; break; } /* if( isprogram ) */ if( prgups == 1 ) { hourshut = dhour; minshut = dmin; } else { if( prgups == 2 || prgups == 3 ) { /* broadcast before firmware shutdown */ if( dmin < 5 ) { if( dhour > 1 ) hourshut = dhour - 1; else hourshut = 23; minshut = 60 - ( 5 - dmin ); } else { hourshut = dhour; minshut = dmin - 5; } } } /* manufacturer */ dstate_setinfo("ups.mfr", "%s", "Microsol"); dstate_setinfo("ups.model", "%s", Model); dstate_setinfo("input.transfer.low", "%03.1f", InDownLim); dstate_setinfo("input.transfer.high", "%03.1f", InUpLim); dstate_addcmd("shutdown.return"); /* CMD_SHUTRET */ dstate_addcmd("shutdown.stayoff"); /* CMD_SHUT */ printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path); prnInfo(); } static void getupdateinfo(void) { unsigned char temp[256]; int tam, isday, hourn, minn; /* time update and programable shutdown block */ time_t tmt; struct tm *now; time( &tmt ); now = localtime( &tmt ); hourn = now->tm_hour; minn = now->tm_min; weekn = now->tm_wday; if( isprogram || prgups == 3 ) { if( isprogram ) isday = IsToday( DaysStd, weekn ); else isday = IsToday( DaysStd, weekn ); if( isday ) printf( TODAY_DD, hourshut, minshut ); if( ( hourn == hourshut ) && ( minn >= minshut ) && isday ) { printf( SHUT_NOW ); progshut = 1; } } /* programable shutdown end block */ pacsize = 25; /* get update package */ temp[0] = 0; /* flush temp buffer */ upsdebugx(3, "%s: requesting %d bytes from ser_get_buf_len()", __func__, pacsize); tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); upsdebugx(2, "%s: received %d bytes from ser_get_buf_len()", __func__, tam); if(tam > 0 && nut_debug_level >= 4) { upsdebug_hex(4, "received from ser_get_buf_len()", temp, tam); } CommReceive((char *)temp, tam); } static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "shutdown.return")) { /* shutdown and restart */ ser_send_char(upsfd, CMD_SHUTRET); /* 0xDE */ /* ser_send_char(upsfd, ENDCHAR); */ return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stayoff")) { /* shutdown now (one way) */ ser_send_char(upsfd, CMD_SHUT); /* 0xDD */ /* ser_send_char(upsfd, ENDCHAR); */ return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_initinfo(void) { getbaseinfo(); upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) { getupdateinfo(); /* new package for updates */ dstate_setinfo("output.voltage", "%03.1f", OutVoltage); dstate_setinfo("input.voltage", "%03.1f", InVoltage); dstate_setinfo("battery.voltage", "%02.1f", BattVoltage); dstate_setinfo("battery.charge", "%03.1f", batcharge); dstate_setinfo("output.current", "%03.1f", OutCurrent); status_init(); if (!SourceFail ) status_set("OL"); /* on line */ else status_set("OB"); /* on battery */ if (Autonomy < 5 ) status_set("LB"); /* low battery */ if( progshut ) { /* software programable shutdown immediately */ if( prgups == 2 ) sendshut(); /* Ups shutdown in 4-5 minutes -- redundant Ups shutdown */ status_set("LB"); /* no low battery but is a force shutdown */ } status_commit(); dstate_setinfo("ups.temperature", "%2.2f", Temperature); dstate_setinfo("input.frequency", "%2.1f", InFreq); dstate_setinfo("ups.load", "%03.1f", upscharge); dstate_dataok(); } /*! @brief Power down the attached load immediately. * Basic idea: find out line status and send appropriate command. * - on battery: send normal shutdown, UPS will return by itself on utility * - on line: send shutdown+return, UPS will cycle and return soon. */ void upsdrv_shutdown(void) { if (!SourceFail) { /* on line */ upslogx(LOG_NOTICE, "On line, sending shutdown+return command...\n"); ser_send_char(upsfd, CMD_SHUTRET ); } else { upslogx(LOG_NOTICE, "On battery, sending normal shutdown command...\n"); ser_send_char(upsfd, CMD_SHUT); } } void upsdrv_help(void) { printf("\nSolis options\n"); printf(" Battery Extension in AH\n"); printf(" battext = 80\n"); printf(" Programable UPS power on/off\n"); printf(" prgshut = 0 (default, no software programable shutdown)\n"); printf(" prgshut = 1 (software programable shutdown without UPS power off)\n"); printf(" prgshut = 2 (software programable shutdown with UPS power off)\n"); printf(" prgshut = 3 (activate UPS programable power on/off)\n"); printf(" Otherwise uses:\n"); printf(" daysweek = 1010101 ( power on days )\n"); printf(" daysoff = 1010101 ( power off days )\n"); printf(" where each digit is a day from sun...sat with 0 = off and 1 = on\n"); printf(" houron = hh:mm hh = hour 0-23 mm = minute 0-59 separated with :\n"); printf(" houroff = hh:mm hh = hour 0-23 mm = minute 0-59 separated with :\n"); printf(" where houron is power-on hour and houroff is shutdown and power-off hour\n"); printf(" Uses daysweek and houron to programing and save UPS power on/off\n"); printf(" These are valid only if prgshut = 2 or 3\n"); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "battext", "Battery Extension (0-80)min"); addvar(VAR_VALUE, "prgshut", "Programable power off (0-3)"); addvar(VAR_VALUE, "daysweek", "Days of week UPS power of/off"); addvar(VAR_VALUE, "daysoff", "Days of week Driver shutdown"); addvar(VAR_VALUE, "houron", "Power on hour (hh:mm)"); addvar(VAR_VALUE, "houroff", "Power off hour (hh:mm)"); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B9600); ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/tripplitesu.c0000644000175000017500000005632712640473702013503 00000000000000/* tripplitesu.c - model specific routines for Tripp Lite SmartOnline (SU*) models Copyright (C) 2003 Allan N. Hessenflow 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 */ /* Notes: The map for commands_available isn't clear. All of the information I have on the Tripp Lite SmartOnline protocol comes from the files TUN.tlp and TUNRT.tlp from their drivers, and some experimentation. One of those files told me what one bit was for in the AVL response, out of 18 that the SU1000RT2U sends or 21 that some other model sends. Later I found a description of the Belkin protocol, which is the same. Unfortunately it gives a definition of the AVL response that conflicts with the one bit I found from TUNRT.tlp, and in fact the response my SU1000RT2U gives, compared to the commands I have found it supports, does not match the Belkin description. So I'm treating the whole field as unknown. It would be nice to be able to use it to determine what variables to make RW. As a workaround, I'm assuming any value I query successfully which also can be set on at least one model, can in fact be set on the one I'm talking to. I didn't just add Tripp Lite support to the existing Belkin driver because I didn't discover that they were the same protocol until after I had put more features in this driver than the Belkin driver has. I'm not calling this a unified Belkin/Tripp Lite driver for a couple of reasons. One is that I don't have a Belkin to test with (and so I explicitly check for Tripp Lite in this drivers initialization - it *may* work fine with a Belkin by simply removing that test). The other reason is that I'd have to come up with a name - I don't know a name for the protocol, and I don't know which company (if either) originated the protocol. Picking one of the companies arbitrarily to name it after just seems wrong. There are a few things still to add. Primarily there's control of individual outlet banks, using (I presume) outlet.n.x variables. I'll probably wait for an example of that to show up in some other driver before adding that, to try to make sure I do it in the way that Russell Kroll envisioned. It also might be nice to give the user control over the delays before shutdown and restart, probably with additional driver parameters. Letting the user turn the buzzer off might be nice too; for that I'd have to investigate whether the command to do that disables it entirely, or just turns it off during the existing alarm condition, to determine whether it would be better implemented through variables or instant commands, and then of course request that those get added to the list of known names. Finally, there are a number of other alarm conditions that can be reported that would be nice to pass on to the user; these would require new variables or new status values. The following parameters (ups.conf) are supported: lowbatt The following variables are supported (RW = read/write): ambient.humidity (1) ambient.temperature (1) battery.charge battery.current (1) battery.temperature battery.voltage battery.voltage.nominal input.frequency input.sensitivity (RW) (1) input.transfer.high (RW) input.transfer.low (RW) input.voltage input.voltage.nominal output.current (1) output.frequency output.voltage output.voltage.nominal ups.firmware ups.id (RW) (1) ups.load ups.mfr ups.model ups.status ups.test.result ups.contacts (1) The following instant commands are supported: load.off load.on shutdown.reboot shutdown.reboot.graceful shutdown.return shutdown.stop test.battery.start test.battery.stop The following ups.status values are supported: BOOST (1) BYPASS LB OB OFF OL OVER (2) RB (2) TRIM (1) (1) these items have not been tested because they are not supported by my SU1000RT2U. (2) these items have not been tested because I haven't tested them. */ #include "main.h" #include "serial.h" #define DRIVER_NAME "Tripp Lite SmartOnline driver" #define DRIVER_VERSION "0.05" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Allan N. Hessenflow ", DRV_EXPERIMENTAL, { NULL } }; #define MAX_RESPONSE_LENGTH 256 static const char *test_result_names[] = { "No test performed", "Passed", "In progress", "General test failed", "Battery failed", "Deep battery test failed", "Aborted" }; static struct { int code; const char *name; } sensitivity[] = { {0, "Normal"}, {1, "Reduced"}, {2, "Low"} }; static struct { int outlet_banks; unsigned long commands_available; } ups; /* bits in commands_available */ #define WDG_AVAILABLE (1UL << 1) /* message types */ #define POLL 'P' #define SET 'S' #define ACCEPT 'A' #define REJECT 'R' #define DATA 'D' /* commands */ #define AUTO_REBOOT "ARB" /* poll/set */ #define AUTO_TEST "ATT" /* poll/set */ #define ATX_REBOOT "ATX" /* poll/set */ #define AVAILABLE "AVL" /* poll */ #define BATTERY_REPLACEMENT_DATE "BRD" /* poll/set */ #define BUZZER_TEST "BTT" /* set */ #define BATTERY_TEST "BTV" /* poll/set */ #define BUZZER "BUZ" /* set */ #define ECONOMIC_MODE "ECO" /* set */ #define ENABLE_BUZZER "EDB" /* set */ #define ENVIRONMENT_INFORMATION "ENV" /* poll */ #define OUTLET_RELAYS "LET" /* poll */ #define MANUFACTURER "MNU" /* poll */ #define MODEL "MOD" /* poll */ #define RATINGS "RAT" /* poll */ #define RELAY_CYCLE "RNF" /* set */ #define RELAY_OFF "ROF" /* set */ #define RELAY_ON "RON" /* set */ #define ATX_RESUME "RSM" /* set */ #define SHUTDOWN_ACTION "SDA" /* set */ #define SHUTDOWN_RESTART "SDR" /* set */ #define SHUTDOWN_TYPE "SDT" /* poll/set */ #define RELAY_STATUS "SOL" /* poll/set */ #define SELECT_OUTPUT_VOLTAGE "SOV" /* poll/set */ #define STATUS_ALARM "STA" /* poll */ #define STATUS_BATTERY "STB" /* poll */ #define STATUS_INPUT "STI" /* poll */ #define STATUS_OUTPUT "STO" /* poll */ #define STATUS_BYPASS "STP" /* poll */ #define TELEPHONE "TEL" /* poll/set */ #define TEST_RESULT "TSR" /* poll */ #define TEST "TST" /* set */ #define TRANSFER_FREQUENCY "TXF" /* poll/set */ #define TRANSFER_VOLTAGE "TXV" /* poll/set */ #define BOOT_DELAY "UBD" /* poll/set */ #define BAUD_RATE "UBR" /* poll/set */ #define IDENTIFICATION "UID" /* poll/set */ #define VERSION_CMD "VER" /* poll */ #define VOLTAGE_SENSITIVITY "VSN" /* poll/set */ #define WATCHDOG "WDG" /* poll/set */ static int do_command(char type, const char *command, const char *parameters, char *response) { char buffer[SMALLBUF]; int count, ret; ser_flush_io(upsfd); if (response) { *response = '\0'; } snprintf(buffer, sizeof(buffer), "~00%c%03d%s%s", type, (int)(strlen(command) + strlen(parameters)), command, parameters); ret = ser_send_pace(upsfd, 10000, "%s", buffer); if (ret <= 0) { upsdebug_with_errno(3, "do_command: send [%s]", buffer); return -1; } upsdebugx(3, "do_command: %d bytes sent [%s] -> OK", ret, buffer); ret = ser_get_buf_len(upsfd, (unsigned char *)buffer, 4, 3, 0); if (ret < 0) { upsdebug_with_errno(3, "do_command: read"); return -1; } if (ret == 0) { upsdebugx(3, "do_command: read -> TIMEOUT"); return -1; } buffer[ret] = '\0'; upsdebugx(3, "do_command: %d byted read [%s]", ret, buffer); if (!strcmp(buffer, "~00D")) { ret = ser_get_buf_len(upsfd, (unsigned char *)buffer, 3, 3, 0); if (ret < 0) { upsdebug_with_errno(3, "do_command: read"); return -1; } if (ret == 0) { upsdebugx(3, "do_command: read -> TIMEOUT"); return -1; } buffer[ret] = '\0'; upsdebugx(3, "do_command: %d bytes read [%s]", ret, buffer); count = atoi(buffer); if (count >= MAX_RESPONSE_LENGTH) { upsdebugx(3, "do_command: response exceeds expected size!"); return -1; } if (count && !response) { upsdebugx(3, "do_command: response not expected!"); return -1; } if (count == 0) { return 0; } ret = ser_get_buf_len(upsfd, (unsigned char *)response, count, 3, 0); if (ret < 0) { upsdebug_with_errno(3, "do_command: read"); return -1; } if (ret == 0) { upsdebugx(3, "do_command: read -> TIMEOUT"); return -1; } response[ret] = '\0'; upsdebugx(3, "do_command: %d bytes read [%s]", ret, response); /* Tripp Lite pads their string responses with spaces. I don't like that, so I remove them. This is safe to do with all responses for this protocol, so I just do that here. */ str_rtrim(response, ' '); return ret; } if (!strcmp(buffer, "~00A")) { return 0; } return -1; } static char *field(char *str, int fieldnum) { while (str && fieldnum--) { str = strchr(str, ';'); if (str) str++; } if (str && *str == ';') return NULL; return str; } static int get_identification(void) { char response[MAX_RESPONSE_LENGTH]; if (do_command(POLL, IDENTIFICATION, "", response) >= 0) { dstate_setinfo("ups.id", "%s", response); return 1; } return 0; } static void set_identification(const char *val) { char response[MAX_RESPONSE_LENGTH]; if (do_command(POLL, IDENTIFICATION, "", response) < 0) return; if (strcmp(val, response)) { strncpy(response, val, MAX_RESPONSE_LENGTH); response[MAX_RESPONSE_LENGTH - 1] = '\0'; do_command(SET, IDENTIFICATION, response, NULL); } } static int get_transfer_voltage_low(void) { char response[MAX_RESPONSE_LENGTH]; char *ptr; if (do_command(POLL, TRANSFER_VOLTAGE, "", response) > 0) { ptr = field(response, 0); if (ptr) dstate_setinfo("input.transfer.low", "%d", atoi(ptr)); return 1; } return 0; } static void set_transfer_voltage_low(int val) { char response[MAX_RESPONSE_LENGTH]; char *ptr; int high; if (do_command(POLL, TRANSFER_VOLTAGE, "", response) <= 0) return; ptr = field(response, 0); if (!ptr || val == atoi(ptr)) return; ptr = field(response, 1); if (!ptr) return; high = atoi(ptr); snprintf(response, sizeof(response), "%d;%d", val, high); do_command(SET, TRANSFER_VOLTAGE, response, NULL); } static int get_transfer_voltage_high(void) { char response[MAX_RESPONSE_LENGTH]; char *ptr; if (do_command(POLL, TRANSFER_VOLTAGE, "", response) > 0) { ptr = field(response, 1); if (ptr) dstate_setinfo("input.transfer.high", "%d", atoi(ptr)); return 1; } return 0; } static void set_transfer_voltage_high(int val) { char response[MAX_RESPONSE_LENGTH]; char *ptr; int low; if (do_command(POLL, TRANSFER_VOLTAGE, "", response) <= 0) return; ptr = field(response, 0); if (!ptr) return; low = atoi(ptr); ptr = field(response, 1); if (!ptr || val == atoi(ptr)) return; snprintf(response, sizeof(response), "%d;%d", low, val); do_command(SET, TRANSFER_VOLTAGE, response, NULL); } static int get_sensitivity(void) { char response[MAX_RESPONSE_LENGTH]; unsigned int i; if (do_command(POLL, VOLTAGE_SENSITIVITY, "", response) <= 0) return 0; for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); i++) { if (sensitivity[i].code == atoi(response)) { dstate_setinfo("input.sensitivity", "%s", sensitivity[i].name); return 1; } } return 0; } static void set_sensitivity(const char *val) { char parm[20]; unsigned int i; for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); i++) { if (!strcasecmp(val, sensitivity[i].name)) { snprintf(parm, sizeof(parm), "%d", i); do_command(SET, VOLTAGE_SENSITIVITY, parm, NULL); break; } } } static void auto_reboot(int enable) { char parm[20]; char response[MAX_RESPONSE_LENGTH]; char *ptr; int mode; if (enable) mode = 1; else mode = 2; if (do_command(POLL, AUTO_REBOOT, "", response) <= 0) return; ptr = field(response, 0); if (!ptr || atoi(ptr) != mode) { snprintf(parm, sizeof(parm), "%d", mode); do_command(SET, AUTO_REBOOT, parm, NULL); } } static int instcmd(const char *cmdname, const char *extra) { int i; char parm[20]; if (!strcasecmp(cmdname, "load.off")) { for (i = 0; i < ups.outlet_banks; i++) { snprintf(parm, sizeof(parm), "%d;1", i + 1); do_command(SET, RELAY_OFF, parm, NULL); } return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "load.on")) { for (i = 0; i < ups.outlet_banks; i++) { snprintf(parm, sizeof(parm), "%d;1", i + 1); do_command(SET, RELAY_ON, parm, NULL); } return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.reboot")) { auto_reboot(1); do_command(SET, SHUTDOWN_RESTART, "1", NULL); do_command(SET, SHUTDOWN_ACTION, "10", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) { auto_reboot(1); do_command(SET, SHUTDOWN_RESTART, "1", NULL); do_command(SET, SHUTDOWN_ACTION, "60", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { auto_reboot(1); do_command(SET, SHUTDOWN_RESTART, "1", NULL); do_command(SET, SHUTDOWN_ACTION, "10", NULL); return STAT_INSTCMD_HANDLED; } #if 0 /* doesn't seem to work */ if (!strcasecmp(cmdname, "shutdown.stayoff")) { auto_reboot(0); do_command(SET, SHUTDOWN_ACTION, "10", NULL); return STAT_INSTCMD_HANDLED; } #endif if (!strcasecmp(cmdname, "shutdown.stop")) { do_command(SET, SHUTDOWN_ACTION, "0", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { do_command(SET, TEST, "3", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.stop")) { do_command(SET, TEST, "0", NULL); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.id")) { set_identification(val); get_identification(); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "input.transfer.low")) { set_transfer_voltage_low(atoi(val)); get_transfer_voltage_low(); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "input.transfer.high")) { set_transfer_voltage_high(atoi(val)); get_transfer_voltage_high(); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "input.sensitivity")) { set_sensitivity(val); get_sensitivity(); return STAT_SET_HANDLED; } upslogx(LOG_NOTICE, "setvar: unknown var [%s]", varname); return STAT_SET_UNKNOWN; } static int init_comm(void) { int i, bit; char response[MAX_RESPONSE_LENGTH]; ups.commands_available = 0; /* Repeat enumerate command 2x, firmware bug on some units garbles 1st response */ if (do_command(POLL, AVAILABLE, "", response) <= 0){ upslogx(LOG_NOTICE, "init_comm: Initial response malformed, retrying in 300ms"); usleep(3E5); } if (do_command(POLL, AVAILABLE, "", response) <= 0) return 0; i = strlen(response); for (bit = 0; bit < i; bit++) if (response[i - bit - 1] == '1') ups.commands_available |= (1UL << bit); if (do_command(POLL, MANUFACTURER, "", response) <= 0) return 0; if (strcmp(response, "Tripp Lite")) return 0; return 1; } void upsdrv_initinfo(void) { char response[MAX_RESPONSE_LENGTH]; unsigned int min_low_transfer, max_low_transfer; unsigned int min_high_transfer, max_high_transfer; unsigned int i; char *ptr; if (!init_comm()) fatalx(EXIT_FAILURE, "Unable to detect Tripp Lite SmartOnline UPS on port %s\n", device_path); min_low_transfer = max_low_transfer = 0; min_high_transfer = max_high_transfer = 0; /* get all the read-only fields here */ if (do_command(POLL, MANUFACTURER, "", response) > 0) dstate_setinfo("ups.mfr", "%s", response); if (do_command(POLL, MODEL, "", response) > 0) dstate_setinfo("ups.model", "%s", response); if (do_command(POLL, VERSION_CMD, "", response) > 0) dstate_setinfo("ups.firmware", "%s", response); if (do_command(POLL, RATINGS, "", response) > 0) { ptr = field(response, 0); if (ptr) dstate_setinfo("input.voltage.nominal", "%d", atoi(ptr)); ptr = field(response, 2); if (ptr) { dstate_setinfo("output.voltage.nominal", "%d", atoi(ptr)); } ptr = field(response, 14); if (ptr) dstate_setinfo("battery.voltage.nominal", "%d", atoi(ptr)); ptr = field(response, 10); if (ptr) min_low_transfer = atoi(ptr); ptr = field(response, 9); if (ptr) max_low_transfer = atoi(ptr); ptr = field(response, 12); if (ptr) min_high_transfer = atoi(ptr); ptr = field(response, 11); if (ptr) max_high_transfer = atoi(ptr); } if (do_command(POLL, OUTLET_RELAYS, "", response) > 0) ups.outlet_banks = atoi(response); /* define things that are settable */ if (get_identification()) { dstate_setflags("ups.id", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.id", 100); } if (get_transfer_voltage_low() && max_low_transfer) { dstate_setflags("input.transfer.low", ST_FLAG_RW); for (i = min_low_transfer; i <= max_low_transfer; i++) dstate_addenum("input.transfer.low", "%d", i); } if (get_transfer_voltage_high() && max_low_transfer) { dstate_setflags("input.transfer.high", ST_FLAG_RW); for (i = min_high_transfer; i <= max_high_transfer; i++) dstate_addenum("input.transfer.high", "%d", i); } if (get_sensitivity()) { dstate_setflags("input.sensitivity", ST_FLAG_RW); for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); i++) dstate_addenum("input.sensitivity", "%s", sensitivity[i].name); } if (ups.outlet_banks) { dstate_addcmd("load.off"); dstate_addcmd("load.on"); } dstate_addcmd("shutdown.reboot"); dstate_addcmd("shutdown.reboot.graceful"); dstate_addcmd("shutdown.return"); #if 0 /* doesn't work */ dstate_addcmd("shutdown.stayoff"); #endif dstate_addcmd("shutdown.stop"); dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); /* add all the variables that change regularly */ upsdrv_updateinfo(); upsh.instcmd = instcmd; upsh.setvar = setvar; printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model"), device_path); } void upsdrv_updateinfo(void) { char response[MAX_RESPONSE_LENGTH]; char *ptr, *ptr2; int i; int flags; int contacts_set; int low_battery; status_init(); if (do_command(POLL, STATUS_OUTPUT, "", response) <= 0) { dstate_datastale(); return; } ptr = field(response, 0); /* require output status field to exist */ if (!ptr) { dstate_datastale(); return; } switch (atoi(ptr)) { case 0: status_set("OL"); break; case 1: status_set("OB"); break; case 2: status_set("BYPASS"); break; case 3: status_set("OL"); status_set("TRIM"); break; case 4: status_set("OL"); status_set("BOOST"); break; case 5: status_set("BYPASS"); break; case 6: break; case 7: status_set("OFF"); break; default: break; } ptr = field(response, 6); if (ptr) dstate_setinfo("ups.load", "%d", atoi(ptr)); ptr = field(response, 3); if (ptr) dstate_setinfo("output.voltage", "%03.1f", (double) atoi(ptr) / 10.0); ptr = field(response, 1); if (ptr) dstate_setinfo("output.frequency", "%03.1f", (double) atoi(ptr) / 10.0); ptr = field(response, 4); if (ptr) dstate_setinfo("output.current", "%03.1f", (double) atoi(ptr) / 10.0); low_battery = 0; if (do_command(POLL, STATUS_BATTERY, "", response) <= 0) { dstate_datastale(); return; } ptr = field(response, 0); if (ptr && atoi(ptr) == 2) status_set("RB"); ptr = field(response, 1); if (ptr && atoi(ptr)) low_battery = 1; ptr = field(response, 8); if (ptr) dstate_setinfo("battery.temperature", "%d", atoi(ptr)); ptr = field(response, 9); if (ptr) { dstate_setinfo("battery.charge", "%d", atoi(ptr)); ptr2 = getval("lowbatt"); if (ptr2 && atoi(ptr2) > 0 && atoi(ptr2) <= 99 && atoi(ptr) <= atoi(ptr2)) low_battery = 1; } ptr = field(response, 6); if (ptr) dstate_setinfo("battery.voltage", "%03.1f", (double) atoi(ptr) / 10.0); ptr = field(response, 7); if (ptr) dstate_setinfo("battery.current", "%03.1f", (double) atoi(ptr) / 10.0); if (low_battery) status_set("LB"); if (do_command(POLL, STATUS_ALARM, "", response) <= 0) { dstate_datastale(); return; } ptr = field(response, 3); if (ptr && atoi(ptr)) status_set("OVER"); if (do_command(POLL, STATUS_INPUT, "", response) > 0) { ptr = field(response, 2); if (ptr) dstate_setinfo("input.voltage", "%03.1f", (double) atoi(ptr) / 10.0); ptr = field(response, 1); if (ptr) dstate_setinfo("input.frequency", "%03.1f", (double) atoi(ptr) / 10.0); } if (do_command(POLL, TEST_RESULT, "", response) > 0) { int r; size_t trsize; r = atoi(response); trsize = sizeof(test_result_names) / sizeof(test_result_names[0]); if ((r < 0) || (r >= (int) trsize)) r = 0; dstate_setinfo("ups.test.result", "%s", test_result_names[r]); } if (do_command(POLL, ENVIRONMENT_INFORMATION, "", response) > 0) { ptr = field(response, 0); if (ptr) dstate_setinfo("ambient.temperature", "%d", atoi(ptr)); ptr = field(response, 1); if (ptr) dstate_setinfo("ambient.humidity", "%d", atoi(ptr)); flags = 0; contacts_set = 0; for (i = 0; i < 4; i++) { ptr = field(response, 2 + i); if (ptr) { contacts_set = 1; if (*ptr == '1') flags |= 1 << i; } } if (contacts_set) dstate_setinfo("ups.contacts", "%02X", flags); } /* if we are here, status is valid */ status_commit(); dstate_dataok(); } void upsdrv_shutdown(void) { char parm[20]; if (!init_comm()) printf("Status failed. Assuming it's on battery and trying a shutdown anyway.\n"); auto_reboot(1); /* in case the power is on, tell it to automatically reboot. if it is off, this has no effect. */ snprintf(parm, sizeof(parm), "%d", 1); /* delay before reboot, in minutes */ do_command(SET, SHUTDOWN_RESTART, parm, NULL); snprintf(parm, sizeof(parm), "%d", 5); /* delay before shutdown, in seconds */ do_command(SET, SHUTDOWN_ACTION, parm, NULL); } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x or ups.conf */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "lowbatt", "Set low battery level, in percent"); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/apcupsd-ups.c0000644000175000017500000001424112640473702013350 00000000000000/* apcupsd-ups.c - client for apcupsd This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "main.h" #include "apcupsd-ups.h" #define DRIVER_NAME "apcupsd network client UPS driver" #define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Andreas Steinmetz ", DRV_STABLE, { NULL } }; static int port=3551; static struct sockaddr_in host; static void process(char *item,char *data) { int i; char *p1; char *p2; for(i=0;nut_data[i].info_type;i++)if(!(nut_data[i].apcupsd_item)) dstate_setinfo(nut_data[i].info_type,"%s", nut_data[i].default_value); else if(!strcmp(nut_data[i].apcupsd_item,item)) switch(nut_data[i].drv_flags&~DU_FLAG_INIT) { case DU_FLAG_STATUS: status_init(); if(!strcmp(data,"COMMLOST")||!strcmp(data,"SHUTTING DOWN")|| !strcmp(data,"NETWORK ERROR")||!strcmp(data,"ERROR")) status_set("OFF"); else if(!strcmp(data,"SELFTEST"))status_set("OB"); else for(;(data=strtok(data," "));data=NULL) { if(!strcmp(data,"CAL"))status_set("CAL"); else if(!strcmp(data,"TRIM"))status_set("TRIM"); else if(!strcmp(data,"BOOST"))status_set("BOOST"); else if(!strcmp(data,"ONLINE"))status_set("OL"); else if(!strcmp(data,"ONBATT"))status_set("OB"); else if(!strcmp(data,"OVERLOAD"))status_set("OVER"); else if(!strcmp(data,"LOWBATT"))status_set("LB"); else if(!strcmp(data,"REPLACEBATT"))status_set("RB"); else if(!strcmp(data,"NOBATT"))status_set("BYPASS"); } status_commit(); break; case DU_FLAG_DATE: if((p1=strchr(data,' '))) { *p1=0; dstate_setinfo(nut_data[i].info_type,"%s",data); *p1=' '; } else dstate_setinfo(nut_data[i].info_type,"%s",data); break; case DU_FLAG_TIME: if((p1=strchr(data,' '))) { *p1=0; if((p2=strchr(p1+1,' '))) { *p2=0; dstate_setinfo(nut_data[i].info_type,"%s",p1+1); *p2=' '; } else dstate_setinfo(nut_data[i].info_type,"%s",p1+1); *p1=' '; } break; case DU_FLAG_FW1: if((p1=strchr(data,'/'))) { for(;p1!=data;p1--)if(p1[-1]!=' ')break; if(*p1==' ') { *p1=0; dstate_setinfo(nut_data[i].info_type,"%s",data); *p1=' '; } else dstate_setinfo(nut_data[i].info_type,"%s",data); } else dstate_setinfo(nut_data[i].info_type,"%s",data); break; case DU_FLAG_FW2: if((p1=strchr(data,'/'))) { for(;*p1;p1++)if(p1[1]!=' ')break; if(*p1&&p1[1])dstate_setinfo(nut_data[i].info_type,"%s", p1+1); } break; default:if(nut_data[i].info_flags&ST_FLAG_STRING) { if((int)strlen(data)>(int)nut_data[i].info_len) data[(int)nut_data[i].info_len]=0; dstate_setinfo(nut_data[i].info_type,"%s",data); } else dstate_setinfo(nut_data[i].info_type, nut_data[i].default_value, atof(data)*nut_data[i].info_len); break; } } static int getdata(void) { int x, fd_flags; short n; char *item; char *data; struct pollfd p; char bfr[1024]; for(x=0;nut_data[x].info_type;x++) if(!(nut_data[x].drv_flags&DU_FLAG_INIT)) dstate_delinfo(nut_data[x].info_type); if((p.fd=socket(AF_INET,SOCK_STREAM,0))==-1) { upsdebugx(1,"socket error"); return -1; } if(connect(p.fd,(struct sockaddr *)&host,sizeof(host))) { upsdebugx(1,"can't connect to apcupsd"); close(p.fd); return -1; } fd_flags = fcntl(p.fd, F_GETFL); fd_flags |= O_NONBLOCK; if(fcntl(p.fd, F_SETFL, fd_flags)) { upsdebugx(1,"unexpected fcntl(fd, F_SETFL, fd_flags|O_NONBLOCK) failure"); close(p.fd); return -1; } p.events=POLLIN; n=htons(6); x=write(p.fd,&n,2); x=write(p.fd,"status",6); /* TODO: double-check for poll() in configure script */ while(poll(&p,1,15000)==1) { if(read(p.fd,&n,2)!=2) { upsdebugx(1,"apcupsd communication error"); close(p.fd); return -1; } if(!(x=ntohs(n))) { close(p.fd); return 0; } else if(x<0||x>=(int)sizeof(bfr)) { upsdebugx(1,"apcupsd communication error"); close(p.fd); return -1; } if(poll(&p,1,15000)!=1)break; if(read(p.fd,bfr,x)!=x) { upsdebugx(1,"apcupsd communication error"); close(p.fd); return -1; } bfr[x]=0; if(!(item=strtok(bfr," \t:\r\n"))) { upsdebugx(1,"apcupsd communication error"); close(p.fd); return -1; } if(!(data=strtok(NULL,"\r\n"))) { upsdebugx(1,"apcupsd communication error"); close(p.fd); return -1; } while(*data==' '||*data=='\t'||*data==':')data++; process(item,data); } upsdebugx(1,"unexpected connection close by apcupsd"); close(p.fd); return -1; } void upsdrv_initinfo(void) { if(!port)fatalx(EXIT_FAILURE,"invalid host or port specified!"); if(getdata())fatalx(EXIT_FAILURE,"can't communicate with apcupsd!"); else dstate_dataok(); poll_interval=60; } void upsdrv_updateinfo(void) { if(getdata())upslogx(LOG_ERR,"can't communicate with apcupsd!"); else dstate_dataok(); poll_interval=60; } void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } void upsdrv_initups(void) { char *p; struct hostent *h; if(device_path&&*device_path) { /* TODO: fix parsing since bare IPv6 addresses contain colons */ if((p=strchr(device_path,':'))) { *p++=0; port=atoi(p); if(port<1||port>65535)port=0; } } else device_path="localhost"; if(!(h=gethostbyname(device_path)))port=0; else memcpy(&host.sin_addr,h->h_addr,4); /* TODO: add IPv6 support */ host.sin_family=AF_INET; host.sin_port=htons(port); } void upsdrv_cleanup(void) { } nut-2.7.4/drivers/genericups.c0000644000175000017500000002151012640473702013245 00000000000000/* genericups.c - support for generic contact-closure UPS models Copyright (C) 1999 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "main.h" #include "serial.h" #include "genericups.h" #define DRIVER_NAME "Generic contact-closure UPS driver" #define DRIVER_VERSION "1.36" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll ", DRV_STABLE, { NULL } }; static int upstype = -1; static void parse_output_signals(const char *value, int *line) { int old_line = *line; /* parse signals the serial port can output */ *line = 0; upsdebugx(4, "%s: enter", __func__); /* Note: for future drivers, please use strtok() or similar tokenizing * methods, such that it is easier to spot configuration mistakes. With * this code, a misspelled control line may go unnoticed. I'd fix it * The Right Way (tm), but these UPSes are ancient. */ if (strstr(value, "DTR") && !strstr(value, "-DTR")) { upsdebugx(3, "%s: override DTR", __func__); *line |= TIOCM_DTR; } if (strstr(value, "RTS") && !strstr(value, "-RTS")) { upsdebugx(3, "%s: override RTS", __func__); *line |= TIOCM_RTS; } if (strstr(value, "ST")) { upsdebugx(3, "%s: override ST", __func__); *line |= TIOCM_ST; } if (strstr(value, "CTS")) { fatalx(EXIT_FAILURE, "Can't override output with CTS (not an output)"); } if (strstr(value, "DCD")) { fatalx(EXIT_FAILURE, "Can't override output with DCD (not an output)"); } if (strstr(value, "RNG")) { fatalx(EXIT_FAILURE, "Can't override output with RNG (not an output)"); } if (strstr(value, "DSR")) { fatalx(EXIT_FAILURE, "Can't override output with DSR (not an output)"); } if(*line == old_line) { upslogx(LOG_NOTICE, "%s: output overrides specified, but no effective difference - check for typos?", __func__); } upsdebugx(4, "%s: exit", __func__); } static void parse_input_signals(const char *value, int *line, int *val) { /* parse signals the serial port can input */ int old_line = *line, old_val = *val; *line = 0; *val = 0; upsdebugx(4, "%s: enter", __func__); if (strstr(value, "CTS")) { *line |= TIOCM_CTS; if (!strstr(value, "-CTS")) { upsdebugx(3, "%s: override CTS (active low)", __func__); *val |= TIOCM_CTS; } else { upsdebugx(3, "%s: override CTS", __func__); } } if (strstr(value, "DCD")) { *line |= TIOCM_CD; if (!strstr(value, "-DCD")) { upsdebugx(3, "%s: override DCD (active low)", __func__); *val |= TIOCM_CD; } else { upsdebugx(3, "%s: override DCD", __func__); } } if (strstr(value, "RNG")) { *line |= TIOCM_RNG; if (!strstr(value, "-RNG")) { upsdebugx(3, "%s: override RNG (active low)", __func__); *val |= TIOCM_RNG; } else { upsdebugx(3, "%s: override RNG", __func__); } } if (strstr(value, "DSR")) { *line |= TIOCM_DSR; if (!strstr(value, "-DSR")) { upsdebugx(3, "%s: override DSR (active low)", __func__); *val |= TIOCM_DSR; } else { upsdebugx(3, "%s: override DSR", __func__); } } if (strstr(value, "DTR")) { fatalx(EXIT_FAILURE, "Can't override input with DTR (not an input)"); } if (strstr(value, "RTS")) { fatalx(EXIT_FAILURE, "Can't override input with RTS (not an input)"); } if (strstr(value, "ST")) { fatalx(EXIT_FAILURE, "Can't override input with ST (not an input)"); } if((*line == old_line) && (*val == old_val)) { upslogx(LOG_NOTICE, "%s: input overrides specified, but no effective difference - check for typos?", __func__); } upsdebugx(4, "%s: exit", __func__); } void upsdrv_initinfo(void) { char *v; /* setup the basics */ dstate_setinfo("ups.mfr", "%s", ((v = getval("mfr")) != NULL) ? v : upstab[upstype].mfr); dstate_setinfo("ups.model", "%s", ((v = getval("model")) != NULL) ? v : upstab[upstype].model); if ((v = getval("serial")) != NULL) { dstate_setinfo("ups.serial", "%s", v); } /* User wants to override the input signal definitions. See also upsdrv_initups(). */ if ((v = getval("OL")) != NULL) { parse_input_signals(v, &upstab[upstype].line_ol, &upstab[upstype].val_ol); upsdebugx(2, "parse_input_signals: OL overridden with %s\n", v); } if ((v = getval("LB")) != NULL) { parse_input_signals(v, &upstab[upstype].line_bl, &upstab[upstype].val_bl); upsdebugx(2, "parse_input_signals: LB overridden with %s\n", v); } } /* normal idle loop - keep up with the current state of the UPS */ void upsdrv_updateinfo(void) { int flags, ol, bl, ret; ret = ioctl(upsfd, TIOCMGET, &flags); if (ret != 0) { upslog_with_errno(LOG_INFO, "ioctl failed"); ser_comm_fail("Status read failed"); dstate_datastale(); return; } ol = ((flags & upstab[upstype].line_ol) == upstab[upstype].val_ol); bl = ((flags & upstab[upstype].line_bl) == upstab[upstype].val_bl); status_init(); if (bl) { status_set("LB"); /* low battery */ } if (ol) { status_set("OL"); /* on line */ } else { status_set("OB"); /* on battery */ } status_commit(); upsdebugx(5, "ups.status: %s %s\n", ol ? "OL" : "OB", bl ? "BL" : ""); ser_comm_good(); dstate_dataok(); } /* show all possible UPS types */ static void listtypes(void) { int i; printf("Valid UPS types:\n\n"); for (i = 0; upstab[i].mfr != NULL; i++) { printf("%i: %s\n", i, upstab[i].desc); } } /* set the flags for this UPS type */ static void set_ups_type(void) { int i; if (!getval("upstype")) { fatalx(EXIT_FAILURE, "No upstype set - see help text / man page!"); } upstype = atoi(getval("upstype")); for (i = 0; upstab[i].mfr != NULL; i++) { if (upstype == i) { upslogx(LOG_INFO, "UPS type: %s\n", upstab[i].desc); return; } } listtypes(); fatalx(EXIT_FAILURE, "\nFatal error: unknown UPS type number"); } /* power down the attached load immediately */ void upsdrv_shutdown(void) { int flags, ret; if (upstype == -1) { fatalx(EXIT_FAILURE, "No upstype set - see help text / man page!"); } flags = upstab[upstype].line_sd; if (flags == -1) { fatalx(EXIT_FAILURE, "No shutdown command defined for this model!"); } if (flags == TIOCM_ST) { #ifndef HAVE_TCSENDBREAK fatalx(EXIT_FAILURE, "Need to send a BREAK, but don't have tcsendbreak!"); #endif ret = tcsendbreak(upsfd, 4901); if (ret != 0) { fatal_with_errno(EXIT_FAILURE, "tcsendbreak"); } return; } ret = ioctl(upsfd, TIOCMSET, &flags); if (ret != 0) { fatal_with_errno(EXIT_FAILURE, "ioctl TIOCMSET"); } if (getval("sdtime")) { int sdtime; sdtime = strtol(getval("sdtime"), (char **) NULL, 10); upslogx(LOG_INFO, "Holding shutdown signal for %d seconds...\n", sdtime); sleep(sdtime); } } void upsdrv_help(void) { listtypes(); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "upstype", "Set UPS type (required)"); addvar(VAR_VALUE, "mfr", "Override manufacturer name"); addvar(VAR_VALUE, "model", "Override model name"); addvar(VAR_VALUE, "serial", "Specify the serial number"); addvar(VAR_VALUE, "CP", "Override cable power setting"); addvar(VAR_VALUE, "OL", "Override on line signal"); addvar(VAR_VALUE, "LB", "Override low battery signal"); addvar(VAR_VALUE, "SD", "Override shutdown setting"); addvar(VAR_VALUE, "sdtime", "Hold time for shutdown value (seconds)"); } void upsdrv_initups(void) { struct termios tio; char *v; set_ups_type(); upsfd = ser_open(device_path); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* don't hang up on last close */ tio.c_cflag &= ~HUPCL; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } /* See if the user wants to override the output signal definitions this must be done here, since we might go to upsdrv_shutdown() immediately. Input signal definition override is handled in upsdrv_initinfo() */ if ((v = getval("CP")) != NULL) { parse_output_signals(v, &upstab[upstype].line_norm); upsdebugx(2, "parse_output_signals: CP overridden with %s\n", v); } if ((v = getval("SD")) != NULL) { parse_output_signals(v, &upstab[upstype].line_sd); upsdebugx(2, "parse_output_signals: SD overridden with %s\n", v); } if (ioctl(upsfd, TIOCMSET, &upstab[upstype].line_norm)) { fatal_with_errno(EXIT_FAILURE, "ioctl TIOCMSET"); } } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/tripplite-hid.c0000644000175000017500000006505612640473702013674 00000000000000/* tripplite-hid.c - data to monitor Tripp Lite USB/HID devices with NUT * * Copyright (C) * 2003 - 2012 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * * Sponsored by MGE UPS SYSTEMS * * 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 "main.h" #include "usbhid-ups.h" #include "tripplite-hid.h" #include "usb-common.h" #define TRIPPLITE_HID_VERSION "TrippLite HID 0.82" /* FIXME: experimental flag to be put in upsdrv_info */ /* For some devices, the reported battery voltage is off by * factor of 10 (due to an error in the report descriptor), * so we need to apply a scale factor to it to get the actual * battery voltage. By default, the factor is 1 (no scaling). */ static double battery_scale = 1.0; static double io_voltage_scale = 1.0; static double io_frequency_scale = 1.0; static double io_current_scale = 1.0; /* Specific handlers for USB device matching */ static void *battery_scale_1dot0(USBDevice_t *device) { /* FIXME: we could remove this one since it's the default! */ battery_scale = 1.0; return NULL; } static void *battery_scale_0dot1(USBDevice_t *device) { battery_scale = 0.1; return NULL; } static void *smart1500lcdt_scale(USBDevice_t *device) { battery_scale = 100000.0; io_voltage_scale = 100000.0; io_frequency_scale = 0.01; io_current_scale = 0.01; return NULL; } /* TrippLite */ #define TRIPPLITE_VENDORID 0x09ae /* Hewlett Packard */ #define HP_VENDORID 0x03f0 /* USB IDs device table */ static usb_device_id_t tripplite_usb_device_table[] = { /* e.g. TrippLite AVR550U */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x1003), battery_scale_0dot1 }, /* e.g. TrippLite AVR750U */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x1007), battery_scale_0dot1 }, /* e.g. TrippLite ECO550UPS */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x1008), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x1009), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x1010), battery_scale_0dot1 }, /* e.g. TrippLite OMNI1000LCD */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x2005), battery_scale_0dot1 }, /* e.g. TrippLite OMNI900LCD */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x2007), battery_scale_0dot1 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x2008), battery_scale_0dot1 }, /* e.g. TrippLite Smart1000LCD */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x2009), battery_scale_0dot1 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x2010), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x2011), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x2012), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x2013), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x2014), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x3008), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x3009), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x3010), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x3011), battery_scale_1dot0 }, /* e.g. TrippLite smart2200RMXL2U */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x3012), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x3013), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x3014), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x3015), battery_scale_1dot0 }, /* e.g. TrippLite Smart1500LCD (newer unit) */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x3016), smart1500lcdt_scale }, /* e.g. TrippLite SmartOnline SU1500RTXL2UA (older unit?) */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x4001), battery_scale_1dot0 }, /* e.g. TrippLite SmartOnline SU6000RT4U? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x4002), battery_scale_1dot0 }, /* e.g. TrippLite SmartOnline SU1500RTXL2ua */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x4003), battery_scale_1dot0 }, /* e.g. TrippLite SmartOnline SU1000XLA */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x4004), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x4005), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x4006), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x4007), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x4008), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(HP_VENDORID, 0x0001), battery_scale_1dot0 }, /* HP R1500 G2 and G3 INTL */ { USB_DEVICE(HP_VENDORID, 0x1fe0), battery_scale_1dot0 }, /* HP T750 G2 */ { USB_DEVICE(HP_VENDORID, 0x1fe1), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(HP_VENDORID, 0x1fe2), battery_scale_1dot0 }, /* HP T1500 G3 */ { USB_DEVICE(HP_VENDORID, 0x1fe3), battery_scale_1dot0 }, /* HP T750 INTL */ { USB_DEVICE(HP_VENDORID, 0x1f06), battery_scale_1dot0 }, /* HP T1000 INTL */ { USB_DEVICE(HP_VENDORID, 0x1f08), battery_scale_1dot0 }, /* HP T1500 INTL */ { USB_DEVICE(HP_VENDORID, 0x1f09), battery_scale_1dot0 }, /* HP R/T 2200 INTL (like SMART2200RMXL2U) */ { USB_DEVICE(HP_VENDORID, 0x1f0a), battery_scale_1dot0 }, /* Terminating entry */ { -1, -1, NULL } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *tripplite_chemistry_fun(double value) { static char buf[20]; const char *model; model = dstate_getinfo("ups.productid"); /* Workaround for AVR 550U firmware bug */ if (!strcmp(model, "1003")) { return "unknown"; } /* Workaround for OMNI1000LCD firmware bug */ if (!strcmp(model, "2005")) { return "unknown"; } return HIDGetIndexString(udev, (int)value, buf, sizeof(buf)); } static info_lkp_t tripplite_chemistry[] = { { 0, NULL, tripplite_chemistry_fun } }; /* returns statically allocated string - must not use it again before done with result! */ static const char *tripplite_battvolt_fun(double value) { static char buf[8]; snprintf(buf, sizeof(buf), "%.1f", battery_scale * value); return buf; } static info_lkp_t tripplite_battvolt[] = { { 0, NULL, tripplite_battvolt_fun } }; static const char *tripplite_iovolt_fun(double value) { static char buf[8]; snprintf(buf, sizeof(buf), "%.1f", io_voltage_scale * value); return buf; } static info_lkp_t tripplite_iovolt[] = { { 0, NULL, tripplite_iovolt_fun } }; static const char *tripplite_iofreq_fun(double value) { static char buf[8]; snprintf(buf, sizeof(buf), "%.1f", io_frequency_scale * value); return buf; } static info_lkp_t tripplite_iofreq[] = { { 0, NULL, tripplite_iofreq_fun } }; static const char *tripplite_ioamp_fun(double value) { static char buf[8]; snprintf(buf, sizeof(buf), "%.1f", io_current_scale * value); return buf; } static info_lkp_t tripplite_ioamp[] = { { 0, NULL, tripplite_ioamp_fun } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* TRIPPLITE usage table */ static usage_lkp_t tripplite_usage_lkp[] = { /* currently unknown: 00ff0001, ffff007d, ffff00c0, ffff00c1, ffff00c2, ffff00c3, ffff00c4, ffff00c5, ffff00d2, ffff0091, ffff00c7 */ { "TLCustom", 0xffff0010 }, { "TLDelayBeforeStartup", 0xffff0056 }, /* in minutes */ { "TLLowVoltageTransferMax", 0xffff0057 }, { "TLLowVoltageTransferMin", 0xffff0058 }, { "TLHighVoltageTransferMax", 0xffff0059 }, { "TLHighVoltageTransferMin", 0xffff005a }, /* Outlet state: * 1- On/Closed * 2- Off/Open * 3- On with pending off * 4- Off with pending on * 5- Unknown * 6- Resolved/Unknown * 7- Failed and Closed * 8- Failed and Open */ { "OutletState", 0xffff007a }, { "OutletCount", 0xffff007b }, /* Number of load segments */ { "UPSFirmwareVersion", 0xffff007c }, { "CommunicationProtocolVersion", 0xffff007d }, /* HID protocol version */ { "CommunicationVersion", 0xffff007e }, /* USB firmware version */ { "iUPSPartNumber", 0xffff007f }, /* String index to Part Number */ { "AutoOnDelay", 0xffff0080 }, /* '0' (no delay) to 'n' (delay in seconds) */ { "TLWatchdog", 0xffff0092 }, { "TLOutletsAvailableMask", 0xffff0095 }, { "TLOutletsStatusMask", 0xffff0096 }, /* it looks like Tripp Lite confused pages 0x84 and 0x85 for the following 4 items, on some OMNI1000LCD devices. */ { "TLCharging", 0x00840044 }, /* conflicts with HID spec! */ /* conflicts with HID spec (and HP implementation) for TrippLite! * Refer to tripplite_discharging_info */ { "TLDischarging", 0x00840045 }, { "TLNeedReplacement", 0x0084004b }, { "TLACPresent", 0x008400d0 }, { NULL, 0 } }; static usage_tables_t tripplite_utab[] = { tripplite_usage_lkp, /* tripplite lookup table; make sure this is first */ hid_usage_lkp, /* generic lookup table */ NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ static hid_info_t tripplite_hid2nut[] = { #ifdef USBHID_UPS_TRIPPLITE_DEBUG /* unmapped variables - meaning unknown */ { "UPS.Flow.0xffff0097", 0, 0, "UPS.Flow.0xffff0097", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff0075", 0, 0, "UPS.TLCustom.[1].0xffff0075", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff0076", 0, 0, "UPS.TLCustom.[1].0xffff0076", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff007c", 0, 0, "UPS.TLCustom.[1].0xffff007c", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff007d", 0, 0, "UPS.TLCustom.[1].0xffff007d", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e0", 0, 0, "UPS.TLCustom.[1].0xffff00e0", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e1", 0, 0, "UPS.TLCustom.[1].0xffff00e1", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e2", 0, 0, "UPS.TLCustom.[1].0xffff00e2", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e3", 0, 0, "UPS.TLCustom.[1].0xffff00e3", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e4", 0, 0, "UPS.TLCustom.[1].0xffff00e4", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e5", 0, 0, "UPS.TLCustom.[1].0xffff00e5", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e6", 0, 0, "UPS.TLCustom.[1].0xffff00e6", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e7", 0, 0, "UPS.TLCustom.[1].0xffff00e7", NULL, "%.0f", 0, NULL }, { "UPS.TLCustom.[1].0xffff00e8", 0, 0, "UPS.TLCustom.[1].0xffff00e8", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c0", 0, 0, "UPS.0xffff0015.[1].0xffff00c0", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c1", 0, 0, "UPS.0xffff0015.[1].0xffff00c1", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c2", 0, 0, "UPS.0xffff0015.[1].0xffff00c2", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c3", 0, 0, "UPS.0xffff0015.[1].0xffff00c3", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c4", 0, 0, "UPS.0xffff0015.[1].0xffff00c4", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c5", 0, 0, "UPS.0xffff0015.[1].0xffff00c5", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00d2", 0, 0, "UPS.0xffff0015.[1].0xffff00d2", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00d3", 0, 0, "UPS.0xffff0015.[1].0xffff00d3", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00d6", 0, 0, "UPS.0xffff0015.[1].0xffff00d6", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0081", 0, 0, "UPS.OutletSystem.Outlet.0xffff0081", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0091", 0, 0, "UPS.OutletSystem.Outlet.0xffff0091", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0093", 0, 0, "UPS.OutletSystem.Outlet.0xffff0093", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0095", 0, 0, "UPS.OutletSystem.Outlet.0xffff0095", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0096", 0, 0, "UPS.OutletSystem.Outlet.0xffff0096", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0098", 0, 0, "UPS.OutletSystem.Outlet.0xffff0098", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00a2", 0, 0, "UPS.OutletSystem.Outlet.0xffff00a2", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00a4", 0, 0, "UPS.OutletSystem.Outlet.0xffff00a4", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00a7", 0, 0, "UPS.OutletSystem.Outlet.0xffff00a7", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00a9", 0, 0, "UPS.OutletSystem.Outlet.0xffff00a9", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00aa", 0, 0, "UPS.OutletSystem.Outlet.0xffff00aa", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00ab", 0, 0, "UPS.OutletSystem.Outlet.0xffff00ab", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff00ac", 0, 0, "UPS.OutletSystem.Outlet.0xffff00ac", NULL, "%.0f", 0, NULL }, { "UPS.PowerSummary.iOEMInformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, #endif /* USBHID_UPS_TRIPPLITE_DEBUG */ /* Device page */ { "device.part", 0, 0, "UPS.TLCustom.[1].iUPSPartNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge", 0, 0, "UPS.BatterySystem.Battery.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.Battery.ConfigVoltage", NULL, "%.1f", HU_FLAG_STATIC, NULL }, { "battery.voltage", 0, 0, "UPS.BatterySystem.Battery.Voltage", NULL, "%s", 0, tripplite_battvolt }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, tripplite_chemistry }, { "battery.temperature", 0, 0, "UPS.BatterySystem.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, /* UPS page */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.TLDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, /* FIXME { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 6, "UPS.TLCustom.[1].TLDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.TLCustom.[1].DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, - what's the right notion behind this one? { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 6, "UPS.TLCustom.[1].AutoOnDelay", NULL, DEFAULT_ONDELAY, 0, NULL}, */ { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.start", 0, 0, "UPS.OutletSystem.Outlet.TLDelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, { "ups.test.result", 0, 0, "UPS.BatterySystem.Test", NULL, "%s", 0, test_read_info }, { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, beeper_info }, { "ups.power.nominal", 0, 0, "UPS.Flow.ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "ups.power", 0, 0, "UPS.OutletSystem.Outlet.ActivePower", NULL, "%.1f", 0, NULL }, { "ups.power", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.1f", 0, NULL }, { "ups.load", 0, 0, "UPS.OutletSystem.Outlet.PercentLoad", NULL, "%.0f", 0, NULL }, /* FIXME: what is the conversion format for this one? * Example on HP T1500 G3 * UPS.TLCustom.[1].UPSFirmwareVersion, Type: Feature, ReportID: 0x0f, Offset: 0, Size: 16, Value: 262 */ { "ups.firmware", 0, 0, "UPS.TLCustom.[1].UPSFirmwareVersion", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* Number of seconds left before the watchdog reboots the UPS (0 = disabled) */ { "ups.watchdog.status", 0, 0, "UPS.OutletSystem.Outlet.TLWatchdog", NULL, "%.0f", 0, NULL }, /* Special case: ups.status */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, HU_FLAG_QUICK_POLL, fullycharged_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyDischarged", NULL, NULL, HU_FLAG_QUICK_POLL, depleted_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, /* repeat some of the above for faulty usage codes (seen on OMNI1000LCD, untested) */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, /* "Redundant" definition to deal with the conflict between * TrippLite units, wrongly defining 0x00840045 as "TLDischarging" * and HP which uses the standard 0x00840045 (as ConfigPercentLoad). * Note that this path should not exist on HP devices. */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLDischarging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, /* Otherwise, define the version for HP devices */ { "ups.load.nominal", 0, 0, "UPS.Flow.ConfigPercentLoad", NULL, "%.0f", 0, NULL }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLCharging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLNeedReplacement", NULL, NULL, 0, replacebatt_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.VoltageOutOfRange", NULL, NULL, 0, vrange_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Buck", NULL, NULL, 0, trim_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Boost", NULL, NULL, 0, boost_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Overload", NULL, NULL, 0, overload_info }, /* This is probably not the correct mapping for all models */ /* { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Used", NULL, NULL, 0, nobattery_info }, */ { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.InternalFailure", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.AwaitingPower", NULL, NULL, 0, awaitingpower_info }, /* Duplicated values { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.FullyCharged", NULL, NULL, HU_FLAG_QUICK_POLL, fullycharged_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.FullyDischarged", NULL, NULL, HU_FLAG_QUICK_POLL, depleted_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, */ /* Input page */ { "input.voltage.nominal", 0, 0, "UPS.PowerSummary.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, "%s", 0, tripplite_iovolt }, { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%s", 0, tripplite_iovolt }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input.Frequency", NULL, "%s", 0, tripplite_iofreq }, { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.low.max", 0, 0, "UPS.PowerConverter.Output.TLLowVoltageTransferMax", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.transfer.low.min", 0, 0, "UPS.PowerConverter.Output.TLLowVoltageTransferMin", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.high.max", 0, 0, "UPS.PowerConverter.Output.TLHighVoltageTransferMax", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.transfer.high.min", 0, 0, "UPS.PowerConverter.Output.TLHighVoltageTransferMin", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* Output page */ { "output.voltage.nominal", 0, 0, "UPS.Flow.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%s", 0, tripplite_iovolt }, { "output.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, tripplite_iovolt }, { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", NULL, "%s", 0, tripplite_ioamp }, { "output.frequency.nominal", 0, 0, "UPS.Flow.ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%s", 0, tripplite_iofreq }, /* instant commands. */ { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Test", NULL, "1", HU_TYPE_CMD, NULL }, /* reported to work on OMNI1000 */ { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Test", NULL, "2", HU_TYPE_CMD, NULL }, /* reported not to work */ { "test.battery.stop", 0, 0, "UPS.BatterySystem.Test", NULL, "3", HU_TYPE_CMD, NULL }, /* reported not to work */ { "load.off.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.TLDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, /* NOTE: the ECO550UPS doesn't support DelayBeforeStartup, so we use the watchdog to trigger a reboot */ { "shutdown.reboot", 0, 0, "UPS.OutletSystem.Outlet.TLWatchdog", NULL, "10", HU_TYPE_CMD, NULL }, /* WARNING: if this timer expires, the UPS will reboot! Defaults to 60 seconds */ { "reset.watchdog", 0, 0, "UPS.OutletSystem.Outlet.TLWatchdog", NULL, "60", HU_TYPE_CMD, NULL }, { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, /* FIXME (to be tested): HP specific (may conflict or differ from TL implementation!) * { "outlet.count", 0, 0, "UPS.TLCustom.[1].OutletCount", NULL, "%.0f", HU_FLAG_STATIC, NULL }, * { "outlet.status", 0, 0, "UPS.TLCustom.[1].OutletState", NULL, "%.0f", HU_FLAG_STATIC, NULL }, 0.284486 Path: UPS.TLCustom.[1].ffff00ff, Type: Feature, ReportID: 0xff, Offset: 0, Size: 8, Value: 255 0.285276 Path: UPS.TLCustom.[1].OutletCount, Type: Feature, ReportID: 0x6d, Offset: 0, Size: 8, Value: 1 0.286260 Path: UPS.TLCustom.[1].OutletState, Type: Feature, ReportID: 0x70, Offset: 0, Size: 8, Value: 1 0.287248 Path: UPS.TLCustom.[1].CommunicationVersion, Type: Feature, ReportID: 0x0e, Offset: 0, Size: 16, Value: 262 0.288901 Path: UPS.TLCustom.[1].CommunicationProtocolVersion, Type: Feature, ReportID: 0x6c, Offset: 0, Size: 16, Value: 2560 0.289903 Path: UPS.TLCustom.[1].TLDelayBeforeStartup, Type: Feature, ReportID: 0x71, Offset: 0, Size: 16, Value: 65535 0.290854 Path: UPS.TLCustom.[1].AutoOnDelay, Type: Feature, ReportID: 0x72, Offset: 0, Size: 16, Value: 65535 */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *tripplite_format_model(HIDDevice_t *hd) { return hd->Product; } static const char *tripplite_format_mfr(HIDDevice_t *hd) { return hd->Vendor; } static const char *tripplite_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int tripplite_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(tripplite_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: switch (hd->VendorID) { case HP_VENDORID: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } /* * this vendor makes lots of USB devices that are * not a UPS, so don't use possibly_supported here */ return 0; case TRIPPLITE_VENDORID: /* reject known non-HID devices */ /* not all Tripp Lite products are HID, some are "serial over USB". */ if (hd->ProductID == 0x0001) { /* e.g. SMART550USB, SMART3000RM2U */ upsdebugx(0, "This Tripp Lite device (%04x/%04x) is not supported by usbhid-ups.\n" "Please use the tripplite_usb driver instead.\n", hd->VendorID, hd->ProductID); return 0; } /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("TrippLite", hd); return 0; /* catch all (not really needed) */ default: return 0; } case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t tripplite_subdriver = { TRIPPLITE_HID_VERSION, tripplite_claim, tripplite_utab, tripplite_hid2nut, tripplite_format_model, tripplite_format_mfr, tripplite_format_serial, }; nut-2.7.4/drivers/oneac.c0000644000175000017500000006566212640443572012210 00000000000000/*vim ts=4*/ /* oneac.c - Driver for Oneac UPS using the Advanced Interface. * * Supported Oneac UPS families in this driver: * EG (late 80s, early 90s, plug-in serial interface card) * ON (early and mid-90s, plug-in serial interface card) * OZ (mid-90s on, DB-25 std., interface slot) * OB (early 2000's on, big cabinet, DB-25 std., interface slot) * * Copyright (C) * 2003 by Eric Lawson * 2012 by Bill Elliot * * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * * 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 * * History: * - 7 February 2012. Bill Elliot * Enhancing the driver for additional capabilities and later units. * * - 28 November 2003. Eric Lawson * More or less complete re-write for NUT 1.5.9 * This was somewhat easier than trying to beat the old driver code * into submission * */ #include "main.h" #include "serial.h" #include "oneac.h" /* Prototypes to allow setting pointer before function is defined */ int setcmd(const char* varname, const char* setvalue); int instcmd(const char *cmdname, const char *extra); #define DRIVER_NAME "Oneac EG/ON/OZ/OB UPS driver" #define DRIVER_VERSION "0.80" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Bill Elliot \n" "Eric Lawson ", DRV_STABLE, { NULL } }; #define SECS 0 /* Serial function wait time*/ #define USEC 500000 /* Rest of serial function wait time*/ #define COMM_TRIES 3 /* Serial retries before "stale" */ static char UpsFamily [3]; /**************************************************************** * Below are functions used only in this oneac driver * ***************************************************************/ /* Since an installed network card may delay responses from the UPS * allow for a repeat of the get request. Also confirm that * the correct number of characters are returned. */ int OneacGetResponse (char* chBuff, const int BuffSize, int ExpectedCount) { int Retries = 10; /* x/2 seconds max with 500000 USEC */ int return_val; do { return_val = ser_get_line(upsfd, chBuff, BuffSize, ENDCHAR, IGNCHARS, SECS, USEC); if (return_val == ExpectedCount) break; upsdebugx (3,"!OneacGetResponse retry (%d, %d)...", return_val,Retries); } while (--Retries > 0); upsdebugx (4,"OneacGetResponse buffer: %s",chBuff); if (Retries == 0) { upsdebugx (2,"!!OneacGetResponse timeout..."); return_val = 1; /* Comms error */ } else { if (Retries < 10) upsdebugx (2,"OneacGetResponse recovered (%d)...", Retries); return_val = 0; /* Good comms */ } return return_val; } void do_battery_test(void) { char buffer[32]; if (getval("testtime") == NULL) snprintf(buffer, 3, "%s", DEFAULT_BAT_TEST_TIME); else { snprintf(buffer, 3, "%s", getval("testtime")); /*the UPS wants this value to always be two characters long*/ /*so put a zero in front of the string, if needed.... */ if (strlen(buffer) < 2) { buffer[2] = '\0'; buffer[1] = buffer[0]; buffer[0] = '0'; } } ser_send(upsfd,"%s%s%s",BAT_TEST_PREFIX,buffer,COMMAND_END); } int SetOutputAllow(const char* lowval, const char* highval) { char buffer[32]; snprintf(buffer, 4, "%.3s", lowval); /*the UPS wants this value to always be three characters long*/ /*so put a zero in front of the string, if needed.... */ if (strlen(buffer) < 3) { buffer[3] = '\0'; buffer[2] = buffer[1]; buffer[1] = buffer[0]; buffer[0] = '0'; } upsdebugx (2,"SetOutputAllow sending %s%.3s,%.3s...", SETX_OUT_ALLOW, buffer, highval); ser_send(upsfd,"%s%.3s,%.3s%s", SETX_OUT_ALLOW, buffer, highval, COMMAND_END); ser_get_line(upsfd,buffer,sizeof(buffer), ENDCHAR, IGNCHARS,SECS,USEC); if(buffer[0] == DONT_UNDERSTAND) { upsdebugx (2,"SetOutputAllow got asterisk back..."); return 1; /* Invalid command */ } return 0; /* Valid command */ } void EliminateLeadingZeroes (const char* buff1, int StringSize, char* buff2, const int buff2size) { int i = 0; int j = 0; memset(buff2, '\0', buff2size); /* Fill with nulls */ /* Find first non-'0' */ while ((buff1[i] == '0') && (i < (StringSize - 1))) { i++; } while (i < StringSize) /* Move rest of string */ { buff2[j++] = buff1[i++]; } } /**************************************************************** * Below are the commands that are called by main * ***************************************************************/ void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B9600); /*get the UPS in the right frame of mind*/ ser_send_pace(upsfd, 100, "%s", COMMAND_END); ser_send_pace(upsfd, 100, "%s", COMMAND_END); sleep (1); } void upsdrv_initinfo(void) { int i,j, k; int VRange=0; int timevalue; int RetValue; char buffer[256], buffer2[32]; /* All families should reply to this request so we can confirm that it is * an ONEAC UPS */ ser_flush_in(upsfd,"",0); ser_send(upsfd,"%c%s",GET_FAMILY,COMMAND_END); if(OneacGetResponse (buffer, sizeof(buffer), 2)) { fatalx(EXIT_FAILURE, "Serial timeout with ONEAC UPS on %s\n", device_path); } else { if (strncmp(buffer,FAMILY_ON,FAMILY_SIZE) != 0 && strncmp(buffer,FAMILY_OZ,FAMILY_SIZE) != 0 && strncmp(buffer,FAMILY_OB,FAMILY_SIZE) != 0 && strncmp(buffer,FAMILY_EG,FAMILY_SIZE) != 0) { fatalx(EXIT_FAILURE, "Did not find an ONEAC UPS on %s\n", device_path); } } /* UPS Model (either EG, ON, OZ or OB series of UPS) */ strncpy(UpsFamily, buffer, FAMILY_SIZE); UpsFamily[2] = '\0'; dstate_setinfo("device.model", "%s",UpsFamily); printf("Found %s family of Oneac UPS\n", UpsFamily); dstate_setinfo("ups.type", "%s", "Line Interactive"); dstate_addcmd("test.battery.start.quick"); dstate_addcmd("test.battery.stop"); dstate_addcmd("test.failure.start"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stop"); dstate_addcmd("shutdown.reboot"); upsh.setvar = setcmd; upsh.instcmd = instcmd; /* set some stuff that shouldn't change after initialization */ /* this stuff is common to all families of UPS */ ser_send(upsfd,"%c%s",GET_ALL,COMMAND_END); if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) == 0) { RetValue = OneacGetResponse (buffer, sizeof(buffer), GETALL_EG_RESP_SIZE); } else { RetValue = OneacGetResponse (buffer, sizeof(buffer), GETALL_RESP_SIZE); } if(RetValue) { fatalx(EXIT_FAILURE, "Serial timeout(2) with ONEAC UPS on %s\n", device_path); } /* Manufacturer */ dstate_setinfo("device.mfr", "%.5s", buffer); /*firmware revision*/ dstate_setinfo("ups.firmware", "%.3s",buffer+7); /*nominal AC frequency setting --either 50 or 60*/ dstate_setinfo("input.frequency.nominal", "%.2s", buffer+20); dstate_setinfo("output.frequency.nominal", "%.2s", buffer+20); /* Shutdown delay in seconds...can be changed by user */ if (getval("offdelay") == NULL) dstate_setinfo("ups.delay.shutdown", "0"); else dstate_setinfo("ups.delay.shutdown", "%s", getval("offdelay")); dstate_setflags("ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.delay.shutdown", GET_SHUTDOWN_RESP_SIZE); /* Setup some ON/OZ/OB only stuff ... i.e. not EG */ if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) != 0) { dstate_addcmd("reset.input.minmax"); /*nominal input voltage*/ VRange = buffer[26]; /* Keep for later use also */ switch (VRange) /* Will be '1' or '2' */ { case V120AC: dstate_setinfo("input.voltage.nominal", "120"); dstate_setinfo("output.voltage.nominal", "120"); break; case V230AC: dstate_setinfo("input.voltage.nominal", "230"); dstate_setinfo("output.voltage.nominal", "230"); break; default: upslogx(LOG_INFO,"Oneac: " "Invalid nom voltage parameter from UPS [%c]", VRange); } } /* Setup some OZ/OB only stuff */ if ((strncmp (UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || (strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) { dstate_addcmd("test.panel.start"); dstate_addcmd("test.battery.start.deep"); dstate_addcmd("beeper.enable"); dstate_addcmd("beeper.disable"); dstate_addcmd("beeper.mute"); dstate_setaux("ups.delay.shutdown", GETX_SHUTDOWN_RESP_SIZE); ser_flush_in(upsfd,"",0); ser_send(upsfd,"%c%s",GETX_ALL_2,COMMAND_END); if(OneacGetResponse (buffer, sizeof(buffer), GETX_ALL2_RESP_SIZE)) { fatalx(EXIT_FAILURE, "Serial timeout(3) with ONEAC UPS on %s\n", device_path); } /* Low and high output trip points */ EliminateLeadingZeroes (buffer+73, 3, buffer2, sizeof(buffer2)); dstate_setinfo("input.transfer.low", "%s", buffer2); dstate_setflags("input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW ); dstate_setaux("input.transfer.low", 3); EliminateLeadingZeroes (buffer+76, 3, buffer2, sizeof(buffer2)); dstate_setinfo("input.transfer.high", "%s", buffer2); dstate_setflags("input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("input.transfer.high", 3); /* Restart delay */ EliminateLeadingZeroes (buffer+84, 4, buffer2, sizeof(buffer2)); dstate_setinfo("ups.delay.start", "%s", buffer2); dstate_setflags("ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.delay.start", 4); /* Low Batt at time */ strncpy(buffer2, buffer+82, 2); buffer2[2]='\0'; timevalue = atoi(buffer2) * 60; /* Change minutes to seconds */ dstate_setinfo("battery.runtime.low", "%d",timevalue); dstate_setflags("battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("battery.runtime.low", 2); /*Get the actual model string for ON UPS reported as OZ/OB family*/ /*UPS Model (full string)*/ memset(buffer2, '\0', 32); strncpy(buffer2, buffer+5, 10); for (i = 9; i >= 0 && buffer2[i] == ' '; --i) { buffer2[i] = '\0'; } dstate_setinfo("device.model", "%s", buffer2); /* Serial number */ dstate_setinfo("device.serial", "%.4s-%.4s", buffer+18, buffer+22); printf("Found %.10s UPS with serial number %.4s-%.4s\n", buffer2, buffer+18, buffer+22); /* Manufacture Date */ dstate_setinfo("ups.mfr.date", "%.6s (yymmdd)", buffer+38); /* Battery Replace Date */ dstate_setinfo("battery.date", "%.6s (yymmdd)", buffer+44); dstate_setflags("battery.date", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("battery.date", 6); /* Real power nominal */ EliminateLeadingZeroes (buffer+55, 5, buffer2, sizeof(buffer2)); dstate_setinfo("ups.realpower.nominal", "%s", buffer2); /* Set up ups.start.auto to be writable */ dstate_setinfo("ups.start.auto", "yes"); dstate_setflags("ups.start.auto", ST_FLAG_STRING | ST_FLAG_RW); dstate_setaux("ups.start.auto", 3); /* Get output window min/max points from OB or OZ v1.9 or later */ if ((strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0) || (strcmp (dstate_getinfo("ups.firmware"), MIN_ALLOW_FW) >= 0 )) { upsdebugx (2,"Can get output window min/max! (%s)", dstate_getinfo("ups.firmware")); ser_send(upsfd,"%s%s",GETX_ALLOW_RANGE,COMMAND_END); if(OneacGetResponse (buffer, sizeof(buffer), GETX_RANGE_RESP_SIZE)) { fatalx(EXIT_FAILURE, "Serial timeout(4) with ONEAC UPS on %s\n",device_path); } strncpy(buffer2, buffer, 3); buffer2[3]='\0'; i = atoi(buffer2); /* Minimum voltage */ strncpy(buffer2, buffer+4, 3); j = atoi(buffer2); /* Maximum voltage */ strncpy(buffer2, buffer+8, 2); buffer2[2]='\0'; k = atoi(buffer2); /* Spread between */ dstate_setinfo("input.transfer.low.min", "%3d", i); dstate_setinfo("input.transfer.low.max", "%3d", j-k); dstate_setinfo("input.transfer.high.min", "%3d", i+k); dstate_setinfo("input.transfer.high.max", "%3d", j); } else { /* Use default values from firmware */ upsdebugx (2,"Using trip defaults (%s)...", dstate_getinfo("ups.firmware")); switch (VRange) /* Held from initial use */ { case V120AC: dstate_setinfo("input.transfer.low.min", "90"); dstate_setinfo("input.transfer.low.max", "120"); dstate_setinfo("input.transfer.high.min", "110"); dstate_setinfo("input.transfer.high.max", "140"); break; case V230AC: dstate_setinfo("input.transfer.low.min", "172"); dstate_setinfo("input.transfer.low.max", "228"); dstate_setinfo("input.transfer.high.min", "212"); dstate_setinfo("input.transfer.high.max", "268"); break; default: ; } } } } void upsdrv_updateinfo(void) { static int CommTry = COMM_TRIES; /* Comm loss counter */ char buffer[256]; /* Main response buffer */ char buffer2[32]; /* Conversion buffer */ char s; int RetValue; int timevalue; /* Start with EG/ON information */ ser_flush_in(upsfd,"",0); /*just in case*/ ser_send (upsfd,"%c%s", GET_ALL, COMMAND_END); if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) == 0) { RetValue = OneacGetResponse (buffer,sizeof(buffer),GETALL_EG_RESP_SIZE); } else { RetValue = OneacGetResponse (buffer, sizeof(buffer), GETALL_RESP_SIZE); } if ((RetValue != 0) && (CommTry == 0)) { ser_comm_fail("Oneac UPS Comm failure continues on port %s", device_path); } else if (RetValue != 0) { if (--CommTry == 0) { ser_comm_fail("Oneac UPS Comm failure on port %s",device_path); dstate_datastale(); } upsdebugx(2,"Oneac: Update serial comm retry value: %d", CommTry); return; } else { CommTry = COMM_TRIES; /* Reset serial retries */ s = buffer[12]; status_init(); alarm_init(); /*take care of the UPS status information*/ if (s == '@') { status_set("OL"); } else { if (s & 0x01) /* On Battery */ { status_set("OB"); } else { status_set("OL"); } if (s & 0x02) /* Low Battery */ status_set("LB"); if (s & 0x04) /* General fault */ { dstate_setinfo("ups.test.result","UPS Internal Failure"); } else { dstate_setinfo("ups.test.result","Normal"); } if (s & 0x08) /* Replace Battery */ status_set("RB"); /* if (s & 0x10) */ /* High Line */ if (s & 0x20) /* Unit is hot */ alarm_set("OVERHEAT"); } /*take care of the reason why the UPS last transferred to battery*/ switch (buffer[13]) { case XFER_BLACKOUT : dstate_setinfo("input.transfer.reason", "Blackout"); break; case XFER_LOW_VOLT : dstate_setinfo("input.transfer.reason", "Low Input Voltage"); break; case XFER_HI_VOLT : dstate_setinfo("input.transfer.reason", "High Input Voltage"); break; case NO_VALUE_YET : dstate_setinfo("input.transfer.reason", "No transfer yet."); break; default : upslogx(LOG_INFO,"Oneac: Unknown reason for UPS battery" " transfer [%c]", buffer[13]); } /* now update info for only the non-EG families of UPS*/ if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) != 0) { dstate_setinfo("ups.load", "0%.2s",buffer+31); /* Output ON or OFF? */ if(buffer[27] == NO_VALUE_YET) status_set("OFF"); /*battery charge*/ if(buffer[10] == YES) dstate_setinfo("battery.charge", "0%.2s",buffer+33); else dstate_setinfo("battery.charge", "100"); EliminateLeadingZeroes (buffer+35, 3, buffer2, sizeof(buffer2)); dstate_setinfo("input.voltage", "%s",buffer2); EliminateLeadingZeroes (buffer+38, 3, buffer2, sizeof(buffer2)); dstate_setinfo("input.voltage.minimum", "%s",buffer2); EliminateLeadingZeroes (buffer+41, 3, buffer2, sizeof(buffer2)); dstate_setinfo("input.voltage.maximum", "%s",buffer2); EliminateLeadingZeroes (buffer+44, 3, buffer2, sizeof(buffer2)); dstate_setinfo("output.voltage", "%s",buffer2); if (buffer[15] == NO_VALUE_YET) { dstate_delinfo("ups.timer.shutdown"); } else { /* A shutdown is underway! */ status_set("FSD"); if(buffer[15] != HIGH_COUNT) { EliminateLeadingZeroes (buffer+15, 3, buffer2, sizeof(buffer2)); dstate_setinfo("ups.timer.shutdown", "%s", buffer2); } else { dstate_setinfo("ups.timer.shutdown", "999"); } } if (buffer[47] == YES) status_set("BOOST"); } /* Now update info for only the OZ/OB families of UPS */ if ((strncmp(UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || (strncmp(UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) { ser_flush_in(upsfd,"",0); /*just in case*/ ser_send (upsfd,"%c%s",GETX_ALL_1,COMMAND_END); RetValue = OneacGetResponse (buffer, sizeof(buffer), GETX_ALL1_RESP_SIZE); if(RetValue) { if (--CommTry == 0) { ser_comm_fail("Oneac (OZ) UPS Comm failure on port %s", device_path); dstate_datastale(); } upsdebugx(2,"Oneac: " "Update (OZ) serial comm retry value: %d", CommTry); } else { CommTry = COMM_TRIES; /* Reset count */ EliminateLeadingZeroes (buffer+57, 5, buffer2, sizeof(buffer2)); dstate_setinfo("ups.realpower", "%s",buffer2); dstate_setinfo("input.frequency", "%.2s.%c", buffer+42,buffer[44]); dstate_setinfo("output.frequency", "%.2s.%c", buffer+76, buffer[78]); EliminateLeadingZeroes (buffer+29, 3, buffer2, sizeof(buffer2)); dstate_setinfo("battery.voltage", "%s.%c",buffer2, buffer[32]); dstate_setinfo("ups.temperature", "%.2s",buffer+13); dstate_setinfo("ups.load", "%.3s",buffer+73); strncpy(buffer2, buffer+19, 4); buffer2[4]='\0'; timevalue = atoi(buffer2) * 60; /* Change mins to secs */ dstate_setinfo("battery.runtime", "%d",timevalue); /* Now some individual requests... */ /* Battery replace date */ ser_send (upsfd,"%c%s",GETX_BATT_REPLACED,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), GETX_DATE_RESP_SIZE)) dstate_setinfo("battery.date", "%.6s (yymmdd)", buffer); /* Low and high output trip points */ ser_send (upsfd,"%c%s",GETX_LOW_OUT_ALLOW,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), GETX_ALLOW_RESP_SIZE)) { EliminateLeadingZeroes (buffer, 3, buffer2,sizeof(buffer2)); dstate_setinfo("input.transfer.low", "%s", buffer2); } ser_send (upsfd,"%c%s",GETX_HI_OUT_ALLOW,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), GETX_ALLOW_RESP_SIZE)) dstate_setinfo("input.transfer.high", "%s", buffer); /* Restart delay */ ser_send (upsfd,"%c%s",GETX_RESTART_DLY,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), GETX_RSTRT_RESP_SIZE)) { EliminateLeadingZeroes (buffer, 4, buffer2, sizeof(buffer2)); dstate_setinfo("ups.delay.start", "%s", buffer2); } /* Buzzer state */ ser_send (upsfd,"%s%s",GETX_BUZZER_WHAT,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), 1)) { switch (buffer[0]) { case BUZZER_ENABLED : dstate_setinfo("ups.beeper.status", "enabled"); break; case BUZZER_DISABLED : dstate_setinfo("ups.beeper.status", "disabled"); break; case BUZZER_MUTED : dstate_setinfo("ups.beeper.status", "muted"); break; default : dstate_setinfo("ups.beeper.status", "enabled"); } } /* Auto start setting */ ser_send (upsfd,"%s%s",GETX_AUTO_START,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), 1)) { if (buffer[0] == '0') dstate_setinfo("ups.start.auto", "yes"); else dstate_setinfo("ups.start.auto", "no"); } /* Low Batt at time */ ser_send (upsfd,"%c%s",GETX_LOW_BATT_TIME,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), 2)) { strncpy(buffer2, buffer, 2); buffer2[2]='\0'; timevalue = atoi(buffer2) * 60; /* Mins to secs */ dstate_setinfo("battery.runtime.low", "%d",timevalue); } /* Shutdown timer */ ser_send (upsfd,"%c%s",GETX_SHUTDOWN,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), GETX_SHUTDOWN_RESP_SIZE)) { /* ON would have handled NO_VALUE_YET and setting FSD * above so only deal with counter value here. */ if (buffer[0] != NO_VALUE_YET) { EliminateLeadingZeroes (buffer, 5, buffer2, sizeof(buffer2)); dstate_setinfo("ups.timer.shutdown", "%s", buffer2); } } /* Restart timer */ ser_send (upsfd,"%s%s",GETX_RESTART_COUNT,COMMAND_END); if(!OneacGetResponse (buffer, sizeof(buffer), GETX_RSTRT_RESP_SIZE)) { if (atoi(buffer) == 0) { dstate_delinfo("ups.timer.start"); } else { EliminateLeadingZeroes (buffer, 4, buffer2, sizeof(buffer2)); dstate_setinfo("ups.timer.start", "%s", buffer2); } } } } alarm_commit(); status_commit(); /* If the comm retry counter is zero then datastale has been set. * We don't want to set dataok or ser_comm_good if that is the case. */ if (CommTry != 0) { dstate_dataok(); ser_comm_good(); } } } void upsdrv_shutdown(void) { ser_send(upsfd,"%s",SHUTDOWN); } void upsdrv_help(void) { printf("\n---------\nNOTE:\n"); printf("You must set the UPS interface card DIP switch to 9600 BPS\n"); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } void upsdrv_makevartable(void) { addvar(VAR_VALUE,"testtime", "Change battery test time from the 2 minute default."); addvar(VAR_VALUE,"offdelay", "Change shutdown delay time from 0 second default."); } int instcmd(const char *cmdname, const char *extra) { int i; upsdebugx(2, "In instcmd with %s and extra %s.", cmdname, extra); if (!strcasecmp(cmdname, "test.failure.start")) { ser_send(upsfd,"%s%s",SIM_PWR_FAIL,COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { i = atoi(dstate_getinfo("ups.delay.shutdown")); if ((strncmp (UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || (strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) { upsdebugx(3, "Shutdown using %c%d...", DELAYED_SHUTDOWN_PREFIX, i); ser_send(upsfd,"%c%d%s",DELAYED_SHUTDOWN_PREFIX, i, COMMAND_END); } else { upsdebugx(3, "Shutdown using %c%03d...",DELAYED_SHUTDOWN_PREFIX, i); ser_send(upsfd,"%c%03d%s",DELAYED_SHUTDOWN_PREFIX, i, COMMAND_END); } return STAT_INSTCMD_HANDLED; } if(!strcasecmp(cmdname, "shutdown.reboot")) { ser_send(upsfd, "%s", SHUTDOWN); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.stop")) { ser_send(upsfd,"%c%s",DELAYED_SHUTDOWN_PREFIX,COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start.quick")) { do_battery_test(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start.deep")) { ser_send(upsfd, "%s%s", TEST_BATT_DEEP, COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.stop")) { if ((strncmp (UpsFamily, FAMILY_EG, FAMILY_SIZE) == 0) || (strncmp (UpsFamily, FAMILY_ON, FAMILY_SIZE) == 0)) { ser_send(upsfd,"%s00%s",BAT_TEST_PREFIX,COMMAND_END); } else { ser_send(upsfd,"%c%s",TEST_ABORT,COMMAND_END); } return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "reset.input.minmax")) { ser_send(upsfd,"%c%s",RESET_MIN_MAX, COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "beeper.enable")) { ser_send(upsfd,"%c%c%s",SETX_BUZZER_PREFIX, BUZZER_ENABLED,COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "beeper.disable")) { ser_send(upsfd,"%c%c%s",SETX_BUZZER_PREFIX,BUZZER_DISABLED,COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "beeper.mute")) { ser_send(upsfd,"%c%c%s",SETX_BUZZER_PREFIX, BUZZER_MUTED, COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.panel.start")) { ser_send(upsfd,"%s%s",TEST_INDICATORS, COMMAND_END); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } int setcmd(const char* varname, const char* setvalue) { upsdebugx(2, "In setcmd for %s with %s...", varname, setvalue); if (!strcasecmp(varname, "ups.delay.shutdown")) { if ((strncmp (UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || (strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) { if (atoi(setvalue) > 65535) { upsdebugx(2, "Too big for OZ/OB (>65535)...(%s)", setvalue); return STAT_SET_UNKNOWN; } } else { if (atoi(setvalue) > 999) { upsdebugx(2, "Too big for EG/ON (>999)...(%s)", setvalue); return STAT_SET_UNKNOWN; } } dstate_setinfo("ups.delay.shutdown", "%s", setvalue); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "input.transfer.low")) { if (SetOutputAllow(setvalue, dstate_getinfo("input.transfer.high"))) { return STAT_SET_UNKNOWN; } else { dstate_setinfo("input.transfer.low" , "%s", setvalue); return STAT_SET_HANDLED; } } if (!strcasecmp(varname, "input.transfer.high")) { if (SetOutputAllow(dstate_getinfo("input.transfer.low"), setvalue)) { return STAT_SET_UNKNOWN; } else { dstate_setinfo("input.transfer.high" , "%s", setvalue); return STAT_SET_HANDLED; } } if (!strcasecmp(varname, "battery.date")) { if(strlen(setvalue) == GETX_DATE_RESP_SIZE) /* yymmdd (6 chars) */ { ser_send(upsfd, "%s%s%s", SETX_BATTERY_DATE, setvalue, COMMAND_END); dstate_setinfo("battery.date", "%s (yymmdd)", setvalue); return STAT_SET_HANDLED; } else { return STAT_SET_UNKNOWN; } } if (!strcasecmp(varname, "ups.delay.start")) { if (atoi(setvalue) <= 9999) { ser_send(upsfd,"%s%s%s",SETX_RESTART_DELAY, setvalue, COMMAND_END); dstate_setinfo("ups.delay.start", "%s", setvalue); return STAT_SET_HANDLED; } else { return STAT_SET_UNKNOWN; } } if (!strcasecmp(varname, "battery.runtime.low")) { if (atoi(setvalue) <= 99) { ser_send(upsfd,"%s%s%s",SETX_LOWBATT_AT, setvalue, COMMAND_END); dstate_setinfo("battery.runtime.low", "%s", setvalue); return STAT_SET_HANDLED; } else { return STAT_SET_UNKNOWN; } } if (!strcasecmp(varname, "ups.start.auto")) { if (!strcasecmp(setvalue, "yes")) { ser_send(upsfd,"%c0%s",SETX_AUTO_START, COMMAND_END); dstate_setinfo("ups.start.auto", "yes"); return STAT_SET_HANDLED; } else if (!strcasecmp(setvalue, "no")) { ser_send(upsfd,"%c1%s",SETX_AUTO_START, COMMAND_END); dstate_setinfo("ups.start.auto", "no"); return STAT_SET_HANDLED; } return STAT_SET_UNKNOWN; } upslogx(LOG_NOTICE, "setcmd: unknown command [%s]", varname); return STAT_SET_UNKNOWN; } nut-2.7.4/drivers/delta_ups-mib.h0000644000175000017500000000201012640444140013617 00000000000000/* delta_ups-mib.h - subdriver to monitor delta_ups SNMP devices with NUT * * Copyright (C) * 2011 - 2012 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DELTA_UPS_MIB_H #define DELTA_UPS_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t delta_ups; #endif /* DELTA_UPS_MIB_H */ nut-2.7.4/drivers/upsdrvctl.c0000644000175000017500000002575712640473702013150 00000000000000/* upsdrvctl.c - UPS driver controller Copyright (C) 2001 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "config.h" #include "proto.h" #include "common.h" #include "upsconf.h" typedef struct { char *upsname; char *driver; char *port; int sdorder; int maxstartdelay; void *next; } ups_t; static ups_t *upstable = NULL; static int maxsdorder = 0, testmode = 0, exec_error = 0; /* timer - keeps us from getting stuck if a driver hangs */ static int maxstartdelay = 45; /* counter - retry that many time(s) to start the driver if it fails to */ static int maxretry = 1; /* timer - delay between each restart attempt of the driver(s) */ static int retrydelay = 5; /* Directory where driver executables live */ static char *driverpath = NULL; /* passthrough to the drivers: chroot path and new user name */ static char *pt_root = NULL, *pt_user = NULL; void do_upsconf_args(char *upsname, char *var, char *val) { ups_t *tmp, *last; /* handle global declarations */ if (!upsname) { if (!strcmp(var, "maxstartdelay")) maxstartdelay = atoi(val); if (!strcmp(var, "driverpath")) { free(driverpath); driverpath = xstrdup(val); } if (!strcmp(var, "maxretry")) maxretry = atoi(val); if (!strcmp(var, "retrydelay")) retrydelay = atoi(val); /* ignore anything else - it's probably for main */ return; } last = tmp = upstable; while (tmp) { last = tmp; if (!strcmp(tmp->upsname, upsname)) { if (!strcmp(var, "driver")) tmp->driver = xstrdup(val); if (!strcmp(var, "port")) tmp->port = xstrdup(val); if (!strcmp(var, "maxstartdelay")) tmp->maxstartdelay = atoi(val); if (!strcmp(var, "sdorder")) { tmp->sdorder = atoi(val); if (tmp->sdorder > maxsdorder) maxsdorder = tmp->sdorder; } return; } tmp = tmp->next; } tmp = xmalloc(sizeof(ups_t)); tmp->upsname = xstrdup(upsname); tmp->driver = NULL; tmp->port = NULL; tmp->next = NULL; tmp->sdorder = 0; tmp->maxstartdelay = -1; /* use global value by default */ if (!strcmp(var, "driver")) tmp->driver = xstrdup(val); if (!strcmp(var, "port")) tmp->port = xstrdup(val); if (last) last->next = tmp; else upstable = tmp; } /* handle sending the signal */ static void stop_driver(const ups_t *ups) { char pidfn[SMALLBUF]; int ret; struct stat fs; upsdebugx(1, "Stopping UPS: %s", ups->upsname); snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), ups->driver, ups->upsname); ret = stat(pidfn, &fs); if ((ret != 0) && (ups->port != NULL)) { snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), ups->driver, xbasename(ups->port)); ret = stat(pidfn, &fs); } if (ret != 0) { upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); exec_error++; return; } upsdebugx(2, "Sending signal to %s", pidfn); if (testmode) return; ret = sendsignalfn(pidfn, SIGTERM); if (ret < 0) { upslog_with_errno(LOG_ERR, "Stopping %s failed", pidfn); exec_error++; return; } } static void waitpid_timeout(const int sig) { /* do nothing */ return; } /* print out a command line at the given debug level. */ static void debugcmdline(int level, const char *msg, char *const argv[]) { char cmdline[LARGEBUF]; snprintf(cmdline, sizeof(cmdline), "%s", msg); while (*argv) { snprintfcat(cmdline, sizeof(cmdline), " %s", *argv++); } upsdebugx(level, "%s", cmdline); } static void forkexec(char *const argv[], const ups_t *ups) { int ret; pid_t pid; pid = fork(); if (pid < 0) fatal_with_errno(EXIT_FAILURE, "fork"); if (pid != 0) { /* parent */ int wstat; struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = waitpid_timeout; sigaction(SIGALRM, &sa, NULL); if (ups->maxstartdelay != -1) alarm(ups->maxstartdelay); else alarm(maxstartdelay); ret = waitpid(pid, &wstat, 0); alarm(0); if (ret == -1) { upslogx(LOG_WARNING, "Startup timer elapsed, continuing..."); exec_error++; return; } if (WIFEXITED(wstat) == 0) { upslogx(LOG_WARNING, "Driver exited abnormally"); exec_error++; return; } if (WEXITSTATUS(wstat) != 0) { upslogx(LOG_WARNING, "Driver failed to start" " (exit status=%d)", WEXITSTATUS(wstat)); exec_error++; return; } /* the rest only work when WIFEXITED is nonzero */ if (WIFSIGNALED(wstat)) { upslog_with_errno(LOG_WARNING, "Driver died after signal %d", WTERMSIG(wstat)); exec_error++; } return; } /* child */ ret = execv(argv[0], argv); /* shouldn't get here */ fatal_with_errno(EXIT_FAILURE, "execv"); } static void start_driver(const ups_t *ups) { char *argv[8]; char dfn[SMALLBUF]; int ret, arg = 0; int initial_exec_error = exec_error, drv_maxretry = maxretry; struct stat fs; upsdebugx(1, "Starting UPS: %s", ups->upsname); snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); ret = stat(dfn, &fs); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "Can't start %s", dfn); argv[arg++] = dfn; argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ argv[arg++] = ups->upsname; /* stick on the chroot / user args if given to us */ if (pt_root) { argv[arg++] = (char *)"-r"; /* FIXME: cast away const */ argv[arg++] = pt_root; } if (pt_user) { argv[arg++] = (char *)"-u"; /* FIXME: cast away const */ argv[arg++] = pt_user; } /* tie it off */ argv[arg++] = NULL; while (drv_maxretry > 0) { int cur_exec_error = exec_error; upsdebugx(2, "%i remaining attempts", drv_maxretry); debugcmdline(2, "exec: ", argv); drv_maxretry--; if (!testmode) { forkexec(argv, ups); } /* driver command succeeded */ if (cur_exec_error == exec_error) { drv_maxretry = 0; exec_error = initial_exec_error; } else { /* otherwise, retry if still needed */ if (drv_maxretry > 0) sleep (retrydelay); } } } static void help(const char *progname) { printf("Starts and stops UPS drivers via ups.conf.\n\n"); printf("usage: %s [OPTIONS] (start | stop | shutdown) []\n\n", progname); printf(" -h display this help\n"); printf(" -r drivers will chroot to \n"); printf(" -t testing mode - prints actions without doing them\n"); printf(" -u drivers started will switch from root to \n"); printf(" -D raise debugging level\n"); printf(" start start all UPS drivers in ups.conf\n"); printf(" start only start driver for UPS \n"); printf(" stop stop all UPS drivers in ups.conf\n"); printf(" stop only stop driver for UPS \n"); printf(" shutdown shutdown all UPS drivers in ups.conf\n"); printf(" shutdown only shutdown UPS \n"); exit(EXIT_SUCCESS); } static void shutdown_driver(const ups_t *ups) { char *argv[9]; char dfn[SMALLBUF]; int arg = 0; upsdebugx(1, "Shutdown UPS: %s", ups->upsname); snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); argv[arg++] = dfn; argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ argv[arg++] = ups->upsname; argv[arg++] = (char *)"-k"; /* FIXME: cast away const */ /* stick on the chroot / user args if given to us */ if (pt_root) { argv[arg++] = (char *)"-r"; /* FIXME: cast away const */ argv[arg++] = pt_root; } if (pt_user) { argv[arg++] = (char *)"-u"; /* FIXME: cast away const */ argv[arg++] = pt_user; } argv[arg++] = NULL; debugcmdline(2, "exec: ", argv); if (!testmode) { forkexec(argv, ups); } } static void send_one_driver(void (*command)(const ups_t *), const char *upsname) { ups_t *ups = upstable; if (!ups) fatalx(EXIT_FAILURE, "Error: no UPS definitions found in ups.conf!\n"); while (ups) { if (!strcmp(ups->upsname, upsname)) { command(ups); return; } ups = ups->next; } fatalx(EXIT_FAILURE, "UPS %s not found in ups.conf", upsname); } /* walk UPS table and send command to all UPSes according to sdorder */ static void send_all_drivers(void (*command)(const ups_t *)) { ups_t *ups; int i; if (!upstable) fatalx(EXIT_FAILURE, "Error: no UPS definitions found in ups.conf"); if (command != &shutdown_driver) { ups = upstable; while (ups) { command(ups); ups = ups->next; } return; } for (i = 0; i <= maxsdorder; i++) { ups = upstable; while (ups) { if (ups->sdorder == i) command(ups); ups = ups->next; } } } static void exit_cleanup(void) { ups_t *tmp, *next; tmp = upstable; while (tmp) { next = tmp->next; free(tmp->driver); free(tmp->port); free(tmp->upsname); free(tmp); tmp = next; } free(driverpath); } int main(int argc, char **argv) { int i; char *prog; void (*command)(const ups_t *) = NULL; printf("Network UPS Tools - UPS driver controller %s\n", UPS_VERSION); prog = argv[0]; while ((i = getopt(argc, argv, "+htu:r:DV")) != -1) { switch(i) { case 'r': pt_root = optarg; break; case 't': testmode = 1; break; case 'u': pt_user = optarg; break; case 'V': exit(EXIT_SUCCESS); case 'D': nut_debug_level++; break; case 'h': default: help(prog); break; } } argc -= optind; argv += optind; if (argc < 1) help(prog); if (testmode) { printf("*** Testing mode: not calling exec/kill\n"); if (nut_debug_level < 2) nut_debug_level = 2; } upsdebugx(2, "\n" "If you're not a NUT core developer, chances are that you're told to enable debugging\n" "to see why a driver isn't working for you. We're sorry for the confusion, but this is\n" "the 'upsdrvctl' wrapper, not the driver you're interested in.\n\n" "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n" "path to the driver binary and some command line option. This is what the driver\n" "starts and you need to copy and paste that line and append the debug flags to that\n" "line (less the 'exec:' prefix).\n"); if (!strcmp(argv[0], "start")) command = &start_driver; if (!strcmp(argv[0], "stop")) command = &stop_driver; if (!strcmp(argv[0], "shutdown")) command = &shutdown_driver; if (!command) fatalx(EXIT_FAILURE, "Error: unrecognized command [%s]", argv[0]); driverpath = xstrdup(DRVPATH); /* set default */ atexit(exit_cleanup); read_upsconf(); if (argc == 1) send_all_drivers(command); else send_one_driver(command, argv[1]); if (exec_error) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } nut-2.7.4/drivers/clone-outlet.c0000644000175000017500000002001712640473702013514 00000000000000/* * clone-outlet.c: clone outlet UPS driver * * Copyright (C) 2009 - Arjen de Korte * * 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 "main.h" #include "parseconf.h" #include #include #include #define DRIVER_NAME "clone outlet UPS Driver" #define DRIVER_VERSION "0.01" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte ", DRV_EXPERIMENTAL, { NULL } }; static struct { struct { char *shutdown; } delay; struct { char *shutdown; } timer; char *status; } prefix = { { NULL }, { NULL }, NULL }; static struct { struct { long shutdown; } delay; struct { long shutdown; } timer; int status; } outlet = { { -1 }, { -1 }, 1 }; static struct { char status[LARGEBUF]; } ups = { "WAIT" }; static int dumpdone = 0; static PCONF_CTX_t sock_ctx; static time_t last_heard = 0, last_ping = 0, last_connfail = 0; static int parse_args(int numargs, char **arg) { if (numargs < 1) { return 0; } if (!strcasecmp(arg[0], "PONG")) { upsdebugx(3, "Got PONG from UPS"); return 1; } if (!strcasecmp(arg[0], "DUMPDONE")) { upsdebugx(3, "UPS: dump is done"); dumpdone = 1; return 1; } if (!strcasecmp(arg[0], "DATASTALE")) { dstate_datastale(); return 1; } if (!strcasecmp(arg[0], "DATAOK")) { dstate_dataok(); return 1; } if (numargs < 2) { return 0; } /* DELINFO */ if (!strcasecmp(arg[0], "DELINFO")) { dstate_delinfo(arg[1]); return 1; } if (numargs < 3) { return 0; } /* SETINFO */ if (!strcasecmp(arg[0], "SETINFO")) { if (!strncasecmp(arg[1], "driver.", 7)) { /* don't pass on upstream driver settings */ return 1; } if (!strcasecmp(arg[1], prefix.delay.shutdown)) { outlet.delay.shutdown = strtol(arg[2], NULL, 10); } if (!strcasecmp(arg[1], prefix.timer.shutdown)) { outlet.timer.shutdown = strtol(arg[2], NULL, 10); } if (!strcasecmp(arg[1], prefix.status)) { outlet.status = strcasecmp(arg[2], "off"); } if (!strcasecmp(arg[1], "ups.status")) { snprintf(ups.status, sizeof(ups.status), "%s", arg[2]); return 1; } dstate_setinfo(arg[1], "%s", arg[2]); return 1; } return 0; } static int sstate_connect(void) { int ret, fd; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", device_path); return -1; } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); if (ret < 0) { time_t now; close(fd); /* rate-limit complaints - don't spam the syslog */ time(&now); if (difftime(now, last_connfail) < 60) { return -1; } last_connfail = now; upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); return -1; } ret = fcntl(fd, F_GETFL, 0); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", device_path); close(fd); return -1; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", device_path); close(fd); return -1; } /* get a dump started so we have a fresh set of data */ ret = write(fd, dumpcmd, strlen(dumpcmd)); if (ret != (int)strlen(dumpcmd)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); close(fd); return -1; } pconf_init(&sock_ctx, NULL); time(&last_heard); dumpdone = 0; /* set ups.status to "WAIT" while waiting for the driver response to dumpcmd */ dstate_setinfo("ups.status", "WAIT"); upslogx(LOG_INFO, "Connected to UPS [%s]", device_path); return fd; } static void sstate_disconnect(void) { if (upsfd < 0) { return; } pconf_finish(&sock_ctx); close(upsfd); upsfd = -1; } static int sstate_sendline(const char *buf) { int ret; if (upsfd < 0) { return -1; /* failed */ } ret = write(upsfd, buf, strlen(buf)); if (ret == (int)strlen(buf)) { return 0; } upslog_with_errno(LOG_NOTICE, "Send to UPS [%s] failed", device_path); return -1; /* failed */ } static int sstate_readline(void) { int i, ret; char buf[SMALLBUF]; if (upsfd < 0) { return -1; /* failed */ } ret = read(upsfd, buf, sizeof(buf)); if (ret < 0) { switch(errno) { case EINTR: case EAGAIN: return 0; default: upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path); return -1; } } for (i = 0; i < ret; i++) { switch (pconf_char(&sock_ctx, buf[i])) { case 1: if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) { time(&last_heard); } continue; case 0: continue; /* haven't gotten a line yet */ default: /* parse error */ upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg); return -1; } } return 0; } static int sstate_dead(int maxage) { time_t now; double elapsed; /* an unconnected ups is always dead */ if (upsfd < 0) { upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", device_path); return -1; /* dead */ } time(&now); /* ignore DATAOK/DATASTALE unless the dump is done */ if (dumpdone && dstate_is_stale()) { upsdebugx(3, "sstate_dead: driver for UPS [%s] says data is stale", device_path); return -1; /* dead */ } elapsed = difftime(now, last_heard); /* somewhere beyond a third of the maximum time - prod it to make it talk */ if ((elapsed > (maxage / 3)) && (difftime(now, last_ping) > (maxage / 3))) { upsdebugx(3, "Send PING to UPS"); sstate_sendline("PING\n"); last_ping = now; } if (elapsed > maxage) { upsdebugx(3, "sstate_dead: didn't hear from driver for UPS [%s] for %g seconds (max %d)", device_path, elapsed, maxage); return -1; /* dead */ } return 0; } void upsdrv_initinfo(void) { } void upsdrv_updateinfo(void) { if (sstate_dead(15)) { sstate_disconnect(); extrafd = upsfd = sstate_connect(); return; } if (sstate_readline()) { sstate_disconnect(); return; } if (outlet.status == 0) { upsdebugx(2, "OFF flag set (%s: switched off)", prefix.status); dstate_setinfo("ups.status", "%s OFF", ups.status); return; } if ((outlet.timer.shutdown > -1) && (outlet.timer.shutdown <= outlet.delay.shutdown)) { upsdebugx(2, "FSD flag set (%s: -1 < [%ld] <= %ld)", prefix.timer.shutdown, outlet.timer.shutdown, outlet.delay.shutdown); dstate_setinfo("ups.status", "FSD %s", ups.status); return; } upsdebugx(3, "%s: power state not critical", getval("prefix")); dstate_setinfo("ups.status", "%s", ups.status); } void upsdrv_shutdown(void) { } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "prefix", "Outlet prefix (mandatory)"); } void upsdrv_initups(void) { char buf[SMALLBUF]; const char *val; val = getval("prefix"); if (!val) { fatalx(EXIT_FAILURE, "Outlet prefix is mandatory for this driver!"); } snprintf(buf, sizeof(buf), "%s.delay.shutdown", val); prefix.delay.shutdown = xstrdup(buf); snprintf(buf, sizeof(buf), "%s.timer.shutdown", val); prefix.timer.shutdown = xstrdup(buf); snprintf(buf, sizeof(buf), "%s.status", val); prefix.status = xstrdup(buf); extrafd = upsfd = sstate_connect(); } void upsdrv_cleanup(void) { free(prefix.delay.shutdown); free(prefix.timer.shutdown); free(prefix.status); sstate_disconnect(); } nut-2.7.4/drivers/mge-xml.c0000644000175000017500000020610212667537407012465 00000000000000/* mge-xml.c Model specific routines for Eaton / MGE XML protocol UPSes Copyright (C) 2008-2009 Arjen de Korte 2009 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "common.h" #include "dstate.h" #include "netxml-ups.h" #include "mge-xml.h" #define MGE_XML_VERSION "MGEXML/0.25" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" #define ST_FLAG_RW 0x0001 #define ST_FLAG_STATIC 0x0002 extern int shutdown_duration; static int mge_ambient_value = 0; static char mge_scratch_buf[256]; static char var[128]; static char val[128]; static int mge_shutdown_pending = 0; typedef enum { ROOTPARENT = NE_XML_STATEROOT, _UNEXPECTED, _PARSEERROR, PRODUCT_INFO = 100, /* "/mgeups/product.xml" */ PI_SUMMARY = 110, PI_HTML_PROPERTIES_PAGE, PI_XML_SUMMARY_PAGE, PI_CENTRAL_CFG, PI_CSV_LOGS, /* /PI_SUMMARY */ PI_ALARMS = 120, PI_SUBSCRIPTION, PI_POLLING, /* /ALARMS */ PI_MANAGEMENT = 130, PI_MANAGEMENT_PAGE, PI_XML_MANAGEMENT_PAGE, /* /MANAGEMENT */ PI_UPS_DATA = 140, PI_GET_OBJECT, PI_SET_OBJECT, /* /UPS_DATA */ /* /PRODUCT_INFO */ SUMMARY = 200, /* "/upsprop.xml" */ SU_OBJECT, /* /SUMMARY */ GET_OBJECT = 300, /* "/getvalue.cgi" */ GO_OBJECT, /* /GET_OBJECT */ SET_OBJECT = 400, /* "/setvalue.cgi" */ SO_OBJECT, /* /SET_OBJECT */ ALARM = 500, XML_CLIENT = 600, XC_GENERAL = 610, XC_STARTUP, XC_SHUTDOWN, XC_BROADCAST } mge_xml_state_t; typedef struct { const char *nutname; /* NUT variable name */ uint32_t nutflags; /* NUT flags (to set in addinfo) */ size_t nutlen; /* length of the NUT string */ const char *xmlname; /* XML variable name */ uint32_t xmlflags; /* XML flags (to be used to determine what kind of variable this is */ size_t xmllen; /* length of the XML string */ const char *(*convert)(const char *value); /* conversion function from XML<->NUT value (returns NULL if no further processing is required) */ } xml_info_t; static const char *online_info(const char *val) { if (val[0] == '1') { STATUS_SET(ONLINE); } else { STATUS_CLR(ONLINE); } return NULL; } static const char *discharging_info(const char *val) { if (val[0] == '1') { STATUS_SET(DISCHRG); /* Workaround NMC bug: both charging and discharging set to 1 */ if(STATUS_BIT(CHRG)) { STATUS_CLR(CHRG); } } else { STATUS_CLR(DISCHRG); } return NULL; } static const char *charging_info(const char *val) { if (val[0] == '1') { STATUS_SET(CHRG); } else { STATUS_CLR(CHRG); } return NULL; } static const char *lowbatt_info(const char *val) { if (val[0] == '1') { STATUS_SET(LOWBATT); } else { STATUS_CLR(LOWBATT); } return NULL; } static const char *overload_info(const char *val) { if (val[0] == '1') { STATUS_SET(OVERLOAD); } else { STATUS_CLR(OVERLOAD); } return NULL; } static const char *replacebatt_info(const char *val) { if (val[0] == '1') { STATUS_SET(REPLACEBATT); } else { STATUS_CLR(REPLACEBATT); } return NULL; } static const char *trim_info(const char *val) { if (val[0] == '1') { STATUS_SET(TRIM); } else { STATUS_CLR(TRIM); } return NULL; } static const char *boost_info(const char *val) { if (val[0] == '1') { STATUS_SET(BOOST); } else { STATUS_CLR(BOOST); } return NULL; } static const char *bypass_aut_info(const char *val) { if (val[0] == '1') { STATUS_SET(BYPASSAUTO); } else { STATUS_CLR(BYPASSAUTO); } return NULL; } static const char *bypass_man_info(const char *val) { if (val[0] == '1') { STATUS_SET(BYPASSMAN); } else { STATUS_CLR(BYPASSMAN); } return NULL; } static const char *off_info(const char *val) { if (val[0] == '0') { STATUS_SET(OFF); } else { STATUS_CLR(OFF); } return NULL; } /* note: this value is reverted (0=set, 1=not set). We report "battery not installed" rather than "battery installed", so that devices that don't implement this variable have a battery by default */ static const char *nobattery_info(const char *val) { if (val[0] == '0') { STATUS_SET(NOBATTERY); } else { STATUS_CLR(NOBATTERY); } return NULL; } static const char *fanfail_info(const char *val) { if (val[0] == '1') { STATUS_SET(FANFAIL); } else { STATUS_CLR(FANFAIL); } return NULL; } #if 0 static const char *shutdownimm_info(const char *val) { if (val[0] == '1') { STATUS_SET(SHUTDOWNIMM); } else { STATUS_CLR(SHUTDOWNIMM); } return NULL; } #endif static const char *overheat_info(const char *val) { if (val[0] == '1') { STATUS_SET(OVERHEAT); } else { STATUS_CLR(OVERHEAT); } return NULL; } static const char *commfault_info(const char *val) { if (val[0] == '1') { STATUS_SET(COMMFAULT); } else { STATUS_CLR(COMMFAULT); } return NULL; } static const char *internalfailure_info(const char *val) { if (val[0] == '1') { STATUS_SET(INTERNALFAULT); } else { STATUS_CLR(INTERNALFAULT); } return NULL; } static const char *battvoltlo_info(const char *val) { if (val[0] == '1') { STATUS_SET(BATTVOLTLO); } else { STATUS_CLR(BATTVOLTLO); } return NULL; } static const char *battvolthi_info(const char *val) { if (val[0] == '1') { STATUS_SET(BATTVOLTHI); } else { STATUS_CLR(BATTVOLTHI); } return NULL; } static const char *chargerfail_info(const char *val) { if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) { STATUS_SET(CHARGERFAIL); } else { STATUS_CLR(CHARGERFAIL); } return NULL; } static const char *vrange_info(const char *val) { if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) { STATUS_SET(VRANGE); } else { STATUS_CLR(VRANGE); } return NULL; } static const char *frange_info(const char *val) { if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) { STATUS_SET(FRANGE); } else { STATUS_CLR(FRANGE); } return NULL; } static const char *fuse_fault_info(const char *val) { if (val[0] == '1') { STATUS_SET(FUSEFAULT); } else { STATUS_CLR(FUSEFAULT); } return NULL; } static const char *yes_no_info(const char *val) { switch(val[0]) { case '1': return "yes"; case '0': return "no"; default: upsdebugx(2, "%s: unexpected value [%s]", __func__, val); return ""; } } static const char *on_off_info(const char *val) { switch(val[0]) { case '1': return "on"; case '0': return "off"; default: upsdebugx(2, "%s: unexpected value [%s]", __func__, val); return ""; } } static const char *convert_deci(const char *val) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.1f", 0.1 * (float)atoi(val)); return mge_scratch_buf; } /* Ignore a zero value if the UPS is not switched off */ static const char *ignore_if_zero(const char *val) { if (atoi(val) == 0) { return NULL; } return convert_deci(val); } /* Set the 'ups.date' from the combined value * (ex. 2008/03/01 15:23:26) and return the time */ static const char *split_date_time(const char *val) { char *last = NULL; snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", val); dstate_setinfo("ups.date", "%s", strtok_r(mge_scratch_buf, " -", &last)); return strtok_r(NULL, " ", &last); } static const char *url_convert(const char *val) { char buf[256], *last = NULL; snprintf(buf, sizeof(buf), "%s", val); snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "/%s", strtok_r(buf, " \r\n\t", &last)); return mge_scratch_buf; } static const char *mge_battery_capacity(const char *val) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)atoi(val) / 3600); return mge_scratch_buf; } static const char *mge_powerfactor_conversion(const char *val) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)atoi(val) / 100); return mge_scratch_buf; } static const char *mge_beeper_info(const char *val) { switch (atoi(val)) { case 1: return "disabled"; case 2: return "enabled"; case 3: return "muted"; } return NULL; } static const char *mge_upstype_conversion(const char *val) { switch (atoi(val)) { case 1: return "offline / line interactive"; case 2: return "online"; case 3: return "online - unitary/parallel"; case 4: return "online - parallel with hot standy"; case 5: return "online - hot standby redundancy"; } return NULL; } static const char *mge_sensitivity_info(const char *val) { switch (atoi(val)) { case 0: return "normal"; case 1: return "high"; case 2: return "low"; } return NULL; } static const char *mge_test_result_info(const char *val) { switch (atoi(val)) { case 1: return "done and passed"; case 2: return "done and warning"; case 3: return "done and error"; case 4: return "aborted"; case 5: return "in progress"; case 6: return "no test initiated"; case 7: return "test scheduled"; } return NULL; } static const char *mge_ambient_info(const char *val) { switch (mge_ambient_value) { case 1: return val; default: return NULL; } } static const char *mge_timer_shutdown(const char *delay_before_shutoff) { if (atoi(delay_before_shutoff) > -1 ) { STATUS_SET(SHUTDOWNIMM); mge_shutdown_pending = 1; if( atoi(delay_before_shutoff) > shutdown_duration ) { STATUS_CLR(SHUTDOWNIMM); mge_shutdown_pending = 0; } } else { STATUS_CLR(SHUTDOWNIMM); mge_shutdown_pending = 0; } return val; } static const char *mge_shutdown_imminent(const char *val) { const int shutdown_delay = atoi(val); /* shutdown is already managed by mge_timer_shutdown, give up */ if(mge_shutdown_pending) { return NULL; } /* We may have "NONE" or "-1" or ?? as value * We also double check both the string and numeric values to be zero!*/ if ((val) && (val[0] == '0') && (shutdown_delay == 0)) { STATUS_SET(SHUTDOWNIMM); } else { STATUS_CLR(SHUTDOWNIMM); } return NULL; } static xml_info_t mge_xml2nut[] = { /* NMC configuration (mapped 1:1 for now) */ { "device.contact", ST_FLAG_RW, 0, "System.Contact", 0, 0, NULL }, { "device.location", ST_FLAG_RW, 0, "System.Location", 0, 0, NULL }, /* Not used for now; might however be used in future for history & stats collection { "System.History.Log.Interval", ST_FLAG_RW, 0, "System.History.Log.Interval", 0, 0, NULL }, */ #if (0) /* not interresting for NUT */ { "System.Environment.Log.Interval", ST_FLAG_RW, 0, "System.Environment.Log.Interval", 0, 0, NULL }, { "System.Outlet[1].iName", ST_FLAG_RW, 0, "System.Outlet[1].iName", 0, 0, NULL }, /* Mapped as ups.delay.shutdown { "System.ShutdownDuration", ST_FLAG_RW, 0, "System.ShutdownDuration", 0, 0, NULL }, */ { "System.ShutdownTimerSelected", ST_FLAG_RW, 0, "System.ShutdownTimerSelected", 0, 0, NULL }, { "System.ShutdownTimer", ST_FLAG_RW, 0, "System.ShutdownTimer", 0, 0, NULL }, /* Mapped as battery.runtime.low { "System.RunTimeToEmptyLimit", ST_FLAG_RW, 0, "System.RunTimeToEmptyLimit", 0, 0, NULL }, */ { "System.RemainingCapacityLimit", ST_FLAG_RW, 0, "System.RemainingCapacityLimit", 0, 0, NULL }, { "System.RestartLevel", ST_FLAG_RW, 0, "System.RestartLevel", 0, 0, NULL }, { "System.Outlet[2].iName", ST_FLAG_RW, 0, "System.Outlet[2].iName", 0, 0, NULL }, /* Mapped as outlet.1.delay.shutdown { "System.Outlet[2].ShutdownDuration", ST_FLAG_RW, 0, "System.Outlet[2].ShutdownDuration", 0, 0, NULL }, */ { "System.Outlet[2].ShutdownTimer", ST_FLAG_RW, 0, "System.Outlet[2].ShutdownTimer", 0, 0, NULL }, { "System.Outlet[2].StartupTimer", ST_FLAG_RW, 0, "System.Outlet[2].StartupTimer", 0, 0, NULL }, { "System.Outlet[2].RemainingCapacityLimit", ST_FLAG_RW, 0, "System.Outlet[2].RemainingCapacityLimit", 0, 0, NULL }, /* For future extension, and support of shutdown on load segment * { "System.Outlet[2].RunTimeToShutdown", ST_FLAG_RW, 0, "System.Outlet[2].RunTimeToShutdown", 0, 0, NULL }, */ { "System.Outlet[3].iName", ST_FLAG_RW, 0, "System.Outlet[3].iName", 0, 0, NULL }, /* Mapped as outlet.2.delay.shutdown { "System.Outlet[3].ShutdownDuration", ST_FLAG_RW, 0, "System.Outlet[3].ShutdownDuration", 0, 0, NULL }, */ { "System.Outlet[3].ShutdownTimer", ST_FLAG_RW, 0, "System.Outlet[3].ShutdownTimer", 0, 0, NULL }, { "System.Outlet[3].StartupTimer", ST_FLAG_RW, 0, "System.Outlet[3].StartupTimer", 0, 0, NULL }, { "System.Outlet[3].RemainingCapacityLimit", ST_FLAG_RW, 0, "System.Outlet[3].RemainingCapacityLimit", 0, 0, NULL }, /* For future extension, and support of shutdown on load segment * { "System.Outlet[3].RunTimeToShutdown", ST_FLAG_RW, 0, "System.Outlet[3].RunTimeToShutdown", 0, 0, NULL }, */ { "System.Outlet[1].OffDelay", ST_FLAG_RW, 0, "System.Outlet[1].OffDelay", 0, 0, NULL }, { "System.Outlet[1].Toggle", ST_FLAG_RW, 0, "System.Outlet[1].Toggle", 0, 0, NULL }, { "System.Outlet[1].OnDelay", ST_FLAG_RW, 0, "System.Outlet[1].OnDelay", 0, 0, NULL }, { "System.Outlet[2].OffDelay", ST_FLAG_RW, 0, "System.Outlet[2].OffDelay", 0, 0, NULL }, { "System.Outlet[2].Toggle", ST_FLAG_RW, 0, "System.Outlet[2].Toggle", 0, 0, NULL }, { "System.Outlet[2].OnDelay", ST_FLAG_RW, 0, "System.Outlet[2].OnDelay", 0, 0, NULL }, { "System.Outlet[3].OffDelay", ST_FLAG_RW, 0, "System.Outlet[3].OffDelay", 0, 0, NULL }, { "System.Outlet[3].Toggle", ST_FLAG_RW, 0, "System.Outlet[3].Toggle", 0, 0, NULL }, { "System.Outlet[3].OnDelay", ST_FLAG_RW, 0, "System.Outlet[3].OnDelay", 0, 0, NULL }, { "System.Login", ST_FLAG_RW, 0, "System.Login", 0, 0, NULL }, { "System.Password", ST_FLAG_RW, 0, "System.Password", 0, 0, NULL }, { "System.Security", ST_FLAG_RW, 0, "System.Security", 0, 0, NULL }, { "System.FirmwareUpgrade", ST_FLAG_RW, 0, "System.FirmwareUpgrade", 0, 0, NULL }, { "System.Network.SNMP.ReadCommunity", ST_FLAG_RW, 0, "System.Network.SNMP.ReadCommunity", 0, 0, NULL }, { "System.Network.SNMP.ReadCommunityName", 0, 0, "System.Network.SNMP.ReadCommunityName", 0, 0, NULL }, { "System.Network.SNMP.ReadCommunitySecurityLevel", 0, 0, "System.Network.SNMP.ReadCommunitySecurityLevel", 0, 0, NULL }, { "System.Network.SNMP.ReadCommunitySecurityRight", 0, 0, "System.Network.SNMP.ReadCommunitySecurityRight", 0, 0, NULL }, { "System.Network.SNMP.WriteCommunity", ST_FLAG_RW, 0, "System.Network.SNMP.WriteCommunity", 0, 0, NULL }, { "System.Network.SNMP.WriteCommunityName", 0, 0, "System.Network.SNMP.WriteCommunityName", 0, 0, NULL }, { "System.Network.SNMP.WriteCommunitySecurityLevel", 0, 0, "System.Network.SNMP.WriteCommunitySecurityLevel", 0, 0, NULL }, { "System.Network.SNMP.WriteCommunitySecurityRight", ST_FLAG_RW, 0, "System.Network.SNMP.WriteCommunitySecurityRight", 0, 0, NULL }, { "System.Network.SNMP.Admin", ST_FLAG_RW, 0, "System.Network.SNMP.Admin", 0, 0, NULL }, { "System.Network.SNMP.AdminPassword", ST_FLAG_RW, 0, "System.Network.SNMP.AdminPassword", 0, 0, NULL }, { "System.Network.SNMP.AdminSecurityLevel", ST_FLAG_RW, 0, "System.Network.SNMP.AdminSecurityLevel", 0, 0, NULL }, { "System.Network.SNMP.AdminSecurityRight", 0, 0, "System.Network.SNMP.AdminSecurityRight", 0, 0, NULL }, { "System.Network.SNMP.User", ST_FLAG_RW, 0, "System.Network.SNMP.User", 0, 0, NULL }, { "System.Network.SNMP.UserPassword", ST_FLAG_RW, 0, "System.Network.SNMP.UserPassword", 0, 0, NULL }, { "System.Network.SNMP.UserSecurityLevel", ST_FLAG_RW, 0, "System.Network.SNMP.UserSecurityLevel", 0, 0, NULL }, { "System.Network.SNMP.UserSecurityRight", 0, 0, "System.Network.SNMP.UserSecurityRight", 0, 0, NULL }, { "System.Network.SNMP.NotificationUserName", ST_FLAG_RW, 0, "System.Network.SNMP.NotificationUserName", 0, 0, NULL }, { "System.Network.SNMP.snmpVersion", ST_FLAG_RW, 0, "System.Network.SNMP.snmpVersion", 0, 0, NULL }, { "System.Network.SNMP.engineBoots", 0, 0, "System.Network.SNMP.engineBoots", 0, 0, NULL }, { "System.Network.Telnet.Access", ST_FLAG_RW, 0, "System.Network.Telnet.Access", 0, 0, NULL }, { "System.Network.Telnet.Security", ST_FLAG_RW, 0, "System.Network.Telnet.Security", 0, 0, NULL }, { "System.Network.Telnet.Console", ST_FLAG_RW, 0, "System.Network.Telnet.Console", 0, 0, NULL }, { "System.Email.Sender", ST_FLAG_RW, 0, "System.Email.Sender", 0, 0, NULL }, { "System.Email.Subject", ST_FLAG_RW, 0, "System.Email.Subject", 0, 0, NULL }, { "System.Email.UPSName", ST_FLAG_RW, 0, "System.Email.UPSName", 0, 0, NULL }, { "System.Email.Message", ST_FLAG_RW, 0, "System.Email.Message", 0, 0, NULL }, { "System.Email.Localization", ST_FLAG_RW, 0, "System.Email.Localization", 0, 0, NULL }, { "System.Email.EventName", ST_FLAG_RW, 0, "System.Email.EventName", 0, 0, NULL }, { "System.Email[0].Recipient", ST_FLAG_RW, 0, "System.Email[0].Recipient", 0, 0, NULL }, { "System.Email[0].Selected", ST_FLAG_RW, 0, "System.Email[0].Selected", 0, 0, NULL }, { "System.Email[0].Enotify", ST_FLAG_RW, 0, "System.Email[0].Enotify", 0, 0, NULL }, { "System.Email[0].Measures.Log", ST_FLAG_RW, 0, "System.Email[0].Measures.Log", 0, 0, NULL }, { "System.Email[0].Events.Log", ST_FLAG_RW, 0, "System.Email[0].Events.Log", 0, 0, NULL }, { "System.Email[0].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[0].SystemEvents.Log", 0, 0, NULL }, { "System.Email[0].Environment.Log", ST_FLAG_RW, 0, "System.Email[0].Environment.Log", 0, 0, NULL }, { "System.Email[0].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[0].Report.Periodicity", 0, 0, NULL }, { "System.Email[0].Report.Hour", ST_FLAG_RW, 0, "System.Email[0].Report.Hour", 0, 0, NULL }, { "System.Email[0].Report.Next", ST_FLAG_RW, 0, "System.Email[0].Report.Next", 0, 0, NULL }, { "System.Email[0].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[0].EventList.Discharging", 0, 0, NULL }, { "System.Email[0].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[0].EventList.ACPresent", 0, 0, NULL }, { "System.Email[0].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[0].EventList.RunTimeToShutdown", 0, 0, NULL }, { "System.Email[0].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[0].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, { "System.Email[0].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[0].EventList.NeedReplacement.1", 0, 0, NULL }, { "System.Email[0].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[0].EventList.NeedReplacement.0", 0, 0, NULL }, { "System.Email[0].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[0].EventList.Overload.1", 0, 0, NULL }, { "System.Email[0].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[0].EventList.Overload.0", 0, 0, NULL }, { "System.Email[0].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[0].EventList.InternalFailure.1", 0, 0, NULL }, { "System.Email[0].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[0].EventList.InternalFailure.0", 0, 0, NULL }, { "System.Email[0].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[0].EventList.CommunicationLost.1", 0, 0, NULL }, { "System.Email[0].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[0].EventList.CommunicationLost.0", 0, 0, NULL }, { "System.Email[0].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[0].EventList.Charger.InternalFailure", 0, 0, NULL }, { "System.Email[0].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[0].EventList.Input[2].Used.1", 0, 0, NULL }, { "System.Email[0].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[0].EventList.Input[2].Used.0", 0, 0, NULL }, { "System.Email[0].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, { "System.Email[0].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, { "System.Email[0].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, { "System.Email[0].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, { "System.Email[0].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[0].EventList.FirmwareUpgrade", 0, 0, NULL }, { "System.Email[0].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[0].EventList.Environment.CommunicationLost", 0, 0, NULL }, { "System.Email[0].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[0].EventList.Environment.Notify", 0, 0, NULL }, { "System.Email[1].Recipient", ST_FLAG_RW, 0, "System.Email[1].Recipient", 0, 0, NULL }, { "System.Email[1].Selected", ST_FLAG_RW, 0, "System.Email[1].Selected", 0, 0, NULL }, { "System.Email[1].Enotify", ST_FLAG_RW, 0, "System.Email[1].Enotify", 0, 0, NULL }, { "System.Email[1].Measures.Log", ST_FLAG_RW, 0, "System.Email[1].Measures.Log", 0, 0, NULL }, { "System.Email[1].Events.Log", ST_FLAG_RW, 0, "System.Email[1].Events.Log", 0, 0, NULL }, { "System.Email[1].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[1].SystemEvents.Log", 0, 0, NULL }, { "System.Email[1].Environment.Log", ST_FLAG_RW, 0, "System.Email[1].Environment.Log", 0, 0, NULL }, { "System.Email[1].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[1].Report.Periodicity", 0, 0, NULL }, { "System.Email[1].Report.Hour", ST_FLAG_RW, 0, "System.Email[1].Report.Hour", 0, 0, NULL }, { "System.Email[1].Report.Next", ST_FLAG_RW, 0, "System.Email[1].Report.Next", 0, 0, NULL }, { "System.Email[1].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[1].EventList.Discharging", 0, 0, NULL }, { "System.Email[1].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[1].EventList.ACPresent", 0, 0, NULL }, { "System.Email[1].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[1].EventList.RunTimeToShutdown", 0, 0, NULL }, { "System.Email[1].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[1].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, { "System.Email[1].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[1].EventList.NeedReplacement.1", 0, 0, NULL }, { "System.Email[1].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[1].EventList.NeedReplacement.0", 0, 0, NULL }, { "System.Email[1].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[1].EventList.Overload.1", 0, 0, NULL }, { "System.Email[1].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[1].EventList.Overload.0", 0, 0, NULL }, { "System.Email[1].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[1].EventList.InternalFailure.1", 0, 0, NULL }, { "System.Email[1].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[1].EventList.InternalFailure.0", 0, 0, NULL }, { "System.Email[1].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[1].EventList.CommunicationLost.1", 0, 0, NULL }, { "System.Email[1].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[1].EventList.CommunicationLost.0", 0, 0, NULL }, { "System.Email[1].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[1].EventList.Charger.InternalFailure", 0, 0, NULL }, { "System.Email[1].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[1].EventList.Input[2].Used.1", 0, 0, NULL }, { "System.Email[1].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[1].EventList.Input[2].Used.0", 0, 0, NULL }, { "System.Email[1].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, { "System.Email[1].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, { "System.Email[1].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, { "System.Email[1].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, { "System.Email[1].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[1].EventList.FirmwareUpgrade", 0, 0, NULL }, { "System.Email[1].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[1].EventList.Environment.CommunicationLost", 0, 0, NULL }, { "System.Email[1].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[1].EventList.Environment.Notify", 0, 0, NULL }, { "System.Email[2].Recipient", ST_FLAG_RW, 0, "System.Email[2].Recipient", 0, 0, NULL }, { "System.Email[2].Selected", ST_FLAG_RW, 0, "System.Email[2].Selected", 0, 0, NULL }, { "System.Email[2].Enotify", ST_FLAG_RW, 0, "System.Email[2].Enotify", 0, 0, NULL }, { "System.Email[2].Measures.Log", ST_FLAG_RW, 0, "System.Email[2].Measures.Log", 0, 0, NULL }, { "System.Email[2].Events.Log", ST_FLAG_RW, 0, "System.Email[2].Events.Log", 0, 0, NULL }, { "System.Email[2].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[2].SystemEvents.Log", 0, 0, NULL }, { "System.Email[2].Environment.Log", ST_FLAG_RW, 0, "System.Email[2].Environment.Log", 0, 0, NULL }, { "System.Email[2].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[2].Report.Periodicity", 0, 0, NULL }, { "System.Email[2].Report.Hour", ST_FLAG_RW, 0, "System.Email[2].Report.Hour", 0, 0, NULL }, { "System.Email[2].Report.Next", ST_FLAG_RW, 0, "System.Email[2].Report.Next", 0, 0, NULL }, { "System.Email[2].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[2].EventList.Discharging", 0, 0, NULL }, { "System.Email[2].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[2].EventList.ACPresent", 0, 0, NULL }, { "System.Email[2].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[2].EventList.RunTimeToShutdown", 0, 0, NULL }, { "System.Email[2].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[2].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, { "System.Email[2].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[2].EventList.NeedReplacement.1", 0, 0, NULL }, { "System.Email[2].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[2].EventList.NeedReplacement.0", 0, 0, NULL }, { "System.Email[2].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[2].EventList.Overload.1", 0, 0, NULL }, { "System.Email[2].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[2].EventList.Overload.0", 0, 0, NULL }, { "System.Email[2].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[2].EventList.InternalFailure.1", 0, 0, NULL }, { "System.Email[2].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[2].EventList.InternalFailure.0", 0, 0, NULL }, { "System.Email[2].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[2].EventList.CommunicationLost.1", 0, 0, NULL }, { "System.Email[2].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[2].EventList.CommunicationLost.0", 0, 0, NULL }, { "System.Email[2].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[2].EventList.Charger.InternalFailure", 0, 0, NULL }, { "System.Email[2].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[2].EventList.Input[2].Used.1", 0, 0, NULL }, { "System.Email[2].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[2].EventList.Input[2].Used.0", 0, 0, NULL }, { "System.Email[2].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, { "System.Email[2].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, { "System.Email[2].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, { "System.Email[2].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, { "System.Email[2].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[2].EventList.FirmwareUpgrade", 0, 0, NULL }, { "System.Email[2].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[2].EventList.Environment.CommunicationLost", 0, 0, NULL }, { "System.Email[2].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[2].EventList.Environment.Notify", 0, 0, NULL }, { "System.Email[3].Recipient", ST_FLAG_RW, 0, "System.Email[3].Recipient", 0, 0, NULL }, { "System.Email[3].Selected", ST_FLAG_RW, 0, "System.Email[3].Selected", 0, 0, NULL }, { "System.Email[3].Enotify", ST_FLAG_RW, 0, "System.Email[3].Enotify", 0, 0, NULL }, { "System.Email[3].Measures.Log", ST_FLAG_RW, 0, "System.Email[3].Measures.Log", 0, 0, NULL }, { "System.Email[3].Events.Log", ST_FLAG_RW, 0, "System.Email[3].Events.Log", 0, 0, NULL }, { "System.Email[3].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[3].SystemEvents.Log", 0, 0, NULL }, { "System.Email[3].Environment.Log", ST_FLAG_RW, 0, "System.Email[3].Environment.Log", 0, 0, NULL }, { "System.Email[3].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[3].Report.Periodicity", 0, 0, NULL }, { "System.Email[3].Report.Hour", ST_FLAG_RW, 0, "System.Email[3].Report.Hour", 0, 0, NULL }, { "System.Email[3].Report.Next", ST_FLAG_RW, 0, "System.Email[3].Report.Next", 0, 0, NULL }, { "System.Email[3].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[3].EventList.Discharging", 0, 0, NULL }, { "System.Email[3].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[3].EventList.ACPresent", 0, 0, NULL }, { "System.Email[3].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[3].EventList.RunTimeToShutdown", 0, 0, NULL }, { "System.Email[3].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[3].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, { "System.Email[3].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[3].EventList.NeedReplacement.1", 0, 0, NULL }, { "System.Email[3].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[3].EventList.NeedReplacement.0", 0, 0, NULL }, { "System.Email[3].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[3].EventList.Overload.1", 0, 0, NULL }, { "System.Email[3].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[3].EventList.Overload.0", 0, 0, NULL }, { "System.Email[3].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[3].EventList.InternalFailure.1", 0, 0, NULL }, { "System.Email[3].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[3].EventList.InternalFailure.0", 0, 0, NULL }, { "System.Email[3].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[3].EventList.CommunicationLost.1", 0, 0, NULL }, { "System.Email[3].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[3].EventList.CommunicationLost.0", 0, 0, NULL }, { "System.Email[3].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[3].EventList.Charger.InternalFailure", 0, 0, NULL }, { "System.Email[3].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[3].EventList.Input[2].Used.1", 0, 0, NULL }, { "System.Email[3].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[3].EventList.Input[2].Used.0", 0, 0, NULL }, { "System.Email[3].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, { "System.Email[3].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, { "System.Email[3].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, { "System.Email[3].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, { "System.Email[3].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[3].EventList.FirmwareUpgrade", 0, 0, NULL }, { "System.Email[3].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[3].EventList.Environment.CommunicationLost", 0, 0, NULL }, { "System.Email[3].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[3].EventList.Environment.Notify", 0, 0, NULL }, { "System.Schedule[0].Off", ST_FLAG_RW, 0, "System.Schedule[0].Off", 0, 0, NULL }, { "System.Schedule[0].On", ST_FLAG_RW, 0, "System.Schedule[0].On", 0, 0, NULL }, { "System.Schedule[1].Off", ST_FLAG_RW, 0, "System.Schedule[1].Off", 0, 0, NULL }, { "System.Schedule[1].On", ST_FLAG_RW, 0, "System.Schedule[1].On", 0, 0, NULL }, { "System.Schedule[2].Off", ST_FLAG_RW, 0, "System.Schedule[2].Off", 0, 0, NULL }, { "System.Schedule[2].On", ST_FLAG_RW, 0, "System.Schedule[2].On", 0, 0, NULL }, { "System.Schedule[3].Off", ST_FLAG_RW, 0, "System.Schedule[3].Off", 0, 0, NULL }, { "System.Schedule[3].On", ST_FLAG_RW, 0, "System.Schedule[3].On", 0, 0, NULL }, { "System.Schedule[4].Off", ST_FLAG_RW, 0, "System.Schedule[4].Off", 0, 0, NULL }, { "System.Schedule[4].On", ST_FLAG_RW, 0, "System.Schedule[4].On", 0, 0, NULL }, { "System.Schedule[5].Off", ST_FLAG_RW, 0, "System.Schedule[5].Off", 0, 0, NULL }, { "System.Schedule[5].On", ST_FLAG_RW, 0, "System.Schedule[5].On", 0, 0, NULL }, { "System.Schedule[6].Off", ST_FLAG_RW, 0, "System.Schedule[6].Off", 0, 0, NULL }, { "System.Schedule[6].On", ST_FLAG_RW, 0, "System.Schedule[6].On", 0, 0, NULL }, { "System.NetworkManagementSystem[0].Name", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].Name", 0, 0, NULL }, { "System.NetworkManagementSystem[0].HostName", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].HostName", 0, 0, NULL }, { "System.NetworkManagementSystem[0].TrapCommunity", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].TrapCommunity", 0, 0, NULL }, { "System.NetworkManagementSystem[0].TrapSnmpVersion", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].TrapSnmpVersion", 0, 0, NULL }, { "System.NetworkManagementSystem[0].TrapSelectedMibs", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].TrapSelectedMibs", 0, 0, NULL }, { "System.NetworkManagementSystem[1].Name", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].Name", 0, 0, NULL }, { "System.NetworkManagementSystem[1].HostName", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].HostName", 0, 0, NULL }, { "System.NetworkManagementSystem[1].TrapCommunity", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].TrapCommunity", 0, 0, NULL }, { "System.NetworkManagementSystem[1].TrapSnmpVersion", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].TrapSnmpVersion", 0, 0, NULL }, { "System.NetworkManagementSystem[1].TrapSelectedMibs", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].TrapSelectedMibs", 0, 0, NULL }, { "System.NetworkManagementSystem[2].Name", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].Name", 0, 0, NULL }, { "System.NetworkManagementSystem[2].HostName", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].HostName", 0, 0, NULL }, { "System.NetworkManagementSystem[2].TrapCommunity", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].TrapCommunity", 0, 0, NULL }, { "System.NetworkManagementSystem[2].TrapSnmpVersion", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].TrapSnmpVersion", 0, 0, NULL }, { "System.NetworkManagementSystem[2].TrapSelectedMibs", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].TrapSelectedMibs", 0, 0, NULL }, { "System.ClientCfg.ShutdownTimer.Select", ST_FLAG_RW, 0, "System.ClientCfg.ShutdownTimer.Select", 0, 0, NULL }, { "System.ClientCfg.ShutdownTimer", ST_FLAG_RW, 0, "System.ClientCfg.ShutdownTimer", 0, 0, NULL }, { "System.ClientCfg.ShutdownDuration", ST_FLAG_RW, 0, "System.ClientCfg.ShutdownDuration", 0, 0, NULL }, { "System.ClientCfg.BroadcastAdmins", ST_FLAG_RW, 0, "System.ClientCfg.BroadcastAdmins", 0, 0, NULL }, { "System.ClientCfg.BroadcastUsers", ST_FLAG_RW, 0, "System.ClientCfg.BroadcastUsers", 0, 0, NULL }, { "Environment.iName", ST_FLAG_RW, 0, "Environment.iName", 0, 0, NULL }, { "Environment.Temperature.Unit", ST_FLAG_RW, 0, "Environment.Temperature.Unit", 0, 0, NULL }, { "Environment.Temperature.Hysteresis", ST_FLAG_RW, 0, "Environment.Temperature.Hysteresis", 0, 0, NULL }, { "Environment.Temperature.Offset", ST_FLAG_RW, 0, "Environment.Temperature.Offset", 0, 0, NULL }, { "Environment.Temperature.HighNotify", ST_FLAG_RW, 0, "Environment.Temperature.HighNotify", 0, 0, NULL }, { "Environment.Temperature.LowNotify", ST_FLAG_RW, 0, "Environment.Temperature.LowNotify", 0, 0, NULL }, { "Environment.Temperature.HighShutdown", ST_FLAG_RW, 0, "Environment.Temperature.HighShutdown", 0, 0, NULL }, { "Environment.Temperature.LowShutdown", ST_FLAG_RW, 0, "Environment.Temperature.LowShutdown", 0, 0, NULL }, { "Environment.Humidity.Hysteresis", ST_FLAG_RW, 0, "Environment.Humidity.Hysteresis", 0, 0, NULL }, { "Environment.Humidity.Offset", ST_FLAG_RW, 0, "Environment.Humidity.Offset", 0, 0, NULL }, { "Environment.Humidity.HighNotify", ST_FLAG_RW, 0, "Environment.Humidity.HighNotify", 0, 0, NULL }, { "Environment.Humidity.LowNotify", ST_FLAG_RW, 0, "Environment.Humidity.LowNotify", 0, 0, NULL }, { "Environment.Humidity.HighShutdown", ST_FLAG_RW, 0, "Environment.Humidity.HighShutdown", 0, 0, NULL }, { "Environment.Humidity.LowShutdown", ST_FLAG_RW, 0, "Environment.Humidity.LowShutdown", 0, 0, NULL }, { "Environment.Input[1].iName", ST_FLAG_RW, 0, "Environment.Input[1].iName", 0, 0, NULL }, { "Environment.Input[1].State[0].Description", ST_FLAG_RW, 0, "Environment.Input[1].State[0].Description", 0, 0, NULL }, { "Environment.Input[1].State[0].Notify", ST_FLAG_RW, 0, "Environment.Input[1].State[0].Notify", 0, 0, NULL }, { "Environment.Input[1].State[0].Shutdown", ST_FLAG_RW, 0, "Environment.Input[1].State[0].Shutdown", 0, 0, NULL }, { "Environment.Input[1].State[1].Description", ST_FLAG_RW, 0, "Environment.Input[1].State[1].Description", 0, 0, NULL }, { "Environment.Input[1].State[1].Notify", ST_FLAG_RW, 0, "Environment.Input[1].State[1].Notify", 0, 0, NULL }, { "Environment.Input[1].State[1].Shutdown", ST_FLAG_RW, 0, "Environment.Input[1].State[1].Shutdown", 0, 0, NULL }, { "Environment.Input[2].iName", ST_FLAG_RW, 0, "Environment.Input[2].iName", 0, 0, NULL }, { "Environment.Input[2].State[0].Description", ST_FLAG_RW, 0, "Environment.Input[2].State[0].Description", 0, 0, NULL }, { "Environment.Input[2].State[0].Notify", ST_FLAG_RW, 0, "Environment.Input[2].State[0].Notify", 0, 0, NULL }, { "Environment.Input[2].State[0].Shutdown", ST_FLAG_RW, 0, "Environment.Input[2].State[0].Shutdown", 0, 0, NULL }, { "Environment.Input[2].State[1].Description", ST_FLAG_RW, 0, "Environment.Input[2].State[1].Description", 0, 0, NULL }, { "Environment.Input[2].State[1].Notify", ST_FLAG_RW, 0, "Environment.Input[2].State[1].Notify", 0, 0, NULL }, { "Environment.Input[2].State[1].Shutdown", ST_FLAG_RW, 0, "Environment.Input[2].State[1].Shutdown", 0, 0, NULL }, { "System.TimeSync", ST_FLAG_RW, 0, "System.TimeSync", 0, 0, NULL }, { "System.TimeNtp", ST_FLAG_RW, 0, "System.TimeNtp", 0, 0, NULL }, { "System.TimeZone", ST_FLAG_RW, 0, "System.TimeZone", 0, 0, NULL }, { "System.TimeDaylight", ST_FLAG_RW, 0, "System.TimeDaylight", 0, 0, NULL }, #endif /* not interresting for NUT */ /* Special case: boolean values that are mapped to ups.status and ups.alarm */ { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", 0, 0, online_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Charging", 0, 0, charging_info }, /* NMC bug: keep discharging test AFTER charging test */ { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", 0, 0, discharging_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", 0, 0, lowbatt_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Overload", 0, 0, overload_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", 0, 0, replacebatt_info }, { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.Buck", 0, 0, trim_info }, { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.Boost", 0, 0, boost_info }, { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.VoltageOutOfRange", 0, 0, vrange_info }, { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.FrequencyOutOfRange", 0, 0, frange_info }, { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.FuseFault", 0, 0, fuse_fault_info }, { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.InternalFailure", 0, 0, internalfailure_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Good", 0, 0, off_info }, /* { NULL, 0, 0, "UPS.PowerConverter.Input[1].PresentStatus.Used", 0, 0, online_info }, */ { NULL, 0, 0, "UPS.PowerConverter.Input[2].PresentStatus.Used", 0, 0, bypass_aut_info }, /* Automatic bypass */ /* { NULL, 0, 0, "UPS.PowerConverter.Input[3].PresentStatus.Used", 0, 0, onbatt_info }, */ { NULL, 0, 0, "UPS.PowerConverter.Input[4].PresentStatus.Used", 0, 0, bypass_man_info }, /* Manual bypass */ { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.FanFailure", 0, 0, fanfail_info }, { NULL, 0, 0, "UPS.BatterySystem.Battery.PresentStatus.Present", 0, 0, nobattery_info }, { NULL, 0, 0, "UPS.BatterySystem.Charger.PresentStatus.InternalFailure", 0, 0, chargerfail_info }, { NULL, 0, 0, "UPS.BatterySystem.Charger.PresentStatus.VoltageTooHigh", 0, 0, battvolthi_info }, { NULL, 0, 0, "UPS.BatterySystem.Charger.PresentStatus.VoltageTooLow", 0, 0, battvoltlo_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", 0, 0, internalfailure_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.CommunicationLost", 0, 0, commfault_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", 0, 0, overheat_info }, /* { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", 0, 0, shutdownimm_info }, */ /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", 0, 0, NULL }, { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimitSetting", 0, 0, NULL }, { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", 0, 0, NULL }, /* Read only */ { "battery.charge.restart", 0, 0, "UPS.PowerSummary.RestartLevel", 0, 0, NULL }, { "battery.capacity", 0, 0, "UPS.BatterySystem.Battery.DesignCapacity", 0, 0, mge_battery_capacity }, /* conversion needed from As to Ah */ { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", 0, 0, NULL }, { "battery.runtime.low", ST_FLAG_RW, 0, "System.RunTimeToEmptyLimit", 0, 0, NULL }, { "battery.temperature", 0, 0, "UPS.BatterySystem.Battery.Temperature", 0, 0, NULL }, { "battery.type", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iDeviceChemistry", 0, 0, NULL }, { "battery.type", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iDeviceChemistery", 0, 0, NULL }, /* [sic] */ { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", 0, 0, NULL }, { "battery.voltage.nominal", ST_FLAG_STATIC, 0, "UPS.BatterySystem.ConfigVoltage", 0, 0, NULL }, { "battery.voltage.nominal", ST_FLAG_STATIC, 0, "UPS.PowerSummary.ConfigVoltage", 0, 0, NULL }, /* mge_battery_voltage_nominal */ { "battery.current", 0, 0, "UPS.PowerSummary.Current", 0, 0, NULL }, { "battery.protection", 0, 0, "UPS.BatterySystem.Battery.DeepDischargeProtection", 0, 0, yes_no_info }, { "battery.energysave", 0, 0, "UPS.PowerConverter.Input[3].EnergySaving", 0, 0, yes_no_info }, /* UPS page */ { "ups.mfr", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iManufacturer", 0, 0, NULL }, { "ups.model", ST_FLAG_STATIC, 0, "System.Description", 0, 0, NULL }, { "ups.model", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iProduct", 0, 0, NULL }, { "ups.model.aux", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iModel", 0, 0, NULL }, { "ups.time", 0, 0, "System.LastAcquisition", 0, 0, split_date_time }, /* -> XML variable System.Location [Computer Room] doesn't map to any NUT variable */ /* -> XML variable System.Contact [Computer Room Manager] doesn't map to any NUT variable */ /* -> XML variable UPS.PowerSummary.iProduct [Evolution] doesn't map to any NUT variable */ /* -> XML variable UPS.PowerSummary.iModel [650] doesn't map to any NUT variable */ { "ups.serial", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iSerialNumber", 0, 0, NULL }, { "ups.firmware", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iVersion", 0, 0, NULL }, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", 0, 0, NULL }, { "ups.load.high", 0, 0, "UPS.Flow[4].ConfigPercentLoad", 0, 0, NULL }, { "ups.delay.shutdown", ST_FLAG_RW, 0, "System.ShutdownDuration", 0, 0, NULL }, { "ups.timer.start", ST_FLAG_RW, 0, "UPS.PowerSummary.DelayBeforeStartup", 0, 0, NULL}, { "ups.timer.shutdown", ST_FLAG_RW, 0, "UPS.PowerSummary.DelayBeforeShutdown", 0, 0, mge_timer_shutdown }, /* Catch shutdown imminent criteria, keep it after UPS.PowerSummary.DelayBeforeShutdown managment */ { NULL, 0, 0, "System.RunTimeToShutdown", 0, 0, mge_shutdown_imminent }, { "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", 0, 0, NULL }, { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", 0, 0, mge_test_result_info }, { "ups.test.interval", 0, 0, "UPS.BatterySystem.Battery.TestPeriod", 0, 0, NULL }, { "ups.beeper.status", 0 ,0, "UPS.BatterySystem.Battery.AudibleAlarmControl", 0, 0, mge_beeper_info }, { "ups.beeper.status", 0 ,0, "UPS.PowerSummary.AudibleAlarmControl", 0, 0, mge_beeper_info }, { "ups.temperature", 0, 0, "UPS.PowerSummary.Temperature", 0, 0, NULL }, { "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", 0, 0, NULL }, { "ups.L1.power", 0, 0, "UPS.PowerConverter.Output.Phase[1].ApparentPower", 0, 0, ignore_if_zero }, { "ups.L2.power", 0, 0, "UPS.PowerConverter.Output.Phase[2].ApparentPower", 0, 0, ignore_if_zero }, { "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase[3].ApparentPower", 0, 0, ignore_if_zero }, { "ups.power.nominal", ST_FLAG_STATIC, 0, "UPS.Flow[4].ConfigApparentPower", 0, 0, NULL }, { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", 0, 0, NULL }, { "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[1].ActivePower", 0, 0, ignore_if_zero }, { "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[2].ActivePower", 0, 0, ignore_if_zero }, { "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[3].ActivePower", 0, 0, ignore_if_zero }, { "ups.realpower.nominal", ST_FLAG_STATIC, 0, "UPS.Flow[4].ConfigActivePower", 0, 0, NULL }, { "ups.start.auto", 0, 0, "UPS.PowerConverter.Input[1].AutomaticRestart", 0, 0, yes_no_info }, { "ups.start.battery", 0, 0, "UPS.PowerConverter.Input[3].StartOnBattery", 0, 0, yes_no_info }, { "ups.start.reboot", 0, 0, "UPS.PowerConverter.Output.ForcedReboot", 0, 0, yes_no_info }, { "ups.type", ST_FLAG_STATIC, 0, "UPS.PowerConverter.ConverterType", 0, 0, mge_upstype_conversion }, /* Input page */ { "input.voltage", 0, 0, "UPS.PowerConverter.Input[1].Voltage", 0, 0, NULL }, { "input.L1-N.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[1].Voltage", 0, 0, NULL }, { "input.L2-N.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[2].Voltage", 0, 0, NULL }, { "input.L3-N.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[3].Voltage", 0, 0, NULL }, { "input.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[12].Voltage", 0, 0, NULL }, { "input.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[23].Voltage", 0, 0, NULL }, { "input.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[31].Voltage", 0, 0, NULL }, { "input.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[11].Voltage", 0, 0, convert_deci }, { "input.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[22].Voltage", 0, 0, convert_deci }, { "input.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Input[1].Phase[33].Voltage", 0, 0, convert_deci }, { "input.voltage.nominal", 0, 0, "UPS.Flow[1].ConfigVoltage", 0, 0, NULL }, { "input.current", 0, 0, "UPS.PowerConverter.Input[1].Current", 0, 0, NULL }, { "input.L1.current", 0, 0, "UPS.PowerConverter.Input[1].Phase[1].Current", 0, 0, convert_deci }, { "input.L2.current", 0, 0, "UPS.PowerConverter.Input[1].Phase[2].Current", 0, 0, convert_deci }, { "input.L3.current", 0, 0, "UPS.PowerConverter.Input[1].Phase[3].Current", 0, 0, convert_deci }, { "input.current.nominal", 0, 0, "UPS.Flow[1].ConfigCurrent", 0, 0, NULL }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input[1].Frequency", 0, 0, NULL }, { "input.frequency.nominal", 0, 0, "UPS.Flow[1].ConfigFrequency", 0, 0, NULL }, { "input.voltage.extended", 0, 0, "UPS.PowerConverter.Output.ExtendedVoltageMode", 0, 0, yes_no_info }, { "input.frequency.extended", 0, 0, "UPS.PowerConverter.Output.ExtendedFrequencyMode", 0, 0, yes_no_info }, /* same as "input.transfer.boost.low" */ { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", 0, 0, NULL }, { "input.transfer.boost.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageBoostTransfer", 0, 0, NULL }, { "input.transfer.boost.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageBoostTransfer", 0, 0, NULL }, { "input.transfer.trim.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageBuckTransfer", 0, 0, NULL }, /* same as "input.transfer.trim.high" */ { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", 0, 0, NULL }, { "input.transfer.trim.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageBuckTransfer", 0, 0, NULL }, { "input.sensitivity", 0, 0, "UPS.PowerConverter.Output.SensitivityMode", 0, 0, mge_sensitivity_info }, /* Bypass page */ { "input.bypass.voltage", 0, 0, "UPS.PowerConverter.Input[2].Voltage", 0, 0, NULL }, { "input.bypass.L1-N.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[1].Voltage", 0, 0, NULL }, { "input.bypass.L2-N.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[2].Voltage", 0, 0, NULL }, { "input.bypass.L3-N.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[3].Voltage", 0, 0, NULL }, { "input.bypass.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[12].Voltage", 0, 0, NULL }, { "input.bypass.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[23].Voltage", 0, 0, NULL }, { "input.bypass.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[31].Voltage", 0, 0, NULL }, { "input.bypass.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[11].Voltage", 0, 0, NULL }, { "input.bypass.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[22].Voltage", 0, 0, NULL }, { "input.bypass.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Input[2].Phase[33].Voltage", 0, 0, NULL }, { "input.bypass.voltage.nominal", 0, 0, "UPS.Flow[2].ConfigVoltage", 0, 0, NULL }, { "input.bypass.current", 0, 0, "UPS.PowerConverter.Input[2].Current", 0, 0, NULL }, { "input.bypass.L1.current", 0, 0, "UPS.PowerConverter.Input[2].Phase[1].Current", 0, 0, NULL }, { "input.bypass.L2.current", 0, 0, "UPS.PowerConverter.Input[2].Phase[2].Current", 0, 0, NULL }, { "input.bypass.L3.current", 0, 0, "UPS.PowerConverter.Input[2].Phase[3].Current", 0, 0, NULL }, { "input.bypass.current.nominal", 0, 0, "UPS.Flow[2].ConfigCurrent", 0, 0, NULL }, { "input.bypass.frequency", 0, 0, "UPS.PowerConverter.Input[2].Frequency", 0, 0, NULL }, { "input.bypass.frequency.nominal", 0, 0, "UPS.Flow[2].ConfigFrequency", 0, 0, NULL }, /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", 0, 0, NULL }, { "output.L1-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[1].Voltage", 0, 0, NULL }, { "output.L2-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[2].Voltage", 0, 0, NULL }, { "output.L3-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[3].Voltage", 0, 0, NULL }, { "output.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[12].Voltage", 0, 0, NULL }, { "output.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[23].Voltage", 0, 0, NULL }, { "output.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[31].Voltage", 0, 0, NULL }, { "output.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[11].Voltage", 0, 0, ignore_if_zero }, { "output.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[22].Voltage", 0, 0, ignore_if_zero }, { "output.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Output.Phase[33].Voltage", 0, 0, ignore_if_zero }, { "output.voltage.nominal", 0, 0, "UPS.Flow[4].ConfigVoltage", 0, 0, NULL }, { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", 0, 0, NULL }, { "output.L1.current", 0, 0, "UPS.PowerConverter.Output.Phase[1].Current", 0, 0, convert_deci }, { "output.L2.current", 0, 0, "UPS.PowerConverter.Output.Phase[2].Current", 0, 0, convert_deci }, { "output.L3.current", 0, 0, "UPS.PowerConverter.Output.Phase[3].Current", 0, 0, convert_deci }, { "output.current.nominal", 0, 0, "UPS.Flow[4].ConfigCurrent", 0, 0, NULL }, { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", 0, 0, NULL }, { "output.frequency.nominal", 0, 0, "UPS.Flow[4].ConfigFrequency", 0, 0, NULL }, { "output.powerfactor", 0, 0, "UPS.PowerConverter.Output.PowerFactor", 0, 0, mge_powerfactor_conversion }, /* Ambient page */ { "ambient.humidity", 0, 0, "Environment.Humidity", 0, 0, NULL }, { "ambient.humidity.high", ST_FLAG_RW, 0, "Environment.Humidity.HighThreshold", 0, 0, NULL }, { "ambient.humidity.low", ST_FLAG_RW, 0, "Environment.Humidity.LowThreshold", 0, 0, NULL }, { "ambient.humidity.maximum", 0, 0, "Environment.PresentStatus.HighHumidity", 0, 0, mge_ambient_info }, { "ambient.humidity.minimum", 0, 0, "Environment.PresentStatus.LowHumidity", 0, 0, mge_ambient_info }, { "ambient.temperature", 0, 0, "Environment.Temperature", 0, 0, NULL }, { "ambient.temperature.high", ST_FLAG_RW, 0, "Environment.Temperature.HighThreshold", 0, 0, NULL }, { "ambient.temperature.low", ST_FLAG_RW, 0, "Environment.Temperature.LowThreshold", 0, 0, NULL }, { "ambient.temperature.maximum", 0, 0, "Environment.PresentStatus.HighTemperature", 0, 0, mge_ambient_info }, { "ambient.temperature.minimum", 0, 0, "Environment.PresentStatus.LowTemperature", 0, 0, mge_ambient_info }, /* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */ { "outlet.id", 0, 0, "UPS.OutletSystem.Outlet[1].OutletID", 0, 0, NULL }, { "outlet.desc", 0, 0, "UPS.OutletSystem.Outlet[1].iName", 0, 0, NULL }, { "outlet.switchable", 0, 0, "UPS.OutletSystem.Outlet[1].PresentStatus.Switchable", 0, 0, yes_no_info }, { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet[2].OutletID", 0, 0, NULL }, { "outlet.1.desc", 0, 0, "UPS.OutletSystem.Outlet[2].iName", 0, 0, NULL }, { "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet[2].PresentStatus.Switchable", 0, 0, yes_no_info }, { "outlet.1.status", 0, 0, "UPS.OutletSystem.Outlet[2].PresentStatus.SwitchOnOff", 0, 0, on_off_info }, /* For low end models, with 1 non backup'ed outlet */ { "outlet.1.status", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", 0, 0, NULL }, /* on_off_info */ { "outlet.1.battery.charge.low", 0, 0, "UPS.OutletSystem.Outlet[2].RemainingCapacityLimit", 0, 0, NULL }, { "outlet.1.timer.start", 0, 0, "UPS.OutletSystem.Outlet[2].DelayBeforeStartup", 0, 0, NULL }, { "outlet.1.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet[2].DelayBeforeShutdown", 0, 0, NULL }, { "outlet.1.delay.start", 0, 0, "UPS.OutletSystem.Outlet[2].StartupTimer", 0, 0, NULL }, /* { "outlet.1.delay.shutdown", 0, 0, "UPS.OutletSystem.Outlet[2].ShutdownTimer", 0, 0, NULL }, */ { "outlet.1.delay.shutdown", ST_FLAG_RW, 0, "System.Outlet[2].ShutdownDuration", 0, 0, NULL }, { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet[3].OutletID", 0, 0, NULL }, { "outlet.2.desc", 0, 0, "UPS.OutletSystem.Outlet[3].iName", 0, 0, NULL }, { "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet[3].PresentStatus.Switchable", 0, 0, yes_no_info }, { "outlet.2.status", 0, 0, "UPS.OutletSystem.Outlet[3].PresentStatus.SwitchOnOff", 0, 0, on_off_info }, { "outlet.2.battery.charge.low", 0, 0, "UPS.OutletSystem.Outlet[3].RemainingCapacityLimit", 0, 0, NULL }, { "outlet.2.timer.start", 0, 0, "UPS.OutletSystem.Outlet[3].DelayBeforeStartup", 0, 0, NULL }, { "outlet.2.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet[3].DelayBeforeShutdown", 0, 0, NULL }, { "outlet.2.delay.start", 0, 0, "UPS.OutletSystem.Outlet[3].StartupTimer", 0, 0, NULL }, /* { "outlet.2.delay.shutdown", 0, 0, "UPS.OutletSystem.Outlet[3].ShutdownTimer", 0, 0, NULL }, */ { "outlet.2.delay.shutdown", ST_FLAG_RW, 0, "System.Outlet[3].ShutdownDuration", 0, 0, NULL }, /* For newer ePDU Monitored */ { "outlet.1.desc", 0, 0, "PDU.OutletSystem.Outlet[1].iName", 0, 0, NULL }, { "outlet.1.current", 0, 0, "PDU.OutletSystem.Outlet[1].Current", 0, 0, convert_deci }, /* FIXME: also map these? * "PDU.OutletSystem.Outlet[1].CurrentLimit" => settable, triggers CurrentTooHigh * "PDU.OutletSystem.Outlet[1].PresentStatus.CurrentTooHigh" (0/1) */ { "outlet.2.desc", 0, 0, "PDU.OutletSystem.Outlet[2].iName", 0, 0, NULL }, { "outlet.2.current", 0, 0, "PDU.OutletSystem.Outlet[2].Current", 0, 0, convert_deci }, { "outlet.3.desc", 0, 0, "PDU.OutletSystem.Outlet[3].iName", 0, 0, NULL }, { "outlet.3.current", 0, 0, "PDU.OutletSystem.Outlet[3].Current", 0, 0, convert_deci }, { "outlet.4.desc", 0, 0, "PDU.OutletSystem.Outlet[2].iName", 0, 0, NULL }, { "outlet.4.current", 0, 0, "PDU.OutletSystem.Outlet[2].Current", 0, 0, convert_deci }, { "outlet.5.desc", 0, 0, "PDU.OutletSystem.Outlet[3].iName", 0, 0, NULL }, { "outlet.5.current", 0, 0, "PDU.OutletSystem.Outlet[3].Current", 0, 0, convert_deci }, { "outlet.6.desc", 0, 0, "PDU.OutletSystem.Outlet[2].iName", 0, 0, NULL }, { "outlet.6.current", 0, 0, "PDU.OutletSystem.Outlet[2].Current", 0, 0, convert_deci }, { "outlet.7.desc", 0, 0, "PDU.OutletSystem.Outlet[3].iName", 0, 0, NULL }, { "outlet.7.current", 0, 0, "PDU.OutletSystem.Outlet[3].Current", 0, 0, convert_deci }, { "outlet.8.desc", 0, 0, "PDU.OutletSystem.Outlet[2].iName", 0, 0, NULL }, { "outlet.8.current", 0, 0, "PDU.OutletSystem.Outlet[2].Current", 0, 0, convert_deci }, { NULL, 0, 0, NULL, 0, 0, NULL } }; /* A start-element callback for element with given namespace/name. */ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, const char *name, const char **atts) { int state = _UNEXPECTED; switch(parent) { case ROOTPARENT: if (!strcasecmp(name, "PRODUCT_INFO")) { /* name="Network Management Card" type="Mosaic M" version="BA" */ /* name="Network Management Card" type="Transverse" version="GB (SN 49EH29101)" */ /* name="Monitored ePDU" type="Monitored ePDU" version="Version Upgrade" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "name")) { snprintf(val, sizeof(val), "%s", atts[i+1]); } if (!strcasecmp(atts[i], "type")) { snprintfcat(val, sizeof(val), "/%s", atts[i+1]); if (!strcasecmp(atts[i+1], "Transverse")) { mge_ambient_value = 1; } else if (strstr(atts[i+1], "ePDU")) { dstate_setinfo("device.type", "pdu"); } } if (!strcasecmp(atts[i], "version")) { char *s; snprintfcat(val, sizeof(val), "/%s", atts[i+1]); s = strstr(val, " (SN "); if (s) { dstate_setinfo("ups.serial", "%s", str_rtrim(s + 5, ')')); s[0] = '\0'; } dstate_setinfo("ups.firmware.aux", "%s", val); } } state = PRODUCT_INFO; break; } if (!strcasecmp(name, "SUMMARY")) { state = SUMMARY; break; } if (!strcasecmp(name, "GET_OBJECT")) { state = GET_OBJECT; break; } if (!strcasecmp(name, "SET_OBJECT")) { state = SET_OBJECT; break; } if (!strcasecmp(name, "ALARM")) { int i; var[0] = val[0] = '\0'; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "object")) { snprintf(var, sizeof(var), "%s", atts[i+1]); } if (!strcasecmp(atts[i], "value")) { snprintf(val, sizeof(var), "%s", atts[i+1]); } if (!strcasecmp(atts[i], "date")) { dstate_setinfo("ups.time", "%s", split_date_time(atts[i+1])); } } state = ALARM; break; } if (!strcasecmp(name, "XML-CLIENT")) { state = XML_CLIENT; break; } break; case PRODUCT_INFO: if (!strcasecmp(name, "SUMMARY")) { state = PI_SUMMARY; break; } if (!strcasecmp(name, "ALARMS")) { state = PI_ALARMS; break; } if (!strcasecmp(name, "MANAGEMENT")) { state = PI_MANAGEMENT; break; } if ( (!strcasecmp(name, "UPS_DATA")) || (!strcasecmp(name, "DEV_DATA")) ) { state = PI_UPS_DATA; break; } break; case PI_SUMMARY: if (!strcasecmp(name, "HTML_PROPERTIES_PAGE")) { /* url="mgeups/default.htm" */ state = PI_HTML_PROPERTIES_PAGE; break; } if (!strcasecmp(name, "XML_SUMMARY_PAGE")) { /* url="upsprop.xml" or url="ws/summary.xml" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "url")) { free(mge_xml_subdriver.summary); mge_xml_subdriver.summary = strdup(url_convert(atts[i+1])); } } state = PI_XML_SUMMARY_PAGE; break; } if (!strcasecmp(name, "CENTRAL_CFG")) { /* url="config.xml" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "url")) { free(mge_xml_subdriver.configure); mge_xml_subdriver.configure = strdup(url_convert(atts[i+1])); } } state = PI_CENTRAL_CFG; break; } if (!strcasecmp(name, "CSV_LOGS")) { /* url="logevent.csv" dateRange="no" eventFiltering="no" */ state = PI_CSV_LOGS; break; } break; case PI_ALARMS: if (!strcasecmp(name, "SUBSCRIPTION")) { /* url="subscribe.cgi" security="basic" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "url")) { free(mge_xml_subdriver.subscribe); mge_xml_subdriver.subscribe = strdup(url_convert(atts[i+1])); } } state = PI_SUBSCRIPTION; break; } if (!strcasecmp(name, "POLLING")) { /* url="mgeups/lastalarms.cgi" security="none" */ state = PI_POLLING; break; } break; case PI_MANAGEMENT: if (!strcasecmp(name, "MANAGEMENT_PAGE")) { /* name="Manager list" id="ManagerList" url="FS/FLASH0/TrapReceiverList.cfg" security="none" */ /* name="Shutdown criteria settings" id="Shutdown" url="FS/FLASH0/ShutdownParameters.cfg" security="none" */ /* name="Network settings" id="Network" url="FS/FLASH0/NetworkSettings.cfg" security="none" */ /* name="Centralized configuration settings" id="ClientCfg" url="FS/FLASH0/CentralizedConfig.cfg" security="none" */ state = PI_MANAGEMENT_PAGE; break; } if (!strcasecmp(name, "XML_MANAGEMENT_PAGE")) { /* name="Set Card Time" id="SetTime" url="management/set_time.xml" security="none" */ state = PI_XML_MANAGEMENT_PAGE; break; } break; case PI_UPS_DATA: if (!strcasecmp(name, "GET_OBJECT")) { /* url="getvalue.cgi" security="none" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "url")) { free(mge_xml_subdriver.getobject); mge_xml_subdriver.getobject = strdup(url_convert(atts[i+1])); } } state = PI_GET_OBJECT; break; } if (!strcasecmp(name, "SET_OBJECT")) { /* url="setvalue.cgi" security="ssl" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "url")) { free(mge_xml_subdriver.setobject); mge_xml_subdriver.setobject = strdup(url_convert(atts[i+1])); } } state = PI_SET_OBJECT; break; } break; case SUMMARY: if (!strcasecmp(name, "OBJECT")) { /* name="UPS.PowerSummary.iProduct" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "name")) { snprintf(var, sizeof(var), "%s", atts[i+1]); val[0] = '\0'; /*don't inherit something from another object */ } } state = SU_OBJECT; break; } break; case GET_OBJECT: if (!strcasecmp(name, "OBJECT")) { /* name="System.RunTimeToEmptyLimit" unit="s" access="RW" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "name")) { snprintf(var, sizeof(var), "%s", atts[i+1]); val[0] = '\0'; /*don't inherit something from another object */ } if (!strcasecmp(atts[i], "access")) { /* do something with RO/RW access? */ } } state = GO_OBJECT; break; } break; case XML_CLIENT: if (!strcasecmp(name, "GENERAL")) { state = XC_GENERAL; break; } case XC_GENERAL: if (!strcasecmp(name, "STARTUP")) { /* config="CENTRALIZED" */ state = XC_STARTUP; break; } if (!strcasecmp(name, "SHUTDOWN")) { /* shutdownTimer="NONE" shutdownDuration="150" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "shutdownTimer")) { dstate_setinfo("driver.timer.shutdown", "%s", atts[i+1]); } if (!strcasecmp(atts[i], "shutdownDuration")) { dstate_setinfo("driver.delay.shutdown", "%s", atts[i+1]); } } state = XC_SHUTDOWN; break; } if (!strcasecmp(name, "BROADCAST")) { /* admins="ON" users="ON" */ state = XC_BROADCAST; break; } } upsdebugx(3, "%s: name <%s> (parent = %d, state = %d)", __func__, name, parent, state); return state; } /* Character data callback; may return non-zero to abort the parse. */ static int mge_xml_cdata_cb(void *userdata, int state, const char *cdata, size_t len) { /* skip empty lines */ if ((len == 1) && (cdata[0] == '\n')) { upsdebugx(3, "%s: cdata ignored (state = %d)", __func__, state); return 0; } upsdebugx(3, "%s: cdata [%.*s] (state = %d)", __func__, (int)len, cdata, state); switch(state) { case ALARM: upsdebugx(2, "ALARM%.*s", (int)len, cdata); break; case SU_OBJECT: case GO_OBJECT: snprintfcat(val, sizeof(val), "%.*s", (int)len, cdata); break; } return 0; } /* End element callback; may return non-zero to abort the parse. */ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, const char *name) { xml_info_t *info; const char *value; /* ignore objects for which no value was set */ if (strlen(val) == 0) { upsdebugx(3, "%s: name ignored, no value set (state = %d)", __func__, name, state); return 0; } upsdebugx(3, "%s: name (state = %d)", __func__, name, state); switch(state) { case ALARM: case SU_OBJECT: case GO_OBJECT: for (info = mge_xml2nut; info->xmlname != NULL; info++) { if (strcasecmp(var, info->xmlname)) { continue; } upsdebugx(3, "-> XML variable %s [%s] maps to NUT variable %s", var, val, info->nutname); if ((info->nutflags & ST_FLAG_STATIC) && dstate_getinfo(info->nutname)) { return 0; } if (info->convert) { value = info->convert(val); } else { value = val; } if (value != NULL) { dstate_setinfo(info->nutname, "%s", value); } return 0; } upsdebugx(3, "-> XML variable %s [%s] doesn't map to any NUT variable", var, val); break; } return 0; } subdriver_t mge_xml_subdriver = { MGE_XML_VERSION, MGE_XML_INITUPS, MGE_XML_INITINFO, NULL, NULL, NULL, NULL, NULL, mge_xml_startelm_cb, mge_xml_cdata_cb, mge_xml_endelm_cb, }; const char *vname_nut2mge_xml(const char *name) { assert(NULL != name); size_t i = 0; for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; if (NULL != info->nutname) if (0 == strcasecmp(name, info->nutname)) return info->xmlname; } return NULL; } const char *vname_mge_xml2nut(const char *name) { assert(NULL != name); size_t i = 0; for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; if (NULL != info->xmlname) if (0 == strcasecmp(name, info->xmlname)) return info->nutname; } return NULL; } char *vvalue_mge_xml2nut(const char *name, const char *value, size_t len) { assert(NULL != name); size_t i = 0; for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; if (NULL != info->nutname) if (0 == strcasecmp(name, info->nutname)) { /* Copy value */ char *vcpy = (char *)malloc((len + 1) * sizeof(char)); if (NULL == vcpy) return vcpy; memcpy(vcpy, value, len * sizeof(char)); vcpy[len] = '\0'; /* Convert */ if (NULL != info->convert) { char *vconv = (char *)info->convert(vcpy); free(vcpy); return vconv; } else return vcpy; } } return NULL; } void vname_register_rw(void) { size_t i = 0; for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; if (NULL != info->nutname && info->nutflags & ST_FLAG_RW) { dstate_setinfo(info->nutname, "%s", ""); dstate_setflags(info->nutname, ST_FLAG_RW); } } } nut-2.7.4/drivers/libusb.h0000644000175000017500000000524012640473702012370 00000000000000/*! * @file libusb.h * @brief HID Library - Generic USB backend for Generic HID Access (using MGE HIDParser) * * @author Copyright (C) * 2003 - 2006 Arnaud Quette * 2005 Peter Selinger * * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com * * The logic of this file is ripped from mge-shut driver (also from * Arnaud Quette), which is a "HID over serial link" UPS driver for * Network UPS Tools * * 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. * * -------------------------------------------------------------------------- */ #ifndef LIBUSB_H #define LIBUSB_H #include "main.h" /* for subdrv_info_t */ #include "usb-common.h" /* for USBDevice_t and USBDeviceMatcher_t */ #include /* libusb header file */ extern upsdrv_info_t comm_upsdrv_info; /*! * usb_communication_subdriver_s: structure to describe the communication routines * @name: can be either "shut" for Serial HID UPS Transfer (from MGE) or "usb" */ typedef struct usb_communication_subdriver_s { const char *name; /* name of this subdriver */ const char *version; /* version of this subdriver */ int (*open)(usb_dev_handle **sdevp, /* try to open the next available */ USBDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, unsigned char *rdbuf, int rdlen)); void (*close)(usb_dev_handle *sdev); int (*get_report)(usb_dev_handle *sdev, int ReportId, unsigned char *raw_buf, int ReportSize ); int (*set_report)(usb_dev_handle *sdev, int ReportId, unsigned char *raw_buf, int ReportSize ); int (*get_string)(usb_dev_handle *sdev, int StringIdx, char *buf, size_t buflen); int (*get_interrupt)(usb_dev_handle *sdev, unsigned char *buf, int bufsize, int timeout); } usb_communication_subdriver_t; extern usb_communication_subdriver_t usb_subdriver; #endif /* LIBUSB_H */ nut-2.7.4/drivers/apcsmart-old.c0000644000175000017500000010326612640443572013502 00000000000000/* apcsmart.c - driver for APC smart protocol units (originally "newapc") Copyright (C) 1999 Russell Kroll (C) 2000 Nigel Metheringham 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 "main.h" #include "serial.h" #include "apcsmart-old.h" #define DRIVER_NAME "APC Smart protocol driver" #define DRIVER_VERSION "2.1" static upsdrv_info_t table_info = { "APC command table", APC_TABLE_VERSION, NULL, 0, { NULL } }; /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" "Nigel Metheringham \n" "Michal Soltys ", DRV_STABLE, { &table_info, NULL } }; #define ALT_CABLE_1 "940-0095B" static int ups_status = 0, quirk_capability_overflow = 0; static apc_vartab_t *vartab_lookup_char(char cmdchar) { int i; for (i = 0; apc_vartab[i].name != NULL; i++) if (apc_vartab[i].cmd == cmdchar) return &apc_vartab[i]; return NULL; } static apc_vartab_t *vartab_lookup_name(const char *var) { int i; for (i = 0; apc_vartab[i].name != NULL; i++) if (!strcasecmp(apc_vartab[i].name, var)) return &apc_vartab[i]; return NULL; } /* FUTURE: change to use function pointers */ /* convert APC formatting to NUT formatting */ static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) { static char tmp[128]; int tval; switch(cmd_entry->flags & APC_FORMATMASK) { case APC_F_PERCENT: case APC_F_VOLT: case APC_F_AMP: case APC_F_CELSIUS: case APC_F_HEX: case APC_F_DEC: case APC_F_SECONDS: case APC_F_LEAVE: /* no conversion for any of these */ return upsval; case APC_F_HOURS: /* convert to seconds */ tval = 60 * 60 * strtol(upsval, NULL, 10); snprintf(tmp, sizeof(tmp), "%d", tval); return tmp; case APC_F_MINUTES: /* Convert to seconds - NUT standard time measurement */ tval = 60 * strtol(upsval, NULL, 10); /* Ignore errors - Theres not much we can do */ snprintf(tmp, sizeof(tmp), "%d", tval); return tmp; case APC_F_REASON: switch (upsval[0]) { case 'R': return "unacceptable utility voltage rate of change"; case 'H': return "high utility voltage"; case 'L': return "low utility voltage"; case 'T': return "line voltage notch or spike"; case 'O': return "no transfers yet since turnon"; case 'S': return "simulated power failure or UPS test"; default: return upsval; } } upslogx(LOG_NOTICE, "Unable to handle conversion of %s", cmd_entry->name); return upsval; } static void ups_status_set(void) { status_init(); if (ups_status & APC_STAT_CAL) status_set("CAL"); /* calibration */ if (ups_status & APC_STAT_TRIM) status_set("TRIM"); /* SmartTrim */ if (ups_status & APC_STAT_BOOST) status_set("BOOST"); /* SmartBoost */ if (ups_status & APC_STAT_OL) status_set("OL"); /* on line */ if (ups_status & APC_STAT_OB) status_set("OB"); /* on battery */ if (ups_status & APC_STAT_OVER) status_set("OVER"); /* overload */ if (ups_status & APC_STAT_LB) status_set("LB"); /* low battery */ if (ups_status & APC_STAT_RB) status_set("RB"); /* replace batt */ if (ups_status == 0) status_set("OFF"); status_commit(); } static void alert_handler(char ch) { switch (ch) { case '!': /* clear OL, set OB */ upsdebugx(4, "alert_handler: OB"); ups_status &= ~APC_STAT_OL; ups_status |= APC_STAT_OB; break; case '$': /* clear OB, set OL */ upsdebugx(4, "alert_handler: OL"); ups_status &= ~APC_STAT_OB; ups_status |= APC_STAT_OL; break; case '%': /* set LB */ upsdebugx(4, "alert_handler: LB"); ups_status |= APC_STAT_LB; break; case '+': /* clear LB */ upsdebugx(4, "alert_handler: not LB"); ups_status &= ~APC_STAT_LB; break; case '#': /* set RB */ upsdebugx(4, "alert_handler: RB"); ups_status |= APC_STAT_RB; break; case '?': /* set OVER */ upsdebugx(4, "alert_handler: OVER"); ups_status |= APC_STAT_OVER; break; case '=': /* clear OVER */ upsdebugx(4, "alert_handler: not OVER"); ups_status &= ~APC_STAT_OVER; break; default: upsdebugx(4, "alert_handler got 0x%02x (unhandled)", ch); break; } ups_status_set(); } static int read_buf(char *buf, size_t buflen) { int ret; ret = ser_get_line_alert(upsfd, buf, buflen, ENDCHAR, POLL_IGNORE, POLL_ALERT, alert_handler, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) { ser_comm_fail("%s", ret ? strerror(errno) : "timeout"); return ret; } ser_comm_good(); return ret; } static int poll_data(apc_vartab_t *vt) { int ret; char tmp[SMALLBUF]; if ((vt->flags & APC_PRESENT) == 0) return 1; upsdebugx(4, "poll_data: %s", vt->name); ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { upslogx(LOG_ERR, "poll_data: ser_send_char failed"); dstate_datastale(); return 0; } if (read_buf(tmp, sizeof(tmp)) < 1) { dstate_datastale(); return 0; } /* no longer supported by the hardware somehow */ if (!strcmp(tmp, "NA")) { dstate_delinfo(vt->name); return 1; } dstate_setinfo(vt->name, "%s", convert_data(vt, tmp)); dstate_dataok(); return 1; } /* check for support or just update a named variable */ static int query_ups(const char *var, int first) { int ret; char temp[256]; const char *ptr; apc_vartab_t *vt; vt = vartab_lookup_name(var); if (!vt) { upsdebugx(1, "query_ups: unknown variable %s", var); return 0; } /* * not first run and already known to not be supported ? */ if (!first && !(vt->flags & APC_PRESENT)) return 0; /* empty the input buffer (while allowing the alert handler to run) */ ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, POLL_IGNORE, POLL_ALERT, alert_handler, 0, 0); ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { upslog_with_errno(LOG_ERR, "query_ups: ser_send_char failed"); return 0; } ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, POLL_IGNORE, POLL_ALERT, alert_handler, SER_WAIT_SEC, SER_WAIT_USEC); if ((ret < 1) && (first == 0)) { ser_comm_fail("%s", ret ? strerror(errno) : "timeout"); return 0; } ser_comm_good(); if ((ret < 1) || (!strcmp(temp, "NA"))) /* not supported */ return 0; vt->flags |= APC_PRESENT; ptr = convert_data(vt, temp); dstate_setinfo(vt->name, "%s", ptr); return 1; /* success */ } static void do_capabilities(void) { const char *ptr, *entptr; char upsloc, temp[512], cmd, loc, etmp[16], *endtemp; int nument, entlen, i, matrix, ret, valid; apc_vartab_t *vt; upsdebugx(1, "APC - About to get capabilities string"); /* If we can do caps, then we need the Firmware revision which has the locale descriptor as the last character (ugh) */ ptr = dstate_getinfo("ups.firmware"); if (ptr) upsloc = ptr[strlen(ptr) - 1]; else upsloc = 0; /* get capability string */ ret = ser_send_char(upsfd, APC_CAPABILITY); /* ^Z */ if (ret != 1) { upslog_with_errno(LOG_ERR, "do_capabilities: ser_send_char failed"); return; } /* note different IGN set since ^Z returns things like # */ ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, MINIGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if ((ret < 1) || (!strcmp(temp, "NA"))) { /* Early Smart-UPS, not as smart as later ones */ /* This should never happen since we only call this if the REQ_CAPABILITIES command is supported */ upslogx(LOG_ERR, "ERROR: APC cannot do capabilities but said it could!"); return; } /* recv always puts a \0 at the end, so this is safe */ /* however it assumes a zero byte cannot be embedded */ endtemp = &temp[0] + strlen(temp); if (temp[0] != '#') { upsdebugx(1, "Unrecognized capability start char %c", temp[0]); upsdebugx(1, "Please report this error [%s]", temp); upslogx(LOG_ERR, "ERROR: unknown capability start char %c!", temp[0]); return; } if (temp[1] == '#') { /* Matrix-UPS */ matrix = 1; ptr = &temp[0]; } else { ptr = &temp[1]; matrix = 0; } /* command char, location, # of entries, entry length */ while (ptr[0] != '\0') { if (matrix) ptr += 2; /* jump over repeating ## */ /* check for idiocy */ if (ptr >= endtemp) { /* if we expected this, just ignore it */ if (quirk_capability_overflow) return; fatalx(EXIT_FAILURE, "Capability string has overflowed\n" "Please report this error\n" "ERROR: capability overflow!" ); } cmd = ptr[0]; loc = ptr[1]; nument = ptr[2] - 48; entlen = ptr[3] - 48; entptr = &ptr[4]; vt = vartab_lookup_char(cmd); valid = vt && ((loc == upsloc) || (loc == '4')); /* mark this as writable */ if (valid) { upsdebugx(1, "Supported capability: %02x (%c) - %s", cmd, loc, vt->name); dstate_setflags(vt->name, ST_FLAG_RW); /* make sure setvar knows what this is */ vt->flags |= APC_RW | APC_ENUM; } for (i = 0; i < nument; i++) { if (valid) { snprintf(etmp, entlen + 1, "%s", entptr); dstate_addenum(vt->name, "%s", convert_data(vt, etmp)); } entptr += entlen; } ptr = entptr; } } static int update_status(void) { int ret; char buf[SMALLBUF]; upsdebugx(4, "update_status"); ser_flush_in(upsfd, IGNCHARS, nut_debug_level); ret = ser_send_char(upsfd, APC_STATUS); if (ret != 1) { upslog_with_errno(LOG_ERR, "update_status: ser_send_char failed"); dstate_datastale(); return 0; } ret = read_buf(buf, sizeof(buf)); if ((ret < 1) || (!strcmp(buf, "NA"))) { dstate_datastale(); return 0; } ups_status = strtol(buf, 0, 16) & 0xff; ups_status_set(); dstate_dataok(); return 1; } static void oldapcsetup(void) { int ret = 0; /* really old models ignore REQ_MODEL, so find them first */ ret = query_ups("ups.model", 1); if (ret != 1) { /* force the model name */ dstate_setinfo("ups.model", "Smart-UPS"); } /* see if this might be an old Matrix-UPS instead */ if (query_ups("output.current", 1)) dstate_setinfo("ups.model", "Matrix-UPS"); query_ups("ups.serial", 1); query_ups("input.voltage", 1); /* This one may fail... no problem */ update_status(); /* If we have come down this path then we dont do capabilities and other shiny features */ } static void protocol_verify(unsigned char cmd) { int i, found; /* see if it's a variable */ for (i = 0; apc_vartab[i].name != NULL; i++) { /* 1:1 here, so the first match is the only match */ if (apc_vartab[i].cmd == cmd) { upsdebugx(3, "UPS supports variable [%s]", apc_vartab[i].name); /* load initial data */ apc_vartab[i].flags |= APC_PRESENT; poll_data(&apc_vartab[i]); /* handle special data for our two strings */ if (apc_vartab[i].flags & APC_STRING) { dstate_setflags(apc_vartab[i].name, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(apc_vartab[i].name, APC_STRLEN); apc_vartab[i].flags |= APC_RW; } return; } } /* check the command list */ /* some cmdchars map onto multiple commands (start and stop) */ found = 0; for (i = 0; apc_cmdtab[i].name != NULL; i++) { if (apc_cmdtab[i].cmd == cmd) { upsdebugx(2, "UPS supports command [%s]", apc_cmdtab[i].name); dstate_addcmd(apc_cmdtab[i].name); apc_cmdtab[i].flags |= APC_PRESENT; found = 1; } } if (found) return; if (isprint(cmd)) upsdebugx(1, "protocol_verify: 0x%02x [%c] unrecognized", cmd, cmd); else upsdebugx(1, "protocol_verify: 0x%02x unrecognized", cmd); } /* some hardware is a special case - hotwire the list of cmdchars */ static int firmware_table_lookup(void) { int ret; unsigned int i, j; char buf[SMALLBUF]; upsdebugx(1, "Attempting firmware lookup using command 'V'"); ret = ser_send_char(upsfd, 'V'); if (ret != 1) { upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_send_char failed"); return 0; } ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); /* * Some UPSes support both 'V' and 'b'. As 'b' doesn't always return * firmware version, we attempt that only if 'V' doesn't work. */ if ((ret < 1) || (!strcmp(buf, "NA"))) { upsdebugx(1, "Attempting firmware lookup using command 'b'"); ret = ser_send_char(upsfd, 'b'); if (ret != 1) { upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_send_char failed"); return 0; } ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) { upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_get_line failed"); return 0; } } upsdebugx(2, "Firmware: [%s]", buf); /* this will be reworked if we get a lot of these things */ if (!strcmp(buf, "451.2.I")) { quirk_capability_overflow = 1; return 0; } for (i = 0; compat_tab[i].firmware != NULL; i++) { if (!strcmp(compat_tab[i].firmware, buf)) { upsdebugx(2, "Matched - cmdchars: %s", compat_tab[i].cmdchars); if (strspn(compat_tab[i].firmware, "05")) { dstate_setinfo("ups.model", "Matrix-UPS"); } else { dstate_setinfo("ups.model", "Smart-UPS"); } /* matched - run the cmdchars from the table */ for (j = 0; j < strlen(compat_tab[i].cmdchars); j++) protocol_verify(compat_tab[i].cmdchars[j]); return 1; /* matched */ } } upsdebugx(2, "Not found in table - trying normal method"); return 0; } static void getbaseinfo(void) { unsigned int i; int ret = 0; char *alrts, *cmds, temp[512]; /* * try firmware lookup first; we could start with 'a', but older models * sometimes return other things than a command set */ if (firmware_table_lookup() == 1) return; upsdebugx(1, "APC - Attempting to find command set"); /* Initially we ask the UPS what commands it takes If this fails we are going to need an alternate strategy - we can deal with that if it happens */ ret = ser_send_char(upsfd, APC_CMDSET); if (ret != 1) { upslog_with_errno(LOG_ERR, "getbaseinfo: ser_send_char failed"); return; } ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if ((ret < 1) || (!strcmp(temp, "NA"))) { /* We have an old dumb UPS - go to specific code for old stuff */ oldapcsetup(); return; } upsdebugx(1, "APC - Parsing out command set"); /* We have the version.alert.cmdchars string NB the alert chars are normally in IGNCHARS so will have been pretty much edited out. You will need to change the ser_get_line above if you want to check those out too.... */ alrts = strchr(temp, '.'); if (alrts == NULL) { fatalx(EXIT_FAILURE, "Unable to split APC version string"); } *alrts++ = 0; cmds = strchr(alrts, '.'); if (cmds == NULL) { fatalx(EXIT_FAILURE, "Unable to find APC command string"); } *cmds++ = 0; for (i = 0; i < strlen(cmds); i++) protocol_verify(cmds[i]); /* if capabilities are supported, add them here */ if (strchr(cmds, APC_CAPABILITY)) do_capabilities(); upsdebugx(1, "APC - UPS capabilities determined"); } /* check for calibration status and either start or stop */ static int do_cal(int start) { char temp[256]; int tval, ret; ret = ser_send_char(upsfd, APC_STATUS); if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cal: ser_send_char failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = read_buf(temp, sizeof(temp)); /* if we can't check the current calibration status, bail out */ if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ tval = strtol(temp, 0, 16); if (tval & APC_STAT_CAL) { /* calibration currently happening */ if (start == 1) { /* requested start while calibration still running */ upslogx(LOG_INFO, "Runtime calibration already in progress"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } /* stop requested */ upslogx(LOG_INFO, "Stopping runtime calibration"); ret = ser_send_char(upsfd, APC_CMD_CALTOGGLE); if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cal: ser_send_char failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = read_buf(temp, sizeof(temp)); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { upslogx(LOG_WARNING, "Stop calibration failed: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } return STAT_INSTCMD_HANDLED; /* FUTURE: success */ } /* calibration not happening */ if (start == 0) { /* stop requested */ upslogx(LOG_INFO, "Runtime calibration not occurring"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } upslogx(LOG_INFO, "Starting runtime calibration"); ret = ser_send_char(upsfd, APC_CMD_CALTOGGLE); if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cal: ser_send_char failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = read_buf(temp, sizeof(temp)); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { upslogx(LOG_WARNING, "Start calibration failed: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } return STAT_INSTCMD_HANDLED; /* FUTURE: success */ } /* get the UPS talking to us in smart mode */ static int smartmode(void) { int ret, tries; char temp[256]; for (tries = 0; tries < 5; tries++) { ret = ser_send_char(upsfd, APC_GOSMART); if (ret != 1) { upslog_with_errno(LOG_ERR, "smartmode: ser_send_char failed"); return 0; } ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if (ret > 0) if (!strcmp(temp, "SM")) return 1; /* success */ sleep(1); /* wait before trying again */ /* it failed, so try to bail out of menus on newer units */ ret = ser_send_char(upsfd, 27); /* ESC */ if (ret != 1) { upslog_with_errno(LOG_ERR, "smartmode: ser_send_char failed"); return 0; } /* eat the response (might be NA, might be something else) */ ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); } return 0; /* failure */ } /* * all shutdown commands should respond with 'OK' or '*' */ static int sdok(void) { char temp[16]; ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); upsdebugx(4, "sdok: got \"%s\"", temp); if (!strcmp(temp, "*") || !strcmp(temp, "OK")) { upsdebugx(4, "Last issued shutdown command succeeded"); return 1; } upsdebugx(1, "Last issued shutdown command failed"); return 0; } /* soft hibernate: S - working only when OB, otherwise ignored */ static int sdcmd_S(int dummy) { ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing soft hibernate"); ser_send_char(upsfd, APC_CMD_SOFTDOWN); return sdok(); } /* soft hibernate, hack version for CS 350 */ static int sdcmd_CS(int tval) { upsdebugx(1, "Using CS 350 'force OB' shutdown method"); if (tval & APC_STAT_OL) { upsdebugx(1, "On-line - forcing OB temporarily"); ser_send_char(upsfd, 'U'); usleep(UPSDELAY); } return sdcmd_S(tval); } /* * hard hibernate: @nnn / @nn * note: works differently for older and new models, see help function for * detailed info */ static int sdcmd_ATn(int cnt) { int n = 0, mmax, ret; const char *strval; char timer[4]; mmax = cnt == 2 ? 99 : 999; if ((strval = getval("wugrace"))) { errno = 0; n = strtol(strval, NULL, 10); if (errno || n < 0 || n > mmax) n = 0; } snprintf(timer, sizeof(timer), "%.*d", cnt, n); ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing hard hibernate with %d minutes additional wakeup delay", n*6); ser_send_char(upsfd, APC_CMD_GRACEDOWN); usleep(CMDLONGDELAY); ser_send_pace(upsfd, UPSDELAY, "%s", timer); ret = sdok(); if (ret || cnt == 3) return ret; /* * "tricky" part - we tried @nn variation and it (unsurprisingly) * failed; we have to abort the sequence with something bogus to have * the clean state; newer upses will respond with 'NO', older will be * silent (YMMV); */ ser_send_char(upsfd, APC_CMD_GRACEDOWN); usleep(UPSDELAY); ser_flush_in(upsfd, IGNCHARS, nut_debug_level); return 0; } /* shutdown: K - delayed poweroff */ static int sdcmd_K(int dummy) { ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing delayed poweroff"); ser_send_char(upsfd, APC_CMD_SHUTDOWN); usleep(CMDLONGDELAY); ser_send_char(upsfd, APC_CMD_SHUTDOWN); return sdok(); } /* shutdown: Z - immediate poweroff */ static int sdcmd_Z(int dummy) { ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing immediate poweroff"); ser_send_char(upsfd, APC_CMD_OFF); usleep(CMDLONGDELAY); ser_send_char(upsfd, APC_CMD_OFF); return sdok(); } static int (*sdlist[])(int) = { sdcmd_S, sdcmd_ATn, /* for @nnn version */ sdcmd_K, sdcmd_Z, sdcmd_CS, sdcmd_ATn, /* for @nn version */ }; #define SDIDX_S 0 #define SDIDX_AT3N 1 #define SDIDX_K 2 #define SDIDX_Z 3 #define SDIDX_CS 4 #define SDIDX_AT2N 5 #define SDCNT 6 static void upsdrv_shutdown_simple(int status) { unsigned int sdtype = 0; char *strval; if ((strval = getval("sdtype"))) { errno = 0; sdtype = strtol(strval, NULL, 10); if (errno || sdtype < 0 || sdtype > 6) sdtype = 0; } switch (sdtype) { case 6: /* hard hibernate */ sdcmd_ATn(3); break; case 5: /* "hack nn" hard hibernate */ sdcmd_ATn(2); break; case 4: /* special hack for CS 350 and similar models */ sdcmd_CS(status); break; case 3: /* delayed poweroff */ sdcmd_K(0); break; case 2: /* instant poweroff */ sdcmd_Z(0); break; case 1: /* * Send a combined set of shutdown commands which can work * better if the UPS gets power during shutdown process * Specifically it sends both the soft shutdown 'S' and the * hard hibernate '@nnn' commands */ upsdebugx(1, "UPS - currently %s - sending soft/hard hibernate commands", (status & APC_STAT_OL) ? "on-line" : "on battery"); /* S works only when OB */ if ((status & APC_STAT_OB) && sdcmd_S(0)) break; sdcmd_ATn(3); break; default: /* * Send @nnn or S, depending on OB / OL status */ if (status & APC_STAT_OL) /* on line */ sdcmd_ATn(3); else sdcmd_S(0); } } static void upsdrv_shutdown_advanced(int status) { const char *strval; const char deforder[] = {48 + SDIDX_S, 48 + SDIDX_AT3N, 48 + SDIDX_K, 48 + SDIDX_Z, 0}; size_t i; int n; strval = getval("advorder"); /* sanitize advorder */ if (!strval || !strlen(strval) || strlen(strval) > SDCNT) strval = deforder; for (i = 0; i < strlen(strval); i++) { if (strval[i] - 48 < 0 || strval[i] - 48 >= SDCNT) { strval = deforder; break; } } /* * try each method in the list with a little bit of handling in certain * cases */ for (i = 0; i < strlen(strval); i++) { switch (strval[i] - 48) { case SDIDX_CS: n = status; break; case SDIDX_AT3N: n = 3; break; case SDIDX_AT2N: default: n = 2; } if (sdlist[strval[i] - 48](n)) break; /* finish if command succeeded */ } } /* power down the attached load immediately */ void upsdrv_shutdown(void) { char temp[32]; int ret, status; if (!smartmode()) upsdebugx(1, "SM detection failed. Trying a shutdown command anyway"); /* check the line status */ ret = ser_send_char(upsfd, APC_STATUS); if (ret == 1) { ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) { upsdebugx(1, "Status read failed ! Assuming on battery state"); status = APC_STAT_LB | APC_STAT_OB; } else { status = strtol(temp, 0, 16); } } else { upsdebugx(1, "Status request failed; assuming on battery state"); status = APC_STAT_LB | APC_STAT_OB; } if (testvar("advorder") && strcasecmp(getval("advorder"), "no")) upsdrv_shutdown_advanced(status); else upsdrv_shutdown_simple(status); } /* 940-0095B support: set DTR, lower RTS */ static void init_serial_0095B(void) { ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); } static void update_info_normal(void) { int i; upsdebugx(3, "update_info_normal: starting"); for (i = 0; apc_vartab[i].name != NULL; i++) { if ((apc_vartab[i].flags & APC_POLL) == 0) continue; if (!poll_data(&apc_vartab[i])) { upsdebugx(3, "update_info_normal: poll_data (%s) failed - " "aborting scan", apc_vartab[i].name); return; } } upsdebugx(3, "update_info_normal: done"); } static void update_info_all(void) { int i; upsdebugx(3, "update_info_all: starting"); for (i = 0; apc_vartab[i].name != NULL; i++) { if (!poll_data(&apc_vartab[i])) { upsdebugx(3, "update_info_all: poll_data (%s) failed - " "aborting scan", apc_vartab[i].name); return; } } upsdebugx(3, "update_info_all: done"); } static int setvar_enum(apc_vartab_t *vt, const char *val) { int i, ret; char orig[256], temp[256]; const char *ptr; ser_flush_in(upsfd, IGNCHARS, nut_debug_level); ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_enum: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } ret = read_buf(orig, sizeof(orig)); if ((ret < 1) || (!strcmp(orig, "NA"))) return STAT_SET_HANDLED; /* FUTURE: failed */ ptr = convert_data(vt, orig); /* suppress redundant changes - easier on the eeprom */ if (!strcmp(ptr, val)) { upslogx(LOG_INFO, "Ignoring enum SET %s='%s' (unchanged value)", vt->name, val); return STAT_SET_HANDLED; /* FUTURE: no change */ } for (i = 0; i < 6; i++) { ret = ser_send_char(upsfd, APC_NEXTVAL); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_enum: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } /* this should return either OK (if rotated) or NO (if not) */ ret = read_buf(temp, sizeof(temp)); if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_SET_HANDLED; /* FUTURE: failed */ /* sanity checks */ if (!strcmp(temp, "NO")) return STAT_SET_HANDLED; /* FUTURE: failed */ if (strcmp(temp, "OK") != 0) return STAT_SET_HANDLED; /* FUTURE: failed */ /* see what it rotated onto */ ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_enum: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } ret = read_buf(temp, sizeof(temp)); if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_SET_HANDLED; /* FUTURE: failed */ ptr = convert_data(vt, temp); upsdebugx(1, "Rotate value: got [%s], want [%s]", ptr, val); if (!strcmp(ptr, val)) { /* got it */ upslogx(LOG_INFO, "SET %s='%s'", vt->name, val); /* refresh data from the hardware */ query_ups(vt->name, 0); return STAT_SET_HANDLED; /* FUTURE: success */ } /* check for wraparound */ if (!strcmp(ptr, orig)) { upslogx(LOG_ERR, "setvar: variable %s wrapped", vt->name); return STAT_SET_HANDLED; /* FUTURE: failed */ } } upslogx(LOG_ERR, "setvar: gave up after 6 tries for %s", vt->name); /* refresh data from the hardware */ query_ups(vt->name, 0); return STAT_SET_HANDLED; } static int setvar_string(apc_vartab_t *vt, const char *val) { unsigned int i; int ret; char temp[256]; ser_flush_in(upsfd, IGNCHARS, nut_debug_level); ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_string: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } ret = read_buf(temp, sizeof(temp)); if ((ret < 1) || (!strcmp(temp, "NA"))) return STAT_SET_HANDLED; /* FUTURE: failed */ /* suppress redundant changes - easier on the eeprom */ if (!strcmp(temp, val)) { upslogx(LOG_INFO, "Ignoring string SET %s='%s' (unchanged value)", vt->name, val); return STAT_SET_HANDLED; /* FUTURE: no change */ } ret = ser_send_char(upsfd, APC_NEXTVAL); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_string: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } usleep(UPSDELAY); for (i = 0; i < strlen(val); i++) { ret = ser_send_char(upsfd, val[i]); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_string: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } usleep(UPSDELAY); } /* pad to 8 chars with CRs */ for (i = strlen(val); i < APC_STRLEN; i++) { ret = ser_send_char(upsfd, 13); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_string: ser_send_char failed"); return STAT_SET_HANDLED; /* FUTURE: failed */ } usleep(UPSDELAY); } ret = read_buf(temp, sizeof(temp)); if (ret < 1) { upslogx(LOG_ERR, "setvar_string: short final read"); return STAT_SET_HANDLED; /* FUTURE: failed */ } if (!strcmp(temp, "NO")) { upslogx(LOG_ERR, "setvar_string: got NO at final read"); return STAT_SET_HANDLED; /* FUTURE: failed */ } /* refresh data from the hardware */ query_ups(vt->name, 0); upslogx(LOG_INFO, "SET %s='%s'", vt->name, val); return STAT_SET_HANDLED; /* FUTURE: failed */ } static int setvar(const char *varname, const char *val) { apc_vartab_t *vt; vt = vartab_lookup_name(varname); if (!vt) return STAT_SET_UNKNOWN; if ((vt->flags & APC_RW) == 0) { upslogx(LOG_WARNING, "setvar: [%s] is not writable", varname); return STAT_SET_UNKNOWN; } if (vt->flags & APC_ENUM) return setvar_enum(vt, val); if (vt->flags & APC_STRING) return setvar_string(vt, val); upslogx(LOG_WARNING, "setvar: Unknown type for [%s]", varname); return STAT_SET_UNKNOWN; } /* actually send the instcmd's char to the ups */ static int do_cmd(apc_cmdtab_t *ct) { int ret; char buf[SMALLBUF]; ser_flush_in(upsfd, IGNCHARS, nut_debug_level); ret = ser_send_char(upsfd, ct->cmd); if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cmd: ser_send_char failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ } /* some commands have to be sent twice with a 1.5s gap */ if (ct->flags & APC_REPEAT) { usleep(CMDLONGDELAY); ret = ser_send_char(upsfd, ct->cmd); if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cmd: ser_send_char failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ } } ret = read_buf(buf, sizeof(buf)); if (ret < 1) return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ if (strcmp(buf, "OK") != 0) { upslogx(LOG_WARNING, "Got [%s] after command [%s]", buf, ct->name); return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ } upslogx(LOG_INFO, "Command: %s", ct->name); return STAT_INSTCMD_HANDLED; /* FUTURE: success */ } /* some commands must be repeated in a window to execute */ static int instcmd_chktime(apc_cmdtab_t *ct) { double elapsed; time_t now; static time_t last = 0; time(&now); elapsed = difftime(now, last); last = now; /* you have to hit this in a small window or it fails */ if ((elapsed < MINCMDTIME) || (elapsed > MAXCMDTIME)) { upsdebugx(1, "instcmd_chktime: outside window for %s (%2.0f)", ct->name, elapsed); return STAT_INSTCMD_HANDLED; /* FUTURE: again */ } return do_cmd(ct); } static int instcmd(const char *cmdname, const char *extra) { int i; apc_cmdtab_t *ct; ct = NULL; for (i = 0; apc_cmdtab[i].name != NULL; i++) if (!strcasecmp(apc_cmdtab[i].name, cmdname)) ct = &apc_cmdtab[i]; if (!ct) { upslogx(LOG_WARNING, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } if ((ct->flags & APC_PRESENT) == 0) { upslogx(LOG_WARNING, "instcmd: command [%s] is not supported", cmdname); return STAT_INSTCMD_UNKNOWN; } if (!strcasecmp(cmdname, "calibrate.start")) return do_cal(1); if (!strcasecmp(cmdname, "calibrate.stop")) return do_cal(0); if (ct->flags & APC_NASTY) return instcmd_chktime(ct); /* nothing special here */ return do_cmd(ct); } /* install pointers to functions for msg handlers called from msgparse */ static void setuphandlers(void) { upsh.setvar = setvar; upsh.instcmd = instcmd; } /* functions that interface with main.c */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "cable", "Specify alternate cable (940-0095B)"); addvar(VAR_VALUE, "wugrace", "Hard hibernate's wakeup grace"); addvar(VAR_VALUE, "sdtype", "Specify simple shutdown method (0-6)"); addvar(VAR_VALUE, "advorder", "Enable advanced shutdown control"); } void upsdrv_initups(void) { char *cable; upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); cable = getval("cable"); if (cable && !strcasecmp(cable, ALT_CABLE_1)) init_serial_0095B(); /* make sure we wake up if the UPS sends alert chars to us */ extrafd = upsfd; } void upsdrv_help(void) { } void upsdrv_initinfo(void) { const char *pmod, *pser; if (!smartmode()) { fatalx(EXIT_FAILURE, "Unable to detect an APC Smart protocol UPS on port %s\n" "Check the cabling, port name or model name and try again", device_path ); } /* manufacturer ID - hardcoded in this particular module */ dstate_setinfo("ups.mfr", "APC"); getbaseinfo(); if (!(pmod = dstate_getinfo("ups.model"))) pmod = "\"unknown model\""; if (!(pser = dstate_getinfo("ups.serial"))) pser = "unknown serial"; upsdebugx(1, "Detected %s [%s] on %s", pmod, pser, device_path); setuphandlers(); } void upsdrv_updateinfo(void) { static time_t last_full = 0; time_t now; /* try to wake up a dead ups once in awhile */ if ((dstate_is_stale()) && (!smartmode())) { ser_comm_fail("Communications with UPS lost - check cabling"); /* reset this so a full update runs when the UPS returns */ last_full = 0; return; } ser_comm_good(); if (!update_status()) return; time(&now); /* refresh all variables hourly */ /* does not catch measure-ups II insertion/removal */ if (difftime(now, last_full) > 3600) { last_full = now; update_info_all(); return; } update_info_normal(); } void upsdrv_cleanup(void) { /* try to bring the UPS out of smart mode */ ser_send_char(upsfd, APC_GODUMB); ser_close(upsfd, device_path); } nut-2.7.4/drivers/microdowell.h0000644000175000017500000004324312640443572013437 00000000000000#ifndef MICRODOWELL_H #define MICRODOWELL_H #ifdef ENTERPRISE_PROTOCOL #include #define STX_CHAR '[' #define ERR_COM_NO_CHARS -999 // nessun carattere dalla porta seriale #define ERR_MSG_TOO_SHORT -998 // messaggio troppo breve: non arriva sino al Checksum #define ERR_MSG_CHECKSUM -997 // checksum non valido #define ERR_COM_TIMEOUT -996 // timeout in lettura #define ERR_CLR_RX_BUFF -900 // bisogna cancellare il buffer (non è un errore!) #define COMMAND_NOT_VALID -50 // comando non valido #define PARAMETER_NOT_VALID -51 // parametro non valido #define ERR_UPS_NOT_FOUND -10 // non trovo un UPS #define ERR_COM_PORT_OPEN -11 // impossibile aprire la porta di comunicazione #define ERR_COM_SET_PORT -12 // impossibile configurare la porta di comunicazione #define ERR_COM_UNDEF_PORT -20 // porta non valida o non esistente #define ERR_USB_DRIVER -13 // impossibile aprire il driver USB #define ERR_USB_COMM_KO -14 // interfaccia USB OK: gruppo spento o guasto #define ERR_UPS_UNKNOWN -30 // impossibile identificare l'UPS #define ERR_PRG_THREAD -40 // impossibile creare uno thread #define ERR_PRG_INVALID_DATA -60 // dati non validi #define ERR_NO_ERROR 0x00 // no errors #define ERR_I2C_BUSY 0x01 // I2C bus busy (e2prom) #define ERR_CMD_CHECKSUM 0x10 // Checksum not valid #define ERR_CMD_UNRECOG 0x11 // unrecognized command #define ERR_EEP_NOBLOCK 0x08 // WRITE: eeprom address not multiple of 8 #define ERR_EEP_OOBOUND 0x09 // READ: eeprom address out of bound (with size) #define ERR_EEP_WADDR1 0x28 // error writing e2prom address #define ERR_EEP_WSADDR1 0x29 // error writing e2prom subaddress #define ERR_EEP_RDATA 0x2A // error reading e2prom data #define ERR_EEP_WADDR2 0x50 // error writing e2prom address #define ERR_EEP_WSADDR2 0x51 // error reading e2prom subaddress #define ERR_EEP_WDATA 0x52 // error writing e2prom data #define ERR_EEP_WADDRVER 0x53 // error writing e2prom address during data verification #define ERR_EEP_WDATAVER 0x54 // error verification e2prom data #define ERR_EEP_VERIFY 0x55 // e2prom data are different from those in the write buffer #define ERR_EEP_CHECKSUM 0x90 // e2prom checksum error #define CMD_ACK 0x06 // ACK #define CMD_NACK 0x15 // NACK #define CMD_GET_STATUS 0x00 // comando di acquisizione STATUS #define CMD_GET_MEASURES 0x01 // comando di acquisizione MISURE #define CMD_GET_CONFIG 0x02 // comando di acquisizione CONFIGURAZIONE #define CMD_GET_BATT_STAT 0x03 // comando di acquisizione Battery+Load Status #define CMD_GET_BAT_LD 0x03 // comando di acquisizione Battery+Load Status #define CMD_GET_MASK 0x07 // comando di acquisizione MASCHERA PUNTI #define CMD_SET_TIMER 0x20 // comando di CONFIGURAZIONE TIMER #define CMD_BATT_TEST 0x21 // comando di CONFIGURAZIONE TEST BATTERIA #define CMD_GET_BATT_TEST 0x22 // comando di LETTURA TEST BATTERIA #define CMD_SD_ONESHOT 0x40 // comando di SCRITTURA SHUTDOWN ONESHOT #define CMD_GET_SD_ONESHOT 0x41 // comando di LETTURA SHUTDOWN ONESHOT #define CMD_SET_SCHEDULE 0x42 // comando di SCRITTURA SCHEDULE #define CMD_GET_SCHEDULE 0x43 // comando di LETTURA SCHEDULE #define CMD_GET_EEP_BLOCK 0x50 // comando di LETTURA BLOCCO EEPROM #define CMD_SET_EEP_BLOCK 0x51 // comando di SCRITTURA BLOCCO EEPROM #define CMD_GET_EEP_SEED 0x52 // comando di acquisizione SEME PROGR. EEPROM #define CMD_INIT 0xF0 // comando di REINIZIALIZZAZIONE UPS #define LEN_ACK 1 // ACK #define LEN_NACK 2 // NACK #define LEN_GET_STATUS 1 // comando di acquisizione STATUS #define LEN_GET_MEASURES 1 // comando di acquisizione MISURE #define LEN_GET_CONFIG 1 // comando di acquisizione CONFIGURAZIONE #define LEN_GET_BATT_STAT 1 // comando di acquisizione Battery+Load Status #define LEN_GET_BAT_LD 1 // comando di acquisizione Battery+Load Status #define LEN_GET_MASK 1 // comando di acquisizione MASCHERA PUNTI #define LEN_SET_TIMER 5 // comando di CONFIGURAZIONE TIMER #define LEN_BATT_TEST 4 // comando di CONFIGURAZIONE TEST BATTERIA #define LEN_GET_BATT_TEST 1 // comando di LETTURA TEST BATTERIA #define LEN_SD_ONESHOT 8 // comando di SCRITTURA SHUTDOWN ONESHOT #define LEN_GET_SD_ONESHOT 1 // comando di LETTURA SHUTDOWN ONESHOT #define LEN_SET_SCHEDULE 8 // comando di SCRITTURA SCHEDULE #define LEN_GET_SCHEDULE 2 // comando di LETTURA SCHEDULE #define LEN_GET_EEP_BLOCK 3 // comando di SCRITTURA BLOCCO EEPROM #define LEN_SET_EEP_BLOCK 12 // comando di SCRITTURA BLOCCO EEPROM #define LEN_GET_EEP_SEED 1 // comando di acquisizione SEME PROGR. EEPROM #define LEN_INIT 1 // comando di REINIZIALIZZAZIONE UPS // non completamente definiti! #define RET_GET_STATUS 10 // comando di acquisizione STATUS #define RET_GET_MEASURES 15 // comando di acquisizione MISURE #define RET_GET_CONFIG 11 // comando di acquisizione CONFIGURAZIONE #define RET_GET_BATT_STAT 10 // comando di acquisizione Battery+Load Status #define RET_GET_BAT_LD 10 // comando di acquisizione Battery+Load Status #define RET_GET_MASK 1 // comando di acquisizione MASCHERA PUNTI #define RET_SET_TIMER 5 // comando di CONFIGURAZIONE TIMER #define RET_BATT_TEST 4 // comando di CONFIGURAZIONE TEST BATTERIA #define RET_GET_BATT_TEST 1 // comando di LETTURA TEST BATTERIA #define RET_SD_ONESHOT 8 // comando di SCRITTURA SHUTDOWN ONESHOT #define RET_GET_SD_ONESHOT 1 // comando di LETTURA SHUTDOWN ONESHOT #define RET_SET_SCHEDULE 8 // comando di SCRITTURA SCHEDULE #define RET_GET_SCHEDULE 8 // comando di LETTURA SCHEDULE #define RET_GET_EEP_BLOCK 11 // comando di SCRITTURA BLOCCO EEPROM #define RET_SET_EEP_BLOCK 12 // comando di SCRITTURA BLOCCO EEPROM #define RET_GET_EEP_SEED 1 // comando di acquisizione SEME PROGR. EEPROM #define RET_INIT 1 // comando di REINIZIALIZZAZIONE UPS //======================================================= // Indirizzi delle variabili memorizzate nella EEPROM: // //======================================================= #define EEP_OPT_BYTE_BLK 0x00 // Option Bytes block #define EEP_MAGIC 0x00 // magic number: deve essere a 0xAA #define EEP_CHECKSUM 0x01 // XOR longitudinale da 0x?? a 0x?? #define EEP_OUT_CONFIG 0x02 // vedi documentazione #define EEP_OPT_BYTE_0 0x02 // vedi documentazione #define EEP_OPT_BYTE_1 0x03 // vedi documentazione #define EEP_STATUS 0x04 // Status UPS #define EEP_THRESHOLD_0 0x08 // Blocco 0 con gli threshold #define EEP_MEAN_MIN 0x08 // UPS minimum AC voltage intervention point #define EEP_MEAN_MAX 0x0A // UPS maximum AC voltage intervention point #define EEP_AVR_MIN 0x0C // AVR minimum AC voltage intervention point #define EEP_AVR_MAX 0x0E // AVR maximum AC voltage intervention point #define EEP_THRESHOLD_1 0x10 // Blocco 1 con gli threshold #define EEP_BLOW_VALUE 0x10 // Battery LOW threshold voltage #define EEP_BEND_VALUE 0x12 // Battery END threshold voltage #define EEP_VMETER_0 0x14 // Led 0 Voltage indicator threshold #define EEP_VMETER_1 0x16 // Led 1 Voltage indicator threshold #define EEP_THRESHOLD_2 0x18 // Blocco 2 con gli threshold #define EEP_VMETER_2 0x18 // Led 2 Voltage indicator threshold #define EEP_VMETER_3 0x1A // Led 3 Voltage indicator threshold #define EEP_VMETER_4 0x1C // Led 4 Voltage indicator threshold #define EEP_FAULT_POINTS 0x20 // number of fault points (in an half sine wave) after which USP enter battery mode #define EEP_CHARGER_MIN 0x21 // Threshold voltage for battery charger #define EEP_TEMP_MAX 0x28 // Maximum temperature: over this value an alarm will be generated #define EEP_TEMP_FAULT 0x2A // Fault temperature: over this value, UPS will shut down immediately #define EEP_FANSPEED_0 0x2C // Temperature for fan activation (low speed) #define EEP_FANSPEED_1 0x2E // Temperature for fan activation (medium speed) #define EEP_FANSPEED_2 0x30 // Temperature for fan activation (high speed) #define EEP_IOUT 0x38 // #define EEP_IOUT_OVRLD 0x38 // Maximum current: over this value an alarm will be generated #define EEP_IOUT_FAULT 0x3A // Fault current: over this value, UPS will shut down immediately #define EEP_PWRMTR_0 0x3C // Led 0 Power indicator threshold #define EEP_PWRMTR_1 0x3E // Led 1 Power indicator threshold #define EEP_PWRMTR_2 0x40 // Led 2 Power indicator threshold #define EEP_PWRMTR_3 0x42 // Led 3 Power indicator threshold #define EEP_PWRMTR_4 0x44 // Led 4 Power indicator threshold #define EEP_PWRMTR_5 0x46 // Between "Full power" and "Overload" Power indicator level #define EEP_INPUT_MASK_0 0x48 // Half sine input mask (0-7 of 10 points) #define EEP_INPUT_MASK_8 0x50 // Half sine input mask (8-9 of 10 points) #define EEP_MIN_VBATT 0x60 // Minimum battery voltaged reached from the last power-on #define EEP_RUNTIME_H 0x62 // Working time in minutes (max ~=32 years) (MSB first) #define EEP_BAT_TIME_M 0x65 // Time in Battery mode: seconds*2 (max ~=390 days). (MSB first) #define EEP_UPS_MODEL 0x80 // Model of the UPS in ASCII. For the Enterprise line, the structure is: // ENTxxyyy // where: xx = Power rating in watts/100 (zero padded at left) // yyy = model variants (if not defined, space padded to the right) #define EEP_NETWRK_ID 0x88 // Network ID in ASCII (not used) - space padded #define EEP_NETWRK_NAME 0x90 // Network Name in ASCII (not used) - space padded #define EEP_SERIAL_NUM 0x98 // Serial Number in ASCII - zero padded (at left) #define EEP_PROD_DATE 0xA0 // #define EEP_PROD_YEAR 0xA0 // Year of production (add 2000) #define EEP_PROD_WEEK 0xA1 // Week of production [0-51] #define EEP_PROD_HW_VER 0xA2 // Hardware version: 2 nibbles ? Major and Minor number. #define EEP_PROD_BRD_VER 0xA3 // Board version: 2 nibbles ? Major and Minor number. #define EEP_PROD_FW_VER 0xA4 // Firmware version: 2 nibbles ? Major and Minor number. #define EEP_PROD_FW_SVER 0xA5 // Firmware SUBversion: 2 nibbles ? Major and Minor number. #define EEP_PROD_BTTRS 0xA6 // number of batteries installed in the UPS #define EEP_BATT_SUBST 0xA8 // battery replacement block #define EEP_BATT_YEAR_0 0xA8 // Year of 1st battery replacement (add 2000) #define EEP_BATT_WEEK_0 0xA9 // Week of 1st battery replacement #define EEP_BATT_YEAR_1 0xAA // Year of 2nd battery replacement (add 2000) #define EEP_BATT_WEEK_1 0xAB // Week of 2nd battery replacement #define EEP_BATT_YEAR_2 0xAC // Year of 3rd battery replacement (add 2000) #define EEP_BATT_WEEK_2 0xAD // Week of 3rd battery replacement #define EEP_BATT_YEAR_3 0xAE // Year of 4th battery replacement (add 2000) #define EEP_BATT_WEEK_3 0xAF // Week of 4th battery replacement #define EEP_SCHEDULE 0xC8 // Start of SCHEDULING table #define EEP_SCHEDULE_0 0xC8 // 1st Scheduling slot #define EEP_SCHEDULE_1 0xD0 // 2nd Scheduling slot #define EEP_SCHEDULE_2 0xD8 // 3rd Scheduling slot #define EEP_SCHEDULE_3 0xE0 // 4th Scheduling slot #define EEP_SCHEDULE_4 0xE8 // 5th Scheduling slot #define EEP_SCHEDULE_5 0xF0 // 6th Scheduling slot #define BIT_NO_COMM_UPS 0x0001 // Event 2 - communications with the UPS lost #define BIT_COMM_OK 0x0002 // Event 3 - communications with the UPS reestablished #define BIT_BATTERY_MODE 0x0004 // Event 5 - the UPS is in Battery mode #define BIT_BATTERY_LOW 0x0008 // Event 6 - the UPS has a LOW battery level #define BIT_MAINS_OK 0x0010 // Event 7 - return of the mains #define BIT_OVERLOAD 0x0020 // Event 8 - the UPS is overloaded #define BIT_HIGH_TEMPERATURE 0x0040 // Event 9 - the UPS is in overtemperature #define BIT_GENERIC_FAULT 0x0080 // Event 11 - there is a generic fault #define BIT_SYSTEM_SHUTDOWN 0x0100 // Event 14 - the UPS will shut down IMMEDIATELY #define BIT_STANDBY 0x0200 // Event 32 - the UPS went in STANDBY MODE #define BIT_BATTERY_END 0x0400 // Event 33 - the UPS went in battery END #define BIT_EVENT_0 0x1000 // evento ?? #define BIT_EVENT_1 0x2000 // evento ?? #define BIT_FIRST_TIME 0x4000 // se è la prima volta che chiamo la funzione #define BIT_POLL_RESTART 0x8000 // se riesco ad identificare l'UPS dopo lo scollegamento, reinvio i dati typedef struct { int Port ; // porta a cui è collegato l' UPS: // 0 = USB // n = COMn char Opened ; // BOOL flag che identifica se la porta è aperta int ErrCode ; // ultimo codice di errore in fase di lettura int ErrCount ; // conteggio degli errori unsigned char PollFlag ; // identifica se posso accedere all'UPS // 0x01: se TRUE, polling abilitato // 0x02: se TRUE, la routine di polling dati è attiva: devo attendere // 0x04: se TRUE, bisogna rileggere la configurazione del gruppo //------------------------------------------------------------------------ unsigned long Counter ; // contatore: viene incrementato ad ogni nuovo POLL unsigned char CommStatus ; // stato delle comunicazioni unsigned char FramePointer ; // puntatore al carattere di START dei dati ricevuti //------------------------------------------------------------------------ char UpsModel[9] ; // modello UPS (8 caratteri) unsigned char ge_2kVA ; // if more or equal to 2KVA char SerialNumber[9] ; // numero di serie dell'UPS unsigned short int YearOfProd ; // anno di produzione dell'UPS unsigned char MonthOfProd ; // Mese di produzione dell'UPS unsigned char DayOfProd ; // Giorno di produzione dell'UPS unsigned char HW_MajorVersion ; // Hardware: Major version unsigned char HW_MinorVersion ; // Hardware: Minor version unsigned char BR_MajorVersion ; // BoardHardware: Major version unsigned char BR_MinorVersion ; // BoardHardware: Minor version unsigned char FW_MajorVersion ; // Firmware: Major version unsigned char FW_MinorVersion ; // Firmware: Minor version unsigned char FW_SubVersion ; // Firmware: SUBVERSION (special releases // for particular customers) unsigned char BatteryNumber ; // number of batteries in UPS //------------------------------------------------------------------------ unsigned long StatusUPS ; // flag di stato dell'UPS 4 byte): 1=TRUE // bit 0 => BATTERY_MODE // bit 1 => BATTERY_LOW // bit 2 => BATTERY_END // bit 3 => ONLINE (funzionamento in rete) // bit 4 => MAINS_ON (rete di ingresso presente) // bit 5 => STANDBY (UPS in modo Standby) // bit 6 => WAIT_MAINS (UPS in fase di INIT + attesa rete) // bit 7 => INIT (UPS in fase di inizializzazione) // ------ // bit 8 => MASK_OK (semionda OK) // bit 9 => MEAN_OK (media tensione di ingresso OK) // bit 10 => SYNC_OK (sincronizzazione semionda OK) // bit 11 => FAULT (generico) // bit 12 => TEMP_MAX (superato livello critico di temperatura) // bit 13 => TEMP_FAULT (Fault da temperatura: UPS si spegne) // bit 14 => LOAD_MAX (soglia overload superata) // bit 15 => LOAD_FAULT (Fault da carico: UPS si spegne) // ------ // bit 16 => INV_FAULT (Fault dell'inverter) // bit 17 => IINV_MAX (eccessiva corrente sull'inverter) // bit 18 => IINV_FAULT (Fault sulla corrente inverter: l'UPS si spegne) // bit 19 => 50_60Hz (1=60Hz) // bit 20 => EEP_FAULT (problemi con la EEPROM) // bit 21 => VOUT_FAULT (tensione uscita troppo bassa) // bit 22 => - non definito - // bit 23 => - non definito - // ------ // bit 24 to 31 => - NON DEFINITI - unsigned short int ShortStatus ; // the LSB 2 bytes of the status unsigned char OutConfig ; // stato uscite UPS float Vinput ; // tensione di INPUT in 1/10 di Volt float Voutput ; // tensione di OUTPUT in 1/10 di Volt float Temp ; // temperatura in 1/10 di grado float InpFreq ; // Frequenza di INPUT in 1/10 di Hz float OutFreq ; // Frequenza di OUTPUT in 1/10 di Hz float OutCurrent ; // Corrente di Output in 1/10 di A unsigned char LoadPerc ; // Percentuale di carico unsigned short int LoadVA ; // Carico in VA float ChgCurrent ; // corrente carica batterie float Vbatt ; // tensione delle batterie in 1/10 di Volt unsigned char PercBatt ; // percentuale di carica della batteria unsigned short int RtimeEmpty; // minuti alla scarica della batteria unsigned char PercEmpty ; // percentuale alla scarica: 0%=scarica, 100%=carica unsigned char OutStatus ; // stato delle uscite unsigned char Year ; // unsigned char Month ; // unsigned char Day ; // unsigned char WeekDay ; // Giorno della settimana unsigned char Hour ; // Ora unsigned char Min ; // minuti unsigned char Sec ; // secondi unsigned char BattLowPerc ; // percentuale carica batteria quando va in BLOW unsigned long Rtime ; // numero di minuti di accensione dell'UPS unsigned long RtimeBatt ; // numero di secondi*2 in modalità batteria dall'accensione //------------------------------------------------------------------------ unsigned int ShutdownDelay ; // Shutdown delay in seconds unsigned int WakeUpDelay ; // WakeUp delay in seconds } ENT_STRUCT ; #endif // ENTERPRISE_PROTOCOL #endif nut-2.7.4/drivers/riello.c0000644000175000017500000005726112640444140012375 00000000000000/* * riello.c: driver core for Riello protocol based UPSes * * Documents describing the protocol implemented by this driver can be * found online at: * * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf * * Copyright (C) 2012 - Elio Parisi * * 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 * * Reference of the derivative work: blazer driver */ #include #include #include "main.h" #include "riello.h" uint8_t foundheader=0; uint16_t buf_ptr_length; uint8_t wait_packet = 0; uint8_t foundnak = 0; uint8_t foundbadcrc = 0; uint8_t commbyte; uint8_t requestSENTR; unsigned char LAST_DATA[6]; uint16_t riello_calc_CRC(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum) { uint8_t i; uint16_t pom, CRC_Word; CRC_Word = 0; switch (type) { case DEV_RIELLOSENTRY: CRC_Word = 0; if (size == 101) { for (i=0; i<100; i++) CRC_Word += buff[i]; } else { for (i=0; i> 4); CRC_Word = (CRC_Word >> 8) ^ pom; buff++; } } else { CRC_Word = 0; for (i=1; iIdentification, &buffer[7], 16); data->Identification[16] = 0; memcpy(data->ModelStr, &buffer[23], 16); data->ModelStr[16] = 0; data->ModelStr[15] = 0; memcpy(data->Version, &buffer[39], 12); data->Version[12] = 0; memcpy(data->Identif_bytes, &buffer[51], 12); data->Identif_bytes[11] = 0; data->NumBat = data->Identif_bytes[7] - 0x30; } void riello_parse_gn(uint8_t* buffer, TRielloData* data) { uint16_t pom_word; uint32_t pom_long; uint8_t j; j = 7; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); if (data->Identif_bytes[0] != '1') pom_long/=100; data->NomPowerKVA = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); if (data->Identif_bytes[0] != '1') pom_long/=100; data->NomPowerKW = pom_long; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NomUbat = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NomBatCap = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NominalUout = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NomFout = pom_word; } void riello_parse_rs(uint8_t* buffer, TRielloData* data, uint8_t numread) { uint16_t pom_word; uint8_t j; j = 7; memcpy(data->StatusCode, &buffer[j], 5); data->StatusCode[5] = 0; data->Boost = riello_test_bit(&buffer[j], 9); data->Buck = riello_test_bit(&buffer[j], 8); data->LockUPS = riello_test_bit(&buffer[j], 2); j+=5; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Finp = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uinp1 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); if (!riello_test_bit(&data->StatusCode[0], 3)) pom_word = 0; data->Fout = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uout1 = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Pout1 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Fbypass = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubypass1 = pom_word; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubat = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->BatCap = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->BatTime = pom_word; if (data->BatTime == 0xfff) data->BatTime = 0xffff; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Tsystem = pom_word; if (numread > 42) { pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uinp2 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uinp3 = pom_word; } else { data->Uinp2 = 0; data->Uinp3 = 0; } if (numread > 48) { pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uout2 = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Pout2 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubypass2 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uout3 = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Pout3 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubypass3 = pom_word; } else { data->Uout2 = 0; data->Pout2 = 0; data->Ubypass2 = 0; data->Uout3 = 0; data->Pout3 = 0; data->Ubypass3 = 0; } } void riello_parse_re(uint8_t* buffer, TRielloData* data) { uint16_t pom_word; uint32_t pom_long; uint8_t j; j = 23; data->Iinp1 = 0xFFFF; data->Iinp2 = 0xFFFF; data->Iinp3 = 0xFFFF; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Iout1 = pom_word; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Iout2 = pom_word; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Iout3 = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout1W = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout2W = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout3W = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout1VA = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout2VA = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout3VA = pom_word; } void riello_parse_rc(uint8_t* buffer, TRielloData* data) { uint8_t j, i; j = 7; for (i = 0; i < 22; i++, j+=2) { data->StatusCodeT[i] = (buffer[j+1]-0x30); data->StatusCodeT[i] |= ((buffer[j]-0x30) << 4); } data->StatusCodeT[23] = 0; data->StatusCodeT[24] = 0; } void riello_parse_sentr(uint8_t* buffer, TRielloData* data) { uint32_t pom; data->Model = buffer[2]+256*buffer[3]; if (data->Model < 3000) { if ((data->Model % 10) >= 4) { if (buffer[100] & 0x01) requestSENTR = SENTR_EXT176; else requestSENTR = SENTR_ALSO240; } else requestSENTR = SENTR_ONLY192; data->NomPowerKVA = data->Model/10; } else { if (((data->Model-3000) % 10) >= 4) { if (buffer[100] & 0x01) requestSENTR = SENTR_EXT176; else requestSENTR = SENTR_ALSO240; } else requestSENTR = SENTR_ONLY192; data->NomPowerKVA = (data->Model-3000)/10; } if (buffer[76] & 0x08) data->NomPowerKW = ((data->NomPowerKVA * 1000) * 9 / 10) / 1000; else data->NomPowerKW = ((data->NomPowerKVA * 1000) * 8 / 10) / 1000; data->NomPowerKVA *= 1000; data->NomPowerKW *= 1000; if (data->Model < 3000) data->Identif_bytes[0] = '3'; else data->Identif_bytes[0] = '4'; data->Identif_bytes[1] = '3'; data->Identif_bytes[6] = '2'; data->SWversion = buffer[4]+256*buffer[5]; data->Version[0] = (uint8_t)(48 + ((data->SWversion / 1000) % 10)); data->Version[1] = (uint8_t)(48 + ((data->SWversion / 100) % 10)); data->Version[2] = '.'; data->Version[3] = (uint8_t)(48 + ((data->SWversion / 10) % 10)); data->Version[4] = (uint8_t)(48 + (data->SWversion % 10)); if (data->Model < 3000) pom = data->Model*100; else pom = (data->Model-3000)*100; if (buffer[0] == SENTR_EXT176) { data->Uinp1 = (buffer[117]+buffer[118]*256)/10; data->Uinp2 = (buffer[119]+buffer[120]*256)/10; data->Uinp3 = (buffer[121]+buffer[122]*256)/10; data->Iinp1 = (buffer[123]+buffer[124]*256)/10; data->Iinp2 = (buffer[125]+buffer[126]*256)/10; data->Iinp3 = (buffer[127]+buffer[128]*256)/10; data->Finp = buffer[41]+256*buffer[42]; data->Uout1 = (buffer[135]+buffer[136]*256)/10; data->Uout2 = (buffer[137]+buffer[138]*256)/10; data->Uout3 = (buffer[139]+buffer[140]*256)/10; data->Iout1 = (buffer[141]+buffer[142]*256)/10; data->Iout2 = (buffer[143]+buffer[144]*256)/10; data->Iout3 = (buffer[145]+buffer[146]*256)/10; data->Pout1 = buffer[62]; data->Pout2 = buffer[63]; data->Pout3 = buffer[64]; data->Ipout1 = buffer[65]*3; } else { data->Uinp1 = buffer[35]*230/100; data->Uinp2 = buffer[36]*230/100; data->Uinp3 = buffer[37]*230/100; data->Iinp1 = ((pom/690)*buffer[38])/100;; data->Iinp2 = ((pom/690)*buffer[39])/100;; data->Iinp3 = ((pom/690)*buffer[40])/100;; data->Finp = buffer[41]+256*buffer[42]; if (buffer[79] & 0x80) { data->Uout1 = buffer[59]*2; data->Uout2 = buffer[60]*2; data->Uout3 = buffer[61]*2; } else { data->Uout1 = buffer[59]; data->Uout2 = buffer[60]; data->Uout3 = buffer[61]; } if (buffer[73]) { if (buffer[73] < 100) buffer[73]+=256; if (data->Model < 3000) /* singlephase */ data->Iout1 = ((pom/buffer[73])*buffer[62])/100; else data->Iout1 = ((pom/buffer[73])*buffer[62])/100/3; data->Iout2 = ((pom/buffer[73])*buffer[63])/100/3; data->Iout3 = ((pom/buffer[73])*buffer[64])/100/3; } else { data->Iout1 = 0; data->Iout2 = 0; data->Iout3 = 0; } if ((data->Model & 0x0007) < 4) { data->Iout1 *= 0.9; data->Iout2 *= 0.9; data->Iout3 *= 0.9; } data->Pout1 = buffer[62]; data->Pout2 = buffer[63]; data->Pout3 = buffer[64]; data->Ipout1 = buffer[65]*3; } if (data->Model < 3000) { data->Ipout2 = 0; data->Ipout3 = 0; } else { data->Ipout2 = buffer[66]*3; data->Ipout3 = buffer[67]*3; } data->Fout = buffer[68]+256*buffer[69]; data->BatTime = buffer[6]+256*buffer[7]; data->BatCap = buffer[8]; if ((buffer[0] == SENTR_ALSO240) || (buffer[0] == SENTR_EXT176)) { if (buffer[100] & 0x80) data->Ubat = buffer[43]+256*buffer[44]; else { if (buffer[44] < buffer[43]) data->Ubat = buffer[44]*2; else data->Ubat = buffer[43]*2; } } else data->Ubat = buffer[43]+256*buffer[44]; data->Ubat *= 10; data->Ibat = buffer[45]+256*buffer[46]; if (!buffer[47]) data->Ibat = data->Ibat*10; data->Tsystem = buffer[48]; data->NomBatCap = buffer[74]+256*buffer[75]; switch (buffer[0]) { case SENTR_EXT176: data->Ubypass1 = (buffer[129]+buffer[130]*256)/10; data->Ubypass2 = (buffer[131]+buffer[132]*256)/10; data->Ubypass3 = (buffer[133]+buffer[134]*256)/10; data->Fbypass = buffer[57]+256*buffer[58]; break; case SENTR_ALSO240: data->Ubypass1 = buffer[51]*2; data->Ubypass2 = buffer[53]*2; data->Ubypass3 = buffer[55]*2; data->Fbypass = buffer[57]+256*buffer[58]; break; default: data->Ubypass1 = buffer[51]+256*buffer[52]; data->Ubypass2 = buffer[53]+256*buffer[54]; data->Ubypass3 = buffer[55]+256*buffer[56]; data->Fbypass = buffer[57]+256*buffer[58]; break; } data->StatusCode[0] = 0x08; data->StatusCode[1] = 0x00; data->StatusCode[2] = 0x00; data->StatusCode[3] = 0x00; data->StatusCode[4] = 0x00; /* Overload if (riello_test_bit(&DevData.StatusCode[4], 2)) */ if (buffer[31] & 128) data->StatusCode[4] |= 0x04; /* Bypass if (riello_test_bit(&DevData.StatusCode[1], 3)) */ if (((buffer[31] & 2) || (riello_test_bit(&buffer[32], 0)) || (riello_test_bit(&buffer[32], 12)) || (riello_test_bit(&buffer[32], 13)) || (riello_test_bit(&buffer[32], 14))) && (!(buffer[34] & 8))) data->StatusCode[1] |= 0x08; /* AC Fail if (riello_test_bit(&DevData.StatusCode[0], 1)) */ if (buffer[31] & 8) data->StatusCode[0] |= 0x02; /* LowBatt if ((riello_test_bit(&DevData.StatusCode[0], 1)) && (riello_test_bit(&DevData.StatusCode[0], 0))) */ if (buffer[31] & 16) { if (buffer[31] & 8) { data->StatusCode[0] |= 0x02; data->StatusCode[0] |= 0x01; } } /* Standby if (!riello_test_bit(&DevData.StatusCode[0], 3)) */ if ((buffer[22] & 2) || (buffer[34] & 4) || (buffer[34] & 8)) data->StatusCode[0] &= 0xF7; /* Battery bad (Replace battery) if (riello_test_bit(&DevData.StatusCode[2], 0)) */ if (buffer[31] & 0x40) data->StatusCode[2] |= 0x01; } void riello_init_serial() { wait_packet = 1; buf_ptr_length = 0; foundbadcrc = 0; foundnak = 0; } uint8_t riello_header(uint8_t type, uint8_t a, uint8_t* length) { LAST_DATA[0] = LAST_DATA[1]; LAST_DATA[1] = LAST_DATA[2]; LAST_DATA[2] = LAST_DATA[3]; LAST_DATA[3] = LAST_DATA[4]; LAST_DATA[4] = LAST_DATA[5]; LAST_DATA[5] = (uint8_t) a; switch (type) { case DEV_RIELLOSENTRY: if (((LAST_DATA[4]>=192) && (LAST_DATA[5]==103)) || ((LAST_DATA[4]==176) && (LAST_DATA[5]==164))) { *length = LAST_DATA[5]; return(1); } break; case DEV_RIELLOGPSER: if ((buf_ptr_length==0) && (LAST_DATA[5]>0x20) && (LAST_DATA[4]==0x2)) return(1); break; } return(0); } uint8_t riello_tail(uint8_t type, uint8_t length) { uint8_t number; switch (type) { case DEV_RIELLOSENTRY: number = length; if (buf_ptr_length >= number) return(1); break; case DEV_RIELLOGPSER: if (LAST_DATA[5] == 0x03) return(1); break; } return(0); } uint8_t riello_test_nak(uint8_t type, uint8_t* buffer) { switch (type) { case DEV_RIELLOGPSER: if (buffer[3] == 0x15) return(1); break; } return(0); } void riello_parse_serialport(uint8_t typedev, uint8_t* buffer, uint8_t checksum) { static uint8_t actual_char, int_i; static uint8_t length; actual_char = commbyte; if ((riello_header(typedev, actual_char, &length)) && (!foundheader)) { upsdebugx(5,"Header detected: LAST_DATA:%X,%X,%X,%X,%X,%X buf_ptr:%i \n\r", LAST_DATA[0], LAST_DATA[1], LAST_DATA[2], LAST_DATA[3], LAST_DATA[4], LAST_DATA[5], buf_ptr_length); foundheader = 1; buf_ptr_length = 1; memset(buffer, 0, BUFFER_SIZE); buffer[0] = LAST_DATA[4]; } if ((foundheader) && (buf_ptr_length < BUFFER_SIZE)) buffer[buf_ptr_length++] = actual_char; if ((foundheader) && (riello_tail(typedev, length))) { upsdebugx(5,"\n\rEnd detected: LAST_DATA:%X,%X,%X,%X,%X,%X buf_ptr:%i \n\r", LAST_DATA[0], LAST_DATA[1], LAST_DATA[2], LAST_DATA[3], LAST_DATA[4], LAST_DATA[5], buf_ptr_length); foundheader = 0; for (int_i=0; int_i<6; int_i++) LAST_DATA[int_i] = 0; if (riello_test_nak(typedev, buffer)) { wait_packet = 0; foundnak = 1; } if (riello_test_crc(typedev, buffer, buf_ptr_length, checksum)) { wait_packet = 0; foundbadcrc = 1; } else { wait_packet = 0; foundbadcrc = 0; } } } nut-2.7.4/drivers/Makefile.am0000644000175000017500000002220412667537407013006 00000000000000# Network UPS Tools: drivers # by default, link programs in this directory with libcommon.la # (libtool version of the static lib, in order to access LTLIBOBJS) #FIXME: SERLIBS is only useful for LDADD_DRIVERS_SERIAL not for LDADD_COMMON LDADD_COMMON = ../common/libcommon.la ../common/libparseconf.la LDADD_DRIVERS = $(LDADD_COMMON) main.o dstate.o LDADD_DRIVERS_SERIAL = $(LDADD_DRIVERS) $(SERLIBS) serial.o # most targets are drivers, so make this the default LDADD = $(LDADD_DRIVERS_SERIAL) # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include if WITH_USB AM_CFLAGS += $(LIBUSB_CFLAGS) endif if WITH_SNMP AM_CFLAGS += $(LIBNETSNMP_CFLAGS) endif if WITH_NEON AM_CFLAGS += $(LIBNEON_CFLAGS) endif if WITH_LIBPOWERMAN AM_CFLAGS += $(LIBPOWERMAN_CFLAGS) endif if WITH_IPMI AM_CFLAGS += $(LIBIPMI_CFLAGS) endif SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ bestfortress bestuferrups bestups dummy-ups etapro everups \ gamatronic genericups isbmex liebert liebert-esp2 masterguard metasys \ oldmge-shut mge-utalk microdowell mge-shut oneac optiups powercom rhino \ safenet skel solis tripplite tripplitesu upscode2 victronups powerpanel \ blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser \ nutdrv_qx SNMP_DRIVERLIST = snmp-ups USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb \ blazer_usb richcomm_usb riello_usb \ nutdrv_atcl_usb \ nutdrv_qx USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST) NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups LINUX_I2C_DRIVERLIST = asem # distribute all drivers, even ones that are not built by default EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(SNMP_DRIVERLIST) $(USB_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST) # construct the list of drivers to build if SOME_DRIVERS driverexec_PROGRAMS = $(DRIVER_BUILD_LIST) else driverexec_PROGRAMS = if WITH_SERIAL driverexec_PROGRAMS += $(SERIAL_DRIVERLIST) endif if WITH_SNMP driverexec_PROGRAMS += $(SNMP_DRIVERLIST) endif if WITH_USB driverexec_PROGRAMS += $(USB_LIBUSB_DRIVERLIST) endif if WITH_NEON driverexec_PROGRAMS += $(NEONXML_DRIVERLIST) endif if WITH_LIBPOWERMAN driverexec_PROGRAMS += powerman-pdu endif if WITH_IPMI driverexec_PROGRAMS += nut-ipmipsu endif if WITH_MACOSX driverexec_PROGRAMS += $(MACOSX_DRIVERLIST) endif if WITH_LINUX_I2C driverexec_PROGRAMS += $(LINUX_I2C_DRIVERLIST) endif else driverexec_PROGRAMS += skel endif # always build upsdrvctl sbin_PROGRAMS = upsdrvctl # ========================================================================== # Driver build details # upsdrvctl: the all-singing all-dancing driver control program upsdrvctl_SOURCES = upsdrvctl.c upsdrvctl_LDADD = $(LDADD_COMMON) # serial drivers: all of them use standard LDADD and CFLAGS al175_SOURCES = al175.c apcsmart_SOURCES = apcsmart.c apcsmart_tabs.c apcsmart_old_SOURCES = apcsmart-old.c bcmxcp_SOURCES = bcmxcp.c bcmxcp_ser.c bcmxcp_LDADD = $(LDADD) -lm belkin_SOURCES = belkin.c belkinunv_SOURCES = belkinunv.c bestfcom_SOURCES = bestfcom.c bestuferrups_SOURCES = bestuferrups.c bestups_SOURCES = bestups.c blazer_ser_SOURCES = blazer.c blazer_ser.c blazer_ser_LDADD = $(LDADD) -lm etapro_SOURCES = etapro.c everups_SOURCES = everups.c gamatronic_SOURCES = gamatronic.c genericups_SOURCES = genericups.c isbmex_SOURCES = isbmex.c isbmex_LDADD = $(LDADD) -lm ivtscd_SOURCES = ivtscd.c liebert_SOURCES = liebert.c liebert_esp2_SOURCES = liebert-esp2.c masterguard_SOURCES = masterguard.c metasys_SOURCES = metasys.c oldmge_shut_SOURCES = mge-shut.c hidparser.c mge_utalk_SOURCES = mge-utalk.c microdowell_SOURCES = microdowell.c oneac_SOURCES = oneac.c optiups_SOURCES = optiups.c powercom_SOURCES = powercom.c powercom_LDADD = $(LDADD) -lm powerpanel_SOURCES = powerpanel.c powerp-bin.c powerp-txt.c powerpanel_LDADD = $(LDADD) -lm rhino_SOURCES = rhino.c rhino_LDADD = $(LDADD) -lm safenet_SOURCES = safenet.c solis_SOURCES = solis.c solis_LDADD = $(LDADD) -lm tripplite_SOURCES = tripplite.c tripplite_LDADD = $(LDADD) -lm tripplitesu_SOURCES = tripplitesu.c upscode2_SOURCES = upscode2.c upscode2_LDADD = $(LDADD) -lm victronups_SOURCES = victronups.c riello_ser_SOURCES = riello.c riello_ser.c riello_ser_LDADD = $(LDADD) -lm # non-serial drivers: these use custom LDADD and/or CFLAGS # dummy dummy_ups_SOURCES = dummy-ups.c dummy_ups_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/clients dummy_ups_LDADD = $(LDADD_DRIVERS) ../clients/libupsclient.la if WITH_SSL dummy_ups_CFLAGS += $(LIBSSL_CFLAGS) dummy_ups_LDADD += $(LIBSSL_LIBS) endif # Clone drivers clone_SOURCES = clone.c clone_outlet_SOURCES = clone-outlet.c # apcupsd client driver apcupsd_ups_SOURCES = apcupsd-ups.c apcupsd_ups_CFLAGS = $(AM_CFLAGS) apcupsd_ups_LDADD = $(LDADD_DRIVERS) # sample skeleton driver skel_SOURCES = skel.c skel_LDADD = $(LDADD_DRIVERS) # USB USBHID_UPS_SUBDRIVERS = apc-hid.c belkin-hid.c cps-hid.c explore-hid.c \ liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c \ openups-hid.c usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ usb-common.c $(USBHID_UPS_SUBDRIVERS) usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) tripplite_usb_SOURCES = tripplite_usb.c libusb.c usb-common.c tripplite_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c usb-common.c bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c usb-common.c blazer_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm nutdrv_atcl_usb_SOURCES = nutdrv_atcl_usb.c usb-common.c nutdrv_atcl_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) richcomm_usb_SOURCES = richcomm_usb.c usb-common.c richcomm_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) riello_usb_SOURCES = riello.c riello_usb.c libusb.c usb-common.c riello_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm # HID-over-serial mge_shut_SOURCES = usbhid-ups.c libshut.c libhid.c hidparser.c mge-hid.c # per-target CFLAGS are necessary here mge_shut_CFLAGS = $(AM_CFLAGS) -DSHUT_MODE mge_shut_LDADD = $(LDADD) # SNMP snmp_ups_SOURCES = snmp-ups.c apc-mib.c baytech-mib.c compaq-mib.c eaton-mib.c \ ietf-mib.c mge-mib.c netvision-mib.c powerware-mib.c raritan-pdu-mib.c \ bestpower-mib.c cyberpower-mib.c delta_ups-mib.c xppc-mib.c huawei-mib.c \ eaton-ats-mib.c apc-ats-mib.c snmp_ups_LDADD = $(LDADD_DRIVERS) $(LIBNETSNMP_LIBS) # NEON XML/HTTP netxml_ups_SOURCES = netxml-ups.c mge-xml.c netxml_ups_LDADD = $(LDADD_DRIVERS) $(LIBNEON_LIBS) # Powerman powerman_pdu_SOURCES = powerman-pdu.c powerman_pdu_LDADD = $(LDADD) $(LIBPOWERMAN_LIBS) # IPMI PSU nut_ipmipsu_SOURCES = nut-ipmipsu.c if WITH_FREEIPMI nut_ipmipsu_SOURCES += nut-libfreeipmi.c endif nut_ipmipsu_LDADD = $(LDADD) $(LIBIPMI_LIBS) # Mac OS X metadriver macosx_ups_LDADD = $(LDADD_DRIVERS) macosx_ups_LDFLAGS = $(LDFLAGS) -framework IOKit -framework CoreFoundation macosx_ups_SOURCES = macosx-ups.c # Asem asem_LDADD = $(LDADD_DRIVERS) asem_SOURCES = asem.c # nutdrv_qx USB/Serial nutdrv_qx_SOURCES = nutdrv_qx.c nutdrv_qx_LDADD = $(LDADD_DRIVERS) -lm nutdrv_qx_CFLAGS = $(AM_CFLAGS) if WITH_SERIAL nutdrv_qx_CFLAGS += -DQX_SERIAL nutdrv_qx_LDADD += $(SERLIBS) serial.o endif if WITH_USB nutdrv_qx_CFLAGS += -DQX_USB nutdrv_qx_SOURCES += libusb.c usb-common.c nutdrv_qx_LDADD += $(LIBUSB_LIBS) endif NUTDRV_QX_SUBDRIVERS = nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \ nutdrv_qx_mecer.c nutdrv_qx_megatec.c nutdrv_qx_megatec-old.c \ nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_voltronic.c \ nutdrv_qx_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS) # ---------------------------------------------------------------------- # List of header files. The purpose of this list is not dependency # tracking (which is automatic), but to ensure these files are # distributed by "make dist". dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \ bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ dummy-ups.h eaton-mib.h explore-hid.h gamatronic.h genericups.h \ hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h libusb.h liebert-hid.h \ main.h mge-hid.h mge-mib.h mge-shut.h mge-utalk.h \ mge-xml.h microdowell.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ powercom.h powerpanel.h powerp-bin.h powerp-txt.h powerware-mib.h raritan-pdu-mib.h \ safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ apcsmart.h apcsmart_tabs.h apcsmart-old.h apcupsd-ups.h cyberpower-mib.h riello.h openups-hid.h \ delta_ups-mib.h nutdrv_qx.h nutdrv_qx_bestups.h nutdrv_qx_blazer-common.h nutdrv_qx_mecer.h \ nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ xppc-mib.h huawei-mib.h eaton-ats-mib.h apc-ats-mib.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, EXTRA_LIBRARIES = libdummy.a libdummy_a_SOURCES = main.c dstate.c serial.c nut-2.7.4/drivers/upscode2.c0000644000175000017500000011044512640473702012633 00000000000000/* upscode2.c - model specific routines for UPSes using the UPScode II command set. This includes PowerWare, Fiskars, Compaq (PowerWare OEM?), some IBM (PowerWare OEM?) Copyright (C) 2002 H?vard Lygre Copyright (C) 2004-2006 Niels Baggesen Copyright (C) 2006 Niklas Edmundsson 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 */ /* * Currently testing against * Fiskars PowerRite Max * Fiskars PowerServer 10 * Fiskars PowerServer 30 * Powerware Profile (9150) * Powerware 9305 * * Also tested against * Compaq T1500h (Per J?nsson ) * Powerware 9120 (Gorm J. Siiger ) * Fiskars PowerServer 10 (Per Larsson ) */ #include "main.h" #include "serial.h" #include "timehead.h" #include "nut_stdint.h" #include #define DRIVER_NAME "UPScode II UPS driver" #define DRIVER_VERSION "0.89" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "H K Lygre, \n" \ "Niels Baggesen \n" \ "Niklas Edmundsson ", DRV_EXPERIMENTAL, { NULL } }; #define ENDCHAR '\n' /* default values */ #define OUT_PACE_USEC 200 #define INP_TIMO_SEC 2 #define FULL_UPDATE_TIMER 60 #define UPSC_BUFLEN 256 /* Size of response buffers from UPS */ /* Status messages from UPS */ #define UPSC_STAT_ONLINE (1UL << 0) #define UPSC_STAT_ONBATT (1UL << 1) #define UPSC_STAT_LOBATT (1UL << 2) #define UPSC_STAT_REPLACEBATT (1UL << 3) #define UPSC_STAT_BOOST (1UL << 4) #define UPSC_STAT_TRIM (1UL << 5) #define UPSC_STAT_OVERLOAD (1UL << 6) #define UPSC_STAT_CALIBRATION (1UL << 7) #define UPSC_STAT_OFF (1UL << 8) #define UPSC_STAT_BYPASS (1UL << 9) #define UPSC_STAT_NOTINIT (1UL << 31) typedef enum { t_ignore, /* Ignore this response */ t_value, /* Sets a NUT variable */ t_final, /* Marks the end of UPS data for this command */ t_string, /* Set a NUT string variable */ t_finstr, /* Set a NUT string variable, and marks end of data */ t_setval, /* Sets a variable in the driver and possibly in NUT */ t_setrecip, /* Sets a driver variable to 1/value and possibly NUT */ t_setpct, t_setrecpct, t_status, /* Sets a status bit */ t_alarm, /* Sets an alarm message */ t_list /* The value must be matched in the list */ } type_t; typedef struct simple_s { const char *code; type_t type; const char *desc; int status; float *aux; struct simple_s *stats; } simple_t; typedef struct { const char *cmd; const char *upsc; const char *upsp; int enabled; } cmd_t; static int can_upda = 0, can_upbs = 0, can_upid = 0, can_uppm = 0, can_updt = 0, can_uptm = 0, can_upsd = 0, can_uppc = 0; static char has_uppm_p[100]; static int input_timeout_sec = INP_TIMO_SEC, output_pace_usec = OUT_PACE_USEC, full_update_timer = FULL_UPDATE_TIMER, use_crlf = 0, use_pre_lf = 0, buffer_empty = 0; static uint32_t status = UPSC_STAT_NOTINIT; static time_t last_full = 0; static float batt_volt_low = 0, batt_volt_nom = 0, batt_volt_high = 0, batt_volt = 0, batt_cap_nom = -1, batt_charge = -1, batt_current = -1, batt_disch_curr_max = 0, batt_runtime = -1, batt_runtime_max = -1, kilo_to_unity = 1000.0, outpwr_factor = 1000.0, nom_out_current = -1, max_out_current = -1; /* To get average battery current */ #define NUM_BATTHIST 60 static float batthist[NUM_BATTHIST]; static int numbatthist=0, lastbatthist=0; static int inited_phaseinfo = 0, num_inphases = 1, num_outphases = 1; /* Status codes for the STAT and STMF status responses */ static simple_t att[] = { { "00", t_ignore }, { "AC", t_alarm, "Aux contact failure", 0 }, { "BA", t_alarm, "Batteries disconnected", 0 }, { "BC", t_alarm, "Backfeed contact failure", 0 }, { "BD", t_alarm, "Abnormal battery discharge", 0 }, { "BF", t_alarm, "Battery fuse failure", 0 }, { "BL", t_status, "Battery low limit", UPSC_STAT_LOBATT }, { "BO", t_alarm, "Battery over voltage", 0 }, { "BP", t_alarm, "Bypass fuse failure", 0 }, { "BR", t_alarm, "Abnormal battery recharge", 0 }, { "BT", t_alarm, "Battery over temperature", 0 }, { "BX", t_alarm, "Bypass unavailable", 0 }, { "BY", t_alarm, "Battery failure", 0 }, { "CE", t_alarm, "Configuration error", 0 }, { "CM", t_alarm, "Battery converter failure", 0 }, { "CT", t_alarm, "Cabinet over temperature", 0 }, { "DO", t_alarm, "DC over voltage", 0 }, { "DU", t_alarm, "DC under voltage", 0 }, { "EP", t_alarm, "Emergency power off", 0 }, { "FF", t_alarm, "Fan failure", 0 }, { "FH", t_alarm, "Line frequency high", 0 }, { "FL", t_alarm, "Line frequency low", 0 }, { "FT", t_alarm, "Filter over temperature", 0 }, { "GF", t_alarm, "Ground failure", 0 }, { "HT", t_alarm, "Charger over temperature", 0 }, { "IB", t_alarm, "Internal data bus failure", 0 }, { "IF", t_alarm, "Inverter fuse failure", 0 }, { "IM", t_alarm, "Inverter failure", 0 }, { "IO", t_alarm, "Inverter over voltage", 0 }, { "IP", t_alarm, "Internal power supply failure", 0 }, { "IT", t_alarm, "Inverter over temperature", 0 }, { "IU", t_alarm, "Inverter under voltage", 0 }, { "IV", t_alarm, "Inverter off", 0 }, { "LR", t_alarm, "Loss of redundancy", 0 }, { "NF", t_alarm, "Neutral fault", 0 }, { "OD", t_status, "UPS not supplying load", UPSC_STAT_OFF }, { "OF", t_alarm, "Oscillator failure", 0 }, { "OL", t_status, "Overload", UPSC_STAT_OVERLOAD }, { "OR", t_alarm, "Redundancy overload", 0 }, { "OV", t_alarm, "Abnormal output voltage", 0 }, { "OW", t_alarm, "Output failure", 0 }, { "PB", t_alarm, "Parallel bus failure", 0 }, { "PE", t_alarm, "Phase rotation error", 0 }, { "RE", t_alarm, "Rectifier off", 0 }, { "RF", t_alarm, "Rectifier fuse failure", 0 }, { "RM", t_alarm, "Rectifier failure", 0 }, { "RT", t_alarm, "Rectifier over temperature", 0 }, { "SM", t_alarm, "Static switch failure", 0 }, { "ST", t_alarm, "Static switch over temperature", 0 }, { "TT", t_alarm, "Trafo over temperature", 0 }, { "UD", t_alarm, "UPS disabled", 0 }, { "UO", t_alarm, "Utility over voltage", 0 }, { "US", t_alarm, "Unsynchronized", 0 }, { "UU", t_alarm, "Utility under voltage", 0 }, { "VE", t_alarm, "internal voltage error", 0 }, { NULL } }; /* Status code for the STLR response */ static simple_t stlr[] = { { "NO", t_ignore }, { "SD", t_status, NULL, UPSC_STAT_TRIM }, { "SU", t_status, NULL, UPSC_STAT_BOOST }, { "DU", t_status, NULL, UPSC_STAT_BOOST }, { NULL } }; /* Status code for the STEA and STEM responses */ static simple_t env[] = { { "HH", t_ignore, "Humidity high", 0 }, { "HL", t_ignore, "Humidity low", 0 }, { "TH", t_ignore, "Temperature high", 0 }, { "TL", t_ignore, "Temperature low", 0 }, { "01", t_ignore, "Environment alarm 1", 0 }, { "02", t_ignore, "Environment alarm 2", 0 }, { "03", t_ignore, "Environment alarm 3", 0 }, { "04", t_ignore, "Environment alarm 4", 0 }, { "05", t_ignore, "Environment alarm 5", 0 }, { "06", t_ignore, "Environment alarm 6", 0 }, { "07", t_ignore, "Environment alarm 7", 0 }, { "08", t_ignore, "Environment alarm 8", 0 }, { "09", t_ignore, "Environment alarm 9", 0 }, { NULL } }; /* Responses for UPSS and UPDS */ static simple_t simple[] = { { "STAT", t_list, NULL, 0, 0, att }, { "STBO", t_status, NULL, UPSC_STAT_ONBATT }, { "STBL", t_status, NULL, UPSC_STAT_LOBATT }, { "STBM", t_ignore }, { "STBP", t_status, NULL, UPSC_STAT_BYPASS }, { "STEA", t_list, NULL, 0, 0, env }, { "STEM", t_list, NULL, 0, 0, env }, { "STLR", t_list, NULL, 0, 0, stlr }, { "STMF", t_list, NULL, 0, 0, att }, { "STOK", t_ignore }, { "STUF", t_status, NULL, UPSC_STAT_ONBATT }, { "BTIME", t_setval, NULL, 0, &batt_runtime }, { "METE1", t_value, "ambient.temperature" }, { "MERH1", t_value, "ambient.humidity" }, { "MIFFF", t_value, "input.frequency" }, { "MIIL1", t_value, "input.current" }, { "MIIL2", t_value, "input.L2.current" }, { "MIIL3", t_value, "input.L3.current" }, { "MIPL1", t_value, "input.realpower" }, { "MIPL2", t_value, "input.L2.realpower" }, { "MIPL3", t_value, "input.L3.realpower" }, { "MISL1", t_value, "input.power" }, { "MISL2", t_value, "input.L2.power" }, { "MISL3", t_value, "input.L3.power" }, { "MIUL1", t_value, "input.voltage" }, { "MIUL2", t_value, "input.L2-N.voltage" }, { "MIUL3", t_value, "input.L3-N.voltage" }, { "MIU12", t_value, "input.L1-L2.voltage" }, { "MIU23", t_value, "input.L2-L3.voltage" }, { "MIU31", t_value, "input.L3-L1.voltage" }, { "MBCH1", t_setval, NULL, 0, &batt_charge }, /* battery.charge */ { "MBIII", t_setval, "battery.current", 0, &batt_current }, { "MBINE", t_ignore, /* "battery.current.negative" */ }, { "MBIPO", t_ignore, /* "battery.current.positive" */ }, { "MBUNE", t_ignore, /* "battery.voltage.negative" */ }, { "MBUPO", t_ignore, /* "battery.voltage.positive" */}, { "MBUUU", t_setval, "battery.voltage", 0, &batt_volt }, { "MLUNE", t_ignore, /* "dc.voltage.negative" */ }, { "MLUPO", t_ignore, /* "dc.voltage.positive" */ }, { "MLUUU", t_ignore, /* "dc.voltage" */ }, { "MOFFF", t_final, "output.frequency" }, { "MOIL1", t_value, "output.current" }, { "MOIL2", t_value, "output.L2.current" }, { "MOIL3", t_value, "output.L3.current" }, { "MOIP1", t_value, "output.current.peak" }, { "MOIP2", t_value, "output.L2.current.peak" }, { "MOIP3", t_value, "output.L3.current.peak" }, { "MOPL1", t_value, "output.realpower", 0, &kilo_to_unity }, { "MOPL2", t_value, "output.L2.realpower", 0, &kilo_to_unity }, { "MOPL3", t_value, "output.L3.realpower", 0, &kilo_to_unity }, { "MOSL1", t_value, "output.power" }, { "MOSL2", t_value, "output.L2.power" }, { "MOSL3", t_value, "output.L3.power" }, { "MOUL1", t_value, "output.voltage" }, { "MOUL2", t_value, "output.L2-N.voltage" }, { "MOUL3", t_value, "output.L3-N.voltage" }, { "MOU12", t_value, "output.L1-L2.voltage" }, { "MOU23", t_value, "output.L2-L3.voltage" }, { "MOU31", t_value, "output.L3-L1.voltage" }, { "MPUL1", t_value, "input.bypass.L1-N.voltage" }, { "MPUL2", t_value, "input.bypass.L2-N.voltage" }, { "MPUL3", t_value, "input.bypass.L3-N.voltage" }, { "MUTE1", t_value, "ups.temperature" }, { NULL } }; /* Responses for UPDV */ static simple_t nominal[] = { { "NIUHH", t_value, "input.voltage.maximum" }, { "NIULL", t_value, "input.voltage.minimum" }, { "NIUNN", t_value, "input.voltage.nominal" }, { "NIIHH", t_value, "input.current.maximum" }, { "NIILL", t_value, "input.current.minimum" }, { "NIINN", t_value, "input.current.nominal" }, { "NIPHH", t_value, "input.realpower.maximum" }, { "NIPNN", t_value, "input.realpower.nominal" }, { "NISHH", t_value, "input.power.maximum" }, { "NISNN", t_value, "input.power.nominal" }, { "NBAHN", t_setval, "battery.capacity.nominal", 0, &batt_cap_nom }, { "NBIHH", t_ignore, "battery charge current maximum" }, { "NBILL", t_setval, NULL, 0, &batt_disch_curr_max}, { "NBINN", t_value, "battery.current.nominal" }, { "NBTHH", t_setval, NULL, 0, &batt_runtime_max}, { "NBUHH", t_setval, "battery.voltage.maximum", 0, &batt_volt_high }, { "NBULL", t_setval, "battery.voltage.minimum", 0, &batt_volt_low }, { "NBUNN", t_setval, "battery.voltage.nominal", 0, &batt_volt_nom }, { "NOFHH", t_value, "output.frequency.maximum" }, { "NOFLL", t_final, "output.frequency.minimum" }, { "NOIHH", t_setval, "output.current.maximum", 0, &max_out_current }, { "NOINN", t_setval, "output.current.nominal", 0, &nom_out_current }, { "NOPNN", t_value, "output.realpower.nominal", 0, &outpwr_factor }, { "NOSNN", t_value, "ups.power.nominal", 0, &outpwr_factor }, { "NOUHH", t_value, "output.voltage.maximum" }, { "NOULL", t_value, "output.voltage.minimum" }, { "NOUNN", t_value, "output.voltage.nominal" }, { "NUTEH", t_value, "ups.temperature.maximum" }, { NULL } }; /* Status responses for UPBS command */ static simple_t battery[] = { { "MBTE1", t_value, "battery.1.temperature" }, { "MBIN1", t_ignore, NULL /* aging index */ }, { "BDAT1", t_string, "battery.1.date" }, { "MBTE2", t_value, "battery.2.temperature.2" }, { "MBIN2", t_ignore, NULL }, { "BDAT2", t_string, "battery.2.date" }, { "MBTE3", t_value, "battery.3.temperature" }, { "MBIN3", t_ignore, NULL }, { "BDAT3", t_string, "battery.3.date" }, { "MBTE4", t_value, "battery.4.temperature" }, { "MBIN4", t_ignore, NULL }, { "BDAT4", t_string, "battery.4.date" }, { "MBTE5", t_value, "battery.5.temperature" }, { "MBIN5", t_ignore, NULL }, { "BDAT5", t_string, "battery.5.date" }, { "MBTE6", t_value, "battery.6.temperature" }, { "MBIN6", t_ignore, NULL }, { "BDAT6", t_string, "battery.6.date" }, { "MBTE7", t_value, "battery.7.temperature" }, { "MBIN7", t_ignore, NULL }, { "BDAT7", t_string, "battery.7.date" }, { "MBTE8", t_value, "battery.8.temperature" }, { "MBIN8", t_ignore, NULL }, { "BDAT8", t_finstr, "battery.8.date" }, { NULL } }; static cmd_t commands[] = { { "load.off", NULL, NULL }, { "load.on", NULL, NULL }, { "shutdown.return", "UPPF", "IJHLDMGCIU" }, { "shutdown.stayoff", "UPPD", "LGGNLMDPGV" }, { "shutdown.stop", "UPPU", NULL }, { "shutdown.reboot", "UPPC", "IJHLDMGCIU" }, { "shutdown.reboot.graceful", NULL, NULL }, { "test.panel.start", "UPIS", NULL }, { "test.panel.stop", NULL, NULL }, { "test.failure.start", NULL, NULL }, { "test.failure.stop", NULL, NULL }, { "test.battery.start", "UPBT", "1" }, { "test.battery.stop", NULL, NULL }, { "calibrate.start", NULL, NULL }, { "calibrate.stop", NULL, NULL }, { "bypass.start", NULL, NULL }, { "bypass.stop", NULL, NULL }, { "reset.input.minmax", NULL, NULL }, { "reset.watchdog", NULL, NULL }, { "beeper.enable", NULL, NULL }, { "beeper.disable", NULL, NULL }, { "beeper.on", NULL, NULL }, { "beeper.off", NULL, NULL }, { NULL } }; static cmd_t variables[] = { { "ups.delay.reboot", "UPCD", "ACCD" }, { "ups.delay.shutdown", "UPSD", "ACSD" }, { NULL } }; static int instcmd (const char *auxcmd, const char *data); static int setvar (const char *var, const char *data); static void upsc_setstatus(unsigned int status); static void upsc_flush_input(void); static void upsc_getbaseinfo(void); static int upsc_commandlist(void); static int upsc_getparams(const char *cmd, const simple_t *table); static int upsc_getvalue(const char *cmd, const char *param, const char *resp, const char *var, char *ret); static int upscsend(const char *cmd); static int upscrecv(char *buf); static int upsc_simple(const simple_t *sp, const char *var, const char *val); static void check_uppm(void); static float batt_charge_pct(void); void upsdrv_help(void) { } void upsdrv_initups(void) { struct termios tio; int baud = B1200; char *str; if ((str = getval("baudrate")) != NULL) { int temp = atoi(str); switch (temp) { case 300: baud = B300; break; case 600: baud = B600; break; case 1200: baud = B1200; break; case 2400: baud = B2400; break; case 4800: baud = B4800; break; case 9600: baud = B9600; break; case 19200: baud = B19200; break; case 38400: baud = B38400; break; default: fatalx(EXIT_FAILURE, "Unrecognized baudrate: %s", str); } upsdebugx(1, "baud_rate = %d", temp); } upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, baud); if (tcgetattr(upsfd, &tio) != 0) fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", device_path); tio.c_lflag = ICANON; tio.c_iflag |= IGNCR; /* Ignore CR */ tio.c_cc[VMIN] = 0; tio.c_cc[VTIME] = 0; tcsetattr(upsfd, TCSANOW, &tio); if ((str = getval("input_timeout")) != NULL) { int temp = atoi(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad input_timeout parameter: %s", str); input_timeout_sec = temp; } upsdebugx(1, "input_timeout = %d Sec", input_timeout_sec); if ((str = getval("output_pace")) != NULL) { int temp = atoi(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad output_pace parameter: %s", str); output_pace_usec = temp; } upsdebugx(1, "output_pace = %d uSec", output_pace_usec); if ((str = getval("full_update_timer")) != NULL) { int temp = atoi(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad full_update_timer parameter: %s", str); full_update_timer = temp; } upsdebugx(1, "full_update_timer = %d Sec", full_update_timer); use_crlf = testvar("use_crlf"); upsdebugx(1, "use_crlf = %d", use_crlf); use_pre_lf = testvar("use_pre_lf"); upsdebugx(1, "use_pre_lf = %d", use_pre_lf); } void upsdrv_initinfo(void) { if (!upsc_commandlist()) { upslogx(LOG_ERR, "No contact with UPS, delaying init."); status = UPSC_STAT_NOTINIT; return; } else { status = 0; } upsc_getbaseinfo(); if (can_upda) { upsc_flush_input(); upscsend("UPDA"); } if (can_upid) { upsc_getvalue("UPID", NULL, "ACID", "ups.id", NULL); } if (can_uppm) { check_uppm(); } /* make sure we have some sensible defaults */ setvar("ups.delay.shutdown", "10"); setvar("ups.delay.reboot", "60"); upsh.instcmd = instcmd; upsh.setvar = setvar; } /* Change a variable name in a table */ static void change_name(simple_t *sp, const char *oldname, const char *newname) { while(sp->code) { if (sp->desc && !strcmp(sp->desc, oldname)) { sp->desc = strdup(newname); if (dstate_getinfo(oldname)) { dstate_setinfo(newname, "%s", dstate_getinfo(oldname)); } dstate_delinfo(oldname); upsdebugx(1, "Changing name: %s => %s", oldname, newname); break; } sp++; } } static float calc_upsload(void) { float load=-1, nom_out_power=-1, nom_out_realpower=-1, maxcurr, tmp; const char *s; /* Some UPSen (Fiskars 9000 for example) only reports current, and * only the max current */ if (nom_out_current > 0) { maxcurr = nom_out_current; } else { maxcurr = max_out_current; } if (maxcurr > 0) { if ((s=dstate_getinfo("output.L1.current")) || (s=dstate_getinfo("output.current"))) { if (sscanf(s, "%f", &tmp) == 1) { load = tmp/maxcurr; } } if ((s=dstate_getinfo("output.L2.current"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp=tmp/maxcurr; if (tmp>load) { load = tmp; } } } if ((s=dstate_getinfo("output.L3.current"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp=tmp/maxcurr; if (tmp>load) { load = tmp; } } } } /* This is aggregated (all phases) */ if ((s=dstate_getinfo("ups.power.nominal"))) { if (sscanf(s, "%f", &nom_out_power) != 1) { nom_out_power = -1; } } if (nom_out_power > 0) { if ((s=dstate_getinfo("output.L1.power"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp /= (nom_out_power/num_outphases); if (tmp>load) { load = tmp; } dstate_setinfo("output.L1.power.percent", "%.1f", tmp*100); } } if ((s=dstate_getinfo("output.L2.power"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp /= (nom_out_power/num_outphases); if (tmp>load) { load = tmp; } dstate_setinfo("output.L2.power.percent", "%.1f", tmp*100); } } if ((s=dstate_getinfo("output.L3.power"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp /= (nom_out_power/num_outphases); if (tmp>load) { load = tmp; } dstate_setinfo("output.L3.power.percent", "%.1f", tmp*100); } } } /* This is aggregated (all phases) */ if ((s=dstate_getinfo("output.realpower.nominal"))) { if (sscanf(s, "%f", &nom_out_realpower) != 1) { nom_out_realpower = -1; } } if (nom_out_realpower >= 0) { if ((s=dstate_getinfo("output.L1.realpower"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp /= (nom_out_realpower/num_outphases); if (tmp>load) { load = tmp; } dstate_setinfo("output.L1.realpower.percent", "%.1f", tmp*100); } } if ((s=dstate_getinfo("output.L2.realpower"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp /= (nom_out_realpower/num_outphases); if (tmp>load) { load = tmp; } dstate_setinfo("output.L2.realpower.percent", "%.1f", tmp*100); } } if ((s=dstate_getinfo("output.L3.realpower"))) { if (sscanf(s, "%f", &tmp) == 1) { tmp /= (nom_out_realpower/num_outphases); if (tmp>load) { load = tmp; } dstate_setinfo("output.L3.realpower.percent", "%.1f", tmp*100); } } } return load; } void upsdrv_updateinfo(void) { time_t now; int ok; float load; if (status & UPSC_STAT_NOTINIT) { upsdrv_initinfo(); } if (status & UPSC_STAT_NOTINIT) { return; } status = 0; ok = upsc_getparams("UPDS", simple); time(&now); if (ok && now - last_full > full_update_timer) { last_full = now; ok = upsc_getparams("UPDV", nominal); if (ok && can_upbs) ok = upsc_getparams("UPBS", battery); } if (!ok) { dstate_datastale(); last_full = 0; return; } if (!inited_phaseinfo) { if (dstate_getinfo("input.L3-L1.voltage") || dstate_getinfo("input.L3-N.voltage")) { num_inphases = 3; change_name(simple, "input.current", "input.L1.current"); change_name(simple, "input.realpower", "input.L1.realpower"); change_name(simple, "input.power", "input.L1.power"); change_name(simple, "input.voltage", "input.L1-N.voltage"); } if (dstate_getinfo("output.L3-L1.voltage") || dstate_getinfo("output.L3-N.voltage")) { const char *s; num_outphases = 3; if ((s=dstate_getinfo("ups.model")) && (!strncmp(s, "UPS9075", 7) || !strncmp(s, "UPS9100", 7) || !strncmp(s, "UPS9150", 7) || !strncmp(s, "UPS9200", 7) || !strncmp(s, "UPS9250", 7) || !strncmp(s, "UPS9300", 7) || !strncmp(s, "UPS9400", 7) || !strncmp(s, "UPS9500", 7) || !strncmp(s, "UPS9600", 7)) ) { /* Insert kludges for Fiskars UPS9000 here */ upslogx(LOG_INFO, "Fiskars UPS9000 detected, protocol kludges activated"); batt_volt_nom = 384; dstate_setinfo("battery.voltage.nominal", "%.0f", batt_volt_nom); } else { outpwr_factor *= 3; } change_name(simple, "output.current", "output.L1.current"); change_name(simple, "output.current.peak", "output.L1.current.peak"); change_name(simple, "output.realpower", "output.L1.realpower"); change_name(simple, "output.power", "output.L1.power"); change_name(simple, "output.voltage", "output.L1-N.voltage"); } dstate_setinfo("input.phases", "%d", num_inphases); dstate_setinfo("output.phases", "%d", num_outphases); inited_phaseinfo=1; } load = calc_upsload(); if (load >= 0) { upsdebugx(2, "ups.load: %.1f", load*100); dstate_setinfo("ups.load", "%.1f", load*100); } else { upsdebugx(2, "ups.load: No value"); } /* TODO/FIXME: Set UPS date/time on startup and daily if needed */ if (can_updt) { char dtbuf[UPSC_BUFLEN]; if (upsc_getvalue("UPDT", "0", "ACDT", NULL, dtbuf)) { dstate_setinfo("ups.date", "%s", dtbuf); } } if (can_uptm) { char tmbuf[UPSC_BUFLEN]; if (upsc_getvalue("UPTM", "0", "ACTM", NULL, tmbuf)) { dstate_setinfo("ups.time", "%s", tmbuf); } } if (batt_charge < 0) { if (batt_current < 0) { /* Reset battery current history if discharging */ numbatthist = lastbatthist = 0; } batt_charge = batt_charge_pct(); } if (batt_charge >= 0) { dstate_setinfo("battery.charge", "%.1f", batt_charge); } else { dstate_delinfo("battery.charge"); } /* 9999 == unknown value */ if (batt_runtime >= 0 && batt_runtime < 9999) { dstate_setinfo("battery.runtime", "%.0f", batt_runtime*60); } else if (load > 0 && batt_disch_curr_max != 0) { float est_battcurr = load * abs(batt_disch_curr_max); /* Peukert equation */ float runtime = (batt_cap_nom*3600)/pow(est_battcurr, 1.35); upsdebugx(2, "Calculated runtime: %.0f seconds", runtime); if (batt_runtime_max > 0 && runtime > batt_runtime_max*60) { runtime = batt_runtime_max*60; } dstate_setinfo("battery.runtime", "%.0f", runtime); } else if (batt_runtime_max > 0) { /* Show max possible runtime as reported by UPS */ dstate_setinfo("battery.runtime", "%.0f", batt_runtime_max*60); } else { dstate_delinfo("battery.runtime"); } /* Some UPSen only provides this when on battery, so reset between * each iteration to make sure we use the right value */ batt_charge = -1; batt_runtime = -1; if (!(status & UPSC_STAT_ONBATT)) status |= UPSC_STAT_ONLINE; upsc_setstatus(status); dstate_dataok(); ser_comm_good(); } void upsdrv_shutdown(void) { upslogx(LOG_EMERG, "Shutting down..."); /* send shutdown command twice, just to be sure */ instcmd("shutdown.reboot", NULL); sleep(1); instcmd("shutdown.reboot", NULL); sleep(1); } static int instcmd (const char *auxcmd, const char *data) { cmd_t *cp; if (!strcasecmp(auxcmd, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.disable'"); return instcmd("beeper.disable", NULL); } if (!strcasecmp(auxcmd, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } upsdebugx(1, "Instcmd: %s %s", auxcmd, data ? data : "\"\""); for (cp = commands; cp->cmd; cp++) { if (strcasecmp(cp->cmd, auxcmd)) { continue; } upscsend(cp->upsc); if (cp->upsp) { upscsend(cp->upsp); } else if (data) { upscsend(data); } return STAT_INSTCMD_HANDLED; } upslogx(LOG_INFO, "instcmd: unknown command %s", auxcmd); return STAT_INSTCMD_UNKNOWN; } static int setvar (const char *var, const char *data) { cmd_t *cp; upsdebugx(1, "Setvar: %s %s", var, data); for (cp = variables; cp->cmd; cp++) { if (strcasecmp(cp->cmd, var)) { continue; } upsc_getvalue(cp->upsc, data, cp->upsp, cp->cmd, NULL); return STAT_SET_HANDLED; } upslogx(LOG_INFO, "Setvar: unsettable variable %s", var); return STAT_SET_UNKNOWN; } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "manufacturer", "manufacturer [unknown]"); addvar(VAR_VALUE, "baudrate", "Serial interface baudrate [1200]"); addvar(VAR_VALUE, "input_timeout", "Command response timeout [2]"); addvar(VAR_VALUE, "output_pace", "Output character delay in usecs [200]"); addvar(VAR_VALUE, "full_update", "Delay between full value downloads [60]"); addvar(VAR_FLAG, "use_crlf", "Use CR-LF to terminate commands to UPS"); addvar(VAR_FLAG, "use_pre_lf", "Use LF to introduce commands to UPS"); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } /* * Generate status string from bitfield */ static void upsc_setstatus(unsigned int status) { /* * I'll look for all available statuses, even though they might not be * supported in the UPScode II protocol. */ status_init(); if (status & UPSC_STAT_ONLINE) status_set("OL"); if (status & UPSC_STAT_ONBATT) status_set("OB"); if (status & UPSC_STAT_LOBATT) status_set("LB"); if (status & UPSC_STAT_REPLACEBATT) status_set("RB"); if (status & UPSC_STAT_BOOST) status_set("BOOST"); if (status & UPSC_STAT_TRIM) status_set("TRIM"); if (status & UPSC_STAT_OVERLOAD) status_set("OVER"); if (status & UPSC_STAT_CALIBRATION) status_set("CAL"); if (status & UPSC_STAT_OFF) status_set("OFF"); if (status & UPSC_STAT_BYPASS) status_set("BYPASS"); status_commit(); } /* Add \r to end of command and send to UPS */ /* returns < 0 on errors, 0 on timeout and > 0 on success. */ static int upscsend(const char *cmd) { int res; res = ser_send_pace(upsfd, output_pace_usec, "%s%s%s", use_pre_lf ? "\n" : "", cmd, use_crlf ? "\r\n" : "\r"); if (res < 0) { upsdebug_with_errno(3, "upscsend"); } else if (res == 0) { upsdebugx(3, "upscsend: Timeout"); } else { upsdebugx(3, "upscsend: '%s'", cmd); } return res; } /* Return a string read from UPS */ /* returns < 0 on errors, 0 on timeout and > 0 on success. */ static int upscrecv(char *buf) { int res; /* NOTE: the serial port is set to use Canonical Mode Input Processing, which means ser_get_buf() either returns one line terminated with ENDCHAR, an error or times out. */ while (1) { res = ser_get_buf(upsfd, buf, UPSC_BUFLEN, input_timeout_sec, 0); if (res != 1) { break; } /* Only one character, must be ENDCHAR */ upsdebugx(3, "upscrecv: Empty line"); } if (res < 0) { upsdebug_with_errno(3, "upscrecv"); } else if (res == 0) { upsdebugx(3, "upscrecv: Timeout"); } else { upsdebugx(3, "upscrecv: %u bytes:\t'%s'", res-1, str_rtrim(buf, ENDCHAR)); } return res; } static void upsc_flush_input(void) { /* char buf[UPSC_BUFLEN]; do { upscrecv(buf); if (strlen(buf) > 0) upsdebugx(1, "Skipping input: %s", buf); } while (strlen(buf) > 0); */ ser_flush_in(upsfd, "", nut_debug_level); } /* check which commands this ups supports. * Returns TRUE if command list was recieved, FALSE otherwise */ static int upsc_commandlist(void) { char buf[UPSC_BUFLEN]; cmd_t *cp; upsc_flush_input(); upscsend("UPCL"); while (1) { upscrecv(buf); if (strlen(buf) == 0) { upslogx(LOG_ERR, "Missing UPCL after UPCL"); return 0; } upsdebugx(2, "Supports command: %s", buf); if (strcmp(buf, "UPBS") == 0) can_upbs = 1; else if (strcmp(buf, "UPPM") == 0) can_uppm = 1; else if (strcmp(buf, "UPID") == 0) can_upid = 1; else if (strcmp(buf, "UPDA") == 0) can_upda = 1; else if (strcmp(buf, "UPDT") == 0) can_updt = 1; else if (strcmp(buf, "UPTM") == 0) can_uptm = 1; else if (strcmp(buf, "UPSD") == 0) can_upsd = 1; else if (strcmp(buf, "UPPC") == 0) can_uppc = 1; for (cp = commands; cp->cmd; cp++) { if (cp->upsc && strcmp(cp->upsc, buf) == 0) { upsdebugx(1, "instcmd: %s %s", cp->cmd, cp->upsc); dstate_addcmd(cp->cmd); cp->enabled = 1; break; } } for (cp = variables; cp->cmd; cp++) { if (cp->upsc && strcmp(cp->upsc, buf) == 0) { upsdebugx(1, "setvar: %s %s", cp->cmd, cp->upsc); cp->enabled = 1; break; } } if (strcmp(buf, "UPCL") == 0) break; } for (cp = variables; cp->cmd; cp++) { if (cp->enabled) { upsc_getvalue(cp->upsc, "0000", cp->upsp, cp->cmd, NULL); dstate_setflags(cp->cmd, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(cp->cmd, 7); } } return 1; } /* get limits and parameters */ static int upsc_getparams(const char *cmd, const simple_t *table) { char var[UPSC_BUFLEN]; char val[UPSC_BUFLEN]; int first = 1; upsc_flush_input(); upscsend(cmd); buffer_empty = 0; while (!buffer_empty) { upscrecv(var); if (strlen(var) == 0) { if (first) upscrecv(var); if (strlen(var) == 0) { ser_comm_fail("Empty string from UPS for %s!", cmd); break; } } first = 0; upscrecv(val); if (strlen(val) == 0) { ser_comm_fail("Empty value from UPS for %s %s!", cmd, var); break; } upsdebugx(2, "Parameter %s %s", var, val); if (!upsc_simple(table, var, val)) upslogx(LOG_ERR, "Unknown response to %s: %s %s", cmd, var, val); } return buffer_empty; } static void check_uppm(void) { int i, last = 0; char var[UPSC_BUFLEN]; char val[UPSC_BUFLEN]; upsc_flush_input(); upscsend("UPPM"); upscsend("0"); upscrecv(var); if (strcmp(var, "ACPM")) upslogx(LOG_ERR, "Bad response to UPPM: %s", var); while (1) { int val, stat; upscrecv(var); if (strlen(var) == 0) break; upsdebugx(2, "UPPM available: %s", var); stat = sscanf(var, "P%2d", &val); if (stat != 1) { upslogx(LOG_ERR, "Bad response to UPPM: %s", var); return; } has_uppm_p[val] = 1; if (val > last) last = val; } for (i = 0; i <= last; i++) { if (!has_uppm_p[i]) continue; upscsend("UPPM"); snprintf(var, sizeof(var), "P%.2d", i); upscsend(var); upscrecv(val); if (strcmp(val, "ACPM")) { upslogx(LOG_ERR, "Bad response to UPPM %s: %s", var, val); continue; } upscrecv(var); upsdebugx(1, "UPPM value: %s", var); } } static int upsc_getvalue(const char *cmd, const char *param, const char *resp, const char *nutvar, char *ret) { char var[UPSC_BUFLEN]; char val[UPSC_BUFLEN]; upsdebugx(2, "Request value: %s %s", cmd, param ? param : "\"\""); upscsend(cmd); if (param) upscsend(param); upscrecv(var); upscrecv(val); upsdebugx(2, "Got value: %s %s", var, val); if (strcmp(var, resp)) { upslogx(LOG_ERR, "Bad response to %s %s: %s %s", cmd, param ? param : "\"\"", var, val); return 0; } else { if (nutvar) dstate_setinfo(nutvar, "%s", val); if (ret) strcpy(ret, val); } return 1; } static void upsc_getbaseinfo(void) { char *buf; dstate_setinfo("ups.mfr", "%s", ((buf = getval("manufacturer")) != NULL) ? buf : "unknown"); if (!upsc_getvalue("UPTP", NULL, "NNAME", "ups.model", NULL)) upsc_getvalue("UPTP", NULL, "NNAME", "ups.model", NULL); upsc_getvalue("UPSN", "0", "ACSN", "ups.serial", NULL); } static int upsc_simple(const simple_t *sp, const char *var, const char *val) { int stat; float fval; while (sp->code) { if (strcmp(sp->code, var) == 0) { switch (sp->type) { case t_setval: stat = sscanf(val, "%f", &fval); if (stat != 1) upslogx(LOG_ERR, "Bad float: %s %s", var, val); if (sp->desc) dstate_setinfo(sp->desc, "%.2f", fval); *sp->aux = fval; break; case t_setrecip: stat = sscanf(val, "%f", &fval); if (stat != 1) upslogx(LOG_ERR, "Bad float: %s %s", var, val); if (sp->desc) dstate_setinfo(sp->desc, "%s", val); *sp->aux = 1/fval; break; case t_setpct: stat = sscanf(val, "%f", &fval); if (stat != 1) upslogx(LOG_ERR, "Bad float: %s %s", var, val); *sp->aux = fval*100; if (sp->desc) dstate_setinfo(sp->desc, "%s", val); break; case t_setrecpct: stat = sscanf(val, "%f", &fval); if (stat != 1) upslogx(LOG_ERR, "Bad float: %s %s", var, val); *sp->aux = 1/fval*100; if (sp->desc) dstate_setinfo(sp->desc, "%s", val); break; case t_final: buffer_empty = 1; case t_value: if (!sp->desc) { break; } if (sscanf(val, "%f", &fval) == 1) { if (sp->aux != NULL) { fval *= *(sp->aux); } dstate_setinfo(sp->desc, "%.2f", fval); } else { upslogx(LOG_ERR, "Bad float in %s: %s", var, val); dstate_setinfo(sp->desc, "%s", val); } break; case t_finstr: buffer_empty = 1; case t_string: if (!sp->desc) { break; } dstate_setinfo(sp->desc, "%s", val); break; case t_status: if (strcmp(val, "00") == 0) ; else if (strcmp(val, "11") == 0) status |= sp->status; else upslogx(LOG_ERR, "Unknown status value: '%s' '%s'", var, val); break; case t_alarm: if (strcmp(val, "00") == 0) ; else if (strcmp(val, "11") == 0) status |= sp->status; else upslogx(LOG_ERR, "Unknown alarm value: '%s' '%s'", var, val); break; case t_ignore: upsdebugx(3, "Ignored value: %s %s", var, val); break; case t_list: if (!upsc_simple(sp->stats, val, "11")) upslogx(LOG_ERR, "Unknown value: %s %s", var, val); break; default: upslogx(LOG_ERR, "Unknown type for %s", var); break; } return 1; } sp++; } return 0; } static float batt_charge_pct(void) { float chg=-1; /* This is only a rough estimate of charge status while charging. * When on battery something like Peukert's equation should be used */ if (batt_current >= 0) { float maxcurr = 10; /* Assume 10A max as default */ float avgcurr = 0; int i; batthist[lastbatthist] = batt_current; lastbatthist = (lastbatthist+1) % NUM_BATTHIST; if (numbatthist < NUM_BATTHIST) { numbatthist++; } for(i=0; i 0) { /* Estimate max charge current based on battery size */ maxcurr = batt_cap_nom * 0.3; } chg = maxcurr - avgcurr; chg *= (100/maxcurr); } /* Old method, assumes battery high/low-voltages provided by UPS are * applicable to battery charge, but they usually aren't */ else if (batt_volt_low > 0 && batt_volt_high > 0 && batt_volt > 0) { if (batt_volt > batt_volt_high) { chg=100; } else if (batt_volt < batt_volt_low) { chg=0; } else { chg = (batt_volt - batt_volt_low) / (batt_volt_high - batt_volt_low); chg*=100; } } else { return -1; } if (chg < 0) { chg = 0; } else if (chg > 100) { chg = 100; } return chg; } /* vim:noet:ts=8:sw=8:cindent */ nut-2.7.4/drivers/isbmex.c0000644000175000017500000002221512640473702012373 00000000000000/* isbmex.c - model specific routines for SOLA/BASIC Mexico (ISBMEX) models Copyright (C) 2005 Ricardo Martinezgarza Copyright (C) 2002 Edscott Wilson Garcia Copyright (C) 1999 Russell Kroll 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 "main.h" #include "serial.h" #include /* for sqrt */ #include #define DRIVER_NAME "ISBMEX UPS driver" #define DRIVER_VERSION "0.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Ricardo Martinezgarza \n" \ "Edscott Wilson Garcia \n" \ "Russell Kroll ", DRV_STABLE, { NULL } }; #define xDEBUG #ifdef DEBUG #define D(x) x #else #define D(x) #endif /*#define ENDCHAR '&'*/ #define MAXTRIES 15 /* #define IGNCHARS "" */ float lagrange(unsigned int vbyte) { float f0, f1, f2, f3, f4, f5, f6; float a, b, c, d, e, g, h; const float x0=144.0, x1=154.0, x2=184.0, x3=195.0, x4=215.0, x5=228.0, x6=249.0; const float fX0=95.1, fX1=98.3, fX2=112.6, fX3=116.5, fX4=126.0, fX5=131.0, fX6=140.3; if(vbyte < 144) return 0.0; f0 = vbyte - x0; f1 = vbyte - x1; f2 = vbyte - x2; f3 = vbyte - x3; f4 = vbyte - x4; f5 = vbyte - x5; f6 = vbyte - x6; b = (f1 * f2 * f3 * f4 * f5 * f6 * fX0)/((x0 - x1) * (x0 - x2)); b = b / ((x0 - x3) * (x0 - x4)); b = b / ((x0 - x5) * (x0 - x6)); c = (f0 * f2 * f3 * f4 * f5 * f6 * fX1)/((x1 - x0) * (x1 - x2)); c = c / ((x1 - x3) * (x1 - x4)); c = c / ((x1 - x5) * (x1 - x6)); d = (f0 * f1 * f3 * f4 * f5 * f6 * fX2)/((x2 - x0) * (x2 - x1)); d = d / ((x2 - x3) * (x2 - x4)); d = d / ((x2 - x5) * (x2 - x6)); e = (f0 * f1 * f2 * f4 * f5 * f6 * fX3)/((x3 - x0) * (x3 - x1)); e = e / ((x3 - x2) * (x3 - x4)); e = e / ((x3 - x5) * (x3 - x6)); a = (f0 * f1 * f2 * f3 * f5 * f6 * fX4)/((x4 - x0) * (x4 - x1)); a = a / ((x4 - x2) * (x4 - x3)); a = a / ((x4 - x5) * (x4 - x6)); g = (f0 * f1 * f2 * f3 * f4 * f6 * fX5)/((x5 - x0) * (x5 - x1)); g = g / ((x5 - x2) * (x5 - x3)); g = g / ((x5 - x4) * (x5 - x6)); h = (f0 * f1 * f2 * f3 * f4 * f5 * fX6)/((x6 - x0) * (x6 - x1)); h = h / ((x6 - x2) * (x6 - x3)); h = h / ((x6 - x4) * (x6 - x5)); return a + b + c + d + e + g + h; } float interpol(float vbytes) { const int x[7]={75,83,87,98,103,118,145}; const float f[7]={96.0,102.0,105.0,113.0,116.0,124.0,140.0}; float l[7]; float t, volts; const int n=6; int i, j; if(vbytes < x[0]) return 0.0; if(vbytes > x[6]) return f[6]; for(i=0; i<=n; i++) { if((int)vbytes == x[i]) return f[i]; } for(i=0; i<=n; i++) { l[i] = 1.0; for(j=0; j<=n; j++) { if(j!=i) l[i] *= (float)(x[i] - x[j]); } l[i] = f[i] / l[i]; } t = 1.0; for(i=0; i<=n; i++) t *= (vbytes - (float)x[i]); volts = 0.0; for(i=0; i<=n; i++) volts += (l[i] * t / (vbytes - (float)x[i])); return volts; } void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "Sola/Basic Mexico"); dstate_setinfo("ups.model", "SR-Inet 280/300/400/480/500/800/1000"); /* high/low voltage */ dstate_setinfo("input.transfer.low", "102.0"); /* defined */ dstate_setinfo("input.transfer.high", "140.0"); /* defined */ dstate_setinfo("output.voltage", "120.0"); /* defined */ /* addinfo(INFO_, "", 0, 0); */ /*printf("Using %s %s on %s\n", getdata(INFO_MFR), getdata(INFO_MODEL), device_path);*/ } static const char *getpacket(int *we_know){ fd_set readfds; struct timeval tv; int bytes_per_packet=0; int ret; static const char *packet_id=NULL; static char buf[256]; const char *s; ssize_t r; bytes_per_packet=*we_know; D(printf("getpacket with %d\n",bytes_per_packet);) FD_ZERO(&readfds); FD_SET(upsfd,&readfds); /* Wait up to 2 seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; ret=select(upsfd+1, &readfds, NULL, NULL, &tv); if (!ret) { s="Nothing received from UPS. Check cable conexion"; upslogx(LOG_ERR, "%s", s); D(printf("%s\n",s);) return NULL; } r=read(upsfd,buf,255); D(printf("%d bytes read: ",r);) buf[r]=0; if (bytes_per_packet && r < bytes_per_packet){ ssize_t rr; D(printf("short read...\n");) usleep(500000); tv.tv_sec = 2; tv.tv_usec = 0; ret=select(upsfd+1, &readfds, NULL, NULL, &tv); if (!ret) return NULL; rr=read(upsfd,buf+r,255-r); r += rr; if (r < bytes_per_packet) return NULL; } if (!bytes_per_packet){ /* packet size determination */ /* if (r%10 && r%9) { printf("disregarding incomplete packet\n"); return NULL; }*/ if (r%10==0) *we_know=10; else if (r%9==0) *we_know=9; return NULL; } /* by here we have bytes_per_packet and a complete packet */ /* lets check if within the complete packet we have a valid packet */ if (bytes_per_packet == 10) packet_id="&&&"; else packet_id="***"; s=strstr(buf,packet_id); /* check validity of packet */ if (!s) { s="isbmex: no valid packet signature!"; upslogx(LOG_ERR, "%s", s); D(printf("%s\n",s);) *we_know=0; return NULL; } D(if (s != buf) printf("overlapping packet received\n");) if ((int) strlen(s) < bytes_per_packet) { D(printf("incomplete packet information\n");) return NULL; } #ifdef DEBUG printf("Got signal:"); {int i;for (i=0;i",(unsigned char)s[i]);} printf("\n"); #endif return s; } void upsdrv_updateinfo(void) { static float high_volt=-1, low_volt=999; const char *buf=NULL; char buf2[17]; int i; static int bytes_per_packet=0; for (i=0;i<5;i++) { if ((buf=getpacket(&bytes_per_packet)) != NULL) break; } if (!bytes_per_packet || !buf) { dstate_datastale(); return; } /* do the parsing */ { float in_volt,battpct,acfreq; double d; D(printf("parsing (%d bytes per packet)\n",bytes_per_packet);) /* input voltage :*/ if (bytes_per_packet==9) { in_volt = lagrange((unsigned char)buf[3]); } else { in_volt = interpol(sqrt((float)((unsigned char)buf[3]*256+(unsigned char)buf[4]))); } snprintf(buf2,16,"%5.1f",in_volt); D(printf("utility=%s\n",buf2);) dstate_setinfo("input.voltage", "%s", buf2); if (in_volt >= high_volt) high_volt=in_volt; snprintf(buf2,16,"%5.1f",high_volt); D(printf("highvolt=%s\n",buf2);) dstate_setinfo("input.voltage.maximum", "%s", buf2); if (in_volt <= low_volt) low_volt=in_volt; snprintf(buf2,16,"%5.1f",low_volt); D(printf("lowvolt=%s\n",buf2);) dstate_setinfo("input.voltage.minimum", "%s", buf2); battpct = ((double)((unsigned char)buf[(bytes_per_packet==10)?5:4])-168.0)*(100.0/(215.0-168.0)); snprintf(buf2,16,"%5.1f",battpct); D(printf("battpct=%s\n",buf2);) dstate_setinfo("battery.charge", "%s", buf2); d=(unsigned char)buf[(bytes_per_packet==10)?6:5]*256 + (unsigned char)buf[(bytes_per_packet==10)?7:6]; acfreq = 1000000/d; snprintf(buf2,16,"%5.2f",acfreq); D(printf("acfreq=%s\n",buf2);) dstate_setinfo("input.frequency", "%s", buf2); D(printf("status: ");) status_init(); switch (buf[(bytes_per_packet==10)?8:7]){ case 48: break; /* normal operation */ case 49: D(printf("BOOST ");) status_set("BOOST"); break; case 50: D(printf("TRIM ");) status_set("TRIM"); default: break; } switch (buf[(bytes_per_packet==10)?9:8]){ case 48: D(printf("OL ");) status_set("OL"); break; case 50: D(printf("LB ");) status_set("LB"); case 49: D(printf("OB ");) status_set("OB"); break; default: break; } D(printf("\n");) status_commit(); } dstate_dataok(); return; } void upsdrv_shutdown(void) { /* shutdown is supported on models with * contact closure. Some ISB models with serial * support support contact closure, some don't. * If yours does support it, then a 12V signal * on pin 9 does the trick (only when ups is * on OB condition) */ /* * here try to do the pin 9 trick, if it does not * work, else:*/ /* fatalx(EXIT_FAILURE, "Shutdown only supported with the Generic Driver, type 6 and special cable"); */ /*fatalx(EXIT_FAILURE, "shutdown not supported");*/ int i; for(i=0;i<=5;i++) { ser_send_char(upsfd, '#'); usleep(50000); } } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B9600); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/bestpower-mib.h0000644000175000017500000000023112640443572013664 00000000000000#ifndef BESTPOWER_MIB_H #define BESTPOWER_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t bestpower; #endif /* BESTPOWER_MIB_H */ nut-2.7.4/drivers/apcsmart-old.h0000644000175000017500000002213712640473702013502 00000000000000/* apcsmart.h - command table for APC smart protocol units Copyright (C) 1999 Russell Kroll (C) 2000 Nigel Metheringham This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "serial.h" #include "timehead.h" #define APC_TABLE_VERSION "version 2.2" /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF */ /* characters ignored by default */ #define IGNCHARS "\015+$|!~%?=#&" /* special characters to ignore */ /* these one is used only during startup, due to ^Z sending certain characters such as # */ #define MINIGNCHARS "\015+$|!" /* minimum set of special characters to ignore */ /* normal polls: characters we don't want to parse (including a few alerts) */ #define POLL_IGNORE "\015&|" /* alert characters we care about - OL, OB, LB, not LB, RB, OVER, not OVER */ #define POLL_ALERT "$!%+#?=" #define UPSDELAY 50000 /* slow down multicharacter commands */ #define CMDLONGDELAY 1500000 /* some commands need a 1.5s gap for safety */ #define SER_WAIT_SEC 3 /* wait up to 3.0 sec for ser_get calls */ #define SER_WAIT_USEC 0 /* dangerous instant commands must be reconfirmed within a 12 second window */ #define MINCMDTIME 3 #define MAXCMDTIME 15 /* it only does two strings, and they're both the same length */ #define APC_STRLEN 8 /* --------------- */ /* status bits */ #define APC_STAT_CAL 1 /* calibration */ #define APC_STAT_TRIM 2 /* SmartTrim */ #define APC_STAT_BOOST 4 /* SmartBoost */ #define APC_STAT_OL 8 /* on line */ #define APC_STAT_OB 16 /* on battery */ #define APC_STAT_OVER 32 /* overload */ #define APC_STAT_LB 64 /* low battery */ #define APC_STAT_RB 128 /* replace battery */ /* serial protocol: special commands - initialization and such */ #define APC_STATUS 'Q' #define APC_GOSMART 'Y' #define APC_GODUMB 'R' #define APC_CMDSET 'a' #define APC_CAPABILITY 26 /* ^Z */ #define APC_NEXTVAL '-' /* --------------- */ /* Driver command table flag values */ #define APC_POLL 0x0001 /* Poll this variable regularly */ #define APC_PRESENT 0x0004 /* Capability seen on this UPS */ #define APC_RW 0x0010 /* read-write variable */ #define APC_ENUM 0x0020 /* enumerated type */ #define APC_STRING 0x0040 /* string */ #define APC_NASTY 0x0100 /* Nasty command - take care */ #define APC_REPEAT 0x0200 /* Command needs sending twice */ #define APC_FORMATMASK 0xFF0000 /* Mask for apc data formats */ #define APC_F_PERCENT 0x020000 /* Data in a percent format */ #define APC_F_VOLT 0x030000 /* Data in a voltage format */ #define APC_F_AMP 0x040000 /* Data in a current/amp format */ #define APC_F_CELSIUS 0x050000 /* Data in a temp/C format */ #define APC_F_HEX 0x060000 /* Data in a hex number format */ #define APC_F_DEC 0x070000 /* Data in a decimal format */ #define APC_F_SECONDS 0x100000 /* Time in seconds */ #define APC_F_MINUTES 0x110000 /* Time in minutes */ #define APC_F_HOURS 0x120000 /* Time in hours */ #define APC_F_REASON 0x130000 /* Reason of transfer */ #define APC_F_LEAVE 0 /* Just pass this through */ typedef struct { const char *name; /* the variable name */ unsigned int flags; /* various flags */ char cmd; /* command character */ } apc_vartab_t; apc_vartab_t apc_vartab[] = { { "ups.firmware.old", 0, 'V' }, { "ups.firmware", 0, 'b' }, { "ups.firmware.aux", 0, 'v' }, { "ups.model", 0, 0x01 }, { "ups.serial", 0, 'n' }, { "ups.mfr.date", 0, 'm' }, { "ups.temperature", APC_POLL|APC_F_CELSIUS, 'C' }, { "ups.load", APC_POLL|APC_F_PERCENT, 'P' }, { "ups.test.interval", APC_F_HOURS, 'E' }, { "ups.test.result", APC_POLL, 'X' }, { "ups.delay.start", APC_F_SECONDS, 'r' }, { "ups.delay.shutdown", APC_F_SECONDS, 'p' }, { "ups.id", APC_STRING, 'c' }, { "ups.contacts", APC_POLL|APC_F_HEX, 'i' }, { "ups.display.language", 0, 0x0C }, { "input.voltage", APC_POLL|APC_F_VOLT, 'L' }, { "input.frequency", APC_POLL|APC_F_DEC, 'F' }, { "input.sensitivity", 0, 's' }, { "input.quality", APC_POLL|APC_F_HEX, '9' }, { "input.transfer.low", APC_F_VOLT, 'l' }, { "input.transfer.high", APC_F_VOLT, 'u' }, { "input.transfer.reason", APC_POLL|APC_F_REASON, 'G' }, { "input.voltage.maximum", APC_POLL|APC_F_VOLT, 'M' }, { "input.voltage.minimum", APC_POLL|APC_F_VOLT, 'N' }, { "output.current", APC_POLL|APC_F_AMP, '/' }, { "output.voltage", APC_POLL|APC_F_VOLT, 'O' }, { "output.voltage.nominal", APC_F_VOLT, 'o' }, { "ambient.humidity", APC_POLL|APC_F_PERCENT, 'h' }, { "ambient.humidity.high", APC_F_PERCENT, '{' }, { "ambient.humidity.low", APC_F_PERCENT, '}' }, { "ambient.temperature", APC_POLL|APC_F_CELSIUS, 't' }, { "ambient.temperature.high", APC_F_CELSIUS, '[' }, { "ambient.temperature.low", APC_F_CELSIUS, ']' }, { "battery.date", APC_STRING, 'x' }, { "battery.charge", APC_POLL|APC_F_PERCENT, 'f' }, { "battery.charge.restart", APC_F_PERCENT, 'e' }, { "battery.voltage", APC_POLL|APC_F_VOLT, 'B' }, { "battery.voltage.nominal", 0, 'g' }, { "battery.runtime", APC_POLL|APC_F_MINUTES, 'j' }, { "battery.runtime.low", APC_F_MINUTES, 'q' }, { "battery.packs", APC_F_DEC, '>' }, { "battery.packs.bad", APC_F_DEC, '<' }, { "battery.alarm.threshold", 0, 'k' }, /* todo: I = alarm enable (hex field) - split into alarm.n.enable J = alarm status (hex field) - split into alarm.n.status 0x15 = output voltage selection (APC_F_VOLT) 0x5C = load power (APC_POLL|APC_F_PERCENT) */ {NULL, 0, 0}, }; /* ------ instant commands ------ */ #define APC_CMD_FPTEST 'A' #define APC_CMD_CALTOGGLE 'D' #define APC_CMD_SHUTDOWN 'K' #define APC_CMD_SOFTDOWN 'S' #define APC_CMD_GRACEDOWN '@' #define APC_CMD_SIMPWF 'U' #define APC_CMD_BTESTTOGGLE 'W' #define APC_CMD_OFF 'Z' #define APC_CMD_ON 0x0E /* ^N */ #define APC_CMD_BYPTOGGLE '^' typedef struct { const char *name; int flags; char cmd; } apc_cmdtab_t; apc_cmdtab_t apc_cmdtab[] = { { "load.off", APC_NASTY|APC_REPEAT, APC_CMD_OFF }, { "load.on", APC_REPEAT, APC_CMD_ON }, { "test.panel.start", 0, APC_CMD_FPTEST }, { "test.failure.start", 0, APC_CMD_SIMPWF }, { "test.battery.start", 0, APC_CMD_BTESTTOGGLE }, { "test.battery.stop", 0, APC_CMD_BTESTTOGGLE }, { "shutdown.return.grace", APC_NASTY, APC_CMD_GRACEDOWN }, { "shutdown.return", APC_NASTY, APC_CMD_SOFTDOWN }, { "shutdown.stayoff", APC_NASTY|APC_REPEAT, APC_CMD_SHUTDOWN }, { "calibrate.start", 0, APC_CMD_CALTOGGLE }, { "calibrate.stop", 0, APC_CMD_CALTOGGLE }, { "bypass.start", 0, APC_CMD_BYPTOGGLE }, { "bypass.stop", 0, APC_CMD_BYPTOGGLE }, { NULL, 0, 0 } }; /* compatibility with hardware that doesn't do APC_CMDSET ('a') */ struct { const char *firmware; const char *cmdchars; int flags; } compat_tab[] = { /* APC Matrix */ { "0XI", "789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 }, { "0XM", "789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 }, { "0ZI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, { "5UI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, { "5ZM", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, /* APC600 */ { "6QD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "6QI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "6TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "6TI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* SmartUPS 900 */ { "7QD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7QI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* SmartUPS 900I */ { "7II", "79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, /* SmartUPS 2000I */ { "9II", "79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, { "9GI", "79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, /* SmartUPS 1250 */ { "8QD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8QI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8TI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* CS 350 */ { "5.4.D", "\1ABPQRSUYbdfgjmnx9", 0 }, /* Smart-UPS 600 */ { "D9", "789ABCEFGKLMNOPQRSUVWXYZ", 0 }, { "D8", "789ABCEFGKLMNOPQRSUVWXYZ", 0 }, { "D7", "789ABCEFGKLMNOPQRSUVWXYZ", 0 }, { "D6", "789ABCEFGKLMNOPQRSUVWXYZ", 0 }, { "D5", "789ABCEFGKLMNOPQRSUVWXYZ", 0 }, { "D4", "789ABCEFGKLMNOPQRSUVWXYZ", 0 }, { NULL, NULL, 0 }, }; nut-2.7.4/drivers/nutdrv_qx_megatec-old.c0000644000175000017500000001477412640473702015412 00000000000000/* nutdrv_qx_megatec-old.c - Subdriver for Megatec/old protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_megatec-old.h" #define MEGATEC_OLD_VERSION "Megatec/old 0.07" /* qx2nut lookup table */ static item_t megatec_old_qx2nut[] = { /* * > [D\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "D\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "D\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "D\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "D\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "D\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "D\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "D\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ { "ups.alarm", 0, NULL, "D\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "D\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ { "ups.beeper.status", 0, NULL, "D\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ /* * > [F\r] * < [#220.0 000 024.0 50.0\r] * 0123456789012345678901 * 0 1 2 */ { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* * > [I\r] * < [#------------- ------ VT12046Q \r] * 012345678901234567890123456789012345678 * 0 1 2 3 */ { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, /* Instant commands */ { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* Testing table */ #ifdef TESTING static testing_t megatec_old_testing[] = { { "D\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, { "F\r", "#230.0 000 024.0 50.0\r", -1 }, { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, { "Q\r", "", -1 }, { "S03\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0000\r", "", -1 }, { "T04\r", "", -1 }, { "TL\r", "", -1 }, { "T\r", "", -1 }, { "CT\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* Subdriver-specific initups */ static void megatec_old_initups(void) { blazer_initups(megatec_old_qx2nut); } /* Subdriver interface */ subdriver_t megatec_old_subdriver = { MEGATEC_OLD_VERSION, blazer_claim_light, megatec_old_qx2nut, megatec_old_initups, NULL, blazer_makevartable, "ACK", NULL, #ifdef TESTING megatec_old_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/nutdrv_qx_mecer.h0000644000175000017500000000175112640473702014320 00000000000000/* nutdrv_qx_mecer.h - Subdriver for Mecer/Voltronic Power P98 UPSes * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_MECER_H #define NUTDRV_QX_MECER_H #include "nutdrv_qx.h" extern subdriver_t mecer_subdriver; #endif /* NUTDRV_QX_MECER_H */ nut-2.7.4/drivers/powercom.h0000644000175000017500000000635012640473702012746 00000000000000/* * powercom.h - defines for the newpowercom.c driver * * Copyrights: * (C) 2002 Simon Rozman * (C) 1999 Peter Bieringer * * 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 * */ /* C-libary includes */ #include #include #include #include #include "serial.h" #include /* nut includes */ #include "timehead.h" /* supported types */ struct type { const char *name; unsigned char num_of_bytes_from_ups; struct method_of_flow_control { const char *name; void (*setup_flow_control)(void); } flowControl; struct validation_byte { unsigned int index_of_byte, required_value; /* An example might explain the intention better then prose. * Suppose we want to validate the data with: * powercom_raw_data[5] == 0x80 * then we will set index_of_byte to 5U and required_value to * 0x80U: { 5U, 0x80U }. */ } validation[3]; /* The validation array is of length 3 because 3 is longest * validation sequence for any type. */ /* Some UPSs must have a minutes and a seconds arguments for * the COUNTER commands while others are known to work with the * seconds argument alone. */ struct delay_for_power_kill { unsigned int delay[2]; /* { minutes, seconds } */ unsigned char minutesShouldBeUsed; /* 'n' in case the minutes value, which is delay[0], should * be skipped and not sent to the UPS. */ } shutdown_arguments; /* parameters to calculate input and output freq., one pair used for * both input and output functions: * The pair [0],[1] defines parameters for 1/(A*x+B) to calculate freq. * from raw data 'x'. */ float freq[2]; /* parameters to calculate load %, two pairs for each type: * First pair [0],[1] defines the parameters for A*x+B to calculate load * from raw data when offline and the second pair [2],[3] is used when * online */ float loadpct[4]; /* parameters to calculate battery %, five parameters for each type: * First three params [0],[1],[2] defines the parameters for A*x+B*y+C to calculate * battery % (x is raw data, y is load %) when offline. * Fourth and fifth parameters [3],[4] are used to calculate D*x+E when online. */ float battpct[5]; /* parameters to calculate utility and output voltage, two pairs for * each type: * First pair [0],[1] defines the parameters for A*x+B to calculate utility * from raw data when line voltage is >=220 and the second pair [2],[3] * is used otherwise. */ float voltage[4]; }; nut-2.7.4/drivers/al175.c0000644000175000017500000007371412640473702011747 00000000000000/* * al175.c - NUT support for Eltek AL175 alarm module. * AL175 shall be in COMLI mode. * * Copyright (C) 2004-2013 Marine & Bridge Navigation Systems * Author: Kirill Smelkov * * 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 */ /* * - NOTE the following document is referenced in this driver: * * TE-36862-B4 "COMLI COMMUNICATION PROTOCOL IMPLEMENTED IN PRS SYSTEMS", * by Eltek A/S * * * - AL175 debug levels: * * 1 user-level trace (status, instcmd, etc...) * 2 status decode errors * 3 COMLI proto handling errors * 4 raw IO trace * */ #include "main.h" #include "serial.h" #include "timehead.h" #include #include #include #include #include #include "nut_stdint.h" typedef uint8_t byte_t; #define DRIVER_NAME "Eltek AL175/COMLI driver" #define DRIVER_VERSION "0.12" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Kirill Smelkov \n" \ "Marine & Bridge Navigation Systems ", DRV_EXPERIMENTAL, { NULL } }; #define STX 0x02 #define ETX 0x03 #define ACK 0x06 /************ * RAW DATA * ************/ /** * raw_data buffer representation */ typedef struct { byte_t *buf; /*!< the whole buffer address */ unsigned buf_size; /*!< the whole buffer size */ byte_t *begin; /*!< begin of content */ byte_t *end; /*!< one-past-end of content */ } raw_data_t; /** * pseudo-alloca raw_data buffer (alloca is not in POSIX) * @param varp ptr-to local raw_data_t variable to which to alloca * @param buf_array array allocated on stack which will be used as storage * (must be auto-variable) * @return alloca'ed memory as raw_data * * Example: * * raw_data_t ack; * byte_t ack_buf[8]; * * raw_alloc_onstack(&ack, ack_buf); */ #define raw_alloc_onstack(varp, buf_array) do { \ (varp)->buf = &(buf_array)[0]; \ (varp)->buf_size = sizeof(buf_array); \ \ (varp)->begin = (varp)->buf; \ (varp)->end = (varp)->buf; \ } while (0) /** * xmalloc raw buffer * @param size size in bytes * @return xmalloc'ed memory as raw_data */ raw_data_t raw_xmalloc(size_t size) { raw_data_t data; data.buf = xmalloc(size); data.buf_size = size; data.begin = data.buf; data.end = data.buf; return data; } /** * free raw_data buffer * @param buf raw_data buffer to free */ void raw_free(raw_data_t *buf) { free(buf->buf); buf->buf = NULL; buf->buf_size = 0; buf->begin = NULL; buf->end = NULL; } /***************************************************************************/ /*************** * COMLI types * ***************/ /** * COMLI message header info * @see 1. INTRODUCTION */ typedef struct { int id; /*!< Id[1:2] */ int stamp; /*!< Stamp[3] */ int type; /*!< Mess Type[4] */ } msg_head_t; /** * COMLI IO header info * @see 1. INTRODUCTION */ typedef struct { unsigned addr; /*!< Addr[5:8] */ unsigned len; /*!< NOB[9:10] */ } io_head_t; /** * maximum allowed io.len value */ #define IO_LEN_MAX 0xff /** * COMLI header info * @see 1. INTRODUCTION */ typedef struct { msg_head_t msg; /*!< message header [1:4] */ io_head_t io; /*!< io header [5:10] */ } comli_head_t; /****************** * MISC UTILITIES * ******************/ /** * convert hex string to int * @param head input string * @param count string length * @return parsed value (>=0) if success, -1 on error */ static long from_hex(const byte_t *head, unsigned len) { long val=0; while (len-- != 0) { int ch = *head; if (!isxdigit(ch)) return -1; /* wrong character */ val *= 0x10; if (isdigit(ch)) { val += (ch-'0'); } else { /* ch = toupper(ch) without locale-related problems */ if (ch < 'A') ch += 'A' - 'a'; val += 0x0A + (ch-'A'); } ++head; } return val; } /** * compute checksum of a buffer * @see 10. CHECKSUM BCC * @param buf buffer address * @param count no. of bytes in the buffer * @return computed checksum */ static byte_t compute_bcc(const byte_t *buf, size_t count) { byte_t bcc=0; unsigned i; for (i=0; i> 7 ) | ( (x & 0x40) >> 5 ) | ( (x & 0x20) >> 3 ) | ( (x & 0x10) >> 1 ) | ( (x & 0x08) << 1 ) | ( (x & 0x04) << 3 ) | ( (x & 0x02) << 5 ) | ( (x & 0x01) << 7 ); *buf = x; ++buf; --count; } } /********************************************************************/ /* * communication basics * * ME (Monitor Equipment) * PRS (Power Rectifier System) /think of it as of UPS in common speak/ * * there are 2 types of transactions: * * 'ACTIVATE COMMAND' * ME -> PRS (al_prep_activate) * ME <- PRS [ack] (al_check_ack) * * * 'READ REGISTER' * ME -> PRS (al_prep_read_req) * ME <- PRS [data] (al_parse_reply) * */ /******************** * COMLI primitives * ********************/ /************************ * COMLI: OUTPUT FRAMES * ************************/ /** * prepare COMLI sentence * @see 1. INTRODUCTION * @param dest [out] where to put the result * @param h COMLI header info * @param buf data part of the sentence * @param count amount of data bytes in the sentence * * @note: the data are copied into the sentence "as-is", there is no conversion is done. * if the caller wants to reverse bits it is necessary to call reverse_bits(...) prior * to comli_prepare. */ static void comli_prepare(raw_data_t *dest, const comli_head_t *h, const void *buf, size_t count) { /* * 0 1 2 3 4 5 6 7 8 9 10 11 - - - N-1 N * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ...data... | ETX | BCC | * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ * * ^ ^ * | | *begin end */ byte_t *out = dest->begin; /* it's caller responsibility to allocate enough space. else it is a bug in the program */ if ( (out+11+count+2) > (dest->buf + dest->buf_size) ) fatalx(EXIT_FAILURE, "too small dest in comli_prepare\n"); out[0] = STX; snprintf((char *)out+1, 10+1, "%02X%1i%1i%04X%02X", h->msg.id, h->msg.stamp, h->msg.type, h->io.addr, h->io.len); memcpy(out+11, buf, count); reverse_bits(out+11, count); out[11+count] = ETX; out[12+count] = compute_bcc(out+1, 10+count+1); dest->end = dest->begin + (11+count+2); } /** * prepare AL175 read data request * @see 2. MESSAGE TYPE 2 (COMMAND SENT FROM MONITORING EQUIPMENT) * @param dest [out] where to put the result * @param addr start address of requested area * @param count no. of requested bytes */ static void al_prep_read_req(raw_data_t *dest, unsigned addr, size_t count) { comli_head_t h; h.msg.id = 0x14; h.msg.stamp = 1; h.msg.type = 2; h.io.addr = addr; h.io.len = count; comli_prepare(dest, &h, NULL, 0); } /** * prepare AL175 activate command * @see 4. MESSAGE TYPE 0 (ACTIVATE COMMAND) * @param dest [out] where to put the result * @param cmd command type [11] * @param subcmd command subtype [12] * @param pr1 first parameter [13:14] * @param pr2 second parameter [15:16] * @param pr3 third parameter [17:18] */ static void al_prep_activate(raw_data_t *dest, byte_t cmd, byte_t subcmd, uint16_t pr1, uint16_t pr2, uint16_t pr3) { comli_head_t h; char data[8+1]; h.msg.id = 0x14; h.msg.stamp = 1; h.msg.type = 0; h.io.addr = 0x4500; h.io.len = 8; /* NOTE: doc says we should use ASCII coding here, but the actual * values are > 0x80, so we use binary coding */ data[0] = cmd; data[1] = subcmd; snprintf(data+2, 6+1, "%2X%2X%2X", pr1, pr2, pr3); comli_prepare(dest, &h, data, 8); } /*********************** * COMLI: INPUT FRAMES * ***********************/ /** * check COMLI frame for correct layout and bcc * @param f frame to check * * @return 0 (ok) -1 (error) */ static int comli_check_frame(/*const*/ raw_data_t f) { int bcc; byte_t *tail; if (*f.begin!=STX) return -1; tail = f.end - 2; if (tail <= f.begin) return -1; if (tail[0]!=ETX) return -1; bcc = compute_bcc(f.begin+1, (f.end - f.begin) - 2/*STX & BCC*/); if (bcc!= tail[1]) return -1; return 0; } /** * parse reply header from PRS * @see 3. MESSAGE TYPE 0 (REPLY FROM PRS ON MESSAGE TYPE 2) * * @param io [out] parsed io_header * @param raw_reply_head [in] raw reply header from PRS * @return 0 (ok), -1 (error) * * @see al_parse_reply */ static int al_parse_reply_head(io_head_t *io, const raw_data_t raw_reply_head) { /* * 0 1 2 3 4 5 6 7 8 9 10 * +-----+---------+-------+------+-------------------------+-----------+-----------+ * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ......... | * +-----+---------+-------+------+-------------------------+-----------+-----------+ * * ^ ^ * | | * begin end */ unsigned long io_addr, io_len; const byte_t *reply_head = raw_reply_head.begin - 1; if ( (raw_reply_head.end - raw_reply_head.begin) != 10) { upsdebugx(3, "%s: wrong size\t(%i != 10)", __func__, (int)(raw_reply_head.end - raw_reply_head.begin)); return -1; /* wrong size */ } if (reply_head[1]!='0' || reply_head[2]!='0') { upsdebugx(3, "%s: wrong id\t('%c%c' != '00')", __func__, reply_head[1], reply_head[2]); return -1; /* wrong id */ } if (reply_head[3]!='1') { upsdebugx(3, "%s: wrong stamp\t('%c' != '1')", __func__, reply_head[3]); return -1; /* wrong stamp */ } if (reply_head[4]!='0') { upsdebugx(3, "%s: wrong type\t('%c' != '0')", __func__, reply_head[4]); return -1; /* wrong type */ } io_addr = from_hex(&reply_head[5], 4); if (io_addr==-1UL) { upsdebugx(3, "%s: invalid addr\t('%c%c%c%c')", __func__, reply_head[5],reply_head[6],reply_head[7],reply_head[8]); return -1; /* wrong addr */ } io_len = from_hex(&reply_head[9], 2); if (io_len==-1UL) { upsdebugx(3, "%s: invalid nob\t('%c%c')", __func__, reply_head[9],reply_head[10]); return -1; /* wrong NOB */ } if (io_len > IO_LEN_MAX) { upsdebugx(3, "nob too big\t(%lu > %i)", io_len, IO_LEN_MAX); return -1; /* too much data claimed */ } io->addr = io_addr; io->len = io_len; return 0; } /** * parse reply from PRS * @see 3. MESSAGE TYPE 0 (REPLY FROM PRS ON MESSAGE TYPE 2) * @param io_head [out] parsed io_header * @param io_buf [in] [out] raw_data where to place incoming data (see ...data... below) * @param raw_reply raw reply from PRS to check * @return 0 (ok), -1 (error) * * @see al_parse_reply_head */ static int al_parse_reply(io_head_t *io_head, raw_data_t *io_buf, /*const*/ raw_data_t raw_reply) { /* * 0 1 2 3 4 5 6 7 8 9 10 11 - - - N-1 N * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ...data... | ETX | BCC | * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ * * ^ ^ * | | * begin end */ int err; unsigned i; const byte_t *reply = raw_reply.begin - 1; /* 1: extract header and parse it */ /*const*/ raw_data_t raw_reply_head = raw_reply; if (raw_reply_head.begin + 10 <= raw_reply_head.end) raw_reply_head.end = raw_reply_head.begin + 10; err = al_parse_reply_head(io_head, raw_reply_head); if (err==-1) return -1; /* 2: process data */ reply = raw_reply.begin - 1; if ( (raw_reply.end - raw_reply.begin) != (ptrdiff_t)(10 + io_head->len)) { upsdebugx(3, "%s: corrupt sentence\t(%i != %i)", __func__, (int)(raw_reply.end - raw_reply.begin), 10 + io_head->len); return -1; /* corrupt sentence */ } /* extract the data */ if (io_buf->buf_size < io_head->len) { upsdebugx(3, "%s: too much data to fit in io_buf\t(%u > %u)", __func__, io_head->len, io_buf->buf_size); return -1; /* too much data to fit in io_buf */ } io_buf->begin = io_buf->buf; io_buf->end = io_buf->begin; for (i=0; ilen; ++i) *(io_buf->end++) = reply[11+i]; reverse_bits(io_buf->begin, (io_buf->end - io_buf->begin) ); upsdebug_hex(3, "\t\t--> payload", io_buf->begin, (io_buf->end - io_buf->begin)); return 0; /* all ok */ } /** * check acknowledge from PRS * @see 5. ACKNOWLEDGE FROM PRS * @param raw_ack raw acknowledge from PRS to check * @return 0 on success, -1 on error */ static int al_check_ack(/*const*/ raw_data_t raw_ack) { /* * 0 1 2 3 4 5 6 7 * +-----+---------+-------+------+-----+-----+-----+ * | STX | IDh IDl | Stamp | type | ACK | ETX | BCC | * +-----+---------+-------+------+-----+-----+-----+ * * ^ ^ * | | * begin end */ const byte_t *ack = raw_ack.begin - 1; if ( (raw_ack.end - raw_ack.begin) !=5) { upsdebugx(3, "%s: wrong size\t(%i != 5)", __func__, (int)(raw_ack.end - raw_ack.begin)); return -1; /* wrong size */ } if (ack[1]!='0' || ack[2]!='0') { upsdebugx(3, "%s: wrong id\t('%c%c' != '00')", __func__, ack[1], ack[2]); return -1; /* wrong id */ } /* the following in not mandated. it is just said it will be * "same as one received". but we always send '1' (0x31) as stamp * (see 4. MESSAGE TYPE 0 (ACTIVATE COMMAND). Hence, stamp checking * is hardcoded here. */ if (ack[3]!='1') { upsdebugx(3, "%s: wrong stamp\t('%c' != '1')", __func__, ack[3]); return -1; /* wrong stamp */ } if (ack[4]!='1') { upsdebugx(3, "%s: wrong type\t('%c' != '1')", __func__, ack[4]); return -1; /* wrong type */ } if (ack[5]!=ACK) { upsdebugx(3, "%s: wrong ack\t(0x%02X != 0x%02X)", __func__, ack[5], ACK); return -1; /* wrong ack */ } return 0; } /******************************************************************/ /********** * SERIAL * **********/ /* clear any flow control (copy from powercom.c) */ static void ser_disable_flow_control (void) { struct termios tio; tcgetattr (upsfd, &tio); tio.c_iflag &= ~ (IXON | IXOFF); tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; upsdebugx(4, "Flow control disable"); /* disable any flow control */ tcsetattr(upsfd, TCSANOW, &tio); } static void flush_rx_queue() { ser_flush_in(upsfd, "", /*verbose=*/nut_debug_level); } /** * transmit frame to PRS * * @param dmsg debug message prefix * @param frame the frame to tansmit * @return 0 (ok) -1 (error) */ static int tx(const char *dmsg, /*const*/ raw_data_t frame) { int err; upsdebug_ascii(3, dmsg, frame.begin, (frame.end - frame.begin)); err = ser_send_buf(upsfd, frame.begin, (frame.end - frame.begin) ); if (err==-1) { upslogx(LOG_ERR, "failed to send frame to PRS: %s", strerror(errno)); return -1; } if (err != (frame.end - frame.begin)) { upslogx(LOG_ERR, "sent incomplete frame to PRS"); return -1; } return 0; } /*********** * CHATTER * ***********/ static time_t T_io_begin; /* start of current I/O transaction */ static int T_io_timeout; /* in seconds */ /* start new I/O transaction with maximum time limit */ static void io_new_transaction(int timeout) { T_io_begin = time(NULL); T_io_timeout = timeout; } /** * get next character from input stream * * @param ch ptr-to where store result * * @return -1 (error) 0 (timeout) >0 (got it) * */ static int get_char(char *ch) { time_t now = time(NULL); long rx_timeout; rx_timeout = T_io_timeout - (now - T_io_begin); /* negative rx_timeout -> time already out */ if (rx_timeout < 0) return 0; return ser_get_char(upsfd, ch, rx_timeout, 0); } /** * get next characters from input stream * * @param buf ptr-to output buffer * @param len buffer length * * @return -1 (error) 0 (timeout) >0 (no. of characters actually read) * */ static int get_buf(byte_t *buf, size_t len) { time_t now = time(NULL); long rx_timeout; rx_timeout = T_io_timeout - (now - T_io_begin); /* negative rx_timeout -> time already out */ if (rx_timeout < 0) return 0; return ser_get_buf_len(upsfd, buf, len, rx_timeout, 0); } /** * scan incoming bytes for specific character * * @return 0 (got it) -1 (error) */ static int scan_for(char c) { char in; int err; while (1) { err = get_char(&in); if (err==-1 || err==0 /*timeout*/) return -1; if (in==c) break; } return 0; } /** * receive 'activate command' ACK from PRS * * @return 0 (ok) -1 (error) */ static int recv_command_ack() { int err; raw_data_t ack; byte_t ack_buf[8]; /* 1: STX */ err = scan_for(STX); if (err==-1) return -1; raw_alloc_onstack(&ack, ack_buf); *(ack.end++) = STX; /* 2: ID1 ID2 STAMP MSG_TYPE ACK ETX BCC */ err = get_buf(ack.end, 7); if (err!=7) return -1; ack.end += 7; /* frame constructed - let's verify it */ upsdebug_ascii(3, "rx (ack):\t\t", ack.begin, (ack.end - ack.begin)); /* generic layout */ err = comli_check_frame(ack); if (err==-1) return -1; /* shrink frame */ ack.begin += 1; ack.end -= 2; return al_check_ack(ack); } /** * receive 'read register' data from PRS * @param io [out] io header of received data * @param io_buf [in] [out] where to place incoming data * * @return 0 (ok) -1 (error) */ static int recv_register_data(io_head_t *io, raw_data_t *io_buf) { int err, ret; raw_data_t reply_head; raw_data_t reply; byte_t reply_head_buf[11]; /* 1: STX */ err = scan_for(STX); if (err==-1) return -1; raw_alloc_onstack(&reply_head, reply_head_buf); *(reply_head.end++) = STX; /* 2: ID1 ID2 STAMP MSG_TYPE ADDR1 ADDR2 ADDR3 ADDR4 LEN1 LEN2 */ err = get_buf(reply_head.end, 10); if (err!=10) return -1; reply_head.end += 10; upsdebug_ascii(3, "rx (head):\t", reply_head.begin, (reply_head.end - reply_head.begin)); /* 3: check header, extract IO info */ reply_head.begin += 1; /* temporarily strip STX */ err = al_parse_reply_head(io, reply_head); if (err==-1) return -1; reply_head.begin -= 1; /* restore STX */ upsdebugx(4, "\t\t--> addr: 0x%x len: 0x%x", io->addr, io->len); /* 4: allocate space for full reply and copy header there */ reply = raw_xmalloc(11/*head*/ + io->len/*data*/ + 2/*ETX BCC*/); memcpy(reply.end, reply_head.begin, (reply_head.end - reply_head.begin)); reply.end += (reply_head.end - reply_head.begin); /* 5: receive tail of the frame */ err = get_buf(reply.end, io->len + 2); if (err!=(int)(io->len+2)) { upsdebugx(4, "rx_tail failed, err=%i (!= %i)", err, io->len+2); ret = -1; goto out; } reply.end += io->len + 2; /* frame constructed, let's verify it */ upsdebug_ascii(3, "rx (head+data):\t", reply.begin, (reply.end - reply.begin)); /* generic layout */ err = comli_check_frame(reply); if (err==-1) { upsdebugx(3, "%s: corrupt frame", __func__); ret = -1; goto out; } /* shrink frame */ reply.begin += 1; reply.end -= 2; /* XXX: a bit of processing duplication here */ ret = al_parse_reply(io, io_buf, reply); out: raw_free(&reply); return ret; } /*****************************************************************/ /********************* * AL175: DO COMMAND * *********************/ /** * do 'ACTIVATE COMMAND' * * @return 0 (ok) -1 (error) */ static int al175_do(byte_t cmd, byte_t subcmd, uint16_t pr1, uint16_t pr2, uint16_t pr3) { int err; raw_data_t CTRL_frame; byte_t CTRL_frame_buf[512]; raw_alloc_onstack(&CTRL_frame, CTRL_frame_buf); al_prep_activate(&CTRL_frame, cmd, subcmd, pr1, pr2, pr3); flush_rx_queue(); /* DROP */ err = tx("tx (ctrl):\t", CTRL_frame); /* TX */ if (err==-1) return -1; return recv_command_ack(); /* RX */ } /** * 'READ REGISTER' * */ static int al175_read(byte_t *dst, unsigned addr, size_t count) { int err; raw_data_t REQ_frame; raw_data_t rx_data; io_head_t io; byte_t REQ_frame_buf[512]; raw_alloc_onstack(&REQ_frame, REQ_frame_buf); al_prep_read_req(&REQ_frame, addr, count); flush_rx_queue(); /* DROP */ err = tx("tx (req):\t", REQ_frame); /* TX */ if (err==-1) return -1; rx_data.buf = dst; rx_data.buf_size = count; rx_data.begin = dst; rx_data.end = dst; err = recv_register_data(&io, &rx_data); if (err==-1) return -1; if ((rx_data.end - rx_data.begin) != (int)count) return -1; if ( (io.addr != addr) || (io.len != count) ) { upsdebugx(3, "%s: io_head mismatch\t(%x,%x != %x,%x)", __func__, io.addr, io.len, addr, (unsigned int)count); return -1; } return 0; } /************* * NUT STUFF * *************/ /**************************** * ACTIVATE COMMANDS table * * see 8. ACTIVATE COMMANDS */ typedef int mm_t; /* minutes */ typedef int VV_t; /* voltage */ #define Z1 , 0 #define Z2 , 0, 0 #define Z3 , 0, 0, 0 #define ACT int ACT TOGGLE_PRS_ONOFF () { return al175_do(0x81, 0x80 Z3); } ACT CANCEL_BOOST () { return al175_do(0x82, 0x80 Z3); } ACT STOP_BATTERY_TEST () { return al175_do(0x83, 0x80 Z3); } ACT START_BATTERY_TEST (VV_t EndVolt, unsigned Minutes) { return al175_do(0x83, 0x81, EndVolt, Minutes Z1); } ACT SET_FLOAT_VOLTAGE (VV_t v) { return al175_do(0x87, 0x80, v Z2); } ACT SET_BOOST_VOLTAGE (VV_t v) { return al175_do(0x87, 0x81, v Z2); } ACT SET_HIGH_BATTERY_LIMIT (VV_t Vhigh) { return al175_do(0x87, 0x82, Vhigh Z2); } ACT SET_LOW_BATTERY_LIMIT (VV_t Vlow) { return al175_do(0x87, 0x83, Vlow Z2); } ACT SET_DISCONNECT_LEVEL_AND_DELAY (VV_t level, mm_t delay) { return al175_do(0x87, 0x84, level, delay Z1); } ACT RESET_ALARMS () { return al175_do(0x88, 0x80 Z3); } ACT CHANGE_COMM_PROTOCOL () { return al175_do(0x89, 0x80 Z3); } ACT SET_VOLTAGE_AT_ZERO_T (VV_t v) { return al175_do(0x8a, 0x80, v Z2); } ACT SET_SLOPE_AT_ZERO_T (VV_t mv_per_degree) { return al175_do(0x8a, 0x81, mv_per_degree Z2); } ACT SET_MAX_TCOMP_VOLTAGE (VV_t v) { return al175_do(0x8a, 0x82, v Z2); } ACT SET_MIN_TCOMP_VOLTAGE (VV_t v) { return al175_do(0x8a, 0x83, v Z2); } ACT SWITCH_TEMP_COMP (int on) { return al175_do(0x8b, 0x80, on Z2); } ACT SWITCH_SYM_ALARM () { return al175_do(0x8c, 0x80 Z3); } /** * extract double value from a word */ static double d16(byte_t data[2]) { return (data[1] + 0x100*data[0]) / 100.0; } void upsdrv_updateinfo(void) { /* int flags; */ byte_t x4000[9]; /* registers from 0x4000 to 0x4040 inclusive */ byte_t x4048[2]; /* 0x4048 - 0x4050 */ byte_t x4100[8]; /* 0x4100 - 0x4138 */ byte_t x4180[8]; /* 0x4180 - 0x41b8 */ byte_t x4300[2]; /* 0x4300 - 0x4308 */ int err; double batt_current = 0.0; upsdebugx(4, " "); upsdebugx(4, "UPDATEINFO"); upsdebugx(4, "----------"); io_new_transaction(/*timeout=*/3); #define RECV(reg) do { \ err = al175_read(x ## reg, 0x ## reg, sizeof(x ## reg)); \ if (err==-1) { \ dstate_datastale(); \ return; \ } \ } while (0) RECV(4000); RECV(4048); RECV(4100); RECV(4180); RECV(4300); status_init(); /* XXX non conformant with NUT naming & not well understood what they mean */ #if 0 /* 0x4000 DIGITAL INPUT 1-8 */ dstate_setinfo("load.fuse", (x4000[0] & 0x80) ? "OK" : "BLOWN"); dstate_setinfo("battery.fuse", (x4000[0] & 0x40) ? "OK" : "BLOWN"); dstate_setinfo("symalarm.fuse", (x4000[0] & 0x20) ? "OK" : "BLOWN"); /* 0x4008 BATTERY INFORMATION */ dstate_setinfo("battery.contactor", (x4000[1] & 0x80) ? "XX" : "YY"); /* FIXME */ dstate_setinfo("load.contactor", (x4000[1] & 0x40) ? "XX" : "YY"); /* FIXME */ dstate_setinfo("lvd.contactor", (x4000[1] & 0x20) ? "XX" : "YY"); /* FIXME */ #endif if (x4000[0] & 0x40){ dstate_setinfo("battery.fuse", "FAIL"); status_set("RB"); }else{ dstate_setinfo("battery.fuse", "OK"); } if (x4000[0] & 0x20){ dstate_setinfo("battery.symmetry", "FAIL"); status_set("RB"); }else{ dstate_setinfo("battery.symmetry", "OK"); } if (x4000[1] & 0x01) /* battery test running */ status_set("TEST"); /* TODO: others from 0x4008 */ /* 0x4010 NOT USED */ /* 0x4018 NOT USED */ switch (x4000[4]) { /* 0x4020 MAINS VOLTAGE STATUS */ case 0: status_set("OL"); break; case 1: status_set("OB"); break; case 2: /* doc: "not applicable" */ default: upsdebugx(2, "%s: invalid mains voltage status\t(%i)", __func__, x4000[4]); } /* 0x4028 SYSTEM ON OFF STATUS */ switch (x4000[5]) { case 0: /* system on */ break; case 1: status_set("OFF"); break; default: upsdebugx(2, "%s: invalid system on/off status\t(%i)", __func__, x4000[5]); } switch (x4000[6]) { /* 0x4030 BATTERY TEST FAIL */ case 0: dstate_setinfo("ups.test.result", "OK"); break; case 1: status_set("RB"); dstate_setinfo("ups.test.result", "FAIL"); break; default: upsdebugx(2, "%s: invalid battery test fail\t(%i)", __func__, x4000[6]); } switch (x4000[7]) { /* 0x4038 BATTERY VOLTAGE STATUS */ case 0: /* normal */ break; case 1: status_set("LB"); break; case 2: status_set("HB"); break; default: upsdebugx(2, "%s: invalid battery voltage status\t(%i)", __func__, x4000[7]); } switch (x4000[8]) { /* 0x4040 POS./NEG. BATT. CURRENT */ case 0: batt_current = +1.0; break; /* positive */ case 1: batt_current = -1.0; break; /* negative */ default: upsdebugx(2, "%s: invalid pos/neg battery current\t(%i)", __func__, x4000[8]); } switch (x4048[0]) { /* 0x4048 BOOST STATUS */ case 0: /* no boost */; break; case 1: status_set("BOOST"); break; default: upsdebugx(2, "%s: invalid boost status\t(%i)", __func__, x4048[0]); } { const char *v=NULL; switch (x4048[1]) { /* 0x4050 SYSTEM VOLTAGE STAT. */ case 0: v = "48"; break; case 1: v = "24"; break; case 2: v = "12"; break; case 3: v = "26"; break; case 4: v = "60"; break; default: upsdebugx(2, "%s: invalid system voltage status\t(%i)", __func__, x4048[1]); } if (v) dstate_setinfo("output.voltage.nominal", "%s", v); } /* 0x4100 BATTERY VOLTAGE REF */ dstate_setinfo("battery.voltage.nominal", "%.2f", d16(x4100+0)); /* 0x4110 BOOST VOLTAGE REF */ dstate_setinfo("input.transfer.boost.low", "%.2f", d16(x4100+2)); /* XXX: boost.high ? */ /* 0x4120 HIGH BATT VOLT REF XXX */ /* 0x4130 LOW BATT VOLT REF XXX */ /* 0x4180 FLOAT VOLTAGE XXX */ /* 0x4190 BATT CURRENT */ batt_current *= d16(x4180+2); dstate_setinfo("battery.current", "%.2f", batt_current); /* 0x41b0 LOAD CURRENT (output.current in NUT) */ dstate_setinfo("output.current", "%.2f", d16(x4180+6)); /* 0x4300 BATTERY TEMPERATURE */ dstate_setinfo("battery.temperature", "%.2f", d16(x4300+0)); status_commit(); upsdebugx(1, "STATUS: %s", dstate_getinfo("ups.status")); dstate_dataok(); /* out: */ return; } void upsdrv_shutdown(void) { /* TODO use TOGGLE_PRS_ONOFF for shutdown */ /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ fatalx(EXIT_FAILURE, "shutdown not supported"); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ } static int instcmd(const char *cmdname, const char *extra) { int err; upsdebugx(1, "INSTCMD: %s", cmdname); io_new_transaction(/*timeout=*/5); /* * test.battery.start * test.battery.stop */ if (!strcasecmp(cmdname, "test.battery.start")) { err = START_BATTERY_TEST(24, 1); return (!err ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_FAILED); } if (!strcasecmp(cmdname, "test.battery.stop")) { err = STOP_BATTERY_TEST(); return (!err ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_FAILED); } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } /* no help */ void upsdrv_help(void) { } /* no -x flags */ void upsdrv_makevartable(void) { } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B9600); ser_disable_flow_control(); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } void upsdrv_initinfo(void) { /* TODO issue short io with UPS to detect it's presence */ /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ dstate_setinfo("ups.mfr", "Eltek"); dstate_setinfo("ups.model", "AL175"); /* ... */ /* instant commands */ dstate_addcmd ("test.battery.start"); dstate_addcmd ("test.battery.stop"); /* TODO rest instcmd(s) */ upsh.instcmd = instcmd; } nut-2.7.4/drivers/usb-common.c0000644000175000017500000002043312640443572013165 00000000000000/* usb-common.c - common useful USB functions Copyright (C) 2008 Arnaud Quette 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 "common.h" #include "usb-common.h" int is_usb_device_supported(usb_device_id_t *usb_device_id_list, USBDevice_t *device) { int retval = NOT_SUPPORTED; usb_device_id_t *usbdev; for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) { if (usbdev->vendorID != device->VendorID) { continue; } /* flag as possibly supported if we see a known vendor */ retval = POSSIBLY_SUPPORTED; if (usbdev->productID != device->ProductID) { continue; } /* call the specific handler, if it exists */ if (usbdev->fun != NULL) { (*usbdev->fun)(device); } return SUPPORTED; } return retval; } /* ---------------------------------------------------------------------- */ /* matchers */ /* helper function: version of strcmp that tolerates NULL * pointers. NULL is considered to come before all other strings * alphabetically. */ static int strcmp_null(char *s1, char *s2) { if (s1 == NULL && s2 == NULL) { return 0; } if (s1 == NULL) { return -1; } if (s2 == NULL) { return 1; } return strcmp(s1, s2); } /* private callback function for exact matches */ static int match_function_exact(USBDevice_t *hd, void *privdata) { USBDevice_t *data = (USBDevice_t *)privdata; if (hd->VendorID != data->VendorID) { return 0; } if (hd->ProductID != data->ProductID) { return 0; } if (strcmp_null(hd->Vendor, data->Vendor) != 0) { return 0; } if (strcmp_null(hd->Product, data->Product) != 0) { return 0; } if (strcmp_null(hd->Serial, data->Serial) != 0) { return 0; } #ifdef DEBUG_EXACT_MATCH_BUS if (strcmp_null(hd->Bus, data->Bus) != 0) { return 0; } #endif return 1; } /* constructor: create an exact matcher that matches the device. * On success, return 0 and store the matcher in *matcher. On * error, return -1 with errno set */ int USBNewExactMatcher(USBDeviceMatcher_t **matcher, USBDevice_t *hd) { USBDeviceMatcher_t *m; USBDevice_t *data; m = malloc(sizeof(*m)); if (!m) { return -1; } data = calloc(1, sizeof(*data)); if (!data) { free(m); return -1; } m->match_function = &match_function_exact; m->privdata = (void *)data; m->next = NULL; data->VendorID = hd->VendorID; data->ProductID = hd->ProductID; data->Vendor = hd->Vendor ? strdup(hd->Vendor) : NULL; data->Product = hd->Product ? strdup(hd->Product) : NULL; data->Serial = hd->Serial ? strdup(hd->Serial) : NULL; #ifdef DEBUG_EXACT_MATCH_BUS data->Bus = hd->Bus ? strdup(hd->Bus) : NULL; #endif *matcher = m; return 0; } /* destructor: free matcher previously created with USBNewExactMatcher */ void USBFreeExactMatcher(USBDeviceMatcher_t *matcher) { USBDevice_t *data; if (!matcher) { return; } data = (USBDevice_t *)matcher->privdata; free(data->Vendor); free(data->Product); free(data->Serial); #ifdef DEBUG_EXACT_MATCH_BUS free(data->Bus); #endif free(data); free(matcher); } /* Private function for compiling a regular expression. On success, * store the compiled regular expression (or NULL) in *compiled, and * return 0. On error with errno set, return -1. If the supplied * regular expression is unparseable, return -2 (an error message can * then be retrieved with regerror(3)). Note that *compiled will be an * allocated value, and must be freed with regfree(), then free(), see * regex(3). As a special case, if regex==NULL, then set * *compiled=NULL (regular expression NULL is intended to match * anything). */ static int compile_regex(regex_t **compiled, char *regex, int cflags) { int r; regex_t *preg; if (regex == NULL) { *compiled = NULL; return 0; } preg = malloc(sizeof(*preg)); if (!preg) { return -1; } r = regcomp(preg, regex, cflags); if (r) { free(preg); return -2; } *compiled = preg; return 0; } /* Private function for regular expression matching. Check if the * entire string str (minus any initial and trailing whitespace) * matches the compiled regular expression preg. Return 1 if it * matches, 0 if not. Return -1 on error with errno set. Special * cases: if preg==NULL, it matches everything (no contraint). If * str==NULL, then it is treated as "". */ static int match_regex(regex_t *preg, char *str) { int r; size_t len = 0; char *string; regmatch_t match; if (!preg) { return 1; } if (!str) { string = xstrdup(""); } else { /* skip leading whitespace */ for (len = 0; len < strlen(str); len++) { if (!strchr(" \t\n", str[len])) { break; } } string = xstrdup(str+len); /* skip trailing whitespace */ for (len = strlen(string); len > 0; len--) { if (!strchr(" \t\n", string[len-1])) { break; } } string[len] = '\0'; } /* test the regular expression */ r = regexec(preg, string, 1, &match, 0); free(string); if (r) { return 0; } /* check that the match is the entire string */ if ((match.rm_so != 0) || (match.rm_eo != (int)len)) { return 0; } return 1; } /* Private function, similar to match_regex, but the argument being * matched is a (hexadecimal) number, rather than a string. It is * converted to a 4-digit hexadecimal string. */ static int match_regex_hex(regex_t *preg, int n) { char buf[10]; snprintf(buf, sizeof(buf), "%04x", n); return match_regex(preg, buf); } /* private data type: hold a set of compiled regular expressions. */ typedef struct regex_matcher_data_s { regex_t *regex[6]; } regex_matcher_data_t; /* private callback function for regex matches */ static int match_function_regex(USBDevice_t *hd, void *privdata) { regex_matcher_data_t *data = (regex_matcher_data_t *)privdata; int r; r = match_regex_hex(data->regex[0], hd->VendorID); if (r != 1) { return r; } r = match_regex_hex(data->regex[1], hd->ProductID); if (r != 1) { return r; } r = match_regex(data->regex[2], hd->Vendor); if (r != 1) { return r; } r = match_regex(data->regex[3], hd->Product); if (r != 1) { return r; } r = match_regex(data->regex[4], hd->Serial); if (r != 1) { return r; } r = match_regex(data->regex[5], hd->Bus); if (r != 1) { return r; } return 1; } /* constructor: create a regular expression matcher. This matcher is * based on six regular expression strings in regex_array[0..5], * corresponding to: vendorid, productid, vendor, product, serial, * bus. Any of these strings can be NULL, which matches * everything. Cflags are as in regcomp(3). Typical values for cflags * are REG_ICASE (case insensitive matching) and REG_EXTENDED (use * extended regular expressions). On success, return 0 and store the * matcher in *matcher. On error, return -1 with errno set, or return * i=1--6 to indicate that the regular expression regex_array[i-1] was * ill-formed (an error message can then be retrieved with * regerror(3)). */ int USBNewRegexMatcher(USBDeviceMatcher_t **matcher, char **regex, int cflags) { int r, i; USBDeviceMatcher_t *m; regex_matcher_data_t *data; m = malloc(sizeof(*m)); if (!m) { return -1; } data = calloc(1, sizeof(*data)); if (!data) { free(m); return -1; } m->match_function = &match_function_regex; m->privdata = (void *)data; m->next = NULL; for (i=0; i<6; i++) { r = compile_regex(&data->regex[i], regex[i], cflags); if (r == -2) { r = i+1; } if (r) { USBFreeRegexMatcher(m); return r; } } *matcher = m; return 0; } void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher) { int i; regex_matcher_data_t *data; if (!matcher) { return; } data = (regex_matcher_data_t *)matcher->privdata; for (i = 0; i < 6; i++) { if (!data->regex[i]) { continue; } regfree(data->regex[i]); free(data->regex[i]); } free(data); free(matcher); } nut-2.7.4/drivers/cyberpower-mib.h0000644000175000017500000000023512640443572014037 00000000000000#ifndef CYBERPOWER_MIB_H #define CYBERPOWER_MIB_H #include "main.h" #include "snmp-ups.h" extern mib2nut_info_t cyberpower; #endif /* CYBERPOWER_MIB_H */ nut-2.7.4/drivers/upshandler.h0000644000175000017500000000300612640443572013255 00000000000000/* upshandler.h - function callbacks used by the drivers Copyright (C) 2003 Russell Kroll This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NUT_UPSHANDLER_H #define NUT_UPSHANDLER_H /* return values for instcmd */ enum { STAT_INSTCMD_HANDLED = 0, /* completed successfully */ STAT_INSTCMD_UNKNOWN, /* unspecified error */ STAT_INSTCMD_INVALID, /* invalid command */ STAT_INSTCMD_FAILED /* command failed */ }; /* return values for setvar */ enum { STAT_SET_HANDLED = 0, /* completed successfully */ STAT_SET_UNKNOWN, /* unspecified error */ STAT_SET_INVALID, /* not writeable */ STAT_SET_FAILED /* writing failed */ }; /* structure for funcs that get called by msg parse routine */ struct ups_handler { int (*setvar)(const char *, const char *); int (*instcmd)(const char *, const char *); }; #endif /* NUT_UPSHANDLER_H */ nut-2.7.4/drivers/bcmxcp.c0000644000175000017500000030727312640473702012372 00000000000000/* bcmxcp.c - driver for powerware UPS Total rewrite of bcmxcp.c (nut ver-1.4.3) * Copyright (c) 2002, Martin Schroeder * * emes -at- geomer.de * * All rights reserved.* Copyright (C) 2004 Kjell Claesson 2004 Tore Ørpetveit 2011 - 2015 Arnaud Quette Thanks to Tore Ørpetveit that sent me the manuals for bcm/xcp. And to Fabio Di Niro and his metasys module. It influenced the layout of this driver. Modified for USB by Wolfgang Ocker ojw0000 2007Apr5 Oliver Wilcock - modified to control individual load segments (outlet.2.shutdown.return) on Powerware PW5125. Modified to support setvar for outlet.n.delay.start by Rich Wrenn (RFW) 9-3-11. Modified to support setvar for outlet.n.delay.shutdown by Arnaud Quette, 9-12-11 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 TODO List: Extend the parsing of the Standard ID Block, to read: Config Block Length: (High priority) Give information if config block is present, and how long it is, if it exist. If config block exist, read the config block and parse the 'Length of the Extended Limits Configuration Block' for extended configuration commands Statistic map Size: (Low priority) May be used to se if there is a Statistic Map. It holds data on the utility power quality for the past month and since last reset. Number of times on battery and how long. Up time and utility frequency deviation. (Only larger ups'es) Size of Alarm History Log: (Low priority) See if it have any alarm history block and enable command to dump it. Maximum Supported Command Length: ( Med. to High priority) Give info about the ups receive buffer size. Size of Alarm Block: ( Med. to High priority) Make a smarter handling of the Active alarm's if we know the length of the Active Alarm Block. Don't need the long loop to parse the alarm's. Maybe use another way to set up the alarm struct in the 'init_alarm_map'. Parse 'Communication Capabilities Block' ( Low priority) Get info of the connected ports ID, number of baud rates, command and respnse length. Parse 'Communication Port List Block': ( Low priority) This block gives info about the communication ports. Some ups'es have multiple comport's, and use one port for eatch load segment. In this block it is possible to get: Number of ports. (In this List) This Comport id (Which Comm Port is reporting this block.) Comport id (Id for eatch port listed. The first comport ID=1) Baudrate of the listed port. Serial config. Port usage: What this Comm Port is being used for: 0 = Unknown usage, No communication occurring. 1 = Undefined / Unknown communication occurring 2 = Waiting to communicate with a UPS 3 = Communication established with a UPS 4 = Waiting to communicate with software or adapter 5 = Communication established software (e.g., LanSafe) or adapter (e.g., ConnectUPS) 6 = Communicating with a Display Device 7 = Multi-drop Serial channel 8 = Communicating with an Outlet Controller Number of outlets. (Number of Outlets "assigned to" (controlled by) this Comm Port) Outlet number. (Each assigned Outlet is listed (1-64)) 'Set outlet parameter command (0x97)' to alter the delay settings or turn the outlet on or off with a delay (0 - 32767 seconds) Rewrite some parts of the driver, to minimise code duplication. (Like the instant commands) Implement support for Password Authorization (XCP spec, §4.3.2) Complete support for settable variables (upsh.setvar) */ #include "main.h" #include /* For ldexp() */ #include /*for FLT_MAX */ #include "nut_stdint.h" /* for uint8_t, uint16_t, uint32_t, ... */ #include "bcmxcp_io.h" #include "bcmxcp.h" #define DRIVER_NAME "BCMXCP UPS driver" #define DRIVER_VERSION "0.31" #define MAX_NUT_NAME_LENGTH 128 #define NUT_OUTLET_POSITION 7 /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Martin Schroeder \n" \ "Kjell Claesson \n" \ "Tore Ørpetveit \n" \ "Arnaud Quette \n" \ "Wolfgang Ocker \n" \ "Oliver Wilcock\n" \ "Prachi Gandhi \n" \ "Alf Høgemark \n" \ "Gavrilov Igor", DRV_STABLE, { &comm_upsdrv_info, NULL } }; static int get_word(const unsigned char*); static long int get_long(const unsigned char*); static float get_float(const unsigned char *data); static void init_command_map(void); static void init_meter_map(void); static void init_alarm_map(void); static bool_t init_command(int size); static void init_config(void); static void init_limit(void); static void init_ext_vars(void); static void init_topology(void); static void init_ups_meter_map(const unsigned char *map, unsigned char len); static void init_ups_alarm_map(const unsigned char *map, unsigned char len); static bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const int mapIndex, const int bitmask, const int alarmMapIndex, const int alarmBlockIndex); static void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value); static int init_outlet(unsigned char len); static void init_system_test_capabilities(void); static int instcmd(const char *cmdname, const char *extra); static int setvar(const char *varname, const char *val); static int decode_instcmd_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg); static int decode_setvar_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg); static float calculate_ups_load(const unsigned char *data); static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting); const char *FreqTol[3] = {"+/-2%", "+/-5%", "+/-7"}; const char *ABMStatus[4] = {"charging", "discharging", "floating", "resting"}; const char *OutletStatus[9] = {"unknown","on/closed","off/open","on with pending","off with pending","unknown","unknown","failed and closed","failed and open"}; /* Standard Authorization Block */ unsigned char AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5}; int nphases = 0; int outlet_block_len = 0; const char *cpu_name[5] = {"Cont:", "Inve:", "Rect:", "Netw:", "Disp:"}; const char *horn_stat[3] = {"disabled", "enabled", "muted"}; /* Battery test results */ info_lkp_t batt_test_info[] = { { 0, "No test initiated", NULL }, { 1, "In progress", NULL }, { 2, "Done and passed", NULL }, { 3, "Aborted", NULL }, { 4, "Done and error", NULL }, { 5, "Test scheduled", NULL }, /* Not sure about the meaning of the below ones! */ { 6, NULL, NULL }, /* The string was present but it has now been removed */ { 7, NULL, NULL }, /* The string was not installed at the last power up */ { 0, NULL, NULL } }; /* Topology map results */ info_lkp_t topology_info[] = { { BCMXCP_TOPOLOGY_OFFLINE_SWITCHER_1P, "Off-line switcher, Single Phase", NULL }, { BCMXCP_TOPOLOGY_LINEINT_UPS_1P, "Line-Interactive UPS, Single Phase", NULL }, { BCMXCP_TOPOLOGY_LINEINT_UPS_2P, "Line-Interactive UPS, Two Phase", NULL }, { BCMXCP_TOPOLOGY_LINEINT_UPS_3P, "Line-Interactive UPS, Three Phase", NULL }, { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_1P, "Dual AC Input, On-Line UPS, Single Phase", NULL }, { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_2P, "Dual AC Input, On-Line UPS, Two Phase", NULL }, { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_3P, "Dual AC Input, On-Line UPS, Three Phase", NULL }, { BCMXCP_TOPOLOGY_ONLINE_UPS_1P, "On-Line UPS, Single Phase", NULL }, { BCMXCP_TOPOLOGY_ONLINE_UPS_2P, "On-Line UPS, Two Phase", NULL }, { BCMXCP_TOPOLOGY_ONLINE_UPS_3P, "On-Line UPS, Three Phase", NULL }, { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_1P, "Parallel Redundant On-Line UPS, Single Phase", NULL }, { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_2P, "Parallel Redundant On-Line UPS, Two Phase", NULL }, { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_3P, "Parallel Redundant On-Line UPS, Three Phase", NULL }, { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_1P, "Parallel for Capacity On-Line UPS, Single Phase", NULL }, { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_2P, "Parallel for Capacity On-Line UPS, Two Phase", NULL }, { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_3P, "Parallel for Capacity On-Line UPS, Three Phase", NULL }, { BCMXCP_TOPOLOGY_SYSTEM_BYPASS_MODULE_3P, "System Bypass Module, Three Phase", NULL }, { BCMXCP_TOPOLOGY_HOT_TIE_CABINET_3P, "Hot-Tie Cabinet, Three Phase", NULL }, { BCMXCP_TOPOLOGY_OUTLET_CONTROLLER_1P, "Outlet Controller, Single Phase", NULL }, { BCMXCP_TOPOLOGY_DUAL_AC_STATIC_SWITCH_3P, "Dual AC Input Static Switch Module, 3 Phase", NULL }, { 0, NULL, NULL } }; /* Command map results */ info_lkp_t command_map_info[] = { { PW_INIT_BAT_TEST, "test.battery.start", NULL }, { PW_LOAD_OFF_RESTART, "shutdown.return", NULL }, { PW_UPS_OFF, "shutdown.stayoff", NULL }, { PW_UPS_ON, "load.on", NULL}, { PW_GO_TO_BYPASS, "bypass.start", NULL}, { 0, NULL, NULL } }; /* System test capabilities results */ info_lkp_t system_test_info[] = { { PW_SYS_TEST_GENERAL, "test.system.start", NULL }, /* { PW_SYS_TEST_SCHEDULE_BATTERY_COMMISSION, "test.battery.start.delayed", NULL }, */ /* { PW_SYS_TEST_ALTERNATE_AC_INPUT, "test.alternate_acinput.start", NULL }, */ { PW_SYS_TEST_FLASH_LIGHTS, "test.panel.start", NULL }, { 0, NULL, NULL } }; /* allocate storage for shared variables (extern in bcmxcp.h) */ BCMXCP_COMMAND_MAP_ENTRY_t bcmxcp_command_map[BCMXCP_COMMAND_MAP_MAX]; BCMXCP_METER_MAP_ENTRY_t bcmxcp_meter_map[BCMXCP_METER_MAP_MAX]; BCMXCP_ALARM_MAP_ENTRY_t bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX]; BCMXCP_STATUS_t bcmxcp_status; /* get_word function from nut driver metasys.c */ int get_word(const unsigned char *buffer) /* return an integer reading a word in the supplied buffer */ { unsigned char a, b; int result; a = buffer[0]; b = buffer[1]; result = b*256 + a; return result; } /* get_long function from nut driver metasys.c for meter readings*/ long int get_long(const unsigned char *buffer) /* return a long integer reading 4 bytes in the supplied buffer.*/ { unsigned char a, b, c, d; long int result; a = buffer[0]; b = buffer[1]; c = buffer[2]; d = buffer[3]; result = (256*256*256*d) + (256*256*c) + (256*b) + a; return result; } /* get_float funktion for convering IEEE-754 to float */ float get_float(const unsigned char *data) { int s, e; unsigned long src; long f; src = ((unsigned long)data[3] << 24) | ((unsigned long)data[2] << 16) | ((unsigned long)data[1] << 8) | ((unsigned long)data[0]); s = (src & 0x80000000UL) >> 31; e = (src & 0x7F800000UL) >> 23; f = (src & 0x007FFFFFUL); if (e == 255 && f != 0) { /* NaN (Not a Number) */ return FLT_MAX; } if (e == 255 && f == 0 && s == 1) { /* Negative infinity */ return -FLT_MAX; } if (e == 255 && f == 0 && s == 0) { /* Positive infinity */ return FLT_MAX; } if (e > 0 && e < 255) { /* Normal number */ f += 0x00800000UL; if (s) f = -f; return ldexp(f, e - 150); } if (e == 0 && f != 0) { /* Denormal number */ if (s) f = -f; return ldexp(f, -149); } if (e == 0 && f == 0 && (s == 1 || s == 0)) { /* Zero */ return 0; } /* Never happens */ upslogx(LOG_ERR, "s = %d, e = %d, f = %lu\n", s, e, f); return 0; } /* lightweight function to calculate the 8-bit * two's complement checksum of buf, using XCP data length (including header) * the result must be 0 for the sequence data to be valid */ int checksum_test(const unsigned char *buf) { unsigned char checksum = 0; int i, length; /* buf[2] is the length of the XCP frame ; add 5 for the header */ length = (int)(buf[2]) + 5; for (i = 0; i < length; i++) { checksum += buf[i]; } /* Compute the 8-bit, Two's Complement checksum now and return it */ checksum = ((0x100 - checksum) & 0xFF); return (checksum == 0); } unsigned char calc_checksum(const unsigned char *buf) { unsigned char c; int i; c = 0; for(i = 0; i < 2 + buf[1]; i++) c -= buf[i]; return c; } void init_command_map() { int i = 0; /* Clean entire map */ memset(&bcmxcp_command_map, 0, sizeof(BCMXCP_COMMAND_MAP_ENTRY_t) * BCMXCP_COMMAND_MAP_MAX); /* Set all command descriptions */ bcmxcp_command_map[PW_ID_BLOCK_REQ].command_desc = "PW_ID_BLOCK_REQ"; bcmxcp_command_map[PW_EVENT_HISTORY_LOG_REQ].command_desc = "PW_EVENT_HISTORY_LOG_REQ"; bcmxcp_command_map[PW_STATUS_REQ].command_desc = "PW_STATUS_REQ"; bcmxcp_command_map[PW_METER_BLOCK_REQ].command_desc = "PW_METER_BLOCK_REQ"; bcmxcp_command_map[PW_CUR_ALARM_REQ].command_desc = "PW_CUR_ALARM_REQ"; bcmxcp_command_map[PW_CONFIG_BLOCK_REQ].command_desc = "PW_CONFIG_BLOCK_REQ"; bcmxcp_command_map[PW_UTILITY_STATISTICS_BLOCK_REQ].command_desc = "PW_UTILITY_STATISTICS_BLOCK_REQ"; bcmxcp_command_map[PW_WAVEFORM_BLOCK_REQ].command_desc = "PW_WAVEFORM_BLOCK_REQ"; bcmxcp_command_map[PW_BATTERY_REQ].command_desc = "PW_BATTERY_REQ"; bcmxcp_command_map[PW_LIMIT_BLOCK_REQ].command_desc = "PW_LIMIT_BLOCK_REQ"; bcmxcp_command_map[PW_TEST_RESULT_REQ].command_desc = "PW_TEST_RESULT_REQ"; bcmxcp_command_map[PW_COMMAND_LIST_REQ].command_desc = "PW_COMMAND_LIST_REQ"; bcmxcp_command_map[PW_OUT_MON_BLOCK_REQ].command_desc = "PW_OUT_MON_BLOCK_REQ"; bcmxcp_command_map[PW_COM_CAP_REQ].command_desc = "PW_COM_CAP_REQ"; bcmxcp_command_map[PW_UPS_TOP_DATA_REQ].command_desc = "PW_UPS_TOP_DATA_REQ"; bcmxcp_command_map[PW_COM_PORT_LIST_BLOCK_REQ].command_desc = "PW_COM_PORT_LIST_BLOCK_REQ"; bcmxcp_command_map[PW_REQUEST_SCRATCHPAD_DATA_REQ].command_desc = "PW_REQUEST_SCRATCHPAD_DATA_REQ"; bcmxcp_command_map[PW_GO_TO_BYPASS].command_desc = "PW_GO_TO_BYPASS"; bcmxcp_command_map[PW_UPS_ON].command_desc = "PW_UPS_ON"; bcmxcp_command_map[PW_LOAD_OFF_RESTART].command_desc = "PW_LOAD_OFF_RESTART"; bcmxcp_command_map[PW_UPS_OFF].command_desc = "PW_UPS_OFF"; bcmxcp_command_map[PW_DECREMENT_OUTPUT_VOLTAGE].command_desc = "PW_DECREMENT_OUTPUT_VOLTAGE"; bcmxcp_command_map[PW_INCREMENT_OUTPUT_VOLTAGE].command_desc = "PW_INCREMENT_OUTPUT_VOLTAGE"; bcmxcp_command_map[PW_SET_TIME_AND_DATE].command_desc = "PW_SET_TIME_AND_DATE"; bcmxcp_command_map[PW_UPS_ON_TIME].command_desc = "PW_UPS_ON_TIME"; bcmxcp_command_map[PW_UPS_ON_AT_TIME].command_desc = "PW_UPS_ON_AT_TIME"; bcmxcp_command_map[PW_UPS_OFF_TIME].command_desc = "PW_UPS_OFF_TIME"; bcmxcp_command_map[PW_UPS_OFF_AT_TIME].command_desc = "PW_UPS_OFF_AT_TIME"; bcmxcp_command_map[PW_SET_CONF_COMMAND].command_desc = "PW_SET_CONF_COMMAND"; bcmxcp_command_map[PW_SET_OUTLET_COMMAND].command_desc = "PW_SET_OUTLET_COMMAND"; bcmxcp_command_map[PW_SET_COM_COMMAND].command_desc = "PW_SET_COM_COMMAND"; bcmxcp_command_map[PW_SET_SCRATHPAD_SECTOR].command_desc = "PW_SET_SCRATHPAD_SECTOR"; bcmxcp_command_map[PW_SET_POWER_STRATEGY].command_desc = "PW_SET_POWER_STRATEGY"; bcmxcp_command_map[PW_SET_REQ_ONLY_MODE].command_desc = "PW_SET_REQ_ONLY_MODE"; bcmxcp_command_map[PW_SET_UNREQUESTED_MODE].command_desc = "PW_SET_UNREQUESTED_MODE"; bcmxcp_command_map[PW_INIT_BAT_TEST].command_desc = "PW_INIT_BAT_TEST"; bcmxcp_command_map[PW_INIT_SYS_TEST].command_desc = "PW_INIT_SYS_TEST"; bcmxcp_command_map[PW_SELECT_SUBMODULE].command_desc = "PW_SELECT_SUBMODULE"; bcmxcp_command_map[PW_AUTHORIZATION_CODE].command_desc = "PW_AUTHORIZATION_CODE"; for(i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) { bcmxcp_command_map[i].command_byte = 0; } } void init_meter_map() { /* Clean entire map */ memset(&bcmxcp_meter_map, 0, sizeof(BCMXCP_METER_MAP_ENTRY_t) * BCMXCP_METER_MAP_MAX); /* Set all corresponding mappings NUT <-> BCM/XCP */ bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_AB].nut_entity = "output.L1-L2.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_BC].nut_entity = "output.L2-L3.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_CA].nut_entity = "output.L3-L1.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_AB].nut_entity = "input.L1-L2.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_BC].nut_entity = "input.L2-L3.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_CA].nut_entity = "input.L3-L1.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_B].nut_entity = "input.L2.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_C].nut_entity = "input.L3.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_WATTS].nut_entity = "input.realpower"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].nut_entity = "ups.power"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VA].nut_entity = "input.power"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_POWER_FACTOR].nut_entity = "output.powerfactor"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_POWER_FACTOR].nut_entity = "input.powerfactor"; if (nphases == 1) { bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "ups.load"; /* TODO: Decide on corresponding three-phase variable mapping. */ bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.current.nominal"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS].nut_entity = "ups.realpower"; } else { bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.L1.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "output.L1.power.percent"; bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_B].nut_entity = "output.L2.power.percent"; bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_C].nut_entity = "output.L3.power.percent"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_A].nut_entity = "output.L1.power"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_B].nut_entity = "output.L2.power"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_C].nut_entity = "output.L3.power"; bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.L1-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_B].nut_entity = "input.bypass.L2-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_C].nut_entity = "input.bypass.L3-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.L1-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.L1.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.L1.current.nominal"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.L1-N.voltage"; } bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_FREQUENCY].nut_entity = "output.frequency"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_FREQUENCY].nut_entity = "input.frequency"; bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_FREQUENCY].nut_entity = "input.bypass.frequency"; bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_CURRENT].nut_entity = "battery.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_VOLTAGE].nut_entity = "battery.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_BATTERY_LEFT].nut_entity = "battery.charge"; bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TIME_REMAINING].nut_entity = "battery.runtime"; bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DCUV_BAR_CHART].nut_entity = "battery.voltage.low"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOW_BATTERY_WARNING_V_BAR_CHART].nut_entity = "battery.charge.low"; bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DISCHARGING_CURRENT_BAR_CHART].nut_entity = "battery.current.total"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_B].nut_entity = "input.L2-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_C].nut_entity = "input.L3-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_AMBIENT_TEMPERATURE].nut_entity = "ambient.temperature"; bcmxcp_meter_map[BCMXCP_METER_MAP_HEATSINK_TEMPERATURE].nut_entity = "ups.temperature"; bcmxcp_meter_map[BCMXCP_METER_MAP_POWER_SUPPLY_TEMPERATURE].nut_entity = "ambient.1.temperature"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B].nut_entity = "output.L2.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C].nut_entity = "output.L3.current"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B_BAR_CHART].nut_entity = "output.L2.current.nominal"; bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C_BAR_CHART].nut_entity = "output.L3.current.nominal"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].nut_entity = "ups.power.nominal"; bcmxcp_meter_map[BCMXCP_METER_MAP_DATE].nut_entity = "ups.date"; bcmxcp_meter_map[BCMXCP_METER_MAP_TIME].nut_entity = "ups.time"; bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TEMPERATURE].nut_entity = "battery.temperature"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_B].nut_entity = "output.L2-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_C].nut_entity = "output.L3-N.voltage"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A].nut_entity = "ups.L1-N.realpower"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_B].nut_entity = "ups.L2-N.realpower"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_C].nut_entity = "ups.L3-N.realpower"; bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A_B_C_BAR_CHART].nut_entity = "ups.realpower.nominal"; bcmxcp_meter_map[BCMXCP_METER_MAP_LINE_EVENT_COUNTER].nut_entity = "input.quality"; } void init_alarm_map() { /* Clean entire map */ memset(&bcmxcp_alarm_map, 0, sizeof(BCMXCP_ALARM_MAP_ENTRY_t) * BCMXCP_ALARM_MAP_MAX); /* Set all alarm descriptions */ bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE].alarm_desc = "INVERTER_AC_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE].alarm_desc = "INVERTER_AC_UNDER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ].alarm_desc = "INVERTER_OVER_OR_UNDER_FREQ"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_AC_OVER_VOLTAGE].alarm_desc = "BYPASS_AC_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_AC_UNDER_VOLTAGE].alarm_desc = "BYPASS_AC_UNDER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_OVER_OR_UNDER_FREQ].alarm_desc = "BYPASS_OVER_OR_UNDER_FREQ"; bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_AC_OVER_VOLTAGE].alarm_desc = "INPUT_AC_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_AC_UNDER_VOLTAGE].alarm_desc = "INPUT_AC_UNDER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_UNDER_OR_OVER_FREQ].alarm_desc = "INPUT_UNDER_OR_OVER_FREQ"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_OVER_VOLTAGE].alarm_desc = "OUTPUT_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNDER_VOLTAGE].alarm_desc = "OUTPUT_UNDER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNDER_OR_OVER_FREQ].alarm_desc = "OUTPUT_UNDER_OR_OVER_FREQ"; bcmxcp_alarm_map[BCMXCP_ALARM_REMOTE_EMERGENCY_PWR_OFF].alarm_desc = "REMOTE_EMERGENCY_PWR_OFF"; bcmxcp_alarm_map[BCMXCP_ALARM_REMOTE_GO_TO_BYPASS].alarm_desc = "REMOTE_GO_TO_BYPASS"; bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_6].alarm_desc = "BUILDING_ALARM_6"; bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_5].alarm_desc = "BUILDING_ALARM_5"; bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_4].alarm_desc = "BUILDING_ALARM_4"; bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_3].alarm_desc = "BUILDING_ALARM_3"; bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_2].alarm_desc = "BUILDING_ALARM_2"; bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_1].alarm_desc = "BUILDING_ALARM_1"; bcmxcp_alarm_map[BCMXCP_ALARM_STATIC_SWITCH_OVER_TEMP].alarm_desc = "STATIC_SWITCH_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OVER_TEMP].alarm_desc = "CHARGER_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_LOGIC_PWR_FAIL].alarm_desc = "CHARGER_LOGIC_PWR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OVER_VOLTAGE_OR_CURRENT].alarm_desc = "CHARGER_OVER_VOLTAGE_OR_CURRENT"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_TEMP].alarm_desc = "INVERTER_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_OVERLOAD].alarm_desc = "OUTPUT_OVERLOAD"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_INPUT_OVER_CURRENT].alarm_desc = "RECTIFIER_INPUT_OVER_CURRENT"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OUTPUT_OVER_CURRENT].alarm_desc = "INVERTER_OUTPUT_OVER_CURRENT"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_OVER_VOLTAGE].alarm_desc = "DC_LINK_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_UNDER_VOLTAGE].alarm_desc = "DC_LINK_UNDER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAILED].alarm_desc = "RECTIFIER_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_FAULT].alarm_desc = "INVERTER_FAULT"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CONNECTOR_FAIL].alarm_desc = "BATTERY_CONNECTOR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_BREAKER_FAIL].alarm_desc = "BYPASS_BREAKER_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_FAIL].alarm_desc = "CHARGER_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RAMP_UP_FAILED].alarm_desc = "RAMP_UP_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_STATIC_SWITCH_FAILED].alarm_desc = "STATIC_SWITCH_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_AD_REF_FAIL].alarm_desc = "ANALOG_AD_REF_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_UNCALIBRATED].alarm_desc = "BYPASS_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_UNCALIBRATED].alarm_desc = "RECTIFIER_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNCALIBRATED].alarm_desc = "OUTPUT_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_UNCALIBRATED].alarm_desc = "INVERTER_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_VOLT_UNCALIBRATED].alarm_desc = "DC_VOLT_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_CURRENT_UNCALIBRATED].alarm_desc = "OUTPUT_CURRENT_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_UNCALIBRATED].alarm_desc = "RECTIFIER_CURRENT_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CURRENT_UNCALIBRATED].alarm_desc = "BATTERY_CURRENT_UNCALIBRATED"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_OFF_STAT_FAIL].alarm_desc = "INVERTER_ON_OFF_STAT_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CURRENT_LIMIT].alarm_desc = "BATTERY_CURRENT_LIMIT"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_STARTUP_FAIL].alarm_desc = "INVERTER_STARTUP_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_BOARD_AD_STAT_FAIL].alarm_desc = "ANALOG_BOARD_AD_STAT_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_CURRENT_OVER_100].alarm_desc = "OUTPUT_CURRENT_OVER_100"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_GROUND_FAULT].alarm_desc = "BATTERY_GROUND_FAULT"; bcmxcp_alarm_map[BCMXCP_ALARM_WAITING_FOR_CHARGER_SYNC].alarm_desc = "WAITING_FOR_CHARGER_SYNC"; bcmxcp_alarm_map[BCMXCP_ALARM_NV_RAM_FAIL].alarm_desc = "NV_RAM_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_BOARD_AD_TIMEOUT].alarm_desc = "ANALOG_BOARD_AD_TIMEOUT"; bcmxcp_alarm_map[BCMXCP_ALARM_SHUTDOWN_IMMINENT].alarm_desc = "SHUTDOWN_IMMINENT"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_LOW].alarm_desc = "BATTERY_LOW"; bcmxcp_alarm_map[BCMXCP_ALARM_UTILITY_FAIL].alarm_desc = "UTILITY_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_SHORT_CIRCUIT].alarm_desc = "OUTPUT_SHORT_CIRCUIT"; bcmxcp_alarm_map[BCMXCP_ALARM_UTILITY_NOT_PRESENT].alarm_desc = "UTILITY_NOT_PRESENT"; bcmxcp_alarm_map[BCMXCP_ALARM_FULL_TIME_CHARGING].alarm_desc = "FULL_TIME_CHARGING"; bcmxcp_alarm_map[BCMXCP_ALARM_FAST_BYPASS_COMMAND].alarm_desc = "FAST_BYPASS_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_AD_ERROR].alarm_desc = "AD_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_INTERNAL_COM_FAIL].alarm_desc = "INTERNAL_COM_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_SELFTEST_FAIL].alarm_desc = "RECTIFIER_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_EEPROM_FAIL].alarm_desc = "RECTIFIER_EEPROM_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_EPROM_FAIL].alarm_desc = "RECTIFIER_EPROM_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_LINE_VOLTAGE_LOSS].alarm_desc = "INPUT_LINE_VOLTAGE_LOSS"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_DC_OVER_VOLTAGE].alarm_desc = "BATTERY_DC_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_OVER_TEMP].alarm_desc = "POWER_SUPPLY_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_FAIL].alarm_desc = "POWER_SUPPLY_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_5V_FAIL].alarm_desc = "POWER_SUPPLY_5V_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_12V_FAIL].alarm_desc = "POWER_SUPPLY_12V_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_OVER_TEMP].alarm_desc = "HEATSINK_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL].alarm_desc = "HEATSINK_TEMP_SENSOR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125].alarm_desc = "RECTIFIER_CURRENT_OVER_125"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL].alarm_desc = "RECTIFIER_FAULT_INTERRUPT_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL].alarm_desc = "RECTIFIER_POWER_CAPASITOR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR].alarm_desc = "INVERTER_PROGRAM_STACK_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL].alarm_desc = "INVERTER_BOARD_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL].alarm_desc = "INVERTER_AD_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_RAM_SELFTEST_FAIL].alarm_desc = "INVERTER_RAM_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_NV_MEMORY_CHECKSUM_FAIL].alarm_desc = "NV_MEMORY_CHECKSUM_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_PROGRAM_CHECKSUM_FAIL].alarm_desc = "PROGRAM_CHECKSUM_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_CPU_SELFTEST_FAIL].alarm_desc = "INVERTER_CPU_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_NETWORK_NOT_RESPONDING].alarm_desc = "NETWORK_NOT_RESPONDING"; bcmxcp_alarm_map[BCMXCP_ALARM_FRONT_PANEL_SELFTEST_FAIL].alarm_desc = "FRONT_PANEL_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_NODE_EEPROM_VERIFICATION_ERROR].alarm_desc = "NODE_EEPROM_VERIFICATION_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_AC_OVER_VOLT_TEST_FAIL].alarm_desc = "OUTPUT_AC_OVER_VOLT_TEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_DC_OVER_VOLTAGE].alarm_desc = "OUTPUT_DC_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_PHASE_ROTATION_ERROR].alarm_desc = "INPUT_PHASE_ROTATION_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_RAMP_UP_TEST_FAILED].alarm_desc = "INVERTER_RAMP_UP_TEST_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OFF_COMMAND].alarm_desc = "INVERTER_OFF_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_COMMAND].alarm_desc = "INVERTER_ON_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_TO_BYPASS_COMMAND].alarm_desc = "TO_BYPASS_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_FROM_BYPASS_COMMAND].alarm_desc = "FROM_BYPASS_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_MODE_COMMAND].alarm_desc = "AUTO_MODE_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_EMERGENCY_SHUTDOWN_COMMAND].alarm_desc = "EMERGENCY_SHUTDOWN_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_SETUP_SWITCH_OPEN].alarm_desc = "SETUP_SWITCH_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_VOLT_INT].alarm_desc = "INVERTER_OVER_VOLT_INT"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_UNDER_VOLT_INT].alarm_desc = "INVERTER_UNDER_VOLT_INT"; bcmxcp_alarm_map[BCMXCP_ALARM_ABSOLUTE_DCOV_ACOV].alarm_desc = "ABSOLUTE_DCOV_ACOV"; bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_A_CURRENT_LIMIT].alarm_desc = "PHASE_A_CURRENT_LIMIT"; bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_B_CURRENT_LIMIT].alarm_desc = "PHASE_B_CURRENT_LIMIT"; bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_C_CURRENT_LIMIT].alarm_desc = "PHASE_C_CURRENT_LIMIT"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_NOT_AVAILABLE].alarm_desc = "BYPASS_NOT_AVAILABLE"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_BREAKER_OPEN].alarm_desc = "RECTIFIER_BREAKER_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CONTACTOR_OPEN].alarm_desc = "BATTERY_CONTACTOR_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_CONTACTOR_OPEN].alarm_desc = "INVERTER_CONTACTOR_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_BREAKER_OPEN].alarm_desc = "BYPASS_BREAKER_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_INV_BOARD_ACOV_INT_TEST_FAIL].alarm_desc = "INV_BOARD_ACOV_INT_TEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_TEMP_TRIP].alarm_desc = "INVERTER_OVER_TEMP_TRIP"; bcmxcp_alarm_map[BCMXCP_ALARM_INV_BOARD_ACUV_INT_TEST_FAIL].alarm_desc = "INV_BOARD_ACUV_INT_TEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_FEEDBACK_ERROR].alarm_desc = "INVERTER_VOLTAGE_FEEDBACK_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_UNDER_VOLTAGE_TIMEOUT].alarm_desc = "DC_UNDER_VOLTAGE_TIMEOUT"; bcmxcp_alarm_map[BCMXCP_ALARM_AC_UNDER_VOLTAGE_TIMEOUT].alarm_desc = "AC_UNDER_VOLTAGE_TIMEOUT"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_UNDER_VOLTAGE_WHILE_CHARGE].alarm_desc = "DC_UNDER_VOLTAGE_WHILE_CHARGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR].alarm_desc = "INVERTER_VOLTAGE_BIAS_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_PHASE_ROTATION].alarm_desc = "RECTIFIER_PHASE_ROTATION"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_PHASER_ROTATION].alarm_desc = "BYPASS_PHASER_ROTATION"; bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_INTERFACE_BOARD_FAIL].alarm_desc = "SYSTEM_INTERFACE_BOARD_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_BOARD_FAIL].alarm_desc = "PARALLEL_BOARD_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_A].alarm_desc = "LOST_LOAD_SHARING_PHASE_A"; bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_B].alarm_desc = "LOST_LOAD_SHARING_PHASE_B"; bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_C].alarm_desc = "LOST_LOAD_SHARING_PHASE_C"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_OVER_VOLTAGE_TIMEOUT].alarm_desc = "DC_OVER_VOLTAGE_TIMEOUT"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TOTALLY_DISCHARGED].alarm_desc = "BATTERY_TOTALLY_DISCHARGED"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_BIAS_ERROR].alarm_desc = "INVERTER_PHASE_BIAS_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR_2].alarm_desc = "INVERTER_VOLTAGE_BIAS_ERROR_2"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_BLEED_COMPLETE].alarm_desc = "DC_LINK_BLEED_COMPLETE"; bcmxcp_alarm_map[BCMXCP_ALARM_LARGE_CHARGER_INPUT_CURRENT].alarm_desc = "LARGE_CHARGER_INPUT_CURRENT"; bcmxcp_alarm_map[BCMXCP_ALARM_INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL].alarm_desc = "INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL"; bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_REDUNDANCY].alarm_desc = "LOSS_OF_REDUNDANCY"; bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_SYNC_BUS].alarm_desc = "LOSS_OF_SYNC_BUS"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_BREAKER_SHUNT_TRIP].alarm_desc = "RECTIFIER_BREAKER_SHUNT_TRIP"; bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_CHARGER_SYNC].alarm_desc = "LOSS_OF_CHARGER_SYNC"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_LOW_LEVEL_TEST_TIMEOUT].alarm_desc = "INVERTER_LOW_LEVEL_TEST_TIMEOUT"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_BREAKER_OPEN].alarm_desc = "OUTPUT_BREAKER_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_CONTROL_POWER_ON].alarm_desc = "CONTROL_POWER_ON"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON].alarm_desc = "INVERTER_ON"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON].alarm_desc = "CHARGER_ON"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_ON].alarm_desc = "BYPASS_ON"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_POWER_LOSS].alarm_desc = "BYPASS_POWER_LOSS"; bcmxcp_alarm_map[BCMXCP_ALARM_ON_MANUAL_BYPASS].alarm_desc = "ON_MANUAL_BYPASS"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_MANUAL_TURN_OFF].alarm_desc = "BYPASS_MANUAL_TURN_OFF"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BLEEDING_DC_LINK_VOLT].alarm_desc = "INVERTER_BLEEDING_DC_LINK_VOLT"; bcmxcp_alarm_map[BCMXCP_ALARM_CPU_ISR_ERROR].alarm_desc = "CPU_ISR_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_ISR_RESTART].alarm_desc = "SYSTEM_ISR_RESTART"; bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_DC].alarm_desc = "PARALLEL_DC"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_NEEDS_SERVICE].alarm_desc = "BATTERY_NEEDS_SERVICE"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CHARGING].alarm_desc = "BATTERY_CHARGING"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_NOT_CHARGED].alarm_desc = "BATTERY_NOT_CHARGED"; bcmxcp_alarm_map[BCMXCP_ALARM_DISABLED_BATTERY_TIME].alarm_desc = "DISABLED_BATTERY_TIME"; bcmxcp_alarm_map[BCMXCP_ALARM_SERIES_7000_ENABLE].alarm_desc = "SERIES_7000_ENABLE"; bcmxcp_alarm_map[BCMXCP_ALARM_OTHER_UPS_ON].alarm_desc = "OTHER_UPS_ON"; bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_INVERTER].alarm_desc = "PARALLEL_INVERTER"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_IN_PARALLEL].alarm_desc = "UPS_IN_PARALLEL"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_BREAKER_REALY_FAIL].alarm_desc = "OUTPUT_BREAKER_REALY_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_CONTROL_POWER_OFF].alarm_desc = "CONTROL_POWER_OFF"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_A"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_B"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_C"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_A"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_B"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_C"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_A"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_B"; bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_C"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_BATTERY].alarm_desc = "UPS_ON_BATTERY"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_BYPASS].alarm_desc = "UPS_ON_BYPASS"; bcmxcp_alarm_map[BCMXCP_ALARM_LOAD_DUMPED].alarm_desc = "LOAD_DUMPED"; bcmxcp_alarm_map[BCMXCP_ALARM_LOAD_ON_INVERTER].alarm_desc = "LOAD_ON_INVERTER"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_COMMAND].alarm_desc = "UPS_ON_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF_COMMAND].alarm_desc = "UPS_OFF_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_LOW_BATTERY_SHUTDOWN].alarm_desc = "LOW_BATTERY_SHUTDOWN"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_ON_ENABLED].alarm_desc = "AUTO_ON_ENABLED"; bcmxcp_alarm_map[BCMXCP_ALARM_SOFTWARE_INCOMPABILITY_DETECTED].alarm_desc = "SOFTWARE_INCOMPABILITY_DETECTED"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_TEMP_SENSOR_FAILED].alarm_desc = "INVERTER_TEMP_SENSOR_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_DC_START_OCCURED].alarm_desc = "DC_START_OCCURED"; bcmxcp_alarm_map[BCMXCP_ALARM_IN_PARALLEL_OPERATION].alarm_desc = "IN_PARALLEL_OPERATION"; bcmxcp_alarm_map[BCMXCP_ALARM_SYNCING_TO_BYPASS].alarm_desc = "SYNCING_TO_BYPASS"; bcmxcp_alarm_map[BCMXCP_ALARM_RAMPING_UPS_UP].alarm_desc = "RAMPING_UPS_UP"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_DELAY].alarm_desc = "INVERTER_ON_DELAY"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_DELAY].alarm_desc = "CHARGER_ON_DELAY"; bcmxcp_alarm_map[BCMXCP_ALARM_WAITING_FOR_UTIL_INPUT].alarm_desc = "WAITING_FOR_UTIL_INPUT"; bcmxcp_alarm_map[BCMXCP_ALARM_CLOSE_BYPASS_BREAKER].alarm_desc = "CLOSE_BYPASS_BREAKER"; bcmxcp_alarm_map[BCMXCP_ALARM_TEMPORARY_BYPASS_OPERATION].alarm_desc = "TEMPORARY_BYPASS_OPERATION"; bcmxcp_alarm_map[BCMXCP_ALARM_SYNCING_TO_OUTPUT].alarm_desc = "SYNCING_TO_OUTPUT"; bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_FAILURE].alarm_desc = "BYPASS_FAILURE"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_OFF_COMMAND_EXECUTED].alarm_desc = "AUTO_OFF_COMMAND_EXECUTED"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_ON_COMMAND_EXECUTED].alarm_desc = "AUTO_ON_COMMAND_EXECUTED"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_FAILED].alarm_desc = "BATTERY_TEST_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_FUSE_FAIL].alarm_desc = "FUSE_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_FAN_FAIL].alarm_desc = "FAN_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_SITE_WIRING_FAULT].alarm_desc = "SITE_WIRING_FAULT"; bcmxcp_alarm_map[BCMXCP_ALARM_BACKFEED_CONTACTOR_FAIL].alarm_desc = "BACKFEED_CONTACTOR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_ON_BUCK].alarm_desc = "ON_BUCK"; bcmxcp_alarm_map[BCMXCP_ALARM_ON_BOOST].alarm_desc = "ON_BOOST"; bcmxcp_alarm_map[BCMXCP_ALARM_ON_DOUBLE_BOOST].alarm_desc = "ON_DOUBLE_BOOST"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERIES_DISCONNECTED].alarm_desc = "BATTERIES_DISCONNECTED"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_CABINET_OVER_TEMP].alarm_desc = "UPS_CABINET_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_TRANSFORMER_OVER_TEMP].alarm_desc = "TRANSFORMER_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_AMBIENT_UNDER_TEMP].alarm_desc = "AMBIENT_UNDER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_AMBIENT_OVER_TEMP].alarm_desc = "AMBIENT_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_CABINET_DOOR_OPEN].alarm_desc = "CABINET_DOOR_OPEN"; bcmxcp_alarm_map[BCMXCP_ALARM_CABINET_DOOR_OPEN_VOLT_PRESENT].alarm_desc = "CABINET_DOOR_OPEN_VOLT_PRESENT"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_SHUTDOWN_PENDING].alarm_desc = "AUTO_SHUTDOWN_PENDING"; bcmxcp_alarm_map[BCMXCP_ALARM_TAP_SWITCHING_REALY_PENDING].alarm_desc = "TAP_SWITCHING_REALY_PENDING"; bcmxcp_alarm_map[BCMXCP_ALARM_UNABLE_TO_CHARGE_BATTERIES].alarm_desc = "UNABLE_TO_CHARGE_BATTERIES"; bcmxcp_alarm_map[BCMXCP_ALARM_STARTUP_FAILURE_CHECK_EPO].alarm_desc = "STARTUP_FAILURE_CHECK_EPO"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTOMATIC_STARTUP_PENDING].alarm_desc = "AUTOMATIC_STARTUP_PENDING"; bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_FAILED].alarm_desc = "MODEM_FAILED"; bcmxcp_alarm_map[BCMXCP_ALARM_INCOMING_MODEM_CALL_STARTED].alarm_desc = "INCOMING_MODEM_CALL_STARTED"; bcmxcp_alarm_map[BCMXCP_ALARM_OUTGOING_MODEM_CALL_STARTED].alarm_desc = "OUTGOING_MODEM_CALL_STARTED"; bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CONNECTION_ESTABLISHED].alarm_desc = "MODEM_CONNECTION_ESTABLISHED"; bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CALL_COMPLETED_SUCCESS].alarm_desc = "MODEM_CALL_COMPLETED_SUCCESS"; bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CALL_COMPLETED_FAIL].alarm_desc = "MODEM_CALL_COMPLETED_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_BREAKER_FAIL].alarm_desc = "INPUT_BREAKER_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_SYSINIT_IN_PROGRESS].alarm_desc = "SYSINIT_IN_PROGRESS"; bcmxcp_alarm_map[BCMXCP_ALARM_AUTOCALIBRATION_FAIL].alarm_desc = "AUTOCALIBRATION_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_SELECTIVE_TRIP_OF_MODULE].alarm_desc = "SELECTIVE_TRIP_OF_MODULE"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OUTPUT_FAILURE].alarm_desc = "INVERTER_OUTPUT_FAILURE"; bcmxcp_alarm_map[BCMXCP_ALARM_ABNORMAL_OUTPUT_VOLT_AT_STARTUP].alarm_desc = "ABNORMAL_OUTPUT_VOLT_AT_STARTUP"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_OVER_TEMP].alarm_desc = "RECTIFIER_OVER_TEMP"; bcmxcp_alarm_map[BCMXCP_ALARM_CONFIG_ERROR].alarm_desc = "CONFIG_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_REDUNDANCY_LOSS_DUE_TO_OVERLOAD].alarm_desc = "REDUNDANCY_LOSS_DUE_TO_OVERLOAD"; bcmxcp_alarm_map[BCMXCP_ALARM_ON_ALTERNATE_AC_SOURCE].alarm_desc = "ON_ALTERNATE_AC_SOURCE"; bcmxcp_alarm_map[BCMXCP_ALARM_IN_HIGH_EFFICIENCY_MODE].alarm_desc = "IN_HIGH_EFFICIENCY_MODE"; bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_NOTICE_ACTIVE].alarm_desc = "SYSTEM_NOTICE_ACTIVE"; bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_ALARM_ACTIVE].alarm_desc = "SYSTEM_ALARM_ACTIVE"; bcmxcp_alarm_map[BCMXCP_ALARM_ALTERNATE_POWER_SOURCE_NOT_AVAILABLE].alarm_desc = "ALTERNATE_POWER_SOURCE_NOT_AVAILABLE"; bcmxcp_alarm_map[BCMXCP_ALARM_CURRENT_BALANCE_FAILURE].alarm_desc = "CURRENT_BALANCE_FAILURE"; bcmxcp_alarm_map[BCMXCP_ALARM_CHECK_AIR_FILTER].alarm_desc = "CHECK_AIR_FILTER"; bcmxcp_alarm_map[BCMXCP_ALARM_SUBSYSTEM_NOTICE_ACTIVE].alarm_desc = "SUBSYSTEM_NOTICE_ACTIVE"; bcmxcp_alarm_map[BCMXCP_ALARM_SUBSYSTEM_ALARM_ACTIVE].alarm_desc = "SUBSYSTEM_ALARM_ACTIVE"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_COMMAND].alarm_desc = "CHARGER_ON_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OFF_COMMAND].alarm_desc = "CHARGER_OFF_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_NORMAL].alarm_desc = "UPS_NORMAL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_ROTATION].alarm_desc = "INVERTER_PHASE_ROTATION"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF].alarm_desc = "UPS_OFF"; bcmxcp_alarm_map[BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE].alarm_desc = "EXTERNAL_COMMUNICATION_FAILURE"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_INPROGRESS].alarm_desc = "BATTERY_TEST_INPROGRESS"; bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_TEST_INPROGRESS].alarm_desc = "SYSTEM_TEST_INPROGRESS"; bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_ABORTED].alarm_desc = "BATTERY_TEST_ABORTED"; } /* Get information on UPS commands */ bool_t init_command(int size) { unsigned char answer[PW_ANSWER_MAX_SIZE]; unsigned char commandByte; const char* nutvalue; int res, iIndex = 0, ncounter, NumComms = 0, i; upsdebugx(1, "entering init_command(%i)", size); res = command_read_sequence(PW_COMMAND_LIST_REQ, answer); if (res <= 0) { upsdebugx(2, "No command list block."); return FALSE; } else { upsdebugx(2, "Command list block supported."); res = answer[iIndex]; NumComms = (int)res; /* Number of commands implemented in this UPS */ upsdebugx(3, "Number of commands implemented in ups %d", res); iIndex++; res = answer[iIndex]; /* Entry length - bytes reported for each command */ iIndex++; upsdebugx(5, "bytes per command %d", res); /* In case of debug - make explanation of values */ upsdebugx(2, "Index\tCmd byte\tDescription"); /* Get command bytes if size of command block matches with size from standard ID block */ if (NumComms + 2 == size) { for (ncounter = 0; ncounter < NumComms; ncounter++) { commandByte = answer[iIndex]; if(commandByte >= 0 && commandByte < BCMXCP_COMMAND_MAP_MAX) { upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, bcmxcp_command_map[commandByte].command_desc); bcmxcp_command_map[commandByte].command_byte = commandByte; } else { upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, "Unknown command, the commandByte is not mapped"); } iIndex++; } /* Map supported commands to instcmd */ for(i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) { if(bcmxcp_command_map[i].command_desc != NULL) { if(bcmxcp_command_map[i].command_byte > 0) { if ((nutvalue = nut_find_infoval(command_map_info, bcmxcp_command_map[i].command_byte, FALSE)) != NULL) { dstate_addcmd(nutvalue); upsdebugx(2, "Added support for instcmd %s", nutvalue); } } } } return TRUE; } else { upsdebugx(1, "Invalid response received from Command List block"); return FALSE; } } } void init_ups_meter_map(const unsigned char *map, unsigned char len) { unsigned int iIndex, iOffset = 0; /* In case of debug - make explanation of values */ upsdebugx(2, "Index\tOffset\tFormat\tNUT"); /* Loop thru map */ for (iIndex = 0; iIndex < len && iIndex < BCMXCP_METER_MAP_MAX; iIndex++) { bcmxcp_meter_map[iIndex].format = map[iIndex]; if (map[iIndex] != 0) { /* Set meter map entry offset */ bcmxcp_meter_map[iIndex].meter_block_index = iOffset; /* Debug info */ upsdebugx(2, "%04d\t%04d\t%2x\t%s", iIndex, iOffset, bcmxcp_meter_map[iIndex].format, (bcmxcp_meter_map[iIndex].nut_entity == NULL ? "None" :bcmxcp_meter_map[iIndex].nut_entity)); iOffset += 4; } } upsdebugx(2, "\n"); } void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value) { long lValue = 0; char sFormat[32]; float fValue; unsigned char dd, mm, yy, cc, hh, ss; /* Paranoid input sanity checks */ if (value == NULL) return; *value = '\0'; if (entry == (unsigned char *)NULL || format == 0x00) return; /* Get data based on format */ if (format == 0xf0) { /* Long integer */ lValue = get_long(entry); snprintf(value, 127, "%d", (int)lValue); } else if ((format & 0xf0) == 0xf0) { /* Fixed point integer */ fValue = get_long(entry) / ldexp(1, format & 0x0f); snprintf(value, 127, "%.2f", fValue); } else if (format <= 0x97) { /* Floating point */ fValue = get_float(entry); /* Format is packed BCD */ snprintf(sFormat, 31, "%%%d.%df", ((format & 0xf0) >> 4), (format & 0x0f)); snprintf(value, 127, sFormat, fValue); } else if (format == 0xe2) { /* Seconds */ lValue = get_long(entry); snprintf(value, 127, "%d", (int)lValue); } else if (format == 0xe0) { /* Date */ /* Format is packed BCD for each byte, and cc uses most signifcant bit to signal date format */ dd = entry[0]; mm = entry[1]; yy = entry[2]; cc = entry[3]; /* Check format type */ if (cc & 0x80) { /* Month:Day format */ snprintf(value, 127, "%d%d/%d%d/%d%d%d%d", ((dd & 0xf0) >> 4), (dd & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f)); } else { /* Julian format */ /* TODO test this, unsure if the day part is correct, i.e. how we use the two bytes mm and dd to calculate the number of julian days */ snprintf(value, 127, "%d%d%d%d:%d%d%d", (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f), (mm & 0x0f), ((dd & 0xf0) >> 4), (dd & 0x0f)); } } else if (format == 0xe1) { /* Time */ /* Format is packed BCD for each byte */ cc = entry[0]; ss = entry[1]; mm = entry[2]; hh = entry[3]; snprintf(value, 127, "%d%d:%d%d:%d%d.%d%d", ((hh & 0xf0) >> 4), (hh & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), ((ss & 0xf0) >> 4), (ss & 0x0f), ((cc & 0xf0) >> 4), (cc & 0x0f)); } else { /* Unknown format */ snprintf(value, 127, "???"); return; } return; } void init_ups_alarm_map(const unsigned char *map, unsigned char len) { unsigned int iIndex = 0; int alarm = 0; /* In case of debug - make explanation of values */ upsdebugx(2, "Index\tAlarm\tSupported"); /* Loop thru map */ for (iIndex = 0; iIndex < len && iIndex < BCMXCP_ALARM_MAP_MAX / 8; iIndex++) { /* Bit 0 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x01, iIndex * 8, alarm) == TRUE) alarm++; /* Bit 1 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x02, iIndex * 8 + 1, alarm) == TRUE) alarm++; /* Bit 2 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x04, iIndex * 8 + 2, alarm) == TRUE) alarm++; /* Bit 3 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x08, iIndex * 8 + 3, alarm) == TRUE) alarm++; /* Bit 4 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x10, iIndex * 8 + 4, alarm) == TRUE) alarm++; /* Bit 5 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x20, iIndex * 8 + 5, alarm) == TRUE) alarm++; /* Bit 6 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x40, iIndex * 8 + 6, alarm) == TRUE) alarm++; /* Bit 7 */ if(set_alarm_support_in_alarm_map(map, iIndex, 0x80, iIndex * 8 + 7, alarm) == TRUE) alarm++; } upsdebugx(2, "\n"); } bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const int mapIndex, const int bitmask, const int alarmMapIndex, const int alarmBlockIndex) { /* Check what the alarm block tells about the support for the alarm */ if (map[mapIndex] & bitmask) { /* Set alarm active */ bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = alarmBlockIndex; } else { /* Set alarm inactive */ bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = -1; } /* Return if the alarm was supported or not */ if(bcmxcp_alarm_map[alarmMapIndex].alarm_block_index >= 0) { /* Debug info */ upsdebugx(2, "%04d\t%s\tYes", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc); return TRUE; } else { /* Debug info */ upsdebugx(3, "%04d\t%s\tNo", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc); return FALSE; } } int init_outlet(unsigned char len) { unsigned char answer[PW_ANSWER_MAX_SIZE]; int iIndex = 0, res, num; int num_outlet, size_outlet; int outlet_num, outlet_state; short auto_dly_off, auto_dly_on; char outlet_name[25]; res = command_read_sequence(PW_OUT_MON_BLOCK_REQ, answer); if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); else upsdebugx(1, "init_outlet(%i), res=%i", len, res); num_outlet = answer[iIndex++]; upsdebugx(2, "Number of outlets: %d", num_outlet); size_outlet = answer[iIndex++]; upsdebugx(2, "Number of bytes: %d", size_outlet); for(num = 1 ; num <= num_outlet ; num++) { outlet_num = answer[iIndex++]; upsdebugx(2, "Outlet number: %d", outlet_num); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.id", num); dstate_setinfo(outlet_name, "%d", outlet_num); outlet_state = answer[iIndex++]; upsdebugx(2, "Outlet state: %d", outlet_state); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.status", num); if (outlet_state>0 && outlet_state <9 ) dstate_setinfo(outlet_name, "%s", OutletStatus[outlet_state] ); auto_dly_off = get_word(answer+iIndex); iIndex += 2; upsdebugx(2, "Auto delay off: %d", auto_dly_off); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.shutdown", num); dstate_setinfo(outlet_name, "%d", auto_dly_off); dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(outlet_name, 5); auto_dly_on = get_word(answer+iIndex); iIndex += 2; upsdebugx(2, "Auto delay on: %d", auto_dly_on); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.start", num); dstate_setinfo(outlet_name, "%d", auto_dly_on); dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(outlet_name, 5); } return num_outlet; } void init_ext_vars(void) { unsigned char answer[PW_ANSWER_MAX_SIZE],cbuf[5]; int length=0,index=0; send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_SET_CONF_COMMAND; cbuf[1] = PW_CONF_REQ; cbuf[2] = 0x0; cbuf[3] = 0x0; length=command_write_sequence(cbuf,4,answer); if (length <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); if (length < 4) //UPS dont have configurable vars return; for( index=3; index < length; index++) { switch(answer[index]){ case PW_CONF_LOW_DEV_LIMIT: dstate_setinfo("input.transfer.boost.high","%d",0); dstate_setflags("input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("input.transfer.boost.high", 3); break; case PW_CONF_HIGH_DEV_LIMIT: dstate_setinfo("input.transfer.trim.low","%d",0); dstate_setflags("input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("input.transfer.trim.low", 3); break; case PW_CONF_LOW_BATT: dstate_setinfo("battery.runtime.low","%d",0); dstate_setflags("battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.runtime.low", 2); break; case PW_CONF_BEEPER: dstate_addcmd("beeper.disable"); dstate_addcmd("beeper.enable"); dstate_addcmd("beeper.mute"); break; case PW_CONF_RETURN_DELAY: dstate_setinfo("input.transfer.delay","%d",0); dstate_setflags("input.transfer.delay", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("input.transfer.delay", 5); break; case PW_CONF_RETURN_CAP: dstate_setinfo("battery.charge.restart","%d",0); dstate_setflags("battery.charge.restart", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.charge.restart", 3); break; case PW_CONF_MAX_TEMP: dstate_setinfo("ambient.temperature.high","%d",0); dstate_setflags("ambient.temperature.high", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ambient.temperature.high", 3); break; case PW_CONF_NOMINAL_OUT_VOLTAGE: dstate_setinfo("output.voltage.nominal","%d",0); dstate_setflags("output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("output.voltage.nominal", 3); break; case PW_CONF_SLEEP_TH_LOAD: dstate_setinfo("battery.energysave.load","%d",0); dstate_setflags("battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.energysave.load", 3); break; case PW_CONF_SLEEP_DELAY: dstate_setinfo("battery.energysave.delay","%d",0); dstate_setflags("battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.energysave.delay", 3); break; case PW_CONF_BATT_STRINGS: dstate_setinfo("battery.packs","%d",0); dstate_setflags("battery.packs", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("battery.packs", 1); break; } } } void init_config(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; int voltage = 0, frequency = 0, res, tmp=0; char sValue[17]; char sPartNumber[17]; res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer); if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); /* Get validation mask for status bitmap */ bcmxcp_status.topology_mask = answer[BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_BYTE3]; /* Nominal output voltage of ups */ voltage = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE)); if (voltage != 0) dstate_setinfo("output.voltage.nominal", "%d", voltage); /* Nominal Output Frequency */ frequency = get_word((answer+BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ)); if (frequency != 0) dstate_setinfo("output.frequency.nominal", "%d", frequency); /*Number of EBM*/ tmp = (int) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3); if (tmp != 0) dstate_setinfo("battery.packs", "%d", tmp); /* UPS serial number */ snprintf(sValue, sizeof(sValue), "%s", answer + BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER); if(sValue[0] != '\0') dstate_setinfo("ups.serial", "%s", sValue); /* UPS Part Number*/ snprintf(sPartNumber, sizeof(sPartNumber), "%s", answer + BCMXCP_CONFIG_BLOCK_PART_NUMBER); if(sPartNumber[0] != '\0') dstate_setinfo("device.part", "%s", sPartNumber); } void init_limit(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; int value, res; res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer); if (res <= 0) { fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); } /* Nominal input voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE)); if (value != 0) { dstate_setinfo("input.voltage.nominal", "%d", value); } /* Nominal input frequency */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ)); if (value != 0) { int fnom = value; dstate_setinfo("input.frequency.nominal", "%d", value); /* Input frequency deviation */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT)); if (value != 0) { value /= 100; dstate_setinfo("input.frequency.low", "%d", fnom - value); dstate_setinfo("input.frequency.high", "%d", fnom + value); } } /* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT)); if (value != 0) { dstate_setinfo("input.transfer.boost.high", "%d", value); } /* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT)); if (value != 0) { dstate_setinfo("input.transfer.trim.low", "%d", value); } /* Low battery warning */ bcmxcp_status.lowbatt = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING] * 60; /* Check if we should warn the user that her shutdown delay is to long? */ if (bcmxcp_status.shutdowndelay > bcmxcp_status.lowbatt) upslogx(LOG_WARNING, "Shutdown delay longer than battery capacity when Low Battery warning is given. (max %d seconds)", bcmxcp_status.lowbatt); /* Horn Status: */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS]; if (value >= 0 && value <= 2) { dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]); } /* Minimum Supported Input Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE)); if (value != 0) { dstate_setinfo("input.transfer.low", "%d", value); } /* Maximum Supported Input Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE)); if (value != 0) { dstate_setinfo("input.transfer.high", "%d", value); } /* Ambient Temperature Lower Alarm Limit */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW]; if (value != 0) { dstate_setinfo("ambient.temperature.low", "%d", value); } /* Ambient Temperature Upper Alarm Limit */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE]; if (value != 0) { dstate_setinfo("ambient.temperature.high", "%d", value); } /*Sleep minimum load*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD]; if (value != 0) { dstate_setinfo("battery.energysave.load", "%d", value); } /* Sleep delay*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY]; if (value != 0) { dstate_setinfo("battery.energysave.delay", "%d", value); } /* Low batt minutes warning*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING]; if (value != 0) { dstate_setinfo("battery.runtime.low", "%d", value); } /* Return to mains delay */ value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY); if (value != 0) { dstate_setinfo("input.transfer.delay","%d",value); } /* Minimum return capacity*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN]; if (value != 0) { dstate_setinfo("battery.charge.restart","%d",value); } } void init_topology(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; const char* nutvalue; int res, value; res = command_read_sequence(PW_UPS_TOP_DATA_REQ, answer); if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); value = get_word(answer); if ((nutvalue = nut_find_infoval(topology_info, value, TRUE)) != NULL) { dstate_setinfo("ups.description", "%s", nutvalue); } } void init_system_test_capabilities(void) { unsigned char answer[PW_ANSWER_MAX_SIZE], cbuf[5]; const char* nutvalue; int res, value, i; /* Query what system test capabilities are supported */ send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_INIT_SYS_TEST; cbuf[1] = PW_SYS_TEST_REPORT_CAPABILITIES; res = command_write_sequence(cbuf, 2, answer); if (res <= 0) { upslogx(LOG_ERR, "Short read from UPS"); return; } if((unsigned char)answer[0] != BCMXCP_RETURN_ACCEPTED) { upsdebugx(2, "System test capabilities list not supported"); return; } /* Add instcmd for system test capabilities */ for(i = 3; i < res; i++) { value = answer[i]; if ((nutvalue = nut_find_infoval(system_test_info, value, TRUE)) != NULL) { upsdebugx(2, "Added support for instcmd %s", nutvalue); dstate_addcmd(nutvalue); } } } void upsdrv_initinfo(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; char *pTmp; char outlet_name[27]; char power_rating[10]; int iRating = 0, iIndex = 0, res, len; int ncpu = 0, buf; int conf_block_len = 0, alarm_block_len = 0, cmd_list_len = 0, topology_block_len = 0; bool_t got_cmd_list = FALSE; /* Init BCM/XCP command descriptions */ init_command_map(); /* Init BCM/XCP alarm descriptions */ init_alarm_map(); /* Get vars from ups.conf */ if (getval("shutdown_delay") != NULL) bcmxcp_status.shutdowndelay = atoi(getval("shutdown_delay")); else bcmxcp_status.shutdowndelay = 120; /* Get information on UPS from UPS ID block */ res = command_read_sequence(PW_ID_BLOCK_REQ, answer); if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); /* Get number of CPU's in ID block */ len = answer[iIndex++]; buf = len * 11; pTmp = xmalloc(buf+1); pTmp[0] = 0; /* If there is one or more CPU number, get it */ if (len > 0) { do { if ((answer[iIndex] != 0x00) || (answer[iIndex+1] != 0x00)) { /* Get the ups firmware. The major number is in the last byte, the minor is in the first */ snprintfcat(pTmp, buf+1, "%s%02x.%02x ", cpu_name[ncpu], answer[iIndex+1], answer[iIndex]); } iIndex += 2; len--; ncpu++; } while ((len > 0) && (ncpu <= 5)); dstate_setinfo("ups.firmware", "%s", pTmp); /* Increment index to point at end of CPU bytes. */ iIndex += len * 2; } free(pTmp); /* Get rating in kVA, if present */ if ((iRating = answer[iIndex++]) > 0) iRating *= 1000; else { /* The rating is given as 2 byte VA */ iRating = get_word(answer+iIndex) * 50; iIndex += 2; } dstate_setinfo("ups.power.nominal", "%d", iRating); /* Get information on Phases from UPS */ nphases = (answer[iIndex++]); dstate_setinfo("output.phases", "%d", nphases); /* Init BCM/XCP <-> NUT meter map */ init_meter_map(); /* Skip UPS' phase angle, as NUT do not care */ iIndex += 1; /* Set manufacturer name */ dstate_setinfo("ups.mfr", "Eaton"); /* Get length of UPS description */ len = answer[iIndex++]; /* Extract and reformat the model string */ pTmp = xmalloc(len+15); snprintf(pTmp, len + 1, "%s", answer + iIndex); pTmp[len+1] = 0; iIndex += len; /* power rating in the model name is in the form "i" * ie "1500i", "500i", ... * some models already includes it, so check to avoid duplication */ snprintf(power_rating, sizeof(power_rating), "%ii", iRating); if (strstr(pTmp, power_rating) == NULL) { snprintfcat(pTmp, len+10, " %s", power_rating); } dstate_setinfo("ups.model", "%s", str_rtrim(pTmp, ' ')); free(pTmp); /* Get meter map info from ups, and init our map */ len = answer[iIndex++]; upsdebugx(2, "Length of meter map: %d\n", len); init_ups_meter_map(answer+iIndex, len); iIndex += len; /* Next is alarm map */ len = answer[iIndex++]; upsdebugx(2, "Length of alarm map: %d\n", len); init_ups_alarm_map(answer+iIndex, len); iIndex += len; /* Then the Config_block_length */ conf_block_len = get_word(answer+iIndex); upsdebugx(2, "Length of Config_block: %d\n", conf_block_len); iIndex += 2; /* Next is statistics map */ len = answer[iIndex++]; upsdebugx(2, "Length of statistics map: %d\n", len); /* init_statistics_map(answer+iIndex, len); */ iIndex += len; /* Size of the alarm history log */ len = get_word(answer+iIndex); upsdebugx(2, "Length of alarm history log: %d\n", len); iIndex += 2; /* Size of custom event log, always 0 according to spec */ iIndex += 2; /* Size of topology block */ topology_block_len = get_word(answer+iIndex); upsdebugx(2, "Length of topology block: %d\n", topology_block_len); iIndex += 2; /* Maximum supported command length */ len = answer[iIndex++]; upsdebugx(2, "Length of max supported command length: %d\n", len); /* Size of command list block */ if (iIndex < res) cmd_list_len = get_word(answer+iIndex); upsdebugx(2, "Length of command list: %d\n", cmd_list_len); iIndex += 2; /* Size of outlet monitoring block */ if (iIndex < res) outlet_block_len = get_word(answer+iIndex); upsdebugx(2, "Length of outlet_block: %d\n", outlet_block_len); iIndex += 2; /* Size of the alarm block */ if (iIndex < res) alarm_block_len = get_word(answer+iIndex); upsdebugx(2, "Length of alarm_block: %d\n", alarm_block_len); /* End of UPS ID block request */ /* Due to a bug in PW5115 firmware, we need to use blocklength > 8. The protocol state that outlet block is only implemented if there is at least 2 outlet block. 5115 has only one outlet, but has outlet block! */ if (outlet_block_len > 8) { len = init_outlet(outlet_block_len); for(res = 1 ; res <= len ; res++) { snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.shutdown.return", res); dstate_addcmd(outlet_name); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.load.on", res); dstate_addcmd(outlet_name); snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.load.off", res); dstate_addcmd(outlet_name); } } /* Get information on UPS configuration */ init_config(); /* Get information on UPS extended limits */ init_limit(); /* Get information on UPS commands */ if (cmd_list_len) got_cmd_list = init_command(cmd_list_len); /* Add default commands if we were not able to query UPS for support */ if(got_cmd_list == FALSE) { dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); dstate_addcmd("test.battery.start"); } /* Get information on UPS topology */ if (topology_block_len) init_topology(); /* Get information on system test capabilities */ if (bcmxcp_command_map[PW_INIT_SYS_TEST].command_byte > 0) { init_system_test_capabilities(); } /* Get information about configurable external variables*/ init_ext_vars(); upsh.instcmd = instcmd; upsh.setvar = setvar; } void upsdrv_updateinfo(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; unsigned char status, topology; char sValue[128]; int iIndex, res,value; bool_t has_ups_load = FALSE; int batt_status = 0; const char *nutvalue; float calculated_load; /* Get info from UPS */ res = command_read_sequence(PW_METER_BLOCK_REQ, answer); if (res <= 0){ upslogx(LOG_ERR, "Short read from UPS"); dstate_datastale(); return; } /* Loop thru meter map, get all data UPS is willing to offer */ for (iIndex = 0; iIndex < BCMXCP_METER_MAP_MAX; iIndex++){ if (bcmxcp_meter_map[iIndex].format != 0 && bcmxcp_meter_map[iIndex].nut_entity != NULL) { decode_meter_map_entry(answer + bcmxcp_meter_map[iIndex].meter_block_index, bcmxcp_meter_map[iIndex].format, sValue); /* Set result */ dstate_setinfo(bcmxcp_meter_map[iIndex].nut_entity, "%s", sValue); /* Check if we read ups.load */ if(has_ups_load == FALSE && !strcasecmp(bcmxcp_meter_map[iIndex].nut_entity, "ups.load")) { has_ups_load = TRUE; } } } /* Calculate ups.load if UPS does not report it directly */ if(has_ups_load == FALSE) { calculated_load = calculate_ups_load(answer); if(calculated_load >= 0.0f) { dstate_setinfo("ups.load", "%5.1f", calculated_load); } } /* Due to a bug in PW5115 firmware, we need to use blocklength > 8. The protocol state that outlet block is only implemented if there is at least 2 outlet block. 5115 has only one outlet, but has outlet block. */ if (outlet_block_len > 8) { init_outlet(outlet_block_len); } /* Get alarm info from UPS */ res = command_read_sequence(PW_CUR_ALARM_REQ, answer); if (res <= 0){ upslogx(LOG_ERR, "Short read from UPS"); dstate_datastale(); return; } else { bcmxcp_status.alarm_on_battery = 0; bcmxcp_status.alarm_low_battery = 0; /* Set alarms */ alarm_init(); /* Loop thru alarm map, get all alarms UPS is willing to offer */ for (iIndex = 0; iIndex < BCMXCP_ALARM_MAP_MAX; iIndex++){ if (bcmxcp_alarm_map[iIndex].alarm_block_index >= 0 && bcmxcp_alarm_map[iIndex].alarm_desc != NULL) { if (answer[bcmxcp_alarm_map[iIndex].alarm_block_index] > 0) { alarm_set(bcmxcp_alarm_map[iIndex].alarm_desc); if (iIndex == BCMXCP_ALARM_UPS_ON_BATTERY) { bcmxcp_status.alarm_on_battery = 1; } else if (iIndex == BCMXCP_ALARM_BATTERY_LOW) { bcmxcp_status.alarm_low_battery = 1; } else if (iIndex == BCMXCP_ALARM_BATTERY_TEST_FAILED) { bcmxcp_status.alarm_replace_battery = 1; } else if (iIndex == BCMXCP_ALARM_BATTERY_NEEDS_SERVICE) { bcmxcp_status.alarm_replace_battery = 1; } } } } /* Confirm alarms */ alarm_commit(); } /* Get status info from UPS */ res = command_read_sequence(PW_STATUS_REQ, answer); if (res <= 0) { upslogx(LOG_ERR, "Short read from UPS"); dstate_datastale(); return; } else { /* Get overall status */ memcpy(&status, answer, sizeof(status)); /* Get topology status bitmap, validate */ memcpy(&topology, answer+1, sizeof(topology)); topology &= bcmxcp_status.topology_mask; /* Set status */ status_init(); switch (status) { case BCMXCP_STATUS_ONLINE: /* On line, everything is fine */ status_set("OL"); break; case BCMXCP_STATUS_ONBATTERY: /* Off line */ if (bcmxcp_status.alarm_on_battery == 0) status_set("OB"); break; case BCMXCP_STATUS_OVERLOAD: /* Overload */ status_set("OL"); status_set("OVER"); break; case BCMXCP_STATUS_TRIM: /* Trim */ status_set("OL"); status_set("TRIM"); break; case BCMXCP_STATUS_BOOST1: case BCMXCP_STATUS_BOOST2: /* Boost */ status_set("OL"); status_set("BOOST"); break; case BCMXCP_STATUS_BYPASS: /* Bypass */ status_set("OL"); status_set("BYPASS"); break; case BCMXCP_STATUS_OFF: /* Mostly off */ status_set("OFF"); break; default: /* Unknown, assume it is OK... */ status_set("OL"); break; } /* We might have to modify status based on topology status */ if ((topology & 0x20) && bcmxcp_status.alarm_low_battery == 0) status_set("LB"); /* And finally, we might need to modify status based on alarms - the most correct way */ if (bcmxcp_status.alarm_on_battery) status_set("OB"); if (bcmxcp_status.alarm_low_battery) status_set("LB"); if (bcmxcp_status.alarm_replace_battery) status_set("RB"); status_commit(); } /* Get battery info from UPS, if exist */ res = command_read_sequence(PW_BATTERY_REQ, answer); if (res <= 0) { upsdebugx(1, "Failed to read Battery Status from UPS"); } else { /* Only parse the status (first byte) * Powerware 5115 RM output: * 02 00 78 1d 42 00 e0 17 42 1e 00 00 00 00 00 00 00 00 00 01 03 * Powerware 9130 output: * 03 0a d7 25 42 0a d7 25 42 00 9a 19 6d 43 cd cc 4c 3e 01 00 01 03 */ upsdebug_hex(2, "Battery Status", answer, res); batt_status = answer[BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS]; if ((nutvalue = nut_find_infoval(batt_test_info, batt_status, TRUE)) != NULL) { dstate_setinfo("ups.test.result", "%s", nutvalue); upsdebugx(2, "Battery Status = %s (%i)", nutvalue, batt_status); } else { upsdebugx(1, "Failed to extract Battery Status from answer"); } /*Extracting internal batteries ABM status*/ /*Placed first in ABM statuses list. For examples above - on position BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS (18): PW5115RM - 0 - no external strings, no status bytes, so next byte (19) - number of ABM statuses, next (20) - first ABM Status for internal batteries. PW9130 - 1 - one external string, so one additional status byte (#19 - 00 - no test run), next(20) - number of ABM statuses, next (21) - ABM Status for internal batteries. */ value=*(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS + *(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS)*1+2 ); upsdebugx(2, "ABM Status = %d ",value); if (value > 0 && value < 5) dstate_setinfo("battery.charger.status","%s",ABMStatus[value-1]); } res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer); if (res <= 0) { upsdebugx(1, "Failed to read EXT LIMITs from UPS"); } else { /* Nominal input voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE)); if (value != 0) { dstate_setinfo("input.voltage.nominal", "%d", value); } /* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT)); if (value != 0) { dstate_setinfo("input.transfer.boost.high", "%d", value); } /* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT)); if (value != 0) { dstate_setinfo("input.transfer.trim.low", "%d", value); } /* Minimum Supported Input Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE)); if (value != 0) { dstate_setinfo("input.transfer.low", "%d", value); } /* Maximum Supported Input Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE)); if (value != 0) { dstate_setinfo("input.transfer.high", "%d", value); } /* Horn Status: */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS]; if (value >= 0 && value <= 2) { dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]); } /* AAmbient Temperature Upper Alarm Limit */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE]; if (value != 0) { dstate_setinfo("ambient.temperature.high", "%d", value); } /*Sleep minimum load*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD]; if (value != 0) { dstate_setinfo("battery.energysave.load", "%d", value); } /* Sleep delay*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY]; if (value != 0) { dstate_setinfo("battery.energysave.delay", "%d", value); } /* Low batt minutes warning*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING]; if (value != 0) { dstate_setinfo("battery.runtime.low", "%d", value); } /* Return to mains delay */ value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY); if (value != 0) { dstate_setinfo("input.transfer.delay","%d",value); } /* Minimum return capacity*/ value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN]; if (value != 0) { dstate_setinfo("battery.charge.restart","%d",value); } }; res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer); if (res <= 0) { upsdebugx(1, "Failed to read CONF BLOCK from UPS"); } else { /*Nominal output voltage*/ value = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE)); if (value != 0) dstate_setinfo("output.voltage.nominal", "%d", value); /*Number of EBM*/ value = (int) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3); if (value != 0) dstate_setinfo("battery.packs", "%d", value); } dstate_dataok(); } float calculate_ups_load(const unsigned char *answer) { char sValue[128]; float output = 0, max_output = -FLT_MAX, fValue = -FLT_MAX; if (bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format != 0 && /* Output VA */ bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format != 0) /* Max output VA */ { decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].meter_block_index, bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format, sValue); output = atof(sValue); decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].meter_block_index, bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format, sValue); max_output = atof(sValue); } else if (bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format != 0 && /* Output A */ bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format != 0) /* Max output A */ { decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].meter_block_index, bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format, sValue); output = atof(sValue); decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].meter_block_index, bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format, sValue); max_output = atof(sValue); } if (max_output > 0.0) fValue = 100 * (output / max_output); return fValue; } void upsdrv_shutdown(void) { upsdebugx(1, "upsdrv_shutdown..."); /* Try to shutdown with delay */ if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } /* If the above doesn't work, try shutdown.stayoff */ if (instcmd("shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } fatalx(EXIT_FAILURE, "Shutdown failed!"); } static int instcmd(const char *cmdname, const char *extra) { unsigned char answer[128], cbuf[6]; char success_msg[40]; char namebuf[MAX_NUT_NAME_LENGTH]; char varname[32]; const char *varvalue = NULL; int res, sec, outlet_num; int sddelay = 0x03; /* outlet off in 3 seconds, by default */ upsdebugx(1, "entering instcmd(%s)", cmdname); if (!strcasecmp(cmdname, "shutdown.return")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_LOAD_OFF_RESTART; cbuf[1] = (unsigned char)(bcmxcp_status.shutdowndelay & 0x00ff); /* "delay" sec delay for shutdown, */ cbuf[2] = (unsigned char)(bcmxcp_status.shutdowndelay >> 8); /* high byte sec. From ups.conf. */ res = command_write_sequence(cbuf, 3, answer); sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2]; snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg); } if (!strcasecmp(cmdname, "shutdown.stayoff")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ res = command_read_sequence(PW_UPS_OFF, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Going down NOW"); } if (!strcasecmp(cmdname, "load.on")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ res = command_read_sequence(PW_UPS_ON, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Enabling"); } if (!strcasecmp(cmdname, "bypass.start")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ res = command_read_sequence(PW_GO_TO_BYPASS, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Bypass enabled"); } /* Note: test result will be parsed from Battery status block, * part of the update loop, and published into ups.test.result */ if (!strcasecmp(cmdname, "test.battery.start")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_INIT_BAT_TEST; cbuf[1] = 0x0A; /* 10 sec start delay for test.*/ cbuf[2] = 0x1E; /* 30 sec test duration.*/ res = command_write_sequence(cbuf, 3, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing battery now"); /* Get test info from UPS ? Should we wait for 50 sec and get the answer from the test. Or return, as we may lose line power and need to do a shutdown.*/ } if (!strcasecmp(cmdname, "test.system.start")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_INIT_SYS_TEST; cbuf[1] = PW_SYS_TEST_GENERAL; res = command_write_sequence(cbuf, 2, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing system now"); } if (!strcasecmp(cmdname, "test.panel.start")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_INIT_SYS_TEST; cbuf[1] = PW_SYS_TEST_FLASH_LIGHTS; cbuf[2] = 0x0A; /* Flash and beep 10 times */ res = command_write_sequence(cbuf, 3, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing panel now"); } if (!strcasecmp(cmdname, "beeper.disable") || !strcasecmp(cmdname, "beeper.enable") || !strcasecmp(cmdname, "beeper.mute")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_SET_CONF_COMMAND; cbuf[1] = PW_CONF_BEEPER; switch (cmdname[7]){ case 'd': case 'D': { cbuf[2] = 0x0; /*disable beeper*/ break; } case 'e': case 'E': { cbuf[2] = 0x1; /*enable beeper*/ break; } case 'm': case 'M': { cbuf[2] = 0x2; break; /*mute beeper*/ } } cbuf[3] = 0x0; /*padding*/ res = command_write_sequence(cbuf, 4, answer); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Beeper status changed"); } strncpy(namebuf, cmdname, sizeof(namebuf)); namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */ if (!strcasecmp(namebuf, "outlet.n.shutdown.return")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ /* Get the shutdown delay, if any */ snprintf(varname, sizeof(varname)-1, "outlet.%c.delay.shutdown", cmdname[NUT_OUTLET_POSITION]); if ((varvalue = dstate_getinfo(varname)) != NULL) { sddelay = atoi(varvalue); } /*if -1 then use global shutdown_delay from ups.conf*/ if (sddelay == -1) sddelay=bcmxcp_status.shutdowndelay; outlet_num = cmdname[NUT_OUTLET_POSITION] - '0'; if (outlet_num < 1 || outlet_num > 9) return STAT_INSTCMD_FAILED; cbuf[0] = PW_LOAD_OFF_RESTART; cbuf[1] = sddelay & 0xff; cbuf[2] = sddelay >> 8; /* high byte of the 2 byte time argument */ cbuf[3] = outlet_num; /* which outlet load segment? Assumes outlet number at position 8 of the command string. */ res = command_write_sequence(cbuf, 4, answer); sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2]; snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg); } if (!strcasecmp(namebuf,"outlet.n.load.on") || !strcasecmp(namebuf,"outlet.n.load.off")){ send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ outlet_num = cmdname[NUT_OUTLET_POSITION] - '0'; if (outlet_num < 1 || outlet_num > 9) return STAT_INSTCMD_FAILED; cbuf[0] = (cmdname[NUT_OUTLET_POSITION+8] == 'n')?PW_UPS_ON:PW_UPS_OFF; /* Cmd oN or not*/ cbuf[1] = outlet_num; /* Outlet number */ res = command_write_sequence(cbuf, 2, answer); snprintf(success_msg, sizeof(success_msg)-1, "Outlet %d is %s",outlet_num, (cmdname[NUT_OUTLET_POSITION+8] == 'n')?"On":"Off"); return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg); } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } static int decode_instcmd_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg) { if (res <= 0) { upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname); dstate_datastale(); return STAT_INSTCMD_FAILED; } /* Decode the status code from command execution */ switch (exec_status) { case BCMXCP_RETURN_ACCEPTED: { upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); upsdrv_comm_good(); return STAT_INSTCMD_HANDLED; break; } case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: { upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname); upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); upsdrv_comm_good(); return STAT_INSTCMD_HANDLED; break; } case BCMXCP_RETURN_BUSY: { upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname); return STAT_INSTCMD_FAILED; break; } case BCMXCP_RETURN_UNRECOGNISED: { upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname); return STAT_INSTCMD_FAILED; break; } case BCMXCP_RETURN_INVALID_PARAMETER: { upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); return STAT_INSTCMD_INVALID; break; } case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: { upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname); return STAT_INSTCMD_INVALID; break; } default: { upslogx(LOG_NOTICE, "[%s] Not supported", cmdname); return STAT_INSTCMD_INVALID; break; } } } void upsdrv_help(void) { } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar(VAR_VALUE, "shutdown_delay", "Specify shutdown delay (seconds)"); addvar(VAR_VALUE, "baud_rate", "Specify communication speed (ex: 9600)"); } int setvar (const char *varname, const char *val) { unsigned char answer[128], cbuf[5]; char namebuf[MAX_NUT_NAME_LENGTH]; char success_msg[50]; int res, sec, outlet_num,tmp; int onOff_setting = PW_AUTO_OFF_DELAY; upsdebugx(1, "entering setvar(%s, %s)", varname, val); if (!strcasecmp(varname, "input.transfer.boost.high")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 460) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_LOW_DEV_LIMIT; cbuf[2]=tmp&0xff; cbuf[3]=tmp>>8; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " BOOST threshold volage set to %d V", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "input.transfer.trim.low")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 110 || tmp > 540) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_HIGH_DEV_LIMIT; cbuf[2]=tmp&0xff; cbuf[3]=tmp>>8; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " TRIM threshold volage set to %d V", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "battery.runtime.low")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 30) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_LOW_BATT; cbuf[2]=tmp; cbuf[3]=0x0; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Low battery warning time set to %d min", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "input.transfer.delay")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 1 || tmp > 18000 ) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_RETURN_DELAY; cbuf[2]=tmp&0xff; cbuf[3]=tmp>>8; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Mains return delay set to %d sec", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "battery.charge.restart")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 100 ) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_RETURN_CAP; cbuf[2]=tmp&0xff; cbuf[3]=0x0; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Mains return minimum battery capacity set to %d %%", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "ambient.temperature.high")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 100 ) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_MAX_TEMP; cbuf[2]=tmp&0xff; cbuf[3]=0x0; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Maximum temperature set to %d C", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "output.voltage.nominal")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 460) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_NOMINAL_OUT_VOLTAGE; cbuf[2]=tmp&0xff; cbuf[3]=tmp>>8; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Nominal output voltage set to %d V", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "battery.energysave.load")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 100) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_SLEEP_TH_LOAD; cbuf[2]=tmp&0xff; cbuf[3]=0x0; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Minimum load before sleep countdown set to %d %%", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "battery.energysave.delay")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 255) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_SLEEP_DELAY; cbuf[2]=tmp&0xff; cbuf[3]=0x0; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, " Delay before sleep shutdown set to %d min", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } if (!strcasecmp(varname, "battery.packs")) { send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ tmp=atoi(val); if (tmp < 0 || tmp > 5) { return STAT_SET_INVALID; } cbuf[0]=PW_SET_CONF_COMMAND; cbuf[1]=PW_CONF_BATT_STRINGS; cbuf[2]=tmp; cbuf[3]=0x0; res = command_write_sequence(cbuf, 4, answer); snprintf(success_msg, sizeof(success_msg)-1, "EBM Count set to %d ", tmp); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } strncpy(namebuf, varname, sizeof(namebuf)); namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */ if ( (!strcasecmp(namebuf, "outlet.n.delay.start")) || (!strcasecmp(namebuf, "outlet.n.delay.shutdown")) ) { if (outlet_block_len <= 8) { return STAT_SET_INVALID; } if (!strcasecmp(namebuf, "outlet.n.delay.start")) { onOff_setting = PW_AUTO_ON_DELAY; } send_write_command(AUTHOR, 4); sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ outlet_num = varname[NUT_OUTLET_POSITION] - '0'; if (outlet_num < 1 || outlet_num > 9) { return STAT_SET_INVALID; } sec = atoi(val); /* Check value: * 0-32767 are valid values * -1 means no Automatic off or restart * for Auto Off Delay: * 0-30 are valid but ill-advised */ if (sec < -1 || sec > 0x7FFF) { return STAT_SET_INVALID; } cbuf[0] = PW_SET_OUTLET_COMMAND; /* Cmd */ cbuf[1] = onOff_setting; /* Set Auto Off (1) or On (2) Delay */ cbuf[2] = outlet_num; /* Outlet number */ cbuf[3] = sec&0xff; /* Delay in seconds LSB */ cbuf[4] = sec>>8; /* Delay in seconds MSB */ res = command_write_sequence(cbuf, 5, answer); snprintf(success_msg, sizeof(success_msg)-1, "Outlet %d %s delay set to %d sec", outlet_num, (onOff_setting == PW_AUTO_ON_DELAY)?"start":"shutdown", sec); return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); } return STAT_SET_INVALID; } static int decode_setvar_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg) { if (res <= 0) { upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname); dstate_datastale(); return STAT_SET_FAILED; } /* Decode the status code from command execution */ switch (exec_status) { case BCMXCP_RETURN_ACCEPTED: { upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); upsdrv_comm_good(); return STAT_SET_HANDLED; break; } case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: { upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname); upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); upsdrv_comm_good(); return STAT_SET_HANDLED; break; } case BCMXCP_RETURN_BUSY: { upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname); return STAT_SET_FAILED; break; } case BCMXCP_RETURN_UNRECOGNISED: { upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname); return STAT_SET_FAILED; break; } case BCMXCP_RETURN_INVALID_PARAMETER: { upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); return STAT_SET_INVALID; break; } case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: { upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname); return STAT_SET_INVALID; break; } default: { upslogx(LOG_NOTICE, "[%s] Not supported", cmdname); return STAT_SET_INVALID; break; } } } /******************************* * Extracted from usbhid-ups.c * *******************************/ /* find the NUT value matching that XCP Item value */ static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting) { info_lkp_t *info_lkp; /* if a conversion function is defined, use 'value' as argument for it */ if (xcp2info->fun != NULL) { return xcp2info->fun(value); } /* use 'value' as an index for a lookup in an array */ for (info_lkp = xcp2info; info_lkp->nut_value != NULL; info_lkp++) { if (info_lkp->xcp_value == (long)value) { upsdebugx(5, "nut_find_infoval: found %s (value: %ld)", info_lkp->nut_value, (long)value); return info_lkp->nut_value; } } if(debug_output_nonexisting == TRUE) { upsdebugx(3, "nut_find_infoval: no matching INFO_* value for this XCP value (%g)", value); } return NULL; } nut-2.7.4/drivers/nutdrv_qx_q1.h0000644000175000017500000000340712640473702013546 00000000000000/* nutdrv_qx_q1.h - Subdriver for Q1 protocol based UPSes * * Copyright (C) * 2013 Daniele Pezzini * * 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 * * NOTE: * This subdriver implements the same protocol as the one used by the 'megatec' subdriver minus the vendor (I) and ratings (F) queries. * In the claim function: * - it doesn't even try to get 'vendor' informations (I) * - it checks only status (Q1), through 'input.voltage' variable * Therefore it should be able to work even if the UPS doesn't support vendor/ratings *and* the user doesn't use the 'novendor'/'norating' flags, as long as: * - the UPS replies a Q1-compliant answer (i.e. not necessary filled with all of the Q1-required data, but at least of the right length and with not available data filled with some replacement character) * - the UPS reports a valid input.voltage (used in the claim function) * - the UPS reports valid status bits (1st, 2nd, 3rd, 6th, 7th are the mandatory ones) * */ #ifndef NUTDRV_QX_Q1_H #define NUTDRV_QX_Q1_H #include "nutdrv_qx.h" extern subdriver_t q1_subdriver; #endif /* NUTDRV_QX_Q1_H */ nut-2.7.4/drivers/main.c0000644000175000017500000004022312640473702012027 00000000000000/* main.c - Network UPS Tools driver core Copyright (C) 1999 Russell Kroll 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 "main.h" #include "dstate.h" /* data which may be useful to the drivers */ int upsfd = -1; char *device_path = NULL; const char *progname = NULL, *upsname = NULL, *device_name = NULL; /* may be set by the driver to wake up while in dstate_poll_fds */ int extrafd = -1; /* for ser_open */ int do_lock_port = 1; /* for dstate->sock_connect, default to asynchronous */ int do_synchronous = 0; /* for detecting -a values that don't match anything */ static int upsname_found = 0; static vartab_t *vartab_h = NULL; /* variables possibly set by the global part of ups.conf */ unsigned int poll_interval = 2; static char *chroot_path = NULL, *user = NULL; /* signal handling */ int exit_flag = 0; /* everything else */ static char *pidfn = NULL; /* print the driver banner */ void upsdrv_banner (void) { int i; printf("Network UPS Tools - %s %s (%s)\n", upsdrv_info.name, upsdrv_info.version, UPS_VERSION); /* process sub driver(s) information */ for (i = 0; upsdrv_info.subdrv_info[i]; i++) { if (!upsdrv_info.subdrv_info[i]->name) { continue; } if (!upsdrv_info.subdrv_info[i]->version) { continue; } printf("%s %s\n", upsdrv_info.subdrv_info[i]->name, upsdrv_info.subdrv_info[i]->version); } } /* power down the attached load immediately */ static void forceshutdown(void) { upslogx(LOG_NOTICE, "Initiating UPS shutdown"); /* the driver must not block in this function */ upsdrv_shutdown(); exit(EXIT_SUCCESS); } /* this function only prints the usage message; it does not call exit() */ static void help_msg(void) { vartab_t *tmp; printf("\nusage: %s -a [OPTIONS]\n", progname); printf(" -a - autoconfig using ups.conf section \n"); printf(" - note: -x after -a overrides ups.conf settings\n\n"); printf(" -V - print version, then exit\n"); printf(" -L - print parseable list of driver variables\n"); printf(" -D - raise debugging level\n"); printf(" -q - raise log level threshold\n"); printf(" -h - display this help\n"); printf(" -k - force shutdown\n"); printf(" -i - poll interval\n"); printf(" -r - chroot to \n"); printf(" -u - switch to (if started as root)\n"); printf(" -x = - set driver variable to \n"); printf(" - example: -x cable=940-0095B\n\n"); if (vartab_h) { tmp = vartab_h; printf("Acceptable values for -x or ups.conf in this driver:\n\n"); while (tmp) { if (tmp->vartype == VAR_VALUE) printf("%40s : -x %s=\n", tmp->desc, tmp->var); else printf("%40s : -x %s\n", tmp->desc, tmp->var); tmp = tmp->next; } } upsdrv_help(); } /* store these in dstate as driver.(parameter|flag) */ static void dparam_setinfo(const char *var, const char *val) { char vtmp[SMALLBUF]; /* store these in dstate for debugging and other help */ if (val) { snprintf(vtmp, sizeof(vtmp), "driver.parameter.%s", var); dstate_setinfo(vtmp, "%s", val); return; } /* no value = flag */ snprintf(vtmp, sizeof(vtmp), "driver.flag.%s", var); dstate_setinfo(vtmp, "enabled"); } /* cram var [= ] data into storage */ static void storeval(const char *var, char *val) { vartab_t *tmp, *last; if (!strncasecmp(var, "override.", 9)) { dstate_setinfo(var+9, "%s", val); dstate_setflags(var+9, ST_FLAG_IMMUTABLE); return; } if (!strncasecmp(var, "default.", 8)) { dstate_setinfo(var+8, "%s", val); return; } tmp = last = vartab_h; while (tmp) { last = tmp; /* sanity check */ if (!tmp->var) { tmp = tmp->next; continue; } /* later definitions overwrite earlier ones */ if (!strcasecmp(tmp->var, var)) { free(tmp->val); if (val) tmp->val = xstrdup(val); /* don't keep things like SNMP community strings */ if ((tmp->vartype & VAR_SENSITIVE) == 0) dparam_setinfo(var, val); tmp->found = 1; return; } tmp = tmp->next; } /* try to help them out */ printf("\nFatal error: '%s' is not a valid %s for this driver.\n", var, val ? "variable name" : "flag"); printf("\n"); printf("Look in the man page or call this driver with -h for a list of\n"); printf("valid variable names and flags.\n"); exit(EXIT_SUCCESS); } /* retrieve the value of variable if possible */ char *getval(const char *var) { vartab_t *tmp = vartab_h; while (tmp) { if (!strcasecmp(tmp->var, var)) return(tmp->val); tmp = tmp->next; } return NULL; } /* see if has been defined, even if no value has been given to it */ int testvar(const char *var) { vartab_t *tmp = vartab_h; while (tmp) { if (!strcasecmp(tmp->var, var)) return tmp->found; tmp = tmp->next; } return 0; /* not found */ } /* callback from driver - create the table for -x/conf entries */ void addvar(int vartype, const char *name, const char *desc) { vartab_t *tmp, *last; tmp = last = vartab_h; while (tmp) { last = tmp; tmp = tmp->next; } tmp = xmalloc(sizeof(vartab_t)); tmp->vartype = vartype; tmp->var = xstrdup(name); tmp->val = NULL; tmp->desc = xstrdup(desc); tmp->found = 0; tmp->next = NULL; if (last) last->next = tmp; else vartab_h = tmp; } /* handle -x / ups.conf config details that are for this part of the code */ static int main_arg(char *var, char *val) { /* flags for main */ if (!strcmp(var, "nolock")) { do_lock_port = 0; dstate_setinfo("driver.flag.nolock", "enabled"); return 1; /* handled */ } if (!strcmp(var, "ignorelb")) { dstate_setinfo("driver.flag.ignorelb", "enabled"); return 1; /* handled */ } /* any other flags are for the driver code */ if (!val) return 0; /* variables for main: port */ if (!strcmp(var, "port")) { device_path = xstrdup(val); device_name = xbasename(device_path); dstate_setinfo("driver.parameter.port", "%s", val); return 1; /* handled */ } if (!strcmp(var, "sddelay")) { upslogx(LOG_INFO, "Obsolete value sddelay found in ups.conf"); return 1; /* handled */ } /* allow per-driver overrides of the global setting */ if (!strcmp(var, "synchronous")) { if (!strcmp(val, "yes")) do_synchronous=1; else do_synchronous=0; return 1; /* handled */ } /* only for upsdrvctl - ignored here */ if (!strcmp(var, "sdorder")) return 1; /* handled */ /* only for upsd (at the moment) - ignored here */ if (!strcmp(var, "desc")) return 1; /* handled */ return 0; /* unhandled, pass it through to the driver */ } static void do_global_args(const char *var, const char *val) { if (!strcmp(var, "pollinterval")) { poll_interval = atoi(val); return; } if (!strcmp(var, "chroot")) { free(chroot_path); chroot_path = xstrdup(val); } if (!strcmp(var, "user")) { free(user); user = xstrdup(val); } if (!strcmp(var, "synchronous")) { if (!strcmp(val, "yes")) do_synchronous=1; else do_synchronous=0; } /* unrecognized */ } void do_upsconf_args(char *confupsname, char *var, char *val) { char tmp[SMALLBUF]; /* handle global declarations */ if (!confupsname) { do_global_args(var, val); return; } /* no match = not for us */ if (strcmp(confupsname, upsname) != 0) return; upsname_found = 1; if (main_arg(var, val)) return; /* flags (no =) now get passed to the driver-level stuff */ if (!val) { /* also store this, but it's a bit different */ snprintf(tmp, sizeof(tmp), "driver.flag.%s", var); dstate_setinfo(tmp, "enabled"); storeval(var, NULL); return; } /* don't let the user shoot themselves in the foot */ if (!strcmp(var, "driver")) { if (strcmp(val, progname) != 0) fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n", confupsname, val, progname); return; } /* allow per-driver overrides of the global setting */ if (!strcmp(var, "pollinterval")) { poll_interval = atoi(val); return; } /* everything else must be for the driver */ storeval(var, val); } /* split -x foo=bar into 'foo' and 'bar' */ static void splitxarg(char *inbuf) { char *eqptr, *val, *buf; /* make our own copy - avoid changing argv */ buf = xstrdup(inbuf); eqptr = strchr(buf, '='); if (!eqptr) val = NULL; else { *eqptr++ = '\0'; val = eqptr; } /* see if main handles this first */ if (main_arg(buf, val)) return; /* otherwise store it for later */ storeval(buf, val); } /* dump the list from the vartable for external parsers */ static void listxarg(void) { vartab_t *tmp; tmp = vartab_h; if (!tmp) return; while (tmp) { switch (tmp->vartype) { case VAR_VALUE: printf("VALUE"); break; case VAR_FLAG: printf("FLAG"); break; default: printf("UNKNOWN"); break; } printf(" %s \"%s\"\n", tmp->var, tmp->desc); tmp = tmp->next; } } static void vartab_free(void) { vartab_t *tmp, *next; tmp = vartab_h; while (tmp) { next = tmp->next; free(tmp->var); free(tmp->val); free(tmp->desc); free(tmp); tmp = next; } } static void exit_cleanup(void) { free(chroot_path); free(device_path); free(user); if (pidfn) { unlink(pidfn); free(pidfn); } dstate_free(); vartab_free(); } static void set_exit_flag(int sig) { exit_flag = sig; } static void setup_signals(void) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = set_exit_flag; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sa.sa_handler = SIG_IGN; sigaction(SIGHUP, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); } int main(int argc, char **argv) { struct passwd *new_uid = NULL; int i, do_forceshutdown = 0; atexit(exit_cleanup); /* pick up a default from configure --with-user */ user = xstrdup(RUN_AS_USER); /* xstrdup: this gets freed at exit */ progname = xbasename(argv[0]); open_syslog(progname); upsdrv_banner(); if (upsdrv_info.status == DRV_EXPERIMENTAL) { printf("Warning: This is an experimental driver.\n"); printf("Some features may not function correctly.\n\n"); } /* build the driver's extra (-x) variable table */ upsdrv_makevartable(); while ((i = getopt(argc, argv, "+a:kDhx:Lqr:u:Vi:")) != -1) { switch (i) { case 'a': upsname = optarg; read_upsconf(); if (!upsname_found) fatalx(EXIT_FAILURE, "Error: Section %s not found in ups.conf", optarg); break; case 'D': nut_debug_level++; break; case 'i': poll_interval = atoi(optarg); break; case 'k': do_lock_port = 0; do_forceshutdown = 1; break; case 'L': listxarg(); exit(EXIT_SUCCESS); case 'q': nut_log_level++; break; case 'r': chroot_path = xstrdup(optarg); break; case 'u': free(user); user = xstrdup(optarg); break; case 'V': /* already printed the banner, so exit */ exit(EXIT_SUCCESS); case 'x': splitxarg(optarg); break; case 'h': help_msg(); exit(EXIT_SUCCESS); default: fatalx(EXIT_FAILURE, "Error: unknown option -%c. Try -h for help.", i); } } argc -= optind; argv += optind; if (argc > 0) { fatalx(EXIT_FAILURE, "Error: too many non-option arguments. Try -h for help."); } if (!upsname_found) { fatalx(EXIT_FAILURE, "Error: specifying '-a id' is now mandatory. Try -h for help."); } /* we need to get the port from somewhere */ if (!device_path) { fatalx(EXIT_FAILURE, "Error: you must specify a port name in ups.conf. Try -h for help."); } upsdebugx(1, "debug level is '%d'", nut_debug_level); new_uid = get_user_pwent(user); if (chroot_path) chroot_start(chroot_path); become_user(new_uid); /* Only switch to statepath if we're not powering off */ /* This avoid case where ie /var is umounted */ if ((!do_forceshutdown) && (chdir(dflt_statepath()))) fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); /* Setup signals to communicate with driver once backgrounded. */ if ((nut_debug_level == 0) && (!do_forceshutdown)) { char buffer[SMALLBUF]; setup_signals(); snprintf(buffer, sizeof(buffer), "%s/%s-%s.pid", altpidpath(), progname, upsname); /* Try to prevent that driver is started multiple times. If a PID file */ /* already exists, send a TERM signal to the process and try if it goes */ /* away. If not, retry a couple of times. */ for (i = 0; i < 3; i++) { struct stat st; if (stat(buffer, &st) != 0) { /* PID file not found */ break; } if (sendsignalfn(buffer, SIGTERM) != 0) { /* Can't send signal to PID, assume invalid file */ break; } upslogx(LOG_WARNING, "Duplicate driver instance detected! Terminating other driver!"); /* Allow driver some time to quit */ sleep(5); } pidfn = xstrdup(buffer); writepid(pidfn); /* before backgrounding */ } /* clear out callback handler data */ memset(&upsh, '\0', sizeof(upsh)); upsdrv_initups(); /* UPS is detected now, cleanup upon exit */ atexit(upsdrv_cleanup); /* now see if things are very wrong out there */ if (upsdrv_info.status == DRV_BROKEN) { fatalx(EXIT_FAILURE, "Fatal error: broken driver. It probably needs to be converted.\n"); } if (do_forceshutdown) forceshutdown(); /* note: device.type is set early to be overriden by the driver * when its a pdu! */ dstate_setinfo("device.type", "ups"); /* publish the top-level data: version numbers, driver name */ dstate_setinfo("driver.version", "%s", UPS_VERSION); dstate_setinfo("driver.version.internal", "%s", upsdrv_info.version); dstate_setinfo("driver.name", "%s", progname); /* get the base data established before allowing connections */ upsdrv_initinfo(); upsdrv_updateinfo(); if (dstate_getinfo("driver.flag.ignorelb")) { int have_lb_method = 0; if (dstate_getinfo("battery.charge") && dstate_getinfo("battery.charge.low")) { upslogx(LOG_INFO, "using 'battery.charge' to set battery low state"); have_lb_method++; } if (dstate_getinfo("battery.runtime") && dstate_getinfo("battery.runtime.low")) { upslogx(LOG_INFO, "using 'battery.runtime' to set battery low state"); have_lb_method++; } if (!have_lb_method) { fatalx(EXIT_FAILURE, "The 'ignorelb' flag is set, but there is no way to determine the\n" "battery state of charge.\n\n" "Only set this flag if both 'battery.charge' and 'battery.charge.low'\n" "and/or 'battery.runtime' and 'battery.runtime.low' are available.\n"); } } /* now we can start servicing requests */ dstate_init(progname, upsname); /* The poll_interval may have been changed from the default */ dstate_setinfo("driver.parameter.pollinterval", "%d", poll_interval); /* The synchronous option may have been changed from the default */ dstate_setinfo("driver.parameter.synchronous", "%s", (do_synchronous==1)?"yes":"no"); /* remap the device.* info from ups.* for the transition period */ if (dstate_getinfo("ups.mfr") != NULL) dstate_setinfo("device.mfr", "%s", dstate_getinfo("ups.mfr")); if (dstate_getinfo("ups.model") != NULL) dstate_setinfo("device.model", "%s", dstate_getinfo("ups.model")); if (dstate_getinfo("ups.serial") != NULL) dstate_setinfo("device.serial", "%s", dstate_getinfo("ups.serial")); if (nut_debug_level == 0) { background(); writepid(pidfn); /* PID changes when backgrounding */ } while (!exit_flag) { struct timeval timeout; gettimeofday(&timeout, NULL); timeout.tv_sec += poll_interval; upsdrv_updateinfo(); while (!dstate_poll_fds(timeout, extrafd) && !exit_flag) { /* repeat until time is up or extrafd has data */ } } /* if we get here, the exit flag was set by a signal handler */ upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); exit(EXIT_SUCCESS); } nut-2.7.4/drivers/powerpanel.c0000644000175000017500000001120212640473702013252 00000000000000/* * powerpanel.c - Model specific routines for CyberPower text/binary * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2008 Arjen de Korte * * 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 "main.h" #include "serial.h" #include "powerp-bin.h" #include "powerp-txt.h" static int mode = 0; static subdriver_t *subdriver[] = { &powpan_binary, &powpan_text, NULL }; #define DRIVER_NAME "CyberPower text/binary protocol UPS driver" #define DRIVER_VERSION "0.27" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Doug Reynolds \n" \ "Arjen de Korte \n" \ "Timothy Pearson ", DRV_EXPERIMENTAL, { NULL } }; /* FIXME: add a sub version for binary and text subdrivers? */ void upsdrv_initinfo(void) { char *s; dstate_setinfo("ups.mfr", "CyberPower"); dstate_setinfo("ups.model", "[unknown]"); dstate_setinfo("ups.serial", "[unknown]"); subdriver[mode]->initinfo(); /* * Allow to override the following parameters */ if ((s = getval("manufacturer")) != NULL) { dstate_setinfo("ups.mfr", "%s", s); } if ((s = getval("model")) != NULL) { dstate_setinfo("ups.model", "%s", s); } if ((s = getval("serial")) != NULL) { dstate_setinfo("ups.serial", "%s", s); } upsh.instcmd = subdriver[mode]->instcmd; upsh.setvar = subdriver[mode]->setvar; } void upsdrv_updateinfo(void) { static int retry = 0; if (subdriver[mode]->updateinfo() < 0) { ser_comm_fail("Status read failed!"); if (retry < 3) { retry++; } else { dstate_datastale(); } return; } retry = 0; ser_comm_good(); dstate_dataok(); } void upsdrv_shutdown(void) { int i, ret; /* * Try to shutdown with delay and automatic reboot if the power * returned in the mean time (newer models support this). */ if (subdriver[mode]->instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { /* Shutdown successful */ return; } /* * Looks like an older type. Assume we're still on battery if * we can't read status or it is telling us we're on battery. */ for (i = 0; i < MAXTRIES; i++) { ret = subdriver[mode]->updateinfo(); if (ret >= 0) { break; } } if (ret) { /* * When on battery, the 'shutdown.stayoff' command will make * the UPS switch back on when the power returns. */ if (subdriver[mode]->instcmd("shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { upslogx(LOG_INFO, "Waiting for power to return..."); return; } } else { /* * Apparently, the power came back already, so we just need to reboot. */ if (subdriver[mode]->instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) { upslogx(LOG_INFO, "Rebooting now..."); return; } } upslogx(LOG_ERR, "Shutdown command failed!"); } void upsdrv_initups(void) { char *version; version = getval("protocol"); upsfd = ser_open(device_path); ser_set_rts(upsfd, 0); /* * Try to autodetect which UPS is connected. */ for (mode = 0; subdriver[mode] != NULL; mode++) { if ((version != NULL) && strcasecmp(version, subdriver[mode]->version)) { continue; } ser_set_dtr(upsfd, 1); usleep(10000); if (subdriver[mode]->initups() > 0) { upslogx(LOG_INFO, "CyberPower UPS with %s protocol on %s detected", subdriver[mode]->version, device_path); return; } ser_set_dtr(upsfd, 0); usleep(10000); } fatalx(EXIT_FAILURE, "CyberPower UPS not found on %s", device_path); } void upsdrv_help(void) { } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "ondelay", "Delay before UPS startup"); addvar(VAR_VALUE, "offdelay", "Delay before UPS shutdown"); addvar(VAR_VALUE, "manufacturer", "manufacturer"); addvar(VAR_VALUE, "model", "modelname"); addvar(VAR_VALUE, "serial", "serialnumber"); addvar(VAR_VALUE, "protocol", "protocol to use [text|binary] (default: autodection)"); } void upsdrv_cleanup(void) { ser_set_dtr(upsfd, 0); ser_close(upsfd, device_path); } nut-2.7.4/drivers/hidparser.h0000644000175000017500000000427612640443572013103 00000000000000/* * hidparser.h: HID Parser header file * * This file is part of the MGE UPS SYSTEMS HID Parser. * * Copyright (C) 1998-2003 MGE UPS SYSTEMS, * Written by Luc Descotils. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * -------------------------------------------------------------------------- */ #ifndef HIDPARS_H #define HIDPARS_H #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif /* __cplusplus */ #include "hidtypes.h" /* * Parse_ReportDesc * -------------------------------------------------------------------------- */ HIDDesc_t *Parse_ReportDesc(const unsigned char *ReportDesc, const int n); /* * Free_ReportDesc * -------------------------------------------------------------------------- */ void Free_ReportDesc(HIDDesc_t *pDesc); /* * FindObject * -------------------------------------------------------------------------- */ int FindObject(HIDDesc_t *pDesc, HIDData_t *pData); HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type); HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc, uint8_t ReportID, uint8_t Offset, uint8_t Type); /* * GetValue * -------------------------------------------------------------------------- */ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue); /* * SetValue * -------------------------------------------------------------------------- */ void SetValue(const HIDData_t *pData, unsigned char *Buf, long Value); #ifdef __cplusplus /* *INDENT-OFF* */ } /* extern "C" */ /* *INDENT-ON* */ #endif /* __cplusplus */ #endif nut-2.7.4/drivers/mge-hid.c0000644000175000017500000017054512640473702012430 00000000000000/* mge-hid.c - data to monitor Eaton / MGE HID (USB and serial) devices * * Copyright (C) * 2003 - 2015 Arnaud Quette * 2015 Arnaud Quette * * Sponsored by MGE UPS SYSTEMS * * 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 * */ /* TODO list: * - better processing of FW info: * * some models (HP R5000) include firmware.aux (00.01.0021;00.01.00) * * other (9130) need more processing (0128 => 1.28) * ... * - better handling of input.transfer.* (need dstate_addrange) * - outlet management logic (Ie, for outlet.X.load.{on,off}.delay * => use outlet.X.delay.{start,stop} */ #include "main.h" /* for getval() */ #include "usbhid-ups.h" #include "mge-hid.h" #define MGE_HID_VERSION "MGE HID 1.39" /* (prev. MGE Office Protection Systems, prev. MGE UPS SYSTEMS) */ /* Eaton */ #define MGE_VENDORID 0x0463 /* Dell */ #define DELL_VENDORID 0x047c /* Powerware */ #define POWERWARE_VENDORID 0x0592 /* Hewlett Packard */ #define HP_VENDORID 0x03f0 /* AEG */ #define AEG_VENDORID 0x2b2d #ifndef SHUT_MODE #include "usb-common.h" /* USB IDs device table */ static usb_device_id_t mge_usb_device_table[] = { /* various models */ { USB_DEVICE(MGE_VENDORID, 0x0001), NULL }, { USB_DEVICE(MGE_VENDORID, 0xffff), NULL }, /* various models */ { USB_DEVICE(DELL_VENDORID, 0xffff), NULL }, /* PW 9140 */ { USB_DEVICE(POWERWARE_VENDORID, 0x0004), NULL }, /* R/T3000 */ { USB_DEVICE(HP_VENDORID, 0x1fe5), NULL }, /* R/T3000 */ { USB_DEVICE(HP_VENDORID, 0x1fe6), NULL }, /* various models */ { USB_DEVICE(HP_VENDORID, 0x1fe7), NULL }, { USB_DEVICE(HP_VENDORID, 0x1fe8), NULL }, /* PROTECT B / NAS */ { USB_DEVICE(AEG_VENDORID, 0xffff), NULL }, /* Terminating entry */ { -1, -1, NULL } }; #endif typedef enum { MGE_DEFAULT_OFFLINE = 0, MGE_PEGASUS = 0x100, MGE_3S = 0x110, /* All offline models have type value < 200! */ MGE_DEFAULT = 0x200, /* for line-interactive and online models */ MGE_EVOLUTION = 0x300, /* MGE Evolution series */ MGE_EVOLUTION_650, MGE_EVOLUTION_850, MGE_EVOLUTION_1150, MGE_EVOLUTION_S_1250, MGE_EVOLUTION_1550, MGE_EVOLUTION_S_1750, MGE_EVOLUTION_2000, MGE_EVOLUTION_S_2500, MGE_EVOLUTION_S_3000, MGE_PULSAR_M = 0x400, /* MGE Pulsar M series */ MGE_PULSAR_M_2200, MGE_PULSAR_M_3000, MGE_PULSAR_M_3000_XL, EATON_5P = 0x500 /* Eaton 5P / 5PX series */ } models_type_t; /* Default to line-interactive or online (ie, not offline). * This is then overriden for offline, through mge_model_names */ static models_type_t mge_type = MGE_DEFAULT; /* Countries definition, for region specific settings and features */ typedef enum { COUNTRY_UNKNOWN = -1, COUNTRY_EUROPE = 0, COUNTRY_US, /* Special European models, which also supports 200 / 208 V */ COUNTRY_EUROPE_208, COUNTRY_WORLDWIDE, COUNTRY_AUSTRALIA, } country_code_t; static int country_code = COUNTRY_UNKNOWN; static char mge_scratch_buf[20]; /* ABM - Advanced Battery Monitoring *********************************** * Synthesis table * HID data | Charger in ABM mode | Charger in Constant mode * UPS.BatterySystem.Charger.ABMEnable | 1 | 0 * UPS.PowerSummary.PresentStatus.ACPresent | On utility | On battery | On utility | On battery * Charger ABM mode | Charging | Floating | Resting | Discharging | Disabled | Disabled * UPS.BatterySystem.Charger.Mode | 1 | 3 | 4 | 2 | 6 | 6 * UPS.PowerSummary.PresentStatus.Charging | 1 | 1 | 1 | 0 | 1 | 0 * UPS.PowerSummary.PresentStatus.Discharging | 0 |  0 |  0 | 1 | 0 | 1 * * Notes (from David G. Miller) to understand ABM status: * When supporting ABM, when a UPS powers up or returns from battery, or * ends the ABM rest mode, it enters charge mode. * Some UPSs run a different charger reference voltage during charge mode * but all the newer models should not be doing that, but basically once * the battery voltage reaches the charger reference level (should be 2.3 * volts/cell), the charger is considered in float mode. Some UPSs will not * annunciate float mode until the charger power starts falling from the maximum * level indicating the battery is truly at the float voltage or in float mode. * The %charge level is based on battery voltage and the charge mode timer * (should be 48 hours) and some UPSs add in a value that’s related to charger * power output. So you can have UPS that enters float mode with anywhere * from 80% or greater battery capacity. * float mode is not important from the software’s perspective, it’s there to * help determine if the charger is advancing correctly. * So in float mode, the charger is charging the battery, so by definition you * can assert the CHRG flag in NUT when in “float†mode or “charge†mode. * When in “rest†mode the charger is not delivering anything to the battery, * but it will when the ABM cycle(28 days) ends, or a battery discharge occurs * and utility returns. This is when the ABM status should be “restingâ€. * If a battery failure is detected that disables the charger, it should be * reporting “off†in the ABM charger status. * Of course when delivering load power from the battery, the ABM status is * discharging. */ #define ABM_UNKNOWN -1 #define ABM_DISABLED 0 #define ABM_ENABLED 1 /* Internal flag to process battery status (CHRG/DISCHRG) and ABM */ static int advanced_battery_monitoring = ABM_UNKNOWN; /* Used to store internally if ABM is enabled or not */ static const char *eaton_abm_enabled_fun(double value) { advanced_battery_monitoring = value; upsdebugx(2, "ABM is %s", (advanced_battery_monitoring==1)?"enabled":"disabled"); /* Return NULL, not to get the value published! */ return NULL; } static info_lkp_t eaton_abm_enabled_info[] = { { 0, "dummy", eaton_abm_enabled_fun }, { 0, NULL, NULL } }; /* Note 1: This point will need more clarification! */ # if 0 /* Used to store internally if ABM is enabled or not (for legacy units) */ static const char *eaton_abm_enabled_legacy_fun(double value) { advanced_battery_monitoring = value; upsdebugx(2, "ABM is %s (legacy data)", (advanced_battery_monitoring==1)?"enabled":"disabled"); /* Return NULL, not to get the value published! */ return NULL; } static info_lkp_t eaton_abm_enabled_legacy_info[] = { { 0, "dummy", eaton_abm_enabled_legacy_fun }, { 0, NULL, NULL } }; #endif /* if 0 */ /* Used to process ABM flags, for battery.charger.status */ static const char *eaton_abm_status_fun(double value) { /* Don't process if ABM is disabled */ if (advanced_battery_monitoring == ABM_DISABLED) { /* Clear any previously published data, in case * the user has switched off ABM */ dstate_delinfo("battery.charger.status"); return NULL; } upsdebugx(2, "ABM numeric status: %i", (int)value); switch ((long)value) { case 1: snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "charging"); break; case 2: snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "discharging"); break; case 3: snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "floating"); break; case 4: snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "resting"); break; case 6: /* ABM Charger Disabled */ snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "off"); break; case 5: /* Undefined - ABM is not activated */ default: /* Return NULL, not to get the value published! */ return NULL; } upsdebugx(2, "ABM string status: %s", mge_scratch_buf); return mge_scratch_buf; } static info_lkp_t eaton_abm_status_info[] = { { 1, "dummy", eaton_abm_status_fun }, { 0, NULL, NULL } }; /* Used to process ABM flags, for ups.status (CHRG/DISCHRG/RB) */ static const char *eaton_abm_chrg_dischrg_fun(double value) { /* Don't process if ABM is disabled */ if (advanced_battery_monitoring == ABM_DISABLED) return NULL; switch ((long)value) { case 1: /* charging status */ case 3: /* floating status */ snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "chrg"); break; case 2: snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "dischrg"); break; case 6: /* ABM Charger Disabled */ case 4: /* resting, nothing to publish! (?) */ case 5: /* Undefined - ABM is not activated */ default: /* Return NULL, not to get the value published! */ return NULL; } upsdebugx(2, "ABM CHRG/DISCHRG legacy string status (ups.status): %s", mge_scratch_buf); return mge_scratch_buf; } static info_lkp_t eaton_abm_chrg_dischrg_info[] = { { 1, "dummy", eaton_abm_chrg_dischrg_fun }, { 0, NULL, NULL } }; /* ABM also implies that standard CHRG/DISCHRG are processed according * to weither ABM is enabled or not... * If ABM is disabled, we publish these legacy status * Otherwise, we don't publish on ups.status, but only battery.charger.status */ /* FIXME: we may prefer to publish the CHRG/DISCHRG status * on battery.charger.status?! */ static const char *eaton_abm_check_dischrg_fun(double value) { if (advanced_battery_monitoring == ABM_DISABLED) { if (value == 1) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "dischrg"); } else { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!dischrg"); } } else { /* Else, ABM is enabled, we should return NULL, * not to get the value published! * However, clear flags that would persist in case of prior * publication in ABM-disabled mode */ snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!dischrg"); } return mge_scratch_buf; } static info_lkp_t eaton_discharging_info[] = { { 1, "dummy", eaton_abm_check_dischrg_fun }, { 0, NULL, NULL } }; static const char *eaton_abm_check_chrg_fun(double value) { if (advanced_battery_monitoring == ABM_DISABLED) { if (value == 1) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "chrg"); } else { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!chrg"); } } else { /* Else, ABM is enabled, we should return NULL, * not to get the value published! * However, clear flags that would persist in case of prior * publication in ABM-disabled mode */ snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!chrg"); } return mge_scratch_buf; } static info_lkp_t eaton_charging_info[] = { { 1, "dummy", eaton_abm_check_chrg_fun }, { 0, NULL, NULL } }; /* The HID path 'UPS.PowerSummary.Time' reports Unix time (ie the number of * seconds since 1970-01-01 00:00:00. This has to be split between ups.date and * ups.time */ static const char *mge_date_conversion_fun(double value) { time_t sec = value; if (strftime(mge_scratch_buf, sizeof(mge_scratch_buf), "%Y/%m/%d", localtime(&sec)) == 10) { return mge_scratch_buf; } upsdebugx(3, "%s: can't compute date %g", __func__, value); return NULL; } static const char *mge_time_conversion_fun(double value) { time_t sec = value; if (strftime(mge_scratch_buf, sizeof(mge_scratch_buf), "%H:%M:%S", localtime(&sec)) == 8) { return mge_scratch_buf; } upsdebugx(3, "%s: can't compute time %g", __func__, value); return NULL; } #ifdef HAVE_STRPTIME /* Conversion back retrieve ups.time to build the full unix time */ static double mge_date_conversion_nuf(const char *value) { struct tm mge_tm; /* build a full value (date) + time string */ snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s %s", value, dstate_getinfo("ups.time")); if (strptime(mge_scratch_buf, "%Y/%m/%d %H:%M:%S", &mge_tm) != NULL) { return mktime(&mge_tm); } upsdebugx(3, "%s: can't compute date %s", __func__, value); return 0; } /* Conversion back retrieve ups.date to build the full unix time */ static double mge_time_conversion_nuf(const char *value) { struct tm mge_tm; /* build a full date + value (time) string */ snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s %s", dstate_getinfo("ups.date"), value); if (strptime(mge_scratch_buf, "%Y/%m/%d %H:%M:%S", &mge_tm) != NULL) { return mktime(&mge_tm); } upsdebugx(3, "%s: can't compute time %s", __func__, value); return 0; } static info_lkp_t mge_date_conversion[] = { { 0, NULL, mge_date_conversion_fun, mge_date_conversion_nuf } }; static info_lkp_t mge_time_conversion[] = { { 0, NULL, mge_time_conversion_fun, mge_time_conversion_nuf } }; #else static info_lkp_t mge_date_conversion[] = { { 0, NULL, mge_date_conversion_fun, NULL } }; static info_lkp_t mge_time_conversion[] = { { 0, NULL, mge_time_conversion_fun, NULL } }; #endif /* HAVE_STRPTIME */ /* The HID path 'UPS.PowerSummary.ConfigVoltage' only reports 'battery.voltage.nominal' for specific UPS series. Ignore the value for other series (default behavior). */ static const char *mge_battery_voltage_nominal_fun(double value) { switch (mge_type & 0xFF00) /* Ignore model byte */ { case MGE_EVOLUTION: if (mge_type == MGE_EVOLUTION_650) { value = 12.0; } break; case MGE_PULSAR_M: case EATON_5P: break; default: return NULL; } snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.0f", value); return mge_scratch_buf; } static info_lkp_t mge_battery_voltage_nominal[] = { { 0, NULL, mge_battery_voltage_nominal_fun } }; /* The HID path 'UPS.PowerSummary.Voltage' only reports 'battery.voltage' for specific UPS series. Ignore the value for other series (default behavior). */ static const char *mge_battery_voltage_fun(double value) { switch (mge_type & 0xFF00) /* Ignore model byte */ { case MGE_EVOLUTION: case MGE_PULSAR_M: case EATON_5P: break; default: return NULL; } snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.1f", value); return mge_scratch_buf; } static info_lkp_t mge_battery_voltage[] = { { 0, NULL, mge_battery_voltage_fun } }; static const char *mge_powerfactor_conversion_fun(double value) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", value / 100); return mge_scratch_buf; } static info_lkp_t mge_powerfactor_conversion[] = { { 0, NULL, mge_powerfactor_conversion_fun } }; static const char *mge_battery_capacity_fun(double value) { snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", value / 3600); return mge_scratch_buf; } static info_lkp_t mge_battery_capacity[] = { { 0, NULL, mge_battery_capacity_fun } }; info_lkp_t eaton_enable_disable_info[] = { { 0, "disabled", NULL }, { 1, "enabled", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_upstype_conversion[] = { { 1, "offline / line interactive", NULL }, { 2, "online", NULL }, { 3, "online - unitary/parallel", NULL }, { 4, "online - parallel with hot standy", NULL }, { 5, "online - hot standby redundancy", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_sensitivity_info[] = { { 0, "normal", NULL }, { 1, "high", NULL }, { 2, "low", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_emergency_stop[] = { { 1, "Emergency stop!", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_wiring_fault[] = { { 1, "Wiring fault!", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_config_failure[] = { { 1, "Fatal EEPROM fault!", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_inverter_volthi[] = { { 1, "Inverter AC voltage too high!", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_inverter_voltlo[] = { { 1, "Inverter AC voltage too low!", NULL }, { 0, NULL, NULL } }; static info_lkp_t mge_short_circuit[] = { { 1, "Output short circuit!", NULL }, { 0, NULL, NULL } }; info_lkp_t mge_onbatt_info[] = { { 1, "!online", NULL }, { 0, "online", NULL }, { 0, NULL, NULL } }; /* allow limiting to ups.model = Protection Station, Ellipse Eco * and 3S (US 750 and AUS 700 only!) */ static const char *eaton_check_pegasus_fun(double value) { switch (mge_type & 0xFF00) /* Ignore model byte */ { case MGE_PEGASUS: break; case MGE_3S: /* Only consider non European models */ if (country_code != COUNTRY_EUROPE) break; default: return NULL; } snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.0f", value); return mge_scratch_buf; } static info_lkp_t pegasus_threshold_info[] = { { 10, "10", eaton_check_pegasus_fun }, { 25, "25", eaton_check_pegasus_fun }, { 60, "60", eaton_check_pegasus_fun }, { 0, NULL, NULL } }; /* allow limiting standard yes/no info (here, to enable ECO mode) to * ups.model = Protection Station, Ellipse Eco and 3S (US 750 and AUS 700 only!) * this allows to enable special flags used in hid_info_t entries (Ie RW) */ static const char *pegasus_yes_no_info_fun(double value) { switch (mge_type & 0xFF00) /* Ignore model byte */ { case MGE_PEGASUS: break; case MGE_3S: /* Only consider non European models */ if (country_code != COUNTRY_EUROPE) break; default: return NULL; } return (value == 0) ? "no" : "yes"; } /* Conversion back of yes/no info */ static double pegasus_yes_no_info_nuf(const char *value) { switch (mge_type & 0xFF00) /* Ignore model byte */ { case MGE_PEGASUS: break; case MGE_3S: /* Only consider non European models */ if (country_code != COUNTRY_EUROPE) break; default: return 0; } if (!strncmp(value, "yes", 3)) return 1; else return 0; } info_lkp_t pegasus_yes_no_info[] = { { 0, "no", pegasus_yes_no_info_fun, pegasus_yes_no_info_nuf }, { 1, "yes", pegasus_yes_no_info_fun, pegasus_yes_no_info_nuf }, { 0, NULL, NULL } }; /* Determine country using UPS.PowerSummary.Country. * If not present: * if PowerConverter.Output.Voltage >= 200 => "Europe" * else default to "US" */ static const char *eaton_check_country_fun(double value) { country_code = value; /* Return NULL, not to get the value published! */ return NULL; } static info_lkp_t eaton_check_country_info[] = { { 0, "dummy", eaton_check_country_fun }, { 0, NULL, NULL } }; /* Limit nominal output voltage according to HV or LV models */ static const char *nominal_output_voltage_fun(double value) { static long nominal = -1; if (nominal < 0) { nominal = value; } switch ((long)nominal) { /* LV models */ case 100: case 110: case 120: case 127: switch ((long)value) { case 100: case 110: case 120: case 127: break; default: return NULL; } break; /* line-interactive and online support 200/208 and 220/230/240*/ /* HV models */ /* 208V */ case 200: case 208: switch ((long)value) { case 200: case 208: break; /* 230V */ case 220: case 230: case 240: if ((mge_type & 0xFF00) >= MGE_DEFAULT) break; default: return NULL; } break; /* HV models */ /* 230V */ case 220: case 230: case 240: switch ((long)value) { case 200: case 208: /* line-interactive and online also support 200 / 208 V * So break on offline models */ if ((mge_type & 0xFF00) < MGE_DEFAULT) return NULL; /* FIXME: Some European models ("5130 RT 3000") also * support both HV values */ if (country_code == COUNTRY_EUROPE_208) break; case 220: case 230: case 240: break; default: return NULL; } break; default: upsdebugx(3, "%s: can't autodetect settable voltages from %g", __func__, value); } snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.0f", value); return mge_scratch_buf; } static info_lkp_t nominal_output_voltage_info[] = { /* line-interactive, starting with Evolution, support both HV values */ /* HV models */ /* 208V */ { 200, "200", nominal_output_voltage_fun }, { 208, "208", nominal_output_voltage_fun }, /* HV models */ /* 230V */ { 220, "220", nominal_output_voltage_fun }, { 230, "230", nominal_output_voltage_fun }, { 240, "240", nominal_output_voltage_fun }, /* LV models */ { 100, "100", nominal_output_voltage_fun }, { 110, "110", nominal_output_voltage_fun }, { 120, "120", nominal_output_voltage_fun }, { 127, "127", nominal_output_voltage_fun }, { 0, NULL, NULL } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* Eaton / MGE HID usage table */ static usage_lkp_t mge_usage_lkp[] = { { "Undefined", 0xffff0000 }, { "STS", 0xffff0001 }, { "Environment", 0xffff0002 }, { "Statistic", 0xffff0003 }, { "StatisticSystem", 0xffff0004 }, /* 0xffff0005-0xffff000f => Reserved */ { "Phase", 0xffff0010 }, { "PhaseID", 0xffff0011 }, { "Chopper", 0xffff0012 }, { "ChopperID", 0xffff0013 }, { "Inverter", 0xffff0014 }, { "InverterID", 0xffff0015 }, { "Rectifier", 0xffff0016 }, { "RectifierID", 0xffff0017 }, { "LCMSystem", 0xffff0018 }, { "LCMSystemID", 0xffff0019 }, { "LCMAlarm", 0xffff001a }, { "LCMAlarmID", 0xffff001b }, { "HistorySystem", 0xffff001c }, { "HistorySystemID", 0xffff001d }, { "Event", 0xffff001e }, { "EventID", 0xffff001f }, { "CircuitBreaker", 0xffff0020 }, { "TransferForbidden", 0xffff0021 }, { "OverallAlarm", 0xffff0022 }, /* renamed to Alarm in Eaton SW! */ { "Dephasing", 0xffff0023 }, { "BypassBreaker", 0xffff0024 }, { "PowerModule", 0xffff0025 }, { "PowerRate", 0xffff0026 }, { "PowerSource", 0xffff0027 }, { "CurrentPowerSource", 0xffff0028 }, { "RedundancyLevel", 0xffff0029 }, { "RedundancyLost", 0xffff002a }, { "NotificationStatus", 0xffff002b }, { "ProtectionLost", 0xffff002c }, { "ConfigurationFailure", 0xffff002d }, { "CompatibilityFailure", 0xffff002e }, /* 0xffff002e-0xffff003f => Reserved */ { "SwitchType", 0xffff0040 }, /* renamed to Type in Eaton SW! */ { "ConverterType", 0xffff0041 }, { "FrequencyConverterMode", 0xffff0042 }, { "AutomaticRestart", 0xffff0043 }, { "ForcedReboot", 0xffff0044 }, { "TestPeriod", 0xffff0045 }, { "EnergySaving", 0xffff0046 }, { "StartOnBattery", 0xffff0047 }, { "Schedule", 0xffff0048 }, { "DeepDischargeProtection", 0xffff0049 }, { "ShortCircuit", 0xffff004a }, { "ExtendedVoltageMode", 0xffff004b }, { "SensitivityMode", 0xffff004c }, { "RemainingCapacityLimitSetting", 0xffff004d }, { "ExtendedFrequencyMode", 0xffff004e }, { "FrequencyConverterModeSetting", 0xffff004f }, { "LowVoltageBoostTransfer", 0xffff0050 }, { "HighVoltageBoostTransfer", 0xffff0051 }, { "LowVoltageBuckTransfer", 0xffff0052 }, { "HighVoltageBuckTransfer", 0xffff0053 }, { "OverloadTransferEnable", 0xffff0054 }, { "OutOfToleranceTransferEnable", 0xffff0055 }, { "ForcedTransferEnable", 0xffff0056 }, { "LowVoltageBypassTransfer", 0xffff0057 }, { "HighVoltageBypassTransfer", 0xffff0058 }, { "FrequencyRangeBypassTransfer", 0xffff0059 }, { "LowVoltageEcoTransfer", 0xffff005a }, { "HighVoltageEcoTransfer", 0xffff005b }, { "FrequencyRangeEcoTransfer", 0xffff005c }, { "ShutdownTimer", 0xffff005d }, { "StartupTimer", 0xffff005e }, { "RestartLevel", 0xffff005f }, { "PhaseOutOfRange", 0xffff0060 }, { "CurrentLimitation", 0xffff0061 }, { "ThermalOverload", 0xffff0062 }, { "SynchroSource", 0xffff0063 }, { "FuseFault", 0xffff0064 }, { "ExternalProtectedTransfert", 0xffff0065 }, { "ExternalForcedTransfert", 0xffff0066 }, { "Compensation", 0xffff0067 }, { "EmergencyStop", 0xffff0068 }, { "PowerFactor", 0xffff0069 }, { "PeakFactor", 0xffff006a }, { "ChargerType", 0xffff006b }, { "HighPositiveDCBusVoltage", 0xffff006c }, { "LowPositiveDCBusVoltage", 0xffff006d }, { "HighNegativeDCBusVoltage", 0xffff006e }, { "LowNegativeDCBusVoltage", 0xffff006f }, { "FrequencyRangeTransfer", 0xffff0070 }, { "WiringFaultDetection", 0xffff0071 }, { "ControlStandby", 0xffff0072 }, { "ShortCircuitTolerance", 0xffff0073 }, { "VoltageTooHigh", 0xffff0074 }, { "VoltageTooLow", 0xffff0075 }, { "DCBusUnbalanced", 0xffff0076 }, { "FanFailure", 0xffff0077 }, { "WiringFault", 0xffff0078 }, { "Floating", 0xffff0079 }, { "OverCurrent", 0xffff007a }, { "RemainingActivePower", 0xffff007b }, { "Energy", 0xffff007c }, { "Threshold", 0xffff007d }, { "OverThreshold", 0xffff007e }, /* 0xffff007f => Reserved */ { "Sensor", 0xffff0080 }, { "LowHumidity", 0xffff0081 }, { "HighHumidity", 0xffff0082 }, { "LowTemperature", 0xffff0083 }, { "HighTemperature", 0xffff0084 }, { "ECOControl", 0xffff0085 }, { "Efficiency", 0xffff0086 }, { "ABMEnable", 0xffff0087 }, { "NegativeCurrent", 0xffff0088 }, { "AutomaticStart", 0xffff0089 }, /* 0xffff008a-0xffff008f => Reserved */ { "Count", 0xffff0090 }, { "Timer", 0xffff0091 }, { "Interval", 0xffff0092 }, { "TimerExpired", 0xffff0093 }, { "Mode", 0xffff0094 }, { "Country", 0xffff0095 }, { "State", 0xffff0096 }, { "Time", 0xffff0097 }, { "Code", 0xffff0098 }, { "DataValid", 0xffff0099 }, { "ToggleTimer", 0xffff009a }, { "BypassTransferDelay", 0xffff009b }, { "HysteresysVoltageTransfer", 0xffff009c }, { "SlewRate", 0xffff009d }, /* 0xffff009e-0xffff009f => Reserved */ { "PDU", 0xffff00a0 }, { "Breaker", 0xffff00a1 }, { "BreakerID", 0xffff00a2 }, { "OverVoltage", 0xffff00a3 }, { "Tripped", 0xffff00a4 }, { "OverEnergy", 0xffff00a5 }, { "OverHumidity", 0xffff00a6 }, { "ConfigurationReset", 0xffff00a7 }, /* renamed from LCDControl in Eaton SW! */ { "Level", 0xffff00a8 }, { "PDUType", 0xffff00a9 }, { "ReactivePower", 0xffff00aa }, { "Pole", 0xffff00ab }, { "PoleID", 0xffff00ac }, { "Reset", 0xffff00ad }, { "WatchdogReset", 0xffff00ae }, /* 0xffff00af-0xffff00df => Reserved */ { "COPIBridge", 0xffff00e0 }, /* 0xffff00e1-0xffff00ef => Reserved */ { "iModel", 0xffff00f0 }, { "iVersion", 0xffff00f1 }, { "iTechnicalLevel", 0xffff00f2 }, { "iPartNumber", 0xffff00f3 }, { "iReferenceNumber", 0xffff00f4 }, { "iGang", 0xffff00f5 }, /* 0xffff00f6-0xffff00ff => Reserved */ /* end of table */ { NULL, 0 } }; static usage_tables_t mge_utab[] = { mge_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* Model Name formating entries */ /* --------------------------------------------------------------- */ typedef struct { const char *iProduct; const char *iModel; models_type_t type; /* enumerated model type */ const char *name; /* optional (defaults to " " if NULL) */ } models_name_t; /* * Do not remove models from this list, but instead comment them * out if not needed. This allows us to quickly add overrides for * specific models only, should this be needed. */ static models_name_t mge_model_names [] = { /* Ellipse models */ { "ELLIPSE", "300", MGE_DEFAULT_OFFLINE, "ellipse 300" }, { "ELLIPSE", "500", MGE_DEFAULT_OFFLINE, "ellipse 500" }, { "ELLIPSE", "650", MGE_DEFAULT_OFFLINE, "ellipse 650" }, { "ELLIPSE", "800", MGE_DEFAULT_OFFLINE, "ellipse 800" }, { "ELLIPSE", "1200", MGE_DEFAULT_OFFLINE, "ellipse 1200" }, /* Ellipse Premium models */ { "ellipse", "PR500", MGE_DEFAULT_OFFLINE, "ellipse premium 500" }, { "ellipse", "PR650", MGE_DEFAULT_OFFLINE, "ellipse premium 650" }, { "ellipse", "PR800", MGE_DEFAULT_OFFLINE, "ellipse premium 800" }, { "ellipse", "PR1200", MGE_DEFAULT_OFFLINE, "ellipse premium 1200" }, /* Ellipse "Pro" */ { "ELLIPSE", "600", MGE_DEFAULT_OFFLINE, "Ellipse 600" }, { "ELLIPSE", "750", MGE_DEFAULT_OFFLINE, "Ellipse 750" }, { "ELLIPSE", "1000", MGE_DEFAULT_OFFLINE, "Ellipse 1000" }, { "ELLIPSE", "1500", MGE_DEFAULT_OFFLINE, "Ellipse 1500" }, /* Ellipse MAX */ { "Ellipse MAX", "600", MGE_DEFAULT_OFFLINE, NULL }, { "Ellipse MAX", "850", MGE_DEFAULT_OFFLINE, NULL }, { "Ellipse MAX", "1100", MGE_DEFAULT_OFFLINE, NULL }, { "Ellipse MAX", "1500", MGE_DEFAULT_OFFLINE, NULL }, /* Protection Center */ { "PROTECTIONCENTER", "420", MGE_DEFAULT_OFFLINE, "Protection Center 420" }, { "PROTECTIONCENTER", "500", MGE_DEFAULT_OFFLINE, "Protection Center 500" }, { "PROTECTIONCENTER", "675", MGE_DEFAULT_OFFLINE, "Protection Center 675" }, /* Protection Station, supports Eco control */ { "Protection Station", "500", MGE_PEGASUS, NULL }, { "Protection Station", "650", MGE_PEGASUS, NULL }, { "Protection Station", "800", MGE_PEGASUS, NULL }, /* Ellipse ECO, also supports Eco control */ { "Ellipse ECO", "650", MGE_PEGASUS, NULL }, { "Ellipse ECO", "800", MGE_PEGASUS, NULL }, { "Ellipse ECO", "1200", MGE_PEGASUS, NULL }, { "Ellipse ECO", "1600", MGE_PEGASUS, NULL }, /* 3S, also supports Eco control on some models (AUS 700 and US 750)*/ { "3S", "450", MGE_DEFAULT_OFFLINE, NULL }, /* US only */ { "3S", "550", MGE_DEFAULT_OFFLINE, NULL }, /* US 120V + EU 230V + AUS 240V */ { "3S", "700", MGE_3S, NULL }, /* EU 230V + AUS 240V (w/ eco control) */ { "3S", "750", MGE_3S, NULL }, /* US 120V (w/ eco control) */ /* Evolution models */ { "Evolution", "500", MGE_DEFAULT, "Pulsar Evolution 500" }, { "Evolution", "800", MGE_DEFAULT, "Pulsar Evolution 800" }, { "Evolution", "1100", MGE_DEFAULT, "Pulsar Evolution 1100" }, { "Evolution", "1500", MGE_DEFAULT, "Pulsar Evolution 1500" }, { "Evolution", "2200", MGE_DEFAULT, "Pulsar Evolution 2200" }, { "Evolution", "3000", MGE_DEFAULT, "Pulsar Evolution 3000" }, { "Evolution", "3000XL", MGE_DEFAULT, "Pulsar Evolution 3000 XL" }, /* Newer Evolution models */ { "Evolution", "650", MGE_EVOLUTION_650, NULL }, { "Evolution", "850", MGE_EVOLUTION_850, NULL }, { "Evolution", "1150", MGE_EVOLUTION_1150, NULL }, { "Evolution", "S 1250", MGE_EVOLUTION_S_1250, NULL }, { "Evolution", "1550", MGE_EVOLUTION_1550, NULL }, { "Evolution", "S 1750", MGE_EVOLUTION_S_1750, NULL }, { "Evolution", "2000", MGE_EVOLUTION_2000, NULL }, { "Evolution", "S 2500", MGE_EVOLUTION_S_2500, NULL }, { "Evolution", "S 3000", MGE_EVOLUTION_S_3000, NULL }, /* Eaton 5P */ { "Eaton 5P", "650", EATON_5P, "5P 650" }, { "Eaton 5P", "850", EATON_5P, "5P 850" }, { "Eaton 5P", "1150", EATON_5P, "5P 1150" }, { "Eaton 5P", "1550", EATON_5P, "5P 1550" }, /* Pulsar M models */ { "PULSAR M", "2200", MGE_PULSAR_M_2200, NULL }, { "PULSAR M", "3000", MGE_PULSAR_M_3000, NULL }, { "PULSAR M", "3000 XL", MGE_PULSAR_M_3000_XL, NULL }, /* Eaton'ified names */ { "EX", "2200", MGE_PULSAR_M_2200, NULL }, { "EX", "3000", MGE_PULSAR_M_3000, NULL }, { "EX", "3000 XL", MGE_PULSAR_M_3000, NULL }, /* Pulsar models (TBR) */ /* { "Pulsar", "700", MGE_DEFAULT, NULL }, */ /* { "Pulsar", "1000", MGE_DEFAULT, NULL }, */ /* { "Pulsar", "1500", MGE_DEFAULT, NULL }, */ /* { "Pulsar", "1000 RT2U", MGE_DEFAULT, NULL }, */ /* { "Pulsar", "1500 RT2U", MGE_DEFAULT, NULL }, */ /* Eaton'ified names (TBR) */ /* { "EX", "700", MGE_DEFAULT, NULL }, */ /* { "EX", "1000", MGE_DEFAULT, NULL }, */ /* { "EX", "1500", MGE_DEFAULT, NULL }, */ /* { "EX", "1000 RT2U", MGE_DEFAULT, NULL }, */ /* { "EX", "1500 RT2U", MGE_DEFAULT, NULL }, */ /* Pulsar MX models */ { "PULSAR", "MX4000", MGE_DEFAULT, "Pulsar MX 4000 RT" }, { "PULSAR", "MX5000", MGE_DEFAULT, "Pulsar MX 5000 RT" }, /* NOVA models */ { "NOVA AVR", "500", MGE_DEFAULT, "Nova 500 AVR" }, { "NOVA AVR", "600", MGE_DEFAULT, "Nova 600 AVR" }, { "NOVA AVR", "625", MGE_DEFAULT, "Nova 625 AVR" }, { "NOVA AVR", "1100", MGE_DEFAULT, "Nova 1100 AVR" }, { "NOVA AVR", "1250", MGE_DEFAULT, "Nova 1250 AVR" }, /* EXtreme C (EMEA) */ { "EXtreme", "700C", MGE_DEFAULT, "Pulsar EXtreme 700C" }, { "EXtreme", "1000C", MGE_DEFAULT, "Pulsar EXtreme 1000C" }, { "EXtreme", "1500C", MGE_DEFAULT, "Pulsar EXtreme 1500C" }, { "EXtreme", "1500CCLA", MGE_DEFAULT, "Pulsar EXtreme 1500C CLA" }, { "EXtreme", "2200C", MGE_DEFAULT, "Pulsar EXtreme 2200C" }, { "EXtreme", "3200C", MGE_DEFAULT, "Pulsar EXtreme 3200C" }, /* EXtreme C (USA, aka "EX RT") */ { "EX", "700RT", MGE_DEFAULT, "Pulsar EX 700 RT" }, { "EX", "1000RT", MGE_DEFAULT, "Pulsar EX 1000 RT" }, { "EX", "1500RT", MGE_DEFAULT, "Pulsar EX 1500 RT" }, { "EX", "2200RT", MGE_DEFAULT, "Pulsar EX 2200 RT" }, { "EX", "3200RT", MGE_DEFAULT, "Pulsar EX 3200 RT" }, /* Comet EX RT three phased */ { "EX", "5RT31", MGE_DEFAULT, "EX 5 RT 3:1" }, { "EX", "7RT31", MGE_DEFAULT, "EX 7 RT 3:1" }, { "EX", "11RT31", MGE_DEFAULT, "EX 11 RT 3:1" }, /* Comet EX RT mono phased */ { "EX", "5RT", MGE_DEFAULT, "EX 5 RT" }, { "EX", "7RT", MGE_DEFAULT, "EX 7 RT" }, { "EX", "11RT", MGE_DEFAULT, "EX 11 RT" }, /* Galaxy 3000 */ { "GALAXY", "3000_10", MGE_DEFAULT, "Galaxy 3000 10 kVA" }, { "GALAXY", "3000_15", MGE_DEFAULT, "Galaxy 3000 15 kVA" }, { "GALAXY", "3000_20", MGE_DEFAULT, "Galaxy 3000 20 kVA" }, { "GALAXY", "3000_30", MGE_DEFAULT, "Galaxy 3000 30 kVA" }, /* end of structure. */ { NULL } }; /* --------------------------------------------------------------- */ /* Data lookup table (HID <-> NUT) */ /* --------------------------------------------------------------- */ static hid_info_t mge_hid2nut[] = { /* Device collection */ /* Just declared to call *hid2info */ { "device.country", ST_FLAG_STRING, 20, "UPS.PowerSummary.Country", NULL, "Europe", HU_FLAG_STATIC, eaton_check_country_info }, /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimitSetting", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_STATIC , NULL }, /* Read only */ { "battery.charge.restart", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.PowerSummary.RestartLevel", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.capacity", 0, 0, "UPS.BatterySystem.Battery.DesignCapacity", NULL, "%s", HU_FLAG_STATIC, mge_battery_capacity }, /* conversion needed from As to Ah */ { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, { "battery.runtime.elapsed", 0, 0, "UPS.StatisticSystem.Input.[1].Statistic.[1].Time", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, { "battery.temperature", 0, 0, "UPS.BatterySystem.Battery.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, { "battery.voltage", 0, 0, "UPS.BatterySystem.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, mge_battery_voltage }, { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, mge_battery_voltage_nominal }, { "battery.protection", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.DeepDischargeProtection", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "battery.energysave", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].EnergySaving", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ConfigPercentLoad", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* Current implementation */ { "battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].EnergySaving.ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* Newer implementation */ { "battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "battery.energysave.realpower", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ConfigActivePower", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* ABM (Advanced Battery Monitoring) processing * Must be processed before the BOOL status */ /* Not published, just to store in internal var. advanced_battery_monitoring */ { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.ABMEnable", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_info }, /* Same as the one above, but for legacy units */ /* Refer to Note 1 (This point will need more clarification!) { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.Used", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_legacy_info }, */ /* This data is the actual ABM status information */ { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_status_info }, /* UPS page */ { "ups.efficiency", 0, 0, "UPS.PowerConverter.Output.Efficiency", NULL, "%.0f", 0, NULL }, { "ups.firmware", 0, 0, "UPS.PowerSummary.iVersion", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.load.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.Flow.[4].ConfigPercentLoad", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, { "ups.test.interval", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.BatterySystem.Battery.TestPeriod", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "ups.beeper.status", 0 ,0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", HU_FLAG_SEMI_STATIC, beeper_info }, { "ups.temperature", 0, 0, "UPS.PowerSummary.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", NULL, "%.0f", 0, NULL }, { "ups.L1.power", 0, 0, "UPS.PowerConverter.Output.Phase.[1].ApparentPower", NULL, "%.0f", 0, NULL }, { "ups.L2.power", 0, 0, "UPS.PowerConverter.Output.Phase.[2].ApparentPower", NULL, "%.0f", 0, NULL }, { "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase.[3].ApparentPower", NULL, "%.0f", 0, NULL }, { "ups.power.nominal", 0, 0, "UPS.Flow.[4].ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.0f", 0, NULL }, { "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase.[1].ActivePower", NULL, "%.0f", 0, NULL }, { "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase.[2].ActivePower", NULL, "%.0f", 0, NULL }, { "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase.[3].ActivePower", NULL, "%.0f", 0, NULL }, { "ups.realpower.nominal", 0, 0, "UPS.Flow.[4].ConfigActivePower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[1].AutomaticRestart", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "ups.start.battery", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].StartOnBattery", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "ups.start.reboot", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.ForcedReboot", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "ups.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.PresentStatus.Switchable", NULL, "%s", HU_FLAG_SEMI_STATIC | HU_FLAG_ENUM, eaton_enable_disable_info }, #ifdef HAVE_STRPTIME { "ups.date", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.Time", NULL, "%s", 0, mge_date_conversion }, { "ups.time", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.Time", NULL, "%s", 0, mge_time_conversion }, #else { "ups.date", 0, 0, "UPS.PowerSummary.Time", NULL, "%s", 0, mge_date_conversion }, { "ups.time", 0, 0, "UPS.PowerSummary.Time", NULL, "%s", 0, mge_time_conversion }, #endif /* HAVE_STRPTIME */ { "ups.type", 0, 0, "UPS.PowerConverter.ConverterType", NULL, "%s", HU_FLAG_STATIC, mge_upstype_conversion }, /* Special case: boolean values that are mapped to ups.status and ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[3].PresentStatus.Used", NULL, NULL, 0, mge_onbatt_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, online_info }, /* These 2 ones are used when ABM is disabled */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_charging_info }, /* And this one when ABM is enabled (same as battery.charger.status) */ { "BOOL", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_chrg_dischrg_info }, /* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Charger.Mode = 1" */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, /* Output overload, Level 1 (FIXME: add the level?) */ { "BOOL", 0, 0, "UPS.PowerConverter.Output.Overload.[1].PresentStatus.OverThreshold", NULL, NULL, 0, overload_info }, /* Output overload, Level 2 (FIXME: add the level?) */ { "BOOL", 0, 0, "UPS.PowerConverter.Output.Overload.[2].PresentStatus.OverThreshold", NULL, NULL, 0, overload_info }, /* Output overload, Level 3 (FIXME: add the level?) */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, /* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Battery.Test = 3 " */ { "BOOL", 0, 0, "UPS.LCMSystem.LCMAlarm.[2].PresentStatus.TimerExpired", NULL, NULL, 0, replacebatt_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Buck", NULL, NULL, 0, trim_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Boost", NULL, NULL, 0, boost_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.VoltageOutOfRange", NULL, NULL, 0, vrange_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.FrequencyOutOfRange", NULL, NULL, 0, frange_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, 0, off_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[2].PresentStatus.Used", NULL, NULL, 0, bypass_auto_info }, /* Automatic bypass */ { "BOOL", 0, 0, "UPS.PowerConverter.Input.[4].PresentStatus.Used", NULL, NULL, 0, bypass_manual_info }, /* Manual bypass */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FanFailure", NULL, NULL, 0, fanfail_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.Present", NULL, NULL, 0, nobattery_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.InternalFailure", NULL, NULL, 0, chargerfail_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.VoltageTooHigh", NULL, NULL, 0, battvolthi_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.VoltageTooHigh", NULL, NULL, 0, battvolthi_info }, /* Battery DC voltage too high! */ { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.VoltageTooHigh", NULL, NULL, 0, battvolthi_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.VoltageTooLow", NULL, NULL, 0, battvoltlo_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.VoltageTooLow", NULL, NULL, 0, mge_onbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, 0, commfault_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, /* Vendor specific ups.alarm */ { "ups.alarm", 0, 0, "UPS.PowerSummary.PresentStatus.EmergencyStop", NULL, NULL, 0, mge_emergency_stop }, { "ups.alarm", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.WiringFault", NULL, NULL, 0, mge_wiring_fault }, { "ups.alarm", 0, 0, "UPS.PowerSummary.PresentStatus.ConfigurationFailure", NULL, NULL, 0, mge_config_failure }, { "ups.alarm", 0, 0, "UPS.PowerConverter.Inverter.PresentStatus.VoltageTooHigh", NULL, NULL, 0, mge_inverter_volthi }, { "ups.alarm", 0, 0, "UPS.PowerConverter.Inverter.PresentStatus.VoltageTooLow", NULL, NULL, 0, mge_inverter_voltlo }, { "ups.alarm", 0, 0, "UPS.PowerConverter.Output.PresentStatus.ShortCircuit", NULL, NULL, 0, mge_short_circuit }, /* Input page */ { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", NULL, "%.1f", 0, NULL }, { "input.L1-N.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[1].Voltage", NULL, "%.1f", 0, NULL }, { "input.L2-N.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[2].Voltage", NULL, "%.1f", 0, NULL }, { "input.L3-N.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[3].Voltage", NULL, "%.1f", 0, NULL }, { "input.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[12].Voltage", NULL, "%.1f", 0, NULL }, { "input.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[23].Voltage", NULL, "%.1f", 0, NULL }, { "input.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[31].Voltage", NULL, "%.1f", 0, NULL }, { "input.voltage.nominal", 0, 0, "UPS.Flow.[1].ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.current", 0, 0, "UPS.PowerConverter.Input.[1].Current", NULL, "%.2f", 0, NULL }, { "input.L1.current", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[1].Current", NULL, "%.1f", 0, NULL }, { "input.L2.current", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[2].Current", NULL, "%.1f", 0, NULL }, { "input.L3.current", 0, 0, "UPS.PowerConverter.Input.[1].Phase.[3].Current", NULL, "%.1f", 0, NULL }, { "input.current.nominal", 0, 0, "UPS.Flow.[1].ConfigCurrent", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.1f", 0, NULL }, { "input.frequency.nominal", 0, 0, "UPS.Flow.[1].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* same as "input.transfer.boost.low" */ { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.boost.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBoostTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HighVoltageBoostTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBuckTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* same as "input.transfer.trim.high" */ { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.trim.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.HighVoltageBuckTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.sensitivity", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerConverter.Output.SensitivityMode", NULL, "%s", HU_FLAG_SEMI_STATIC, mge_sensitivity_info }, { "input.voltage.extended", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.ExtendedVoltageMode", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "input.frequency.extended", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.ExtendedFrequencyMode", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, /* Bypass page */ { "input.bypass.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.L1-N.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[1].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.L2-N.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[2].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.L3-N.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[3].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[12].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[23].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[31].Voltage", NULL, "%.1f", 0, NULL }, { "input.bypass.voltage.nominal", 0, 0, "UPS.Flow.[2].ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.bypass.current", 0, 0, "UPS.PowerConverter.Input.[2].Current", NULL, "%.2f", 0, NULL }, { "input.bypass.L1.current", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[1].Current", NULL, "%.1f", 0, NULL }, { "input.bypass.L2.current", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[2].Current", NULL, "%.1f", 0, NULL }, { "input.bypass.L3.current", 0, 0, "UPS.PowerConverter.Input.[2].Phase.[3].Current", NULL, "%.1f", 0, NULL }, { "input.bypass.current.nominal", 0, 0, "UPS.Flow.[2].ConfigCurrent", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "input.bypass.frequency", 0, 0, "UPS.PowerConverter.Input.[2].Frequency", NULL, "%.1f", 0, NULL }, { "input.bypass.frequency.nominal", 0, 0, "UPS.Flow.[2].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.L1-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[1].Voltage", NULL, "%.1f", 0, NULL }, { "output.L2-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[2].Voltage", NULL, "%.1f", 0, NULL }, { "output.L3-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[3].Voltage", NULL, "%.1f", 0, NULL }, { "output.L1-L2.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[12].Voltage", NULL, "%.1f", 0, NULL }, { "output.L2-L3.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[23].Voltage", NULL, "%.1f", 0, NULL }, { "output.L3-L1.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[31].Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.Flow.[4].ConfigVoltage", NULL, "%.0f", HU_FLAG_SEMI_STATIC | HU_FLAG_ENUM, nominal_output_voltage_info }, { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", NULL, "%.2f", 0, NULL }, { "output.L1.current", 0, 0, "UPS.PowerConverter.Output.Phase.[1].Current", NULL, "%.1f", 0, NULL }, { "output.L2.current", 0, 0, "UPS.PowerConverter.Output.Phase.[2].Current", NULL, "%.1f", 0, NULL }, { "output.L3.current", 0, 0, "UPS.PowerConverter.Output.Phase.[3].Current", NULL, "%.1f", 0, NULL }, { "output.current.nominal", 0, 0, "UPS.Flow.[4].ConfigCurrent", NULL, "%.2f", 0, NULL }, { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.1f", 0, NULL }, /* FIXME: can be RW (50/60) (or on .nominal)? */ { "output.frequency.nominal", 0, 0, "UPS.Flow.[4].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "output.powerfactor", 0, 0, "UPS.PowerConverter.Output.PowerFactor", NULL, "%s", 0, mge_powerfactor_conversion }, /* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */ { "outlet.id", 0, 0, "UPS.OutletSystem.Outlet.[1].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[1].OutletID", NULL, "Main Outlet", HU_FLAG_ABSENT, NULL }, { "outlet.switchable", 0, 0, "UPS.OutletSystem.Outlet.[1].PresentStatus.Switchable", NULL, "%s", HU_FLAG_STATIC, yes_no_info }, /* On Protection Station, the line below is the power consumption threshold * on the master outlet used to automatically power off the slave outlets. * Values: 10, 25 (default) or 60 VA. */ { "outlet.power", ST_FLAG_RW | ST_FLAG_STRING, 6, "UPS.OutletSystem.Outlet.[1].ConfigApparentPower", NULL, "%s", HU_FLAG_SEMI_STATIC | HU_FLAG_ENUM, pegasus_threshold_info }, { "outlet.power", 0, 0, "UPS.OutletSystem.Outlet.[1].ApparentPower", NULL, "%.0f", 0, NULL }, { "outlet.realpower", 0, 0, "UPS.OutletSystem.Outlet.[1].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.current", 0, 0, "UPS.OutletSystem.Outlet.[1].Current", NULL, "%.2f", 0, NULL }, { "outlet.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[1].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ /* First outlet */ { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.[2].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "outlet.1.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[2].OutletID", NULL, "PowerShare Outlet 1", HU_FLAG_ABSENT, NULL }, { "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.Switchable", NULL, "%s", HU_FLAG_STATIC, yes_no_info }, { "outlet.1.status", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.SwitchOn/Off", NULL, "%s", 0, on_off_info }, /* For low end models, with 1 non backup'ed outlet */ { "outlet.1.status", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, "%s", 0, on_off_info }, /* FIXME: change to outlet.1.battery.charge.low, as in mge-xml.c?! */ { "outlet.1.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[2].RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.1.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.1.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].StartupTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.1.power", 0, 0, "UPS.OutletSystem.Outlet.[2].ApparentPower", NULL, "%.0f", 0, NULL }, { "outlet.1.realpower", 0, 0, "UPS.OutletSystem.Outlet.[2].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.1.current", 0, 0, "UPS.OutletSystem.Outlet.[2].Current", NULL, "%.2f", 0, NULL }, { "outlet.1.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[2].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ /* Second outlet */ { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet.[3].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "outlet.2.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[3].OutletID", NULL, "PowerShare Outlet 2", HU_FLAG_ABSENT, NULL }, /* needed for Pegasus to enable master/slave mode: * FIXME: rename to something more suitable (outlet.?) */ { "outlet.2.switchable", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", NULL, "%s", HU_FLAG_SEMI_STATIC, pegasus_yes_no_info }, /* Generic version (RO) for other models */ { "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", NULL, "%s", 0, yes_no_info }, { "outlet.2.status", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.SwitchOn/Off", NULL, "%s", 0, on_off_info }, { "outlet.2.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.2.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.2.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].StartupTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.2.power", 0, 0, "UPS.OutletSystem.Outlet.[3].ApparentPower", NULL, "%.0f", 0, NULL }, { "outlet.2.realpower", 0, 0, "UPS.OutletSystem.Outlet.[3].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.2.current", 0, 0, "UPS.OutletSystem.Outlet.[3].Current", NULL, "%.2f", 0, NULL }, { "outlet.2.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[3].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ /* instant commands. */ /* splited into subset while waiting for extradata support * ie: test.battery.start quick */ { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, { "load.off.delay", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL}, { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, /* Command for the outlet collection */ { "outlet.1.load.off", 0, 0, "UPS.OutletSystem.Outlet.[2].DelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL }, { "outlet.1.load.on", 0, 0, "UPS.OutletSystem.Outlet.[2].DelayBeforeStartup", NULL, "0", HU_TYPE_CMD, NULL }, { "outlet.2.load.off", 0, 0, "UPS.OutletSystem.Outlet.[3].DelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL }, { "outlet.2.load.on", 0, 0, "UPS.OutletSystem.Outlet.[3].DelayBeforeStartup", NULL, "0", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL } }; /* * All the logic for finely formatting the MGE model name and device * type matching (used for device specific values or corrections). * Returns pointer to (dynamically allocated) model name. */ static char *get_model_name(const char *iProduct, const char *iModel) { models_name_t *model = NULL; upsdebugx(2, "get_model_name(%s, %s)\n", iProduct, iModel); /* Search for device type and formatting rules */ for (model = mge_model_names; model->iProduct; model++) { if(model->name) { upsdebugx(2, "comparing with: %s", model->name); } else { upsdebugx(2, "comparing with: %s %s", model->iProduct, model->iModel); } if (strcmp(iProduct, model->iProduct)) { continue; } if (strcmp(iModel, model->iModel)) { continue; } mge_type = model->type; break; } if (!model->name) { /* * Model not found or NULL (use default) so construct * model name by concatenation of iProduct and iModel */ char buf[SMALLBUF]; snprintf(buf, sizeof(buf), "%s %s", iProduct, iModel); return strdup(buf); } return strdup(model->name); } static const char *mge_format_model(HIDDevice_t *hd) { char product[SMALLBUF]; char model[SMALLBUF]; double value; /* Dell has already a fully formatted name in iProduct */ if (hd->VendorID == DELL_VENDORID) { return hd->Product; } /* Get iProduct and iModel strings */ snprintf(product, sizeof(product), "%s", hd->Product ? hd->Product : "unknown"); HIDGetItemString(udev, "UPS.PowerSummary.iModel", model, sizeof(model), mge_utab); /* Fallback to ConfigApparentPower */ if ((strlen(model) < 1) && (HIDGetItemValue(udev, "UPS.Flow.[4].ConfigApparentPower", &value, mge_utab) == 1 )) { snprintf(model, sizeof(model), "%i", (int)value); } if (strlen(model) > 0) { free(hd->Product); hd->Product = get_model_name(product, model); } return hd->Product; } static const char *mge_format_mfr(HIDDevice_t *hd) { return hd->Vendor ? hd->Vendor : "Eaton"; } static const char *mge_format_serial(HIDDevice_t *hd) { return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int mge_claim(HIDDevice_t *hd) { #ifndef SHUT_MODE int status = is_usb_device_supported(mge_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: switch (hd->VendorID) { case HP_VENDORID: case DELL_VENDORID: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } /* * this vendor makes lots of USB devices that are * not a UPS, so don't use possibly_supported here */ return 0; default: /* Valid for Eaton */ /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("Eaton / MGE", hd); return 0; } case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } #else return 1; #endif } subdriver_t mge_subdriver = { MGE_HID_VERSION, mge_claim, mge_utab, mge_hid2nut, mge_format_model, mge_format_mfr, mge_format_serial, }; nut-2.7.4/drivers/nutdrv_qx_bestups.c0000644000175000017500000005661412640473702014715 00000000000000/* nutdrv_qx_bestups.c - Subdriver for Best Power/Sola Australia UPSes * * Copyright (C) * 2014 Daniele Pezzini * Based on: * bestups.c - Copyright (C) * 1999 Russell Kroll * Jason White * * 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 "main.h" #include "nutdrv_qx.h" #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_bestups.h" #define BESTUPS_VERSION "BestUPS 0.06" /* Support functions */ static int bestups_claim(void); static void bestups_initups(void); static void bestups_makevartable(void); /* Answer preprocess functions */ static int bestups_preprocess_id_answer(item_t *item, const int len); /* Preprocess functions */ static int bestups_process_setvar(item_t *item, char *value, const size_t valuelen); static int bestups_process_bbb_status_bit(item_t *item, char *value, const size_t valuelen); static int bestups_manufacturer(item_t *item, char *value, const size_t valuelen); static int bestups_model(item_t *item, char *value, const size_t valuelen); static int bestups_batt_runtime(item_t *item, char *value, const size_t valuelen); static int bestups_batt_packs(item_t *item, char *value, const size_t valuelen); static int bestups_get_pins_shutdown_mode(item_t *item, char *value, const size_t valuelen); static int bestups_voltage_settings(item_t *item, char *value, const size_t valuelen); /* ups.conf settings */ static int pins_shutdown_mode; /* General settings */ static int inverted_bbb_bit = 0; /* == Ranges/enums == */ /* Range for ups.delay.start */ info_rw_t bestups_r_ondelay[] = { { "60", 0 }, { "599940", 0 }, { "", 0 } }; /* Range for ups.delay.shutdown */ static info_rw_t bestups_r_offdelay[] = { { "12", 0 }, { "5940", 0 }, { "", 0 } }; /* Range for number of battery packs */ static info_rw_t bestups_r_batt_packs[] = { { "0", 0 }, { "5", 0 }, { "", 0 } }; /* Range for pin shutdown mode */ static info_rw_t bestups_r_pins_shutdown_mode[] = { { "0", 0 }, { "6", 0 }, { "", 0 } }; /* == qx2nut lookup table == */ static item_t bestups_qx2nut[] = { /* Query UPS for status * > [Q1\r] * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ /* { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, *//* Beeper status: not supported; always 0 */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, bestups_process_bbb_status_bit }, /* Bypass/Boost or Buck Active - keep this one at the end as it needs the processed data from the previous items */ /* Query UPS for ratings and model infos * > [ID\r] * < [FOR,750,120,120,20.0,27.6\r] case #1: length = 26 * < [FOR,1500,120,120,20.0,27.6\r] case #2: length = 27 * < [FOR,3000,120,120,20.0,100.6\r] case #3: length = 28 * < [FOR, 750,120,120,20.0, 27.6\r] after being preprocessed: length = 28 * 0123456789012345678901234567 * 0 1 2 */ { "device.mfr", 0, NULL, "ID\r", "", 28, 0, "", 0, 2, "%s", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, bestups_manufacturer }, { "device.model", 0, NULL, "ID\r", "", 28, 0, "", 0, 2, "%s", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, bestups_model }, { "ups.power.nominal", 0, NULL, "ID\r", "", 28, 0, "", 4, 7, "%.0f", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, NULL }, { "input.voltage.nominal", 0, NULL, "ID\r", "", 28, 0, "", 9, 11, "%.0f", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, NULL }, { "output.voltage.nominal", 0, NULL, "ID\r", "", 28, 0, "", 13, 15, "%.0f", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, NULL }, { "battery.voltage.low", 0, NULL, "ID\r", "", 28, 0, "", 17, 20, "%.1f", QX_FLAG_SEMI_STATIC, NULL, bestups_preprocess_id_answer, NULL }, { "battery.voltage.high", 0, NULL, "ID\r", "", 28, 0, "", 22, 26, "%.1f", QX_FLAG_SEMI_STATIC, NULL, bestups_preprocess_id_answer, NULL }, /* Query UPS for battery runtime (not available on the Patriot Pro/Sola 320 model series) * > [RT\r] * < [025\r] * 0123 * 0 */ { "battery.runtime", 0, NULL, "RT\r", "", 4, 0, "", 0, 2, "%.0f", QX_FLAG_SKIP, NULL, NULL, bestups_batt_runtime }, /* Query UPS for number of battery packs (available only on the Axxium/Sola 620 model series) * > [BP?\r] * < [02\r] * 012 * 0 */ { "battery.packs", ST_FLAG_RW, bestups_r_batt_packs, "BP?\r", "", 3, 0, "", 0, 1, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, bestups_batt_packs }, /* Set number of battery packs to n (integer, 0-5) (available only on the Axxium/Sola 620 model series) * > [BPn\r] * < [] */ { "battery.packs", 0, bestups_r_batt_packs, "BP%.0f\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, bestups_process_setvar }, /* Query UPS for shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) * > [SS?\r] * < [0\r] * 01 * 0 */ { "pins_shutdown_mode", ST_FLAG_RW, bestups_r_pins_shutdown_mode, "SS?\r", "", 2, 0, "", 0, 0, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, bestups_get_pins_shutdown_mode }, /* Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) to n (integer, 0-6) * > [SSn\r] * < [] */ { "pins_shutdown_mode", 0, bestups_r_pins_shutdown_mode, "SS%.0f\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, bestups_process_setvar }, /* Query UPS for voltage settings * > [M\r] * < [0\r] * 01 * 0 */ { "input.transfer.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "input.transfer.boost.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "input.transfer.boost.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "input.voltage.nominal", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "output.voltage.nominal", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "input.transfer.trim.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "input.transfer.trim.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, { "input.transfer.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, /* Instant commands */ { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stayoff", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, /* Server-side settable vars */ { "ups.delay.start", ST_FLAG_RW, bestups_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, { "ups.delay.shutdown", ST_FLAG_RW, bestups_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, /* End of structure. */ { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } }; /* == Testing table == */ #ifdef TESTING static testing_t bestups_testing[] = { { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00100000\r", -1 }, { "ID\r", "FOR,750,120,120,20.0,27.6\r", -1 }, { "RT\r", "015\r", -1 }, { "BP?\r", "02\r", -1 }, { "BP1\r", "", -1 }, { "SS?\r", "0\r", -1 }, { "SS2\r", "", -1 }, { "M\r", "0\r", -1 }, { "S03\r", "", -1 }, { "C\r", "", -1 }, { "S02R0005\r", "", -1 }, { "S.5R0001\r", "", -1 }, { "T04\r", "", -1 }, { "TL\r", "", -1 }, { "T\r", "", -1 }, { "CT\r", "", -1 }, { NULL } }; #endif /* TESTING */ /* == Support functions == */ /* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ static int bestups_claim(void) { /* We need at least Q1 and ID to run this subdriver */ item_t *item = find_nut_info("input.voltage", 0, 0); /* Don't know what happened */ if (!item) return 0; /* No reply/Unable to get value */ if (qx_process(item, NULL)) return 0; /* Unable to process value */ if (ups_infoval_set(item) != 1) return 0; /* UPS Model */ item = find_nut_info("device.model", 0, 0); /* Don't know what happened */ if (!item) { dstate_delinfo("input.voltage"); return 0; } /* No reply/Unable to get value */ if (qx_process(item, NULL)) { dstate_delinfo("input.voltage"); return 0; } /* Unable to process value */ if (ups_infoval_set(item) != 1) { dstate_delinfo("input.voltage"); return 0; } return 1; } /* Subdriver-specific initups */ static void bestups_initups(void) { blazer_initups_light(bestups_qx2nut); } /* Subdriver-specific flags/vars */ static void bestups_makevartable(void) { addvar(VAR_VALUE, "pins_shutdown_mode", "Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) to n (integer, 0-6)"); blazer_makevartable_light(); } /* == Answer preprocess functions == */ /* Preprocess the answer we got back from the UPS when queried with 'ID\r': make data begin always at the same indexes */ static int bestups_preprocess_id_answer(item_t *item, const int len) { int i; char refined[SMALLBUF] = "", rawval[SMALLBUF] = "", *token, *saveptr = NULL; if (len <= 0) return len; if (len < 25 || len > 27) { upsdebugx(4, "%s: wrong length [%s: %d]", __func__, item->info_type, len); return -1; } /* e.g.: * 1. item->answer = "FOR,750,120,120,20.0,27.6\r"; len = 26 * 2. item->answer = "FOR,1500,120,120,20.0,27.6\r"; len = 27 * 3. item->answer = "FOR,3000,120,120,20.0,100.6\r"; len = 28 */ upsdebugx(4, "read: '%.*s'", (int)strcspn(item->answer, "\r"), item->answer); snprintf(rawval, sizeof(rawval), "%s", item->answer); for (i = 1, token = strtok_r(rawval, ",", &saveptr); token != NULL; i++, token = strtok_r(NULL, ",", &saveptr)) { switch (i) { case 1: snprintf(refined, sizeof(refined), "%s", token); continue; case 2: /* Output power */ snprintfcat(refined, sizeof(refined), ",%4s", token); continue; case 6: /* Battery voltage at full charge (+ trailing CR) */ snprintfcat(refined, sizeof(refined), ",%6s", token); continue; default: snprintfcat(refined, sizeof(refined), ",%s", token); } } if (i != 7 || strlen(refined) != 28) { upsdebugx(2, "noncompliant reply: '%.*s'", (int)strcspn(refined, "\r"), refined); return -1; } upsdebugx(4, "read: '%.*s'", (int)strcspn(refined, "\r"), refined); /* e.g.: item->answer = "FOR, 750,120,120,20.0, 27.6\r"; len = 28 */ return snprintf(item->answer, sizeof(item->answer), "%s", refined); } /* == Preprocess functions == */ /* *SETVAR(/NONUT)* Preprocess setvars */ static int bestups_process_setvar(item_t *item, char *value, const size_t valuelen) { if (!strlen(value)) { upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); return -1; } double val = strtod(value, NULL); if (!strcasecmp(item->info_type, "pins_shutdown_mode")) { if (val == pins_shutdown_mode) { upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); return -1; } } snprintf(value, valuelen, item->command, val); return 0; } /* Bypass/Boost or Buck status */ static int bestups_process_bbb_status_bit(item_t *item, char *value, const size_t valuelen) { /* Bypass/Boost/Buck bit is not reliable when a battery test, shutdown or on battery condition occurs: always ignore it in these cases */ if (!(qx_status() & STATUS(OL)) || (qx_status() & (STATUS(CAL) | STATUS(FSD)))) { if (item->value[0] == '1') item->value[0] = '0'; return blazer_process_status_bits(item, value, valuelen); } /* UPSes with inverted bypass/boost/buck bit */ if (inverted_bbb_bit) { if (item->value[0] == '1') item->value[0] = '0'; else if (item->value[0] == '0') item->value[0] = '1'; } return blazer_process_status_bits(item, value, valuelen); } /* Identify UPS manufacturer */ static int bestups_manufacturer(item_t *item, char *value, const size_t valuelen) { /* Best Power devices */ if ( !strcmp(item->value, "AX1") || !strcmp(item->value, "FOR") || !strcmp(item->value, "FTC") || !strcmp(item->value, "PR2") || !strcmp(item->value, "PRO") ) { snprintf(value, valuelen, item->dfl, "Best Power"); return 0; } /* Sola Australia devices */ if ( !strcmp(item->value, "325") || !strcmp(item->value, "520") || !strcmp(item->value, "620") ) { snprintf(value, valuelen, item->dfl, "Sola Australia"); return 0; } /* Unknown devices */ snprintf(value, valuelen, item->dfl, "Unknown"); return 0; } /* Identify UPS model and unskip qx2nut table's items accordingly */ static int bestups_model(item_t *item, char *value, const size_t valuelen) { item_t *unskip; /* Best Power devices */ if (!strcmp(item->value, "AX1")) { snprintf(value, valuelen, item->dfl, "Axxium Rackmount"); } else if (!strcmp(item->value, "FOR")) { snprintf(value, valuelen, item->dfl, "Fortress"); } else if (!strcmp(item->value, "FTC")) { snprintf(value, valuelen, item->dfl, "Fortress Telecom"); } else if (!strcmp(item->value, "PR2")) { snprintf(value, valuelen, item->dfl, "Patriot Pro II"); inverted_bbb_bit = 1; } else if (!strcmp(item->value, "PRO")) { snprintf(value, valuelen, item->dfl, "Patriot Pro"); inverted_bbb_bit = 1; /* Sola Australia devices */ } else if ( !strcmp(item->value, "320") || !strcmp(item->value, "325") || !strcmp(item->value, "520") || !strcmp(item->value, "525") || !strcmp(item->value, "620") ) { snprintf(value, valuelen, "Sola %s", item->value); /* Unknown devices */ } else { snprintf(value, valuelen, item->dfl, "Unknown (%s)", item->value); upslogx(LOG_INFO, "Unknown model detected - please report this ID: '%s'", item->value); } /* Unskip qx2nut table's items according to the UPS model */ /* battery.runtime var is not available on the Patriot Pro/Sola 320 model series: leave it skipped in these cases, otherwise unskip it */ if (strcmp(item->value, "PRO") && strcmp(item->value, "320")) { unskip = find_nut_info("battery.runtime", 0, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } /* battery.packs var is available only on the Axxium/Sola 620 model series: unskip it in these cases */ if (!strcmp(item->value, "AX1") || !strcmp(item->value, "620")) { unskip = find_nut_info("battery.packs", 0, QX_FLAG_SETVAR); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; } return 0; } /* Battery runtime */ static int bestups_batt_runtime(item_t *item, char *value, const size_t valuelen) { double runtime; if (strspn(item->value, "0123456789 .") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } /* Battery runtime is reported by the UPS in minutes, NUT expects seconds */ runtime = strtod(item->value, NULL) * 60; snprintf(value, valuelen, item->dfl, runtime); return 0; } /* Battery packs */ static int bestups_batt_packs(item_t *item, char *value, const size_t valuelen) { item_t *unskip; if (strspn(item->value, "0123456789 ") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } snprintf(value, valuelen, item->dfl, strtol(item->value, NULL, 10)); /* Unskip battery.packs setvar */ unskip = find_nut_info("battery.packs", QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* *NONUT* Get shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) as set in the UPS */ static int bestups_get_pins_shutdown_mode(item_t *item, char *value, const size_t valuelen) { item_t *unskip; if (strspn(item->value, "0123456789") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } pins_shutdown_mode = strtol(item->value, NULL, 10); snprintf(value, valuelen, item->dfl, pins_shutdown_mode); /* We were not asked by the user to change the value */ if ((item->qxflags & QX_FLAG_NONUT) && !getval(item->info_type)) return 0; /* Unskip setvar */ unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); /* Don't know what happened */ if (!unskip) return -1; unskip->qxflags &= ~QX_FLAG_SKIP; return 0; } /* Voltage settings */ static int bestups_voltage_settings(item_t *item, char *value, const size_t valuelen) { int index, val; const char *nominal_voltage; const struct { const int low; /* Low voltage -> input.transfer.low / input.transfer.boost.low */ const int boost; /* Boost voltage -> input.transfer.boost.high */ const int nominal; /* Nominal voltage -> input.voltage.nominal / output.voltage.nominal */ const int buck; /* Buck voltage -> input.transfer.trim.low */ const int high; /* High voltage -> input.transfer.high / input.transfer.trim.high */ } voltage_settings[] = { /* U models voltage limits, for: * - Fortress (750U, 1050U, 1425U, 1800U and 2250U) * - Fortress Rackmount (750, 1050, 1425, 1800, and 2250 VA) * - Patriot Pro II (400U, 750U, and 1000U) */ /* M low boost nominal buck high */ /* 0 */ { 96, 109, 120, 130, 146 }, /* LEDs lit: 2,3,4 (Default) */ /* 1 */ { 96, 109, 120, 138, 156 }, /* LEDs lit: 1,3,4 */ /* 2 */ { 90, 104, 120, 130, 146 }, /* LEDs lit: 2,3,5 */ /* 3 */ { 90, 104, 120, 138, 156 }, /* LEDs lit: 1,3,5 */ /* 4 */ { 90, 104, 110, 120, 130 }, /* LEDs lit: 3,4,5 */ /* 5 */ { 90, 104, 110, 130, 146 }, /* LEDs lit: 2,4,5 */ /* 6 */ { 90, 96, 110, 120, 130 }, /* LEDs lit: 3,4,6 */ /* 7 */ { 90, 96, 110, 130, 146 }, /* LEDs lit: 2,4,6 */ /* 8 */ { 96, 109, 128, 146, 156 }, /* LEDs lit: 1,2,4 */ /* 9 */ { 90, 104, 128, 146, 156 }, /* LEDs lit: 1,2,5 */ /* E models voltage limits, for: * - Fortress (750E, 1050E, 1425E, and 2250E) * - Fortress Rackmount (750, 1050, 1425, and 2250 VA) * - Patriot Pro II (400E, 750E, and 1000E) */ /* M low boost nominal buck high */ /* 0 */ { 200, 222, 240, 250, 284 }, /* LEDs lit: 2,3,4 */ /* 1 */ { 200, 222, 240, 264, 290 }, /* LEDs lit: 1,3,4 */ /* 2 */ { 188, 210, 240, 250, 284 }, /* LEDs lit: 2,3,5 */ /* 3 */ { 188, 210, 240, 264, 290 }, /* LEDs lit: 1,3,5 */ /* 4 */ { 188, 210, 230, 244, 270 }, /* LEDs lit: 3,4,5 (Default) */ /* 5 */ { 188, 210, 230, 250, 284 }, /* LEDs lit: 2,4,5 */ /* 6 */ { 180, 200, 230, 244, 270 }, /* LEDs lit: 3,4,6 */ /* 7 */ { 180, 200, 230, 250, 284 }, /* LEDs lit: 2,4,6 */ /* 8 */ { 165, 188, 208, 222, 244 }, /* LEDs lit: 4,5,6 */ /* 9 */ { 165, 188, 208, 244, 270 } /* LEDs lit: 3,5,6 */ }; if (strspn(item->value, "0123456789") != strlen(item->value)) { upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); return -1; } index = strtol(item->value, NULL, 10); if (index < 0 || index > 9) { upsdebugx(2, "%s: value '%d' out of range [0..9]", __func__, index); return -1; } nominal_voltage = dstate_getinfo("input.voltage.nominal"); if (!nominal_voltage) nominal_voltage = dstate_getinfo("output.voltage.nominal"); if (!nominal_voltage) { upsdebugx(2, "%s: unable to get nominal voltage", __func__); return -1; } /* E models */ if (strtol(nominal_voltage, NULL, 10) > 160) index += 10; if (!strcasecmp(item->info_type, "input.transfer.low") || !strcasecmp(item->info_type, "input.transfer.boost.low")) { val = voltage_settings[index].low; } else if (!strcasecmp(item->info_type, "input.transfer.boost.high")) { val = voltage_settings[index].boost; } else if (!strcasecmp(item->info_type, "input.voltage.nominal") || !strcasecmp(item->info_type, "output.voltage.nominal")) { val = voltage_settings[index].nominal; } else if (!strcasecmp(item->info_type, "input.transfer.trim.low")) { val = voltage_settings[index].buck; } else if (!strcasecmp(item->info_type, "input.transfer.trim.high") || !strcasecmp(item->info_type, "input.transfer.high")) { val = voltage_settings[index].high; } else { /* Don't know what happened */ return -1; } snprintf(value, valuelen, item->dfl, val); return 0; } /* == Subdriver interface == */ subdriver_t bestups_subdriver = { BESTUPS_VERSION, bestups_claim, bestups_qx2nut, bestups_initups, NULL, bestups_makevartable, NULL, NULL, #ifdef TESTING bestups_testing, #endif /* TESTING */ }; nut-2.7.4/drivers/liebert.c0000644000175000017500000000771512640443572012544 00000000000000/* liebert.c - support for Liebert UPS models via MultiLink cable. Copyright (C) 2002 Russell Kroll Based on old-style multilink.c driver: Copyright (C) 2001 Rick Lyons 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 "main.h" #include "serial.h" #define DRIVER_NAME "Liebert MultiLink UPS driver" #define DRIVER_VERSION "1.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" \ "Rick Lyons ", DRV_EXPERIMENTAL, { NULL } }; #define ML_ONBATTERY 0x55 void upsdrv_shutdown(void) { /* XXX: replace with a proper shutdown function (raise DTR) */ /* worse yet: stock cables don't support shutdown at all */ fatalx(EXIT_FAILURE, "shutdown not supported"); } void upsdrv_initinfo(void) { char *tmp; tmp = getval("mfr"); if (!tmp) dstate_setinfo("ups.mfr", "Liebert"); else dstate_setinfo("ups.mfr", "%s", tmp); tmp = getval("model"); if (!tmp) dstate_setinfo("ups.model", "MultiLink"); else dstate_setinfo("ups.model", "%s", tmp); } /* require this many OBs or LBs before actually setting it */ #define DEBOUNCE 3 /* normal idle loop - keep up with the current state of the UPS */ void upsdrv_updateinfo(void) { unsigned char c; unsigned int ob, lb; static unsigned int ob_state = 0, ob_last = 0, ob_ctr = 0; static unsigned int lb_state = 0, lb_last = 0, lb_ctr = 0; ob = lb = 0; /* the UPS connects RX to TX when on battery, so test for loopback */ ser_flush_in(upsfd, "", 0); c = ML_ONBATTERY; ser_send_char(upsfd, c); if (ser_get_char(upsfd, &c, 1, 0) == 1) { while (ser_get_char(upsfd, &c, 1, 0) == 1) continue; if (c == ML_ONBATTERY) ob = 1; } if (ser_get_dcd(upsfd)) lb = 1; /* state machine below to ensure status changes are debounced */ /* OB/OL state change: reset counter */ if (ob_last != ob) ob_ctr = 0; else ob_ctr++; upsdebugx(2, "OB: state %d last %d now %d ctr %d", ob_state, ob_last, ob, ob_ctr); if (ob_ctr >= DEBOUNCE) { if (ob != ob_state) { upsdebugx(2, "OB: toggling state"); if (ob_state == 0) ob_state = 1; else ob_state = 0; } } ob_last = ob; /* now do it again for LB */ /* state change: reset counter */ if (lb_last != lb) lb_ctr = 0; else lb_ctr++; upsdebugx(2, "LB: state %d last %d now %d ctr %d", lb_state, lb_last, lb, lb_ctr); if (lb_ctr >= DEBOUNCE) { if (lb != lb_state) { upsdebugx(2, "LB: toggling state"); if (lb_state == 0) lb_state = 1; else lb_state = 0; } } lb_last = lb; status_init(); if (ob_state == 1) status_set("OB"); /* on battery */ else status_set("OL"); /* on line */ if (lb_state == 1) status_set("LB"); /* low battery */ status_commit(); dstate_dataok(); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "mfr", "Override manufacturer name"); addvar(VAR_VALUE, "model", "Override model name"); } void upsdrv_help(void) { } void upsdrv_initups(void) { upsfd = ser_open(device_path); /* Speed should not matter (see comments in upsdrv_updateinfo), * but set it relatively low in case there are problems with higher * speeds. */ ser_set_speed(upsfd, device_path, B9600); /* raise RTS */ ser_set_rts(upsfd, 1); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } nut-2.7.4/drivers/powerp-txt.c0000644000175000017500000003446112640473702013243 00000000000000/* * powerp-txt.c - Model specific routines for CyberPower text * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2008 Arjen de Korte * 2012 Timothy Pearson * * 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 */ /* * Throughout this driver, READ and WRITE comments are shown. These are * the typical commands to and replies from the UPS that was used for * decoding the protocol (with a serial logger). */ #include "main.h" #include "serial.h" #include "powerp-txt.h" typedef struct { float i_volt; float o_volt; int o_load; int b_chrg; int u_temp; float i_freq; unsigned char flags[2]; unsigned char has_b_volt; float b_volt; unsigned char has_o_freq; float o_freq; unsigned char has_runtime; int runtime; int c_unknwn; float q_unknwn; } status_t; static int ondelay = 1; /* minutes */ static int offdelay = 60; /* seconds */ static char powpan_answer[SMALLBUF]; static struct { const char *var; const char *get; const char *set; } vartab[] = { { "input.transfer.high", "P6\r", "C2:%03d\r" }, { "input.transfer.low", "P7\r", "C3:%03d\r" }, { "battery.charge.low", "P8\r", "C4:%02d\r" }, { NULL } }; static struct { const char *cmd; const char *command; } cmdtab[] = { { "test.battery.start.quick", "T\r" }, { "test.battery.stop", "CT\r" }, { "beeper.enable", "C7:1\r" }, { "beeper.disable", "C7:0\r" }, { "beeper.on", NULL }, { "beeper.off", NULL }, { "shutdown.stop", "C\r" }, { NULL } }; static int powpan_command(const char *command) { int ret; ser_flush_io(upsfd); ret = ser_send_pace(upsfd, UPSDELAY, "%s", command); if (ret < 0) { upsdebug_with_errno(3, "send"); return -1; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); return -1; } upsdebug_hex(3, "send", command, strlen(command)); usleep(100000); ret = ser_get_line(upsfd, powpan_answer, sizeof(powpan_answer), ENDCHAR, IGNCHAR, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 0) { upsdebug_with_errno(3, "read"); upsdebug_hex(4, " \\_", powpan_answer, strlen(powpan_answer)); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); upsdebug_hex(4, " \\_", powpan_answer, strlen(powpan_answer)); return -1; } upsdebug_hex(3, "read", powpan_answer, ret); return ret; } static int powpan_instcmd(const char *cmdname, const char *extra) { int i; char command[SMALLBUF]; if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.disable'"); return powpan_instcmd("beeper.disable", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'"); return powpan_instcmd("beeper.enable", NULL); } for (i = 0; cmdtab[i].cmd != NULL; i++) { if (strcasecmp(cmdname, cmdtab[i].cmd)) { continue; } if ((powpan_command(cmdtab[i].command) == 2) && (!strcasecmp(powpan_answer, "#0"))) { return STAT_INSTCMD_HANDLED; } upslogx(LOG_ERR, "%s: command [%s] failed", __func__, cmdname); return STAT_INSTCMD_FAILED; } if (!strcasecmp(cmdname, "shutdown.return")) { if (offdelay < 60) { snprintf(command, sizeof(command), "Z.%d\r", offdelay / 6); } else { snprintf(command, sizeof(command), "Z%02d\r", offdelay / 60); } } else if (!strcasecmp(cmdname, "shutdown.stayoff")) { if (offdelay < 60) { snprintf(command, sizeof(command), "S.%d\r", offdelay / 6); } else { snprintf(command, sizeof(command), "S%02d\r", offdelay / 60); } } else if (!strcasecmp(cmdname, "shutdown.reboot")) { if (offdelay < 60) { snprintf(command, sizeof(command), "S.%dR%04d\r", offdelay / 6, ondelay); } else { snprintf(command, sizeof(command), "S%02dR%04d\r", offdelay / 60, ondelay); } } else { upslogx(LOG_NOTICE, "%s: command [%s] unknown", __func__, cmdname); return STAT_INSTCMD_UNKNOWN; } if ((powpan_command(command) == 2) && (!strcasecmp(powpan_answer, "#0"))) { return STAT_INSTCMD_HANDLED; } upslogx(LOG_ERR, "%s: command [%s] failed", __func__, cmdname); return STAT_INSTCMD_FAILED; } static int powpan_setvar(const char *varname, const char *val) { char command[SMALLBUF]; int i; for (i = 0; vartab[i].var != NULL; i++) { if (strcasecmp(varname, vartab[i].var)) { continue; } if (!strcasecmp(val, dstate_getinfo(varname))) { upslogx(LOG_INFO, "%s: [%s] no change for variable [%s]", __func__, val, varname); return STAT_SET_HANDLED; } snprintf(command, sizeof(command), vartab[i].set, atoi(val)); if ((powpan_command(command) == 2) && (!strcasecmp(powpan_answer, "#0"))) { dstate_setinfo(varname, "%s", val); return STAT_SET_HANDLED; } upslogx(LOG_ERR, "%s: setting variable [%s] to [%s] failed", __func__, varname, val); return STAT_SET_UNKNOWN; } upslogx(LOG_ERR, "%s: variable [%s] not found", __func__, varname); return STAT_SET_UNKNOWN; } static void powpan_initinfo(void) { int i; char *s; dstate_setinfo("ups.delay.start", "%d", 60 * ondelay); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); /* * NOTE: The reply is already in the buffer, since the P4\r command * was used for autodetection of the UPS. No need to do it again. */ if ((s = strtok(&powpan_answer[1], ",")) != NULL) { dstate_setinfo("ups.model", "%s", str_rtrim(s, ' ')); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("ups.firmware", "%s", s); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("ups.serial", "%s", s); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("ups.mfr", "%s", str_rtrim(s, ' ')); } /* * WRITE P3\r * READ #12.0,002,008.0,00\r */ if (powpan_command("P3\r") > 0) { if ((s = strtok(&powpan_answer[1], ",")) != NULL) { dstate_setinfo("battery.voltage.nominal", "%g", strtod(s, NULL)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("battery.packs", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("battery.capacity", "%g", strtod(s, NULL)); } } /* * WRITE P2\r * READ #1200,0720,120,47,63\r */ if (powpan_command("P2\r") > 0) { if ((s = strtok(&powpan_answer[1], ",")) != NULL) { dstate_setinfo("ups.power.nominal", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("ups.realpower.nominal", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("input.voltage.nominal", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("input.frequency.low", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("input.frequency.high", "%li", strtol(s, NULL, 10)); } } /* * WRITE P1\r * READ #120,138,088,20\r */ if (powpan_command("P1\r") > 0) { if ((s = strtok(&powpan_answer[1], ",")) != NULL) { dstate_setinfo("input.voltage.nominal", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("input.transfer.high", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("input.transfer.low", "%li", strtol(s, NULL, 10)); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("battery.charge.low", "%li", strtol(s, NULL, 10)); } } for (i = 0; cmdtab[i].cmd != NULL; i++) { dstate_addcmd(cmdtab[i].cmd); } for (i = 0; vartab[i].var != NULL; i++) { if (!dstate_getinfo(vartab[i].var)) { continue; } if (powpan_command(vartab[i].get) < 1) { continue; } if ((s = strtok(&powpan_answer[1], ",")) != NULL) { dstate_setflags(vartab[i].var, ST_FLAG_RW); dstate_addenum(vartab[i].var, "%li", strtol(s, NULL, 10)); } while ((s = strtok(NULL, ",")) != NULL) { dstate_addenum(vartab[i].var, "%li", strtol(s, NULL, 10)); } } /* * WRITE P5\r * READ #\r */ if (powpan_command("P5\r") > 0) { /* * Looking at the format of the commands "P\r" it seems likely * that this command exists also. Let's see if someone cares to * tell us if it does (should be visible when running with -DDDDD). */ } /* * WRITE P9\r * READ #\r */ if (powpan_command("P9\r") > 0) { /* * Looking at the format of the commands "P\r" it seems likely * that this command exists also. Let's see if someone cares to * tell us if it does (should be visible when running with -DDDDD). */ } /* * Cancel pending shutdown. * WRITE C\r * READ #0\r */ powpan_command("C\r"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); dstate_addcmd("shutdown.reboot"); } static int powpan_status(status_t *status) { int ret; ser_flush_io(upsfd); /* * WRITE D\r * READ #I119.0O119.0L000B100T027F060.0S..\r * 01234567890123456789012345678901234 * 0 1 2 3 */ ret = ser_send_pace(upsfd, UPSDELAY, "D\r"); if (ret < 0) { upsdebug_with_errno(3, "send"); return -1; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); return -1; } upsdebug_hex(3, "send", "D\r", 2); usleep(200000); ret = ser_get_buf_len(upsfd, powpan_answer, 35, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 0) { upsdebug_with_errno(3, "read"); upsdebug_hex(4, " \\_", powpan_answer, 35); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); upsdebug_hex(4, " \\_", powpan_answer, 35); return -1; } upsdebug_hex(3, "read", powpan_answer, ret); ret = sscanf(powpan_answer, "#I%fO%fL%dB%dT%dF%fS%2c\r", &status->i_volt, &status->o_volt, &status->o_load, &status->b_chrg, &status->u_temp, &status->i_freq, status->flags); if (ret >= 7) { status->has_b_volt = 0; status->has_o_freq = 0; status->has_runtime = 0; } else { ret = ser_get_buf_len(upsfd, powpan_answer+35, 23, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 0) { upsdebug_with_errno(3, "read"); upsdebug_hex(4, " \\_", powpan_answer+35, 23); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); upsdebug_hex(4, " \\_", powpan_answer+35, 23); return -1; } upsdebug_hex(3, "read", powpan_answer, ret); ret = sscanf(powpan_answer, "#I%fO%fL%dB%dV%fT%dF%fH%fR%dC%dQ%fS%2c\r", &status->i_volt, &status->o_volt, &status->o_load, &status->b_chrg, &status->b_volt, &status->u_temp, &status->i_freq, &status->o_freq, &status->runtime, &status->c_unknwn, &status->q_unknwn, status->flags); status->has_b_volt = 1; status->has_o_freq = 1; status->has_runtime = 1; dstate_setinfo("battery.voltage.nominal", "%g", 72.0); dstate_setinfo("output.voltage.nominal", "%g", 120.0); } if (ret < 7) { upsdebugx(4, "Parsing status string failed"); return -1; } return 0; } static int powpan_updateinfo(void) { status_t status; if (powpan_status(&status)) { return -1; } dstate_setinfo("input.voltage", "%.1f", status.i_volt); dstate_setinfo("output.voltage", "%.1f", status.o_volt); dstate_setinfo("ups.load", "%d", status.o_load); dstate_setinfo("input.frequency", "%.1f", status.i_freq); dstate_setinfo("ups.temperature", "%d", status.u_temp); dstate_setinfo("battery.charge", "%d", status.b_chrg); if (status.has_b_volt) { dstate_setinfo("battery.voltage", "%.1f", status.b_volt); } if (status.has_o_freq) { dstate_setinfo("output.frequency", "%.1f", status.o_freq); } if (status.has_runtime) { dstate_setinfo("battery.runtime", "%d", status.runtime*60); } status_init(); if (status.flags[0] & 0x40) { status_set("OB"); } else { status_set("OL"); } if (status.flags[0] & 0x20) { status_set("LB"); } /* !OB && !TEST */ if (!(status.flags[0] & 0x48)) { if (status.o_volt < 0.5 * status.i_volt) { upsdebugx(2, "%s: output voltage too low", __func__); } else if (status.o_volt < 0.95 * status.i_volt) { status_set("TRIM"); } else if (status.o_volt < 1.05 * status.i_volt) { /* ignore */ } else if (status.o_volt < 1.5 * status.i_volt) { status_set("BOOST"); } else { upsdebugx(2, "%s: output voltage too high", __func__); } } if (status.flags[0] & 0x08) { status_set("TEST"); } if (status.flags[0] == 0) { status_set("OFF"); } status_commit(); return (status.flags[0] & 0x40) ? 1 : 0; } static int powpan_initups(void) { int ret, i; upsdebugx(1, "Trying text protocol..."); ser_set_speed(upsfd, device_path, B2400); /* This fails for many devices, so don't bother to complain */ powpan_command("\r\r"); for (i = 0; i < MAXTRIES; i++) { const char *val; /* * WRITE P4\r * READ #BC1200 ,1.600,000000000000,CYBER POWER * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ ret = powpan_command("P4\r"); if (ret < 1) { continue; } if (ret < 46) { upsdebugx(2, "Expected 46 bytes, but only got %d", ret); continue; } if (powpan_answer[0] != '#') { upsdebugx(2, "Expected start character '#', but got '%c'", powpan_answer[0]); continue; } val = getval("ondelay"); if (val) { ondelay = strtol(val, NULL, 10); } if ((ondelay < 0) || (ondelay > 9999)) { fatalx(EXIT_FAILURE, "Start delay '%d' out of range [0..9999]", ondelay); } val = getval("offdelay"); if (val) { offdelay = strtol(val, NULL, 10); } if ((offdelay < 6) || (offdelay > 600)) { fatalx(EXIT_FAILURE, "Shutdown delay '%d' out of range [6..600]", offdelay); } /* Truncate to nearest setable value */ if (offdelay < 60) { offdelay -= (offdelay % 6); } else { offdelay -= (offdelay % 60); } return ret; } return -1; } subdriver_t powpan_text = { "text", powpan_instcmd, powpan_setvar, powpan_initups, powpan_initinfo, powpan_updateinfo }; nut-2.7.4/drivers/optiups.c0000644000175000017500000004326512640443572012621 00000000000000/* optiups.c - OptiSafe UPS (very loosely based on the nut 0.45.5 driver) Copyright (C) 1999 Russell Kroll Copyright (C) 2006 Scott Heavner [Use my alioth acct: sheavner] Support for Zinto D from ONLINE USV (only minor differences to OptiSafe UPS) added by Matthias Goebl 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 "main.h" #include "serial.h" #define DRIVER_NAME "Opti-UPS driver" #define DRIVER_VERSION "1.01" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" \ "Scott Heavner \n" \ "Matthias Goebl ", DRV_STABLE, { NULL } }; #define HELP "\n" \ "**********************************************************" "\n" \ "This driver has been tested only with the OPTI SAFE 420E using" "\n" \ "the custom cable described in the NUT Opti-UPS protocol page." "\n" \ "Seriously, it does _NOT_ work with the [windows] cable provided with" "\n" \ "the UPS or a standard serial cable. (I don't know if OPTI makes other" "\n" \ "UPS models that will work with another cable?) Standard linux serial" "\n" \ "port drivers do not support the required DTR handshaking." "\n" \ "" "\n" \ " UPS 6 -> PC 3 This 3 wire cable pinout maps DTR to CTS." "\n" \ " UPS 9 -> PC 2" "\n" \ " UPS 4 -> PC 5" "\n" \ "" "\n" \ "This driver has also been tested with a Zinto D from Online-USV AG," "\n" \ "using their special cable:" "\n" \ " UPS 6 -> PC 3" "\n" \ " UPS 9 -> PC 2" "\n" \ " UPS 7 -> PC 5" "\n" \ "It works even with a pl2303 usb-serial converter." "\n" \ "**********************************************************" "\n" /* See http://www.networkupstools.org/protocols/optiups.html and the end of this * file for more information on the cable and the OPTI-UPS serial protocol used on * at least the older OPTI UPS models (420E, 820ES). */ #define ENDCHAR '\n' #define IGNCHARS "\r" /* Our custom options available with -x */ #define OPTI_MINPOLL "status_only" #define OPTI_FAKELOW "fake_lowbatt" #define OPTI_NOWARN_NOIMP "nowarn_noimp" #define OPTI_POWERUP "powerup" /* All serial commands put their response in the same buffer space */ static char _buf[256]; /* Model */ static int optimodel = 0; enum { OPTIMODEL_DEFAULT = 0, OPTIMODEL_ZINTO =1 }; /* Status bits returned by the "AG" command */ enum { OPTISBIT_NOOUTPUT = 2, OPTISBIT_OVERLOAD = 8, OPTISBIT_REPLACE_BATTERY = 16, OPTISBIT_ON_BATTERY_POWER = 32, OPTISBIT_LOW_BATTERY = 64 }; /* Helper struct for the optifill() function */ typedef struct ezfill_s { const char *cmd; const char *var; const float scale; } ezfill_t; /* These can be polled right into a string usable by NUT. * Others such as "AG" and "BV" require some transformation of the return value */ static ezfill_t _pollv[] = { { "NV", "input.voltage" }, { "OL", "ups.load", 1.0 }, { "OV", "output.voltage" }, { "FF", "input.frequency", 0.1 }, { "BT", "ups.temperature" }, }; static ezfill_t _pollv_zinto[] = { { "NV", "input.voltage", 2.0 }, { "OL", "ups.load", 1.0 }, { "OV", "output.voltage", 2.0 }, { "OF", "output.frequency", 0.1 }, { "NF", "input.frequency", 0.1 }, { "BT", "ups.temperature" }, }; /* model "IO" is parsed differently in upsdrv_initinfo() */ static ezfill_t _initv[] = { { "IM", "ups.mfr" }, { "IZ", "ups.serial" }, { "IS", "ups.firmware" }, }; /* All serial reads of the OPTI-UPS go through here. We always expect a CR/LF terminated * response. Unknown/Unimplemented commands return ^U (0x15). Actions that complete * successfully return ^F (0x06). */ static inline int optireadline(void) { int r; usleep(150000); r = ser_get_line(upsfd, _buf, sizeof(_buf), ENDCHAR, IGNCHARS, 0, 500000 ); _buf[sizeof(_buf)-1] = 0; if ( r > 0 ) { if ( r < (int)sizeof(_buf) ) _buf[r] = 0; if ( _buf[0] == 0x15 ) { r=-2; upsdebugx(1, "READ: "); } if ( _buf[0] == 0x06 ) { upsdebugx(2, "READ: "); } else { upsdebugx(2, "READ: \"%s\"", _buf ); } } else upsdebugx(1, "READ ERROR: %d", r ); return r; } /* Send a command and read the response. Command response is in global _buf. * Return * > 0 implies success. * -1 serial timeout * -2 unknown/unimplemented command */ static inline int optiquery( const char *cmd ) { upsdebugx(2, "SEND: \"%s\"", cmd ); ser_send( upsfd, "%s", cmd ); if ( optimodel == OPTIMODEL_ZINTO ) ser_send( upsfd, "\r\n" ); return optireadline(); } /* Uses the ezfill_t structure to map UPS commands to the NUT variable destinations */ static void optifill( ezfill_t *a, int len ) { int i, r; /* Some things are easy to poll and store */ for ( i=0; i 1e-20 ) { float f = strtol( _buf, NULL, 10 ) * a[i].scale; dstate_setinfo( a[i].var, "%.1f", f ); } else { dstate_setinfo( a[i].var, "%s", _buf); } } } /* Handle custom (but standardized) NUT commands */ static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.failure.start")) { optiquery( "Ts" ); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "load.off")) { /* You do realize this will kill power to ourself. Would probably only * be useful for killing power for a slave computer */ if ( optimodel == OPTIMODEL_ZINTO ) { optiquery( "Ct1" ); optiquery( "Cs0000000" ); sleep(2); return STAT_INSTCMD_HANDLED; } optiquery( "Ct0" ); optiquery( "Cs00000000" ); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "load.on")) { if ( optimodel == OPTIMODEL_ZINTO ) { optiquery( "Ct1" ); optiquery( "Cu0000000" ); sleep(2); return STAT_INSTCMD_HANDLED; } optiquery( "Ct0" ); optiquery( "Cu00000000" ); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "shutdown.return")) { /* This shuts down the UPS. When the power returns to the UPS, * it will power back up in its default state. */ if ( optimodel == OPTIMODEL_ZINTO ) { optiquery( "Ct1" ); optiquery( "Cu0000010" ); optiquery( "Cs0000001" ); return STAT_INSTCMD_HANDLED; } optiquery( "Ct1" ); optiquery( "Cs00000010" ); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "shutdown.stayoff")) { /* This actually stays off as long as the batteries hold, * if the line power comes back before the batteries die, * the UPS will never powerup its output stage!!! */ if ( optimodel == OPTIMODEL_ZINTO ) { optiquery( "Ct1" ); optiquery( "Cs0000001" ); return STAT_INSTCMD_HANDLED; } optiquery( "Ct0" ); optiquery( "Cs00000010" ); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "shutdown.stop")) { /* Aborts a shutdown that is couting down via the Cs command */ optiquery( "Cs-0000001" ); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } /* Handle variable setting */ static int setvar(const char *varname, const char *val) { int status; if (sscanf(val, "%d", &status) != 1) { return STAT_SET_UNKNOWN; } if (strcasecmp(varname, "outlet.1.switch") == 0) { status = status==1 ? 1 : 0; dstate_setinfo( "outlet.1.switch", "%d", status); optiquery(status ? "Oi11" : "Oi10"); dstate_dataok(); return STAT_SET_HANDLED; } return STAT_SET_UNKNOWN; } void upsdrv_initinfo(void) { int r; /* If an Zinto Online-USV is off, switch it on first. */ /* It sends only "2" when off, without "\r\n", and doesn't */ /* answer other commands. Therefore without power we'll be */ /* unable to identify the ups. */ if ( testvar(OPTI_POWERUP) && optiquery( "AG" ) < 1 ) { ser_send( upsfd, "AG\r\n" ); r = ser_get_char(upsfd, &_buf[0], 1, 0); if ( r == 1 && _buf[0] == '2' ) { upslogx( LOG_WARNING, "ups was off, switching it on" ); optiquery( "Ct1" ); optiquery( "Cu0000000" ); /* wait for power up */ sleep(15); } } /* Autodetect an Online-USV (only Zinto D is known to work) */ r = optiquery( "IM" ); if ( r > 0 && !strcasecmp(_buf, "ONLINE") ) { optimodel = OPTIMODEL_ZINTO; optiquery( "Om11" ); optiquery( "Om21" ); optiquery( "ON" ); } optifill( _initv, sizeof(_initv)/sizeof(_initv[0]) ); /* Parse out model into longer string -- is this really USEFUL??? */ r = optiquery( "IO" ); if ( r < 1 ) fatal_with_errno(EXIT_FAILURE, "can't retrieve model" ); else { switch ( _buf[r-1] ) { case 'E': case 'P': case 'V': dstate_setinfo("ups.model", "Power%cS %s", _buf[r-1], _buf ); break; default: dstate_setinfo("ups.model", "%s", _buf ); break; } } /* Parse out model into longer string */ r = optiquery( "IM" ); if ( r > 0 && !strcasecmp(_buf, "ONLINE") ) { dstate_setinfo("ups.mfr", "ONLINE USV-Systeme AG"); r = optiquery( "IO" ); if ( r < 1 ) fatal_with_errno(EXIT_FAILURE, "can't retrieve model" ); switch ( _buf[0] ) { case 'D': dstate_setinfo("ups.model", "Zinto %s", _buf ); break; default: dstate_setinfo("ups.model", "%s", _buf ); break; } } dstate_addcmd("test.failure.start"); dstate_addcmd("load.off"); dstate_addcmd("load.on"); if( optimodel != OPTIMODEL_ZINTO ) dstate_addcmd("shutdown.stop"); dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); upsh.instcmd = instcmd; if ( optimodel == OPTIMODEL_ZINTO ) { dstate_setinfo("outlet.desc", "%s", "Main Outlet 1+2"); dstate_setinfo("outlet.1.desc", "%s", "Switchable Outlet 3+4"); dstate_setinfo("outlet.id", "%d", 1); dstate_setinfo("outlet.1.id", "%d", 2); dstate_setinfo("outlet.switchable", "%d", 0); dstate_setinfo("outlet.1.switchable", "%d", 1); dstate_setinfo("outlet.1.switch", "%d", 1); dstate_setflags("outlet.1.switch", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("outlet.1.switch", 1); upsh.setvar = setvar; } } void upsdrv_updateinfo(void) { int r = optiquery( "AG" ); /* Online-UPS send only "2" when off, without "\r\n" */ if ( r < 1 && optimodel == OPTIMODEL_ZINTO ) { ser_send( upsfd, "AG\r\n" ); r = ser_get_char(upsfd, &_buf[0], 1, 0); if ( r == 1 && _buf[0] == '2' ) { status_init(); status_set("OFF"); status_commit(); return; } } if ( r < 1 ) { upslogx(LOG_ERR, "can't retrieve ups status" ); dstate_datastale(); } else { int s = strtol( _buf, NULL, 16 ); status_init(); if ( s & OPTISBIT_OVERLOAD ) status_set("OVER"); if ( s & OPTISBIT_REPLACE_BATTERY ) status_set("RB"); if ( s & OPTISBIT_ON_BATTERY_POWER ) status_set("OB"); else status_set("OL"); if ( s & OPTISBIT_NOOUTPUT ) status_set("OFF"); if ( s & OPTISBIT_LOW_BATTERY ) status_set("LB"); if ( testvar(OPTI_FAKELOW) ) /* FOR TESTING */ status_set("LB"); status_commit(); dstate_dataok(); } /* Get out of here now if minimum polling is desired */ if ( testvar(OPTI_MINPOLL) ) return; /* read some easy settings */ if ( optimodel == OPTIMODEL_ZINTO ) optifill( _pollv_zinto, sizeof(_pollv_zinto)/sizeof(_pollv_zinto[0]) ); else optifill( _pollv, sizeof(_pollv)/sizeof(_pollv[0]) ); /* Battery voltage is harder */ r = optiquery( "BV" ); if ( r < 1 ) upslogx( LOG_WARNING, "cannot retrieve battery voltage" ); else { float p, v = strtol( _buf, NULL, 10 ) / 10.0; dstate_setinfo("battery.voltage", "%.1f", v ); /* battery voltage range: 10.4 - 13.0 VDC */ p = ((v - 10.4) / 2.6) * 100.0; if ( p > 100.0 ) p = 100.0; dstate_setinfo("battery.charge", "%.1f", p ); } } void upsdrv_shutdown(void) { /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ /* If get no response, assume on battery & battery low */ int s = OPTISBIT_ON_BATTERY_POWER | OPTISBIT_LOW_BATTERY; int r = optiquery( "AG" ); if ( r < 1 ) { upslogx(LOG_ERR, "can't retrieve ups status during shutdown" ); } else { s = strtol( _buf, NULL, 16 ); } /* Turn output stage back on if power returns - but really means * turn off ups if on battery */ optiquery( "Ct1" ); /* What happens, if the power comes back *after* reading the ups status and * before the shutdown command? For "Online-UPS Zinto D" *always* asking for * "shutdown shortly and power-up later" works perfectly, because it forces * a power cycle, even for the named race condition. * For Opti-UPS I have no information, so I wouldn't dare to change it. * BTW, Zinto expects only 7 digits after Cu/Cs. * (Matthias Goebl) */ if ( optimodel == OPTIMODEL_ZINTO ) { /* On line power: Power up in 60 seconds (30 seconds after the following shutdown) */ /* On battery: Power up when the line power returns */ optiquery( "Cu0000060" ); /* Shutdown in 30 seconds */ optiquery( "Cs0000030" ); return; } /* Just cycling power, schedule output stage to come back on in 60 seconds */ if ( !(s&OPTISBIT_ON_BATTERY_POWER) ) optiquery( "Cu00000600" ); /* Shutdown in 8 seconds */ optiquery( "Cs00000080" ); } void upsdrv_help(void) { printf(HELP); } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { addvar(VAR_FLAG, OPTI_MINPOLL, "Only poll for critical status variables"); addvar(VAR_FLAG, OPTI_FAKELOW, "Fake a low battery status" ); addvar(VAR_FLAG, OPTI_NOWARN_NOIMP, "Supress warnings of unsupported commands"); addvar(VAR_FLAG, OPTI_POWERUP, "(Zinto D) Power-up UPS at start (cannot identify a powered-down Zinto D)"); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); /* NO PARSED COMMAND LINE VARIABLES */ } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } /******************************************* * COMMANDS THAT QUERY THE UPS ******************************************* * IM - manufacturer * IO - model * IS - firmware version * IZ - serial number <> (unsupported on 420E, returns ^U) * * BS - ??? returns <2> * BV - battery voltage (in deciVolts) <0140> * BT - battery temperature (deg C) <0033> (returns ^U on OptiUPS 420E) * * NF - input line frequency <600> * NV - input line voltage <116> * * OS - ?? return <3> * OF - output stage frequency (in 0.1 Hz) <600> * OV - output stage voltage <118> * OL - output stage load <027> * * FV - Input voltage <120> * FF - Input Frequency (0.1Hz) <600> * FO - Output volts <120> * FR - Output Frequency (0.1Hz) <600> * FA - Output VA <420> * FP - Ouptu power <252> * FU - ?? returns <2> * FB - ?? * FH - High Transfer Point <144> * FL - Low Transfer Point <093> * FT - Transfer point? <121> * * AG - UPS status (bitmapped ASCII hex value) <00> * bit 2: 1 = * bit 3: 1 = overload * bit 4: 1 = replace battery * bit 5: 1 = on battery, 0 = on line * bit 6: 1 = low battery * TR - Test results <00> * 00 = Unknown * 01 = Passed * 02 = Warning * 03 = Error * 04 = Aborted * 05 = In Progress * 06 = No test init * ******************************************* * ACTIONS ******************************************* * * Ts - Start test * Ct0 - set power down mode (when running on battery) * Ct0 = power down only ouput stage * Ct1 = complete power down * Cs00000000 - power down after delay (count in 0.1s) * Cs00000100 = power down in 10s * Cs-0000001 = cancel power down request * Cu00000000 - power down after delay (count in 0.1s) * Cu00000050 = power up in 5s * Cu00000000 = power up now * * CT - returns last setting passed to Ct command <0> * ******************************************* * UNKNOWN COMMANDS (on 420e anyways) ******************************************* * Fu * Fv * Ff * Fo * Fr * Fb * Fl * Fh */ nut-2.7.4/drivers/ivtscd.c0000644000175000017500000001352412640443572012405 00000000000000/* * ivtscd.c - model specific routines for the IVT Solar Controller driver * * Copyright (C) 2009 Arjen de Korte * * 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 "main.h" #include "serial.h" #define DRIVER_NAME "IVT Solar Controller driver" #define DRIVER_VERSION "0.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arjen de Korte ", DRV_EXPERIMENTAL, { NULL } }; static struct { struct { float act; float low; float min; float nom; float max; } voltage; struct { float min; float act; float max; } current; float temperature; } battery; static int ivt_status(void) { char reply[SMALLBUF]; int ret, i, j = 0; ser_flush_io(upsfd); /* * send: F\n */ ret = ser_send(upsfd, "F"); if (ret < 0) { upsdebug_with_errno(3, "send"); return -1; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); return -1; } upsdebugx(3, "send: F"); sleep(1); /* allow controller some time to digest this */ /* * read: R:12,57;- 1,1;20;12,57;13,18;- 2,1; 1,5;\n */ ret = ser_get_buf(upsfd, reply, sizeof(reply), 1, 0); if (ret < 0) { upsdebug_with_errno(3, "read"); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); return -1; } upsdebugx(3, "read: %.*s", (int)strcspn(reply, "\r\n"), reply); upsdebug_hex(4, " \\_", reply, ret); for (i = 0; i < ret; i++) { switch(reply[i]) { case ',': /* convert ',' to '.' */ reply[j++] = '.'; break; case ' ': /* skip over white space */ case '\0': /* skip over null characters */ break; default: /* leave the rest as is */ reply[j++] = reply[i]; break; } } reply[j++] = '\0'; ret = sscanf(reply, "R:%f;%f;%f;%f;%f;%f;%f;", &battery.voltage.act, &battery.current.act, &battery.temperature, &battery.voltage.min, &battery.voltage.max, &battery.current.min, &battery.current.max); upsdebugx(3, "Parsed %d parameters from reply", ret); return ret; } static int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "reset.input.minmax")) { ser_send(upsfd, "L"); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_initinfo(void) { if (ivt_status() < 7) { fatal_with_errno(EXIT_FAILURE, "IVT Solar Controller not detected"); } /* set the device general information */ dstate_setinfo("device.mfr", "IVT"); dstate_setinfo("device.model", "Solar Controller Device"); dstate_setinfo("device.type", "scd"); dstate_addcmd("reset.input.minmax"); upsh.instcmd = instcmd; } void upsdrv_updateinfo(void) { if (ivt_status() < 7) { dstate_datastale(); return; } dstate_setinfo("battery.voltage", "%.2f", battery.voltage.act); dstate_setinfo("battery.voltage.minimum", "%.2f", battery.voltage.min); dstate_setinfo("battery.voltage.maximum", "%.2f", battery.voltage.max); dstate_setinfo("battery.current", "%.1f", battery.current.act); dstate_setinfo("battery.current.minimum", "%.1f", battery.current.min); dstate_setinfo("battery.current.maximum", "%.1f", battery.current.max); dstate_setinfo("battery.temperature", "%.0f", battery.temperature); status_init(); if (battery.current.act > 0) { status_set("OL"); /* charging */ } else { status_set("OB"); /* discharging */ } if (battery.voltage.act < battery.voltage.low) { status_set("LB"); } status_commit(); dstate_dataok(); } void upsdrv_shutdown(void) { while (1) { if (ivt_status() < 7) { continue; } if (battery.voltage.act < battery.voltage.nom) { continue; } fatalx(EXIT_SUCCESS, "Power is back!"); } } void upsdrv_help(void) { } void upsdrv_makevartable(void) { } void upsdrv_initups(void) { struct termios tio; const char *val; upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); if (tcgetattr(upsfd, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcgetattr"); } /* * Use canonical mode input processing (to read reply line) */ tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ tio.c_iflag |= IGNCR; /* Ignore CR */ tio.c_iflag |= IGNBRK; /* Ignore break condition */ tio.c_oflag |= ONLCR; /* Map NL to CR-NL on output */ tio.c_cc[VEOF] = _POSIX_VDISABLE; tio.c_cc[VEOL] = _POSIX_VDISABLE; tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VINTR] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; tio.c_cc[VQUIT] = _POSIX_VDISABLE; tio.c_cc[VSUSP] = _POSIX_VDISABLE; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); } /* * Set DTR and clear RTS to provide power for the serial interface. */ ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); val = dstate_getinfo("battery.voltage.nominal"); battery.voltage.nom = (val) ? strtod(val, NULL) : 12.00; val = dstate_getinfo("battery.voltage.low"); battery.voltage.low = (val) ? strtod(val, NULL) : 10.80; if (battery.voltage.nom <= battery.voltage.low) { fatalx(EXIT_FAILURE, "Nominal battery voltage must be higher than low battery voltage!"); } } void upsdrv_cleanup(void) { ser_set_dtr(upsfd, 0); ser_close(upsfd, device_path); } nut-2.7.4/drivers/solis.h0000644000175000017500000004573712667537407012274 00000000000000/* solis.h - Microsol Solis UPS hardware Copyright (C) 2004 Silvino B. Magalhaes 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 2004/10/10 - Version 0.10 - Initial release 2004/10/20 - Version 0.20 - add Battery information in driver 2004/10/26 - Version 0.30 - add commands and test shutdown 2004/10/30 - Version 0.40 - add model data structs 2004/11/22 - Version 0.50 - add internal e external shutdown programming 2005/06/16 - Version 0.60 - save external shutdown programming to ups, support new cables and Solaris compilation 2015/09/19 - Version 0.63 - patch for correct reading for Microsol Back-Ups BZ1200-BR */ #ifndef INCLUDED_SOLIS_H #define INCLUDED_SOLIS_H typedef int bool_t; /* autonomy constants */ const static int bext[5] = {14,18,28,18,1}; const static int nompow[5] = { 1000,1500,2000,3000,1200 }; const static int inds[6] = { 0,0,1,2,3,4 }; const static double InVolt_offset = 30.; const static struct { int maxi; /* power internals */ int minc[21]; /* power minimal index */ int maxc[21]; /* power maximus index */ int mm[21][39]; /* autonomy time [minutes] */ } auton[5] = {{ 7, { 139,139,142,141,141,141,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } , { 176,174,172,172,170,170,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } , { { 1,1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,7,8,10,11,12,15,17,19,22,25,28,31,35,40,43,47,52,59,64,68,75,75 }, { 1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,5,5,6,7,8,9,10,11,12,13,15,18,20,21,24,28,30,32,33,33,34,34,34,34 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,13,15,17,19,21,22,23,23,23,23,23,23 }, { 1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,4,5,6,7,7,8,9,19,11,12,13,14,16,18,20,20,20,20,20,20 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,4,5,5,6,6,6,7,7,8,8,10,11,12,13,14,14,14,14,14,14,14,14 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,3,3,4,4,5,6,6,6,7,7,8,9,10,11,12,12,12,12,12,12,12,12,12,12 }, { 1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,4,4,5,5,6,6,6,7,7,8,9,10,10,10,10,10,10,10,10,10,10,10,10,10 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, },}, { 10, { 141,141,141,141,141,141,141,141,141,141,0,0,0,0,0,0,0,0,0,0,0 } , { 177,177,177,172,173,172,172,172,172,172,0,0,0,0,0,0,0,0,0,0,0 } , { { 1,1,1,1,1,1,1,1,1,2,2,3,3,5,6,7,9,10,12,15,16,18,22,25,27,30,36,42,45,48,55,60,64,70,78, 84,90,97,104 }, { 1,1,1,1,1,1,1,1,1,2,2,2,3,2,3,4,5,6,7,8,10,12,13,14,16,19,22,25,28,30,35,38,42,46,52,55, 58,60,62 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,4,5,7,9,10,12,13,16,20,21,23,26,28,30,34,35,37,38,39,40 ,40,40 }, { 1,1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,4,5,6,7,8,9,10,11,13,14,16,18,21,22,24,27,27,27,27,27,27 }, { 1,1,1,1,1,1,1,1,1,2,2,2,2,3,3,4,4,4,4,4,5,6,7,8,9,10,11,12,14,15,16,19,20,22,23,23,23,23,23 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,3,3,4,4,4,4,5,6,7,8,9,10,11,12,13,14,16,17,18,18,18,18,18,18 }, { 1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,3,4,4,4,4,4,5,7,7,7,8,9,10,11,12,14,14,14,14,14,14,14 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,5,6,7,7,8,8,9,10,11,12,13,13,13,13,13,13,13,13 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,4,4,4,4,4,5,6,7,7,8,9,10,10,11,11,12,12,12,12,12,12 }, { 1,1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,5,6,6,7,7,8,8,8,9,9,10,10,10,10,10,10,10,10 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, },}, { 13, { 141,141,141,141,141,141,141,141,141,141,141,141,141,0,0,0,0,0,0,0,0 } , { 177,177,177,172,173,172,172,172,172,172,172,172,172,0,0,0,0,0,0,0,0 } , { { 1,1,1,1,1,1,2,2,2,3,4,5,5,8,10,11,13,15,18,22,24,28,34,40,43,48,56,65,70,76,85,93,100,110,122,132,141,152,162 }, { 1,1,1,1,1,1,1,2,2,2,3,3,3,4,5,6,8,9,10,12,15,18,20,22,25,30,35,40,44,48,54,60,65,73,80,85,92,94,95 }, { 1,1,1,1,1,1,1,2,2,3,3,3,4,4,4,5,5,6,8,10,14,16,18,20,24,30,32,36,40,44,48,52,54,58,59,60 ,62,62,62 }, { 1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,5,5,6,8,9,10,12,13,15,17,20,22,24,28,32,35,38,42,42,42,42,42,42 }, { 1,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,7,8,10,11,12,14,15,17,18,21,24,26,29,32,34,36,36,36,36,36 }, { 1,1,1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,7,8,9,10,12,13,15,16,18,20,22,24,26,28,28,28,28,28,28 }, { 1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,4,5,5,6,6,7,8,9,10,11,13,14,15,17,19,21,22,22,22,22,22,22 }, { 1,1,1,1,1,1,1,2,2,2,3,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,12,14,15,17,18,19,20,20,20,20,20,20,20 }, { 1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,4,4,5,6,6,7,7,8,9,10,11,12,13,15,15,16,16,16,16,16,16,16,16 }, { 1,1,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,7,8,9,9,10,11,12,12,12,13,13,14,14,14,14,14,14,14,14 }, { 1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,5,5,6,7,7,8,9,10,10,10,11,11,12,12,12,12,12,12,12,12 }, { 1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,5,5,6,6,7,7,8,8,8,9,9,10,10,10,10,10,10,10,10 }, { 1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,4,4,4,5,5,6,6,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, },}, { 21, { 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 } , { 177,175,173,171,169,169,168,168,167,167,166,165,164,162,160,160,160,160,159,158,157 } , { { 1,1,1,1,2,2,3,4,5,6,7,8,9,10,11,12,13,14,16,19,22,24,27,30,33,37,42,44,50,55,60,65,70,75 ,80,88,93,95,100 }, { 1,1,1,1,2,2,3,4,5,6,7,8,9,10,11,12,13,14,15,18,20,22,25,27,31,35,38,42,46,50,55,61,66,71 ,76,83,89,89,89 }, { 1,1,1,1,2,2,3,3,4,5,6,7,8,8,9,10,11,12,13,15,18,19,22,24,26,30,33,38,40,45,50,55,60,66,70,70,70,70,70 }, { 1,1,1,1,1,1,2,2,3,4,5,6,7,7,8,9,10,10,11,13,16,17,19,21,23,25,28,34,36,39,45,50,55,55,55,55,55,55,55 }, { 1,1,1,1,1,1,2,2,3,4,4,5,6,6,7,8,8,9,10,12,14,15,17,18,20,21,25,28,30,34,39,39,39,39,39,39,39,39,39 }, { 1,1,1,1,1,1,2,2,3,4,4,4,5,5,6,7,8,8,9,10,12,13,15,16,18,19,22,25,26,28,30,30,30,30,30,30,30,30,30 }, { 1,1,1,1,1,1,2,2,2,3,3,3,4,4,5,6,7,7,8,9,11,12,13,14,17,17,20,22,24,26,26,26,26,26,26,26,26,26,26 }, { 1,1,1,1,1,1,2,2,2,3,3,3,4,4,5,5,6,7,7,8,10,11,12,13,15,16,18,20,22,23,23,23,23,23,23,23,23,23,23 }, { 1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,5,6,7,7,9,10,11,13,14,15,16,18,19,19,19,19,19,19,19,19,19,19,19 }, { 1,1,1,1,1,1,1,1,1,2,2,2,2,3,4,4,5,6,7,7,8,10,11,13,14,15,15,16,17,17,17,17,17,17,17,17,17,17,17 }, { 1,1,1,1,1,1,1,1,1,2,2,2,2,3,4,4,5,6,7,7,8,9,10,11,13,14,13,14,14,14,14,14,14,14,14,14,14,14,14 }, { 1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,5,5,6,6,7,8,9,10,12,13,12,13,13,13,13,13,13,13,13,13,13,13,13 }, { 1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,5,5,6,6,7,8,8,9,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,5,6,6,7,7,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,5,6,6,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,5,6,6,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }, { 1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }, { 1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }, { 1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 }, { 1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 }, },}, { 21, { 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141 } , { 177,175,173,171,169,169,168,168,167,167,166,165,164,162,160,160,160,160,159,158,157 } , { { 1,1,1,2,5,5,8,11,14,16,19,22,36,40,44,48,52,56,64,76,88,96,145,162, 178,199,226,237,270,297,324,351,378,405,432,475,502,513,540 }, { 1,1,1,2,3,5,8,11,14,16,19,22,36,40,44,48,52,56,60,72,80,88,135,145, 167,189,205,226,248,270,297,329,356,383,410,448,480,480,480 }, { 1,1,1,2,3,5,6,8,11,14,16,19,32,32,36,40,44,48,52,60,72,76,118,129, 140,162,178,205,216,243,270,297,324,356,378,378,378,378,378 }, { 1,1,1,2,3,4,5,6,8,11,14,16,28,28,32,36,40,40,44,52,64,68,102, 113,124,135,151,183,194,210,243,270,297,297,297,297,297,297,297 }, { 1,1,1,2,3,4,5,6,8,11,11,14,24,24,28,32,32,36,40,48,56,60,91, 97,108,113,135,151,162,183,210,210,210,210,210,210,210,210,210 }, { 1,1,1,2,3,4,5,6,8,11,11,11,20,20,24,28,32,32,36,40,48,52,81, 86,97,102,118,135,140,151,162,162,162,162,162,162,162,162,162 }, { 1,1,1,2,3,4,5,6,7,8,8,8,16,16,20,24,28,28,32,36,44,48,70,75, 91,91,108,118,129,140,140,140,140,140,140,140,140,140,140 }, { 1,1,1,2,2,3,4,5,5,8,8,8,16,16,20,20,24,28,28,32,40,44,64,70, 81,86,97,108,118,124,124,124,124,124,124,124,124,124,124 }, { 1,1,1,2,2,3,3,4,4,5,5,6,12,12,16,16,20,24,28,28,36,40,59, 70,75,81,86,97,102,102,102,102,102,102,102,102,102,102,102 }, { 1,1,1,2,2,3,3,4,4,5,5,8,10,12,16,16,20,24,28,28,32,40, 59,70,75,81,81,86,91,91,91,91,91,91,91,91,91,91,91 }, { 1,1,1,2,2,3,3,4,4,5,6,8,10,12,16,16,20,24,28,28,32,36,54, 59,70,75,70,75,75,75,75,75,75,75,75,75,75,75,75 }, { 1,1,2,2,2,2,2,2,2,5,6,8,10,12,16,18,20,22,24,26,28,36,48, 54,60,65,70,70,70,70,70,70,70,70,70,70,70,70,70 }, { 1,1,1,2,2,3,3,4,4,5,6,8,10,12,15,16,18,20,22,24,28,32,43, 48,58,64,64,64,64,64,64,64,64,64,64,64,64,64,64 }, { 1,1,1,2,2,3,3,4,4,4,5,6,8,10,12,16,20,24,26,28,30,32,42, 48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48 }, { 1,1,1,2,2,3,3,4,4,4,5,6,8,10,12,15,18,20,24,28,30,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 }, { 1,1,1,2,2,3,3,4,4,4,5,6,8,10,12,16,18,22,24,28,30,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 }, { 1,1,1,2,2,3,3,4,4,4,5,6,8,10,12,16,18,20,22,24,28,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 }, { 1,1,1,2,2,3,3,4,4,4,5,6,8,10,12,16,18,20,22,24,28,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32 }, { 1,1,1,2,2,3,3,4,5,5,6,8,10,12,14,16,18,20,22,24,28,28,28, 28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28 }, { 1,1,1,2,2,3,3,4,4,5,5,6,8,10,12,16,18,20,22,24,24,24,24, 24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 }, { 1,1,1,2,2,3,3,3,4,5,5,6,8,10,12,15,16,18,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 } },}, }; /* ----------------------------------------------------------- * Solis constants for data ajustment * ----------------------------------------------------------- */ const static struct { double m_infreq; double m_appp_offset; double m_involt193[2]; double m_involt194[2]; double m_incurr[2]; double m_battvolt[2]; double m_outvolt_s[2][2]; double m_outvolt_i[2][2]; double m_outcurr_s[2][2]; double m_outcurr_i[2][2]; double m_utilp_s[2][2]; double m_utilp_i[2][2]; double m_appp_s[2][2]; double m_appp_i[2][2]; } ctab[6] = { { 101620.0, 25.0, { 1.141, 13.0 }, { 1.141, 13.0 }, { 22.2, 800.0 }, { 1.0/7.19, 0.0 }, { { 1.45, 13.0 },{ 1.4, 17.0 } }, { { 1.45, 13.0 },{ 1.4, 17.0 } }, { { 1.0/20.5, 0.15 }, { 1.0/20.5, 0.15 } }, { { 1.0/20.5, 0.15 }, { 1.0/20.5, 0.15 } }, { { 1.0/12.8, 16.0 }, { 1.0/12.3, 15.0 } }, { { 1.0/12.8, 16.0 }, { 1.0/12.3, 15.0 } }, { { 1.0/13.46, 16.7 }, { 1.0/12.9, 19.0 } }, { { 1.0/13.46, 16.7 }, { 1.0/12.9, 19.0 } } }, { 101715.0, 28.0, { 1.141, 12.0 }, { 2.5, -250.0 }, { 22.2, 800.0 }, { 1.0/7.19, 0.6}, { { 1.44, 13.0 }, { 1.4, 18.0 } }, { { 2.9, 13.0 }, { 3.15, 2.0 } }, { { 1.0/21.75, 0.15 }, { 1.0/21.75, 0.15 } }, { { 1.0/49.0, 0.1 }, { 1.0/49.0, 0.1 } }, { { 1.0/12.87, 15.6 }, { 1.0/12.2, 13.0 } }, { { 1.0/14.5, 20.0 }, { 1.0/14.0, 25.0 } }, { { 1.0/13.5, 16.0 }, { 1.0/12.9, 17.0 } }, { { 1.0/15.4, 22.0 }, { 1.0/14.5, 22.0 } } }, { 101620.0, 35.0, { 1.141, 13.0 }, { 2.5, -250.0 }, { 22.2, 800.0 }, { 1.0/7.0, 0.0 }, { { 1.375, 16.0 }, { 1.41, 16.0 } }, { { 2.8, 20.0 }, { 2.9, 18.0 } }, { { 1.0/16.5, 0.1 }, { 1.0/16.8, 0.0 } }, { { 1.0/32.5, 0.0 }, { 1.0/32.5, 0.0 } }, { { 1.0/10.2, 11.0 }, { 1.0/9.4, 15.0 } }, { { 1.0/10.1, 26.0 }, { 1.0/9.4, 30.0 } }, { { 1.0/10.2, 11.0 }, { 1.0/9.4, 15.0 } }, { { 1.0/10.6, 26.0 }, { 1.0/9.8, 30.0 } } }, { 101700.0, 40.0, { 1.141, 13.0 }, { 2.5, -250.0 }, { 35.0, 800.0 }, { 1.0/7.19, 1.1 }, { { 1.45, 11.8 }, { 1.65, 0.0 } }, { { 2.93, 13.0 }, { 3.0, 12.0 } }, { { 1.0/12.2, 0.32 }, { 1.0/12.2, 0.32 } }, { { 1.0/23.2, 0.2 }, { 1.0/23.2, 0.20 } }, { { 1.0/7.0, 16.5 }, { 1.0/6.85, 13.0 } }, { { 1.0/7.15, 30.0 }, { 1.0/6.87, 23.0 } }, { { 1.0/7.45, 28.0 }, { 1.0/7.25, 18.2 } }, { { 1.0/7.55, 37.0 }, { 1.0/7.25, 29.0 } } }, { 101800.0, 56.0, { 1.127, 12.0 }, { 2.5, -250.0 }, { 35.0, 1000.0 }, { 1.0/3.52, 0.0 }, { { 1.41, 13.0 }, { 1.4, 17.0 } }, { { 2.73, 25.0 }, { 2.73, 30.0 } }, { { 1.0/8.15, 0.25 }, { 1.0/8.15, 0.25 } }, { { 1.0/16.0, 0.4 }, { 1.0/15.0, 0.4 } }, { { 1.0/4.87, 19.0 }, { 1.0/4.55, 17.0 } }, { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } }, { { 1.0/5.15, 29.0 }, { 1.0/4.8, 26.0 } }, { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } } }, /*STAY1200_USB double m_infreq; double m_appp_offset; double m_involt193[2]; double m_involt194[2]; double m_incurr[2]; double m_battvolt[2]; double m_outvolt_s[2][2]; double m_outvolt_i[2][2]; double m_outcurr_s[2][2]; double m_outcurr_i[2][2]; double m_utilp_s[2][2]; double m_utilp_i[2][2]; double m_appp_s[2][2]; double m_appp_i[2][2]; */ { 101800.0, //m_infreq 56.0, //m_appp_offset { 1.64, 9.34 },// m_involt193 - ok { 2.5, -250.0 }, //m_involt194 { 35.0, 1000.0 }, //m_incurr { 0.1551, 0.2525 }, //m_battvolt { { 1.41, 13.0 }, { 1.4, 17.0 } }, //m_outvolt_s { { 2.73, 25.0 }, { 2.73, 30.0 } }, //m_outvolt_i { { 1.0/8.15, 0.25 }, { 1.0/8.15, 0.25 } }, //m_outcurr_s { { 1.0/16.0, 0.4 }, { 1.0/15.0, 0.4 } }, //m_outcurr_i { { 1.0/4.87, 19.0 }, { 1.0/4.55, 17.0 } }, //m_utilp_s { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } }, //m_utilp_i { { 1.0/5.15, 29.0 }, { 1.0/4.8, 26.0 } }, //m__app_s { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } } //m_app_i } }; /* Date, time and programming group */ static int const BASE_YEAR = 1998; static int Day, Month, Year; static int isprogram = 0, progshut = 0, prgups = 0; static int dian=0, mesn=0, anon=0, weekn=0; static int dhour, dmin, lhour, lmin, ihour,imin, isec, hourshut, minshut; static unsigned char DaysOnWeek=0, DaysOffWeek=0, DaysStd = 0; static char seman[4]; /* buffers */ static unsigned char RecPack[25]; static unsigned char ConfigPack[12]; /* unsigned char MibData[161]; unsigned char DumpPack[242]; */ /* Identification */ static const char *Model; static int SolisModel, imodel; static int InputValue, Out220; /* protocol */ static int pacsize; /* Status group */ static unsigned char InputStatus,OutputStatus, BattStatus; /* Events group */ static unsigned char SourceEvents, OutputEvents, BattEvents; /* logical */ static bool_t detected = 0; static bool_t SourceFail, SourceLast, FailureFlag, SourceReturn, SuperHeat; static bool_t SuperHeatLast, OverCharge, OverChargeLast, LowBatt; static bool_t CriticBatt, CriticBattLast, Flag_inversor, InversorOn, InversorOnLast; /* Input group */ static double InVoltage, InCurrent, InFreq; static double InDownLim, InUpLim, NomInVolt; /* Output group */ static double OutVoltage, OutCurrent, OutFreq, OutDownLim, OutUpLim, NomOutVolt; /* Battery group */ static int Autonomy, BattExtension, maxauto; static double BattVoltage, Temperature, batcharge; /* Power group */ static double AppPower, UtilPower, upscharge; static int ChargePowerFactor, NominalPower, UpsPowerFactor; static void prnInfo(void); static int IsToday( unsigned char, int ); static void AutonomyCalc( int ); static void ScanReceivePack(void); static void CommReceive(const char*, int ); static void getbaseinfo(void); static void getupdateinfo(void); #endif /* INCLUDED_SOLIS_H */ nut-2.7.4/drivers/mge-shut.h0000644000175000017500000004700312640443572012646 00000000000000/* mge-shut.h - monitor MGE UPS for NUT with SHUT protocol * * Copyright (C) 2002 - 2005 * Arnaud Quette & * Philippe Marzouk * * Sponsored by MGE UPS SYSTEMS * * 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 "hidparser.h" #include "hidtypes.h" #define DEFAULT_TIMEOUT 3000 #define MAX_STRING 64 #define DEFAULT_LOWBATT 30 /* low battery level, in % */ #define DEFAULT_ONDELAY 3 /* delay between return of utility power and powering up of load, in 10 seconds units */ #define DEFAULT_OFFDELAY 20 /* delay befor power off, in seconds */ #define OFF_NOTIFICATION 1 /* notification off */ #define LIGHT_NOTIFICATION 2 /* light notification */ #define COMPLETE_NOTIFICATION 3 /* complete notification for UPSs which do * not support disabling it like some early * Ellipse models */ #define DEFAULT_NOTIFICATION COMPLETE_NOTIFICATION /* HID definitions */ #define HID_REPORT_TYPE_INPUT 0x01 #define HID_REPORT_TYPE_OUTPUT 0x02 #define HID_REPORT_TYPE_FEATURE 0x03 #define REQUEST_TYPE_USB 0x80 #define REQUEST_TYPE_HID 0x81 #define REQUEST_TYPE_GET_REPORT 0xa1 #define REQUEST_TYPE_SET_REPORT 0x21 #define DEVICE_DESCRIPTOR 0x0001 #define CONFIG_DESCRIPTOR 0x0002 #define STRING_DESCRIPTOR 0x0003 #define INTERFACE_DESCRIPTOR 0x0004 #define ENDPOINT_DESCRIPTOR 0x0005 #define HID_DESCRIPTOR 0x0021 #define REPORT_DESCRIPTOR 0x0022 #define MAX_REPORT_SIZE 0x1800 /* SHUT definitions - From Simplified SHUT spec */ #define SHUT_TYPE_REQUEST 0x01 #define SHUT_TYPE_RESPONSE 0x04 #define SHUT_TYPE_NOTIFY 0x05 #define SHUT_OK 0x06 #define SHUT_NOK 0x15 #define SHUT_SYNC 0x16 /* complete notifications - not yet managed but needed for some early Ellipse models */ #define SHUT_SYNC_LIGHT 0x17 /* partial notifications */ #define SHUT_SYNC_OFF 0x18 /* disable notifications - only do polling */ #define SHUT_PKT_LAST 0x80 /* From SHUT specifications */ typedef struct hid_packet { unsigned char bmRequestType; unsigned char bRequest; unsigned short wValue; unsigned short wIndex; unsigned short wLength; /* unsigned char padding[8]; for use with shut_set_report */ } hid_packet_t; typedef union hid_data_u { hid_packet_t hid_pkt; unsigned char raw_pkt[8]; /* max report lengh, was 8 */ } hid_data_t; typedef struct shut_packet { unsigned char bType; unsigned char bLength; hid_data_t data; unsigned char bChecksum; } shut_packet_t; typedef union shut_data_u { shut_packet_t shut_pkt; unsigned char raw_pkt[11]; } shut_data_t; /* From USB/HID specifications */ typedef struct hid_descriptor { unsigned char bLength; unsigned char bDescriptorType; unsigned short bcdHID; unsigned char bCountryCode; unsigned char bNumDescriptors; unsigned char bReportDescriptorType; unsigned short wDescriptorLength; } hid_descriptor_t; typedef union hid_desc_data_u { hid_descriptor_t hid_desc; unsigned char raw_desc[9]; /* max report lengh, aws 9 */ } hid_desc_data_t; typedef struct device_descriptor { unsigned char bLength; unsigned char bDescriptorType; unsigned short bcdUSB; unsigned char bDeviceClass; unsigned char bDeviceSubClass; unsigned char bDeviceProtocol; unsigned char bMaxPacketSize0; unsigned short idVendor; unsigned short idProduct; unsigned short bcdDevice; unsigned char iManufacturer; unsigned char iProduct; unsigned char iSerialNumber; unsigned char bNumConfigurations; } device_descriptor_t; typedef union device_desc_data_u { device_descriptor_t dev_desc; unsigned char raw_desc[18]; } device_desc_data_t; /* --------------------------------------------------------------- */ /* Explicit Booleans */ /* --------------------------------------------------------------- */ #define SHUT_FLAG_OK (1 << 0) /* show element to upsd. */ #define SHUT_FLAG_STATIC (1 << 1) /* retrieve info only once. */ #define SHUT_FLAG_ABSENT (1 << 2) /* data is absent in the device, use default value. */ #define SHUT_FLAG_STALE (1 << 3) /* data stale, don't try too often. */ #define SHUT_FLAG_DELAY (1 << 4) /* delay type value: formated differently. */ /* --------------------------------------------------------------- */ /* Model Name formating entries */ /* --------------------------------------------------------------- */ typedef struct { const char *iProduct; const char *iModel; const char *finalname; } models_name_t; models_name_t models_names [] = { /* Ellipse models */ { "ELLIPSE", "300", "ellipse 300" }, { "ELLIPSE", "500", "ellipse 500" }, { "ELLIPSE", "650", "ellipse 650" }, { "ELLIPSE", "800", "ellipse 800" }, { "ELLIPSE", "1200", "ellipse 1200" }, /* Ellipse Premium models */ { "ellipse", "PR500", "ellipse premium 500" }, { "ellipse", "PR650", "ellipse premium 650" }, { "ellipse", "PR800", "ellipse premium 800" }, { "ellipse", "PR1200", "ellipse premium 1200" }, /* Ellipse "Pro" */ { "ELLIPSE", "600", "Ellipse 600" }, { "ELLIPSE", "750", "Ellipse 750" }, { "ELLIPSE", "1000", "Ellipse 1000" }, { "ELLIPSE", "1500", "Ellipse 1500" }, /* Ellipse "MAX" */ { "Ellipse MAX", "600", "Ellipse MAX 600" }, { "Ellipse MAX", "850", "Ellipse MAX 850" }, { "Ellipse MAX", "1100", "Ellipse MAX 1100" }, { "Ellipse MAX", "1500", "Ellipse MAX 1500" }, /* Protection Center */ { "PROTECTIONCENTER", "420", "Protection Center 420" }, { "PROTECTIONCENTER", "500", "Protection Center 500" }, { "PROTECTIONCENTER", "675", "Protection Center 675" }, /* Pulsar Evolution models */ { "Evolution", "500", "Pulsar Evolution 500" }, { "Evolution", "800", "Pulsar Evolution 800" }, { "Evolution", "1100", "Pulsar Evolution 1100" }, { "Evolution", "1500", "Pulsar Evolution 1500" }, { "Evolution", "2200", "Pulsar Evolution 2200" }, { "Evolution", "3000", "Pulsar Evolution 3000" }, { "Evolution", "3000XL", "Pulsar Evolution 3000 XL" }, /* Newer Evolution models */ { "Evolution", "650", "Evolution 650" }, { "Evolution", "850", "Evolution 850" }, { "Evolution", "1150", "Evolution 1150" }, { "Evolution", "S 1250", "Evolution S 1250" }, { "Evolution", "1550", "Evolution 1550" }, { "Evolution", "S 1750", "Evolution S 1750" }, { "Evolution", "2000", "Evolution 2000" }, { "Evolution", "S 2500", "Evolution S 2500" }, { "Evolution", "S 3000", "Evolution S 3000" }, /* Pulsar M models */ { "PULSAR M", "2200", "Pulsar M 2200" }, { "PULSAR M", "3000", "Pulsar M 3000" }, { "PULSAR M", "3000 XL", "Pulsar M 3000 XL" }, /* Eaton'ified names */ { "EX", "2200", "EX 2200" }, { "EX", "3000", "EX 3000" }, { "EX", "3000 XL", "EX 3000 XL" }, /* Pulsar models */ { "Pulsar", "700", "Pulsar 700" }, { "Pulsar", "1000", "Pulsar 1000" }, { "Pulsar", "1500", "Pulsar 1500" }, { "Pulsar", "1000 RT2U", "Pulsar 1000 RT2U" }, { "Pulsar", "1500 RT2U", "Pulsar 1500 RT2U" }, /* Eaton'ified names */ { "EX", "700", "EX 700" }, { "EX", "1000", "EX 1000" }, { "EX", "1500", "EX 1500" }, { "EX", "1000 RT2U", "EX 1000 RT2U" }, { "EX", "1500 RT2U", "EX 1500 RT2U" }, /* Pulsar MX models */ { "PULSAR", "MX4000", "Pulsar MX 4000 RT" }, { "PULSAR", "MX5000", "Pulsar MX 5000 RT" }, /* NOVA models */ { "NOVA AVR", "600", "NOVA 600 AVR" }, { "NOVA AVR", "625", "Nova AVR 625" }, { "NOVA AVR", "1100", "NOVA 1100 AVR" }, { "NOVA AVR", "1250", "Nova AVR 1250" }, /* EXtreme C (EMEA) */ { "EXtreme", "700C", "Pulsar EXtreme 700C" }, { "EXtreme", "1000C", "Pulsar EXtreme 1000C" }, { "EXtreme", "1500C", "Pulsar EXtreme 1500C" }, { "EXtreme", "1500CCLA", "Pulsar EXtreme 1500C CLA" }, { "EXtreme", "2200C", "Pulsar EXtreme 2200C" }, { "EXtreme", "3200C", "Pulsar EXtreme 3200C" }, /* EXtreme C (USA, aka "EX RT") */ { "EX", "700RT", "Pulsar EX 700 RT" }, { "EX", "1000RT", "Pulsar EX 1000 RT" }, { "EX", "1500RT", "Pulsar EX 1500 RT" }, { "EX", "2200RT", "Pulsar EX 2200 RT" }, { "EX", "3200RT", "Pulsar EX 3200 RT" }, /* Comet EX RT three phased */ { "EX", "5RT31", "EX 5 RT 3:1" }, { "EX", "7RT31", "EX 7 RT 3:1" }, { "EX", "11RT31", "EX 11 RT 3:1" }, /* Comet EX RT single phased */ { "EX", "5RT", "EX 5 RT" }, { "EX", "7RT", "EX 7 RT" }, { "EX", "11RT", "EX 11 RT" }, /* Galaxy 3000 */ { "GALAXY", "3000_10", "Galaxy 3000 10 kVA" }, { "GALAXY", "3000_15", "Galaxy 3000 15 kVA" }, { "GALAXY", "3000_20", "Galaxy 3000 20 kVA" }, { "GALAXY", "3000_30", "Galaxy 3000 30 kVA" }, /* FIXME: To be completed (Comet, Galaxy, Esprit, ...) */ /* end of structure. */ { NULL, NULL, "Generic SHUT model" } }; /* for lookup between HID values and NUT values*/ typedef struct { long hid_value; /* HID value */ const char *nut_value; /* NUT value */ } info_lkp_t; /* Actual value lookup tables => should be fine for all Mfrs (TODO: validate it!) */ /* --------------------------------------------------------------- */ /* Lookup values between NUT and HID */ /* --------------------------------------------------------------- */ info_lkp_t onbatt_info[] = { { 0, "OB" }, { 1, "OL" }, { 0, "NULL" } }; info_lkp_t discharging_info[] = { { 1, "DISCHRG" }, { 0, "NULL" } }; info_lkp_t charging_info[] = { { 1, "CHRG" }, { 0, "NULL" } }; info_lkp_t lowbatt_info[] = { { 1, "LB" }, { 0, "NULL" } }; info_lkp_t overbatt_info[] = { { 1, "OVER" }, { 0, "NULL" } }; info_lkp_t replacebatt_info[] = { { 1, "RB" }, { 0, "NULL" } }; info_lkp_t shutdownimm_info[] = { { 1, "LB" }, { 0, "NULL" } }; info_lkp_t trim_info[] = { { 1, "TRIM" }, { 0, "NULL" } }; info_lkp_t boost_info[] = { { 1, "BOOST" }, { 0, "NULL" } }; /* TODO: add BYPASS, OFF, CAL */ info_lkp_t test_write_info[] = { { 0, "No test" }, { 1, "Quick test" }, { 2, "Deep test" }, { 3, "Abort test" }, { 0, "NULL" } }; info_lkp_t test_read_info[] = { { 1, "Done and passed" }, { 2, "Done and warning" }, { 3, "Done and error" }, { 4, "Aborted" }, { 5, "In progress" }, { 6, "No test initiated" }, { 0, "NULL" } }; /* --------------------------------------------------------------- */ /* Query Commands and their Mapping to INFO_ Variables */ /* --------------------------------------------------------------- */ /* Structure defining how to query UPS for a variable and write information to INFO structure. */ typedef struct { const char *type; /* INFO_* element */ int flags; /* INFO-element flags to set in addinfo */ int length; /* INFO-element length of strings */ const char *item_path; /* HID object (fully qualified string path) */ const char *fmt; /* printf format string for INFO entry */ const char *dfl; /* default value */ unsigned long shut_flags; /* specific SHUT flags */ info_lkp_t *hid2info; /* lookup table between HID and NUT values */ } mge_info_item_t; /* Array containing information to translate between UTalk and NUT info * NOTE: * - Array is terminated by element with type NULL. * - Essential INFO items (ups.{mfr, model, firmware, status} are * handled separately. * - Array is NOT const, since "shut_flags" can be changed. */ /* FIXME: should be shared with mgehid.h */ static mge_info_item_t mge_info[] = { /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", "%i", NULL, SHUT_FLAG_OK, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimitSetting", "%ld", NULL, SHUT_FLAG_OK, NULL }, /* RW, to be caught first if exists... */ { "battery.charge.low", ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimit", "%ld", NULL, SHUT_FLAG_OK, NULL }, /* ... or Read only */ { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", "%.0d", NULL, SHUT_FLAG_OK, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, 20, NULL, "%s", "MGE UPS SYSTEMS", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", "%i", NULL, SHUT_FLAG_OK, NULL }, { "ups.timer.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeShutdown", "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY, NULL }, { "ups.timer.reboot", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeReboot", "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY, NULL }, { "ups.timer.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeStartup", "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY, NULL }, /* FIXME: miss ups.power */ { "ups.power.nominal", ST_FLAG_STRING, 5, "UPS.Flow.[4].ConfigApparentPower", "%i", NULL, SHUT_FLAG_OK, NULL }, { "ups.test.interval", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.TestPeriod", "%i", NULL, SHUT_FLAG_OK, NULL }, { "ups.test.result", ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.Test", "%i", NULL, SHUT_FLAG_OK, &test_read_info[0] }, /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", "%i", NULL, SHUT_FLAG_OK, NULL }, { "output.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", "%i", NULL, SHUT_FLAG_OK, NULL }, { "output.current", 0, 0, "UPS.PowerSummary.Output.Current", "%i", NULL, SHUT_FLAG_OK, NULL }, { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", "%i", NULL, SHUT_FLAG_OK, NULL }, /* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */ /* TODO: add an iterative semantic [%x] to factorise outlets */ { "outlet.id", 0, 0, "UPS.OutletSystem.Outlet.[1].OutletID", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[1].OutletID", "%s", "Main Outlet", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, { "outlet.switchable", 0, 0, "UPS.OutletSystem.Outlet.[1].PresentStatus.Switchable", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.[2].OutletID", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.1.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[2].OutletID", "%s", "PowerShare Outlet 1", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, { "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.Switchable", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.1.switch", ST_FLAG_RW | ST_FLAG_STRING, 2, "UPS.OutletSystem.Outlet.[2].PresentStatus.SwitchOn/Off", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.1.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[2].RemainingCapacityLimit", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.1.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].DelayBeforeShutdown", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.1.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].DelayBeforeStartup", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet.[3].OutletID", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.2.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[3].OutletID", "%s", "PowerShare Outlet 2", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, { "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.2.switch", ST_FLAG_RW | ST_FLAG_STRING, 2, "UPS.OutletSystem.Outlet.[3].PresentStatus.SwitchOn/Off", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.2.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].RemainingCapacityLimit", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.2.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].DelayBeforeShutdown", "%i", NULL, SHUT_FLAG_OK, NULL }, { "outlet.2.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].DelayBeforeStartup", "%i", NULL, SHUT_FLAG_OK, NULL }, /* Input page */ { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", "%i", NULL, SHUT_FLAG_OK, NULL }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", "%i", NULL, SHUT_FLAG_OK, NULL }, /* terminating element */ { NULL, 0, 0, "\0", "\0", NULL, 0, NULL } }; /* temporary usage code lookup */ typedef struct { const char *usage_name; const uint32_t usage_code; } usage_lkp_t; /* FIXME: share this data structure with libhid.c */ static usage_lkp_t usage_lkp[] = { /* Power Device Page */ { "PresentStatus", 0x00840002 }, { "UPS", 0x00840004 }, { "BatterySystem", 0x00840010 }, { "Battery", 0x00840012 }, { "BatteryID", 0x00840013 }, { "PowerConverter", 0x00840016 }, { "OutletSystem", 0x00840018 }, { "Input", 0x0084001a }, { "Output", 0x0084001c }, { "Outlet", 0x00840020 }, { "OutletID", 0x00840021 }, { "PowerSummary", 0x00840024 }, { "Voltage", 0x00840030 }, { "Current", 0x00840031 }, { "Frequency", 0x00840032 }, { "PercentLoad", 0x00840035 }, { "ConfigVoltage", 0x00840040 }, { "ConfigCurrent", 0x00840041 }, { "ConfigFrequency", 0x00840042 }, { "ConfigApparentPower", 0x00840043 }, { "LowVoltageTransfer", 0x00840053 }, { "HighVoltageTransfer", 0x00840054 }, { "DelayBeforeReboot", 0x00840055 }, { "DelayBeforeStartup", 0x00840056 }, { "DelayBeforeShutdown", 0x00840057 }, { "Test", 0x00840058 }, { "Good", 0x00840061 }, { "Overload", 0x00840065 }, /* sic */ { "SwitchOn/Off", 0x0084006b }, { "Switchable", 0x0084006c }, { "Used", 0x0084006d }, { "Flow", 0x0084001e }, /* Battery System Page */ { "RemainingCapacityLimit", 0x00850029 }, { "BelowRemainingCapacityLimit", 0x00850042 }, { "RemainingCapacity", 0x00850066 }, { "RunTimeToEmpty", 0x00850068 }, { "ACPresent", 0x008500d0 }, { "Charging", 0x00850044 }, { "Discharging", 0x00850045 }, { "NeedReplacement", 0x0085004b }, /* MGE UPS SYSTEMS Page */ { "iModel", 0xffff00f0 }, { "RemainingCapacityLimitSetting", 0xffff004d }, { "TestPeriod", 0xffff0001 }, { "\0", 0x0 } }; /* SHUT / HID functions Prototypes */ int shut_ups_start(void); u_char shut_checksum(const u_char *buf, int bufsize); int shut_token_send(u_char token); int shut_packet_send (hid_data_t *hdata, int datalen, u_char token); int shut_packet_recv (u_char *Buf, int datalen); int shut_get_descriptor(int desctype, u_char *pkt, int reportlen); int shut_get_string(int strindex, char *string, int stringlen); int shut_get_report(int id, u_char *pkt, int reportlen); int shut_set_report(int id, u_char *pkt, int reportlen); int shut_identify_ups (void); int shut_wait_ack (void); void shut_ups_status(void); int hid_init_device(void); const char *get_model_name(char *iProduct, char *iModel); int hid_lookup_usage(char *name); int hid_get_value(const char *item_path); int hid_set_value(const char *varname, const char *val); u_short lookup_path(const char *HIDpath, HIDData_t *data); int instcmd(const char *cmdname, const char *extra); void setline(int set); int serial_read (int read_timeout, u_char *readbuf); int serial_send(u_char *buf, int len); void make_string(u_char *buf, int datalen, char *string); mge_info_item_t *shut_find_info(const char *varname); nut-2.7.4/drivers/bcmxcp.h0000644000175000017500000011560012667537407012402 00000000000000/* * bcmxcp.h -- header for BCM/XCP module */ #ifndef _POWERWARE_H #define _POWERWARE_H #include "timehead.h" /* Have to wait at least 0,25 sec max 16 sec */ /* 1 second is too short for PW9120 (leads to communication errors). So we set it to 2 seconds */ #define PW_SLEEP 2 #define PW_MAX_TRY 3 /* How many times we try to send data. */ #define PW_COMMAND_START_BYTE (unsigned char)0xAB #define PW_LAST_SEQ (unsigned char)0x80 /* bit flag to indicate final sequence */ #define PW_SEQ_MASK (unsigned char)0x7F /* bit mask to extract just the sequence # */ #define PW_HEADER_LENGTH 4 /* Size of response header */ #define PW_ANSWER_MAX_SIZE 256 /* No Autorisation required */ #define PW_ID_BLOCK_REQ (unsigned char)0x31 /* Model name, ... length 1 */ #define PW_EVENT_HISTORY_LOG_REQ (unsigned char)0x32 /* List alarms that have occured. length 1 */ #define PW_STATUS_REQ (unsigned char)0x33 /* On Line, On Bypass, ... length 1-2 */ #define PW_METER_BLOCK_REQ (unsigned char)0x34 /* Current UPS status (Load, utility,...) length 1 */ #define PW_CUR_ALARM_REQ (unsigned char)0x35 /* Current alarm and event request. length 1 */ #define PW_CONFIG_BLOCK_REQ (unsigned char)0x36 /* Model serial#, ... length 1 */ #define PW_UTILITY_STATISTICS_BLOCK_REQ (unsigned char)0x38 /* List utility power quality. length 1 */ #define PW_WAVEFORM_BLOCK_REQ (unsigned char)0x3A /* Sampled waveform data. length 7 */ #define PW_BATTERY_REQ (unsigned char)0x3B /* Charging, floating, ... length 1 */ #define PW_LIMIT_BLOCK_REQ (unsigned char)0x3C /* Configuration (Bypass thresholds,...). length 1 */ #define PW_TEST_RESULT_REQ (unsigned char)0x3F /* Get the results for a system test. length 1 */ #define PW_COMMAND_LIST_REQ (unsigned char)0x40 /* Available commands. length 1 */ #define PW_OUT_MON_BLOCK_REQ (unsigned char)0x41 /* Outlet monitor request length 1 */ #define PW_COM_CAP_REQ (unsigned char)0x42 /* Request communication capabilities. length 2 */ #define PW_UPS_TOP_DATA_REQ (unsigned char)0x43 /* Request ups topology data requset. length 1 */ #define PW_COM_PORT_LIST_BLOCK_REQ (unsigned char)0x44 /* Request communication port list. length 1 */ #define PW_REQUEST_SCRATCHPAD_DATA_REQ (unsigned char)0x45 /* Request data from scratchpad. length 2*/ /* Need autorisation before these commands */ #define PW_GO_TO_BYPASS (unsigned char)0x88 /* Transfer load from inverter to bypass. length 1 or 3 */ #define PW_UPS_ON (unsigned char)0x89 /* UPS on command. length 1-2 */ #define PW_LOAD_OFF_RESTART (unsigned char)0x8A /* Delayed LoadPowerOff & Restart command. length 2-4 */ #define PW_UPS_OFF (unsigned char)0x8B /* UPS off command. length 1-2 */ #define PW_DECREMENT_OUTPUT_VOLTAGE (unsigned char)0x8C /* Decrease output voltage. length 1 */ #define PW_INCREMENT_OUTPUT_VOLTAGE (unsigned char)0x8D /* Increase output voltage. length 1 */ #define PW_SET_TIME_AND_DATE (unsigned char)0x90 /* Set the real-time clock inside UPS. length 8 */ #define PW_UPS_ON_TIME (unsigned char)0x91 /* Scheduled UPS on in n minutes. length 3-4 */ #define PW_UPS_ON_AT_TIME (unsigned char)0x92 /* Schedule UPS on at specified date and time. length 7-8 */ #define PW_UPS_OFF_TIME (unsigned char)0x93 /* Scheduled UPS off in n minutes. length 3-4 */ #define PW_UPS_OFF_AT_TIME (unsigned char)0x94 /* Schedule UPS off at specified date and time. length 7-8 */ #define PW_SET_CONF_COMMAND (unsigned char)0x95 /* Set configuration command. length 4 */ #define PW_SET_OUTLET_COMMAND (unsigned char)0x97 /* Set outlet parameter command length 5. not in 5115 */ #define PW_SET_COM_COMMAND (unsigned char)0x98 /* Set communication parameter command. length 5 */ #define PW_SET_SCRATHPAD_SECTOR (unsigned char)0x99 /* Write data to scratchpad. length 3 or 18 */ #define PW_SET_POWER_STRATEGY (unsigned char)0x9A /* Set the power strategy. length 2 */ #define PW_SET_REQ_ONLY_MODE (unsigned char)0xA0 /* Set request only mode command. length 1 */ #define PW_SET_UNREQUESTED_MODE (unsigned char)0xA1 /* Set unrequested mode command. length 1 */ #define PW_INIT_BAT_TEST (unsigned char)0xB1 /* Initiate battery test command. length 3 */ #define PW_INIT_SYS_TEST (unsigned char)0xB2 /* Initiate general system test command. length 2 */ #define PW_SELECT_SUBMODULE (unsigned char)0xCE /* Select a sub module. length 2-7 */ #define PW_AUTHORIZATION_CODE (unsigned char)0xCF /* Authorization code. length 4 or 7 */ /* Define the XCP system test */ #define PW_SYS_TEST_GENERAL (unsigned char)0x01 /* Initiate General system Test */ #define PW_SYS_TEST_SCHEDULE_BATTERY_COMMISSION (unsigned char)0x02 /* Schedule Battery Commissioning Test */ #define PW_SYS_TEST_ALTERNATE_AC_INPUT (unsigned char)0x03 /* Test Alternate AC Input */ #define PW_SYS_TEST_FLASH_LIGHTS (unsigned char)0x04 /* Flash the Lights Test */ #define PW_SYS_TEST_REPORT_CAPABILITIES (unsigned char)0xFF /* Report Systems Test Capabilities */ /* Outlet operations */ #define PW_ALL_OUTLETS 0 #define PW_AUTO_OFF_DELAY 1 #define PW_AUTO_ON_DELAY 2 /* 0 means Abort countdown */ #define PW_TURN_OFF_DELAY 3 #define PW_TURN_ON_DELAY 4 /* Config vars*/ #define PW_CONF_BYPASS_FREQ_DEV_LIMIT 0x01 #define PW_CONF_LOW_DEV_LIMIT 0x02 #define PW_CONF_HIGH_DEV_LIMIT 0x03 #define PW_CONF_PHASE_DEV_LIMIT 0x04 #define PW_CONF_LOW_BATT 0x05 #define PW_CONF_BEEPER 0x06 #define PW_CONF_RETURN_DELAY 0x07 #define PW_CONF_RETURN_CAP 0x08 #define PW_CONF_MAX_TEMP 0x0a #define PW_CONF_NOMINAL_OUT_VOLTAGE 0x0b #define PW_CONF_SLEEP_TH_LOAD 0x0d #define PW_CONF_SLEEP_DELAY 0x0e #define PW_CONF_BATT_STRINGS 0x0f #define PW_CONF_REQ 0xff /* Config block offsets */ #define BCMXCP_CONFIG_BLOCK_MACHINE_TYPE_CODE 0 #define BCMXCP_CONFIG_BLOCK_MODEL_NUMBER 2 #define BCMXCP_CONFIG_BLOCK_MODEL_CONF_WORD 4 #define BCMXCP_CONFIG_BLOCK_INPUT_FREQ_DEV_LIMIT 6 #define BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE 8 #define BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ 10 #define BCMXCP_CONFIG_BLOCK_OUTPUT_PHASE_ANGLE 12 #define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD1 14 #define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_BYTE3 16 /* KEEP THIS UNTILL PARSING OK. USE THIS BYTE. */ #define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD2 16 #define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD3 18 #define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD4 20 #define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD1 22 /* Undefined at this time.*/ #define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD2 24 /* Per cell inverter shutdown voltage at full rated load. (volt/cell)* 100 */ #define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3 26 /* LOW BYTE Number of battery strings. HIGH BYTE undefined at this time.*/ #define BCMXCP_CONFIG_BLOCK_EXTENDED_BLOCK_LENGTH 47 #define BCMXCP_CONFIG_BLOCK_PART_NUMBER 48 #define BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER 64 /*Battery block offsets*/ #define BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS 0 #define BCMXCP_BATTDATA_BLOCK_BATT_VOLTS_T1 1 #define BCMXCP_BATTDATA_BLOCK_BATT_VOLTS_T2 5 #define BCMXCP_BATTDATA_BLOCK_TEST_DURATION 9 #define BCMXCP_BATTDATA_BLOCK_UTIL_VOLT 10 #define BCMXCP_BATTDATA_BLOCK_INPUT_CURRENT 14 #define BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS 18 /*BATT_TEST_STATUS for external strings (1 byte each) if BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS == 0 no external test statuses at all*/ /*next - number of ABM Statuses - at least 1 for internal batteries*/ /* Index for Extende Limits block offsets */ #define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE 0 #define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ 2 #define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_TRUE_POWER 4 #define BCMXCP_EXT_LIMITS_BLOCK_COMM_SPEC_VERSION 6 #define BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT 8 #define BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT 10 #define BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT 12 #define BCMXCP_EXT_LIMITS_BLOCK_PHASE_DEV_LIMIT 14 #define BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING 16 #define BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS 17 #define BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE 18 #define BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE 20 #define BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY 22 #define BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN 24 #define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW 25 #define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE 26 #define BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD 29 #define BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY 30 /* Indexes for meter map */ #define BCMXCP_METER_MAP_OUTPUT_VOLTS_AB 0 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VOLTS_BC 1 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VOLTS_CA 2 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VOLTS_AB 3 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VOLTS_BC 4 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VOLTS_CA 5 /* mapped */ #define BCMXCP_METER_MAP_INVERTER_VOLTS_AB 6 #define BCMXCP_METER_MAP_INVERTER_VOLTS_BC 7 #define BCMXCP_METER_MAP_INVERTER_VOLTS_CA 8 #define BCMXCP_METER_MAP_BYPASS_VOLTS_AB 9 #define BCMXCP_METER_MAP_BYPASS_VOLTS_BC 10 #define BCMXCP_METER_MAP_BYPASS_VOLTS_CA 11 #define BCMXCP_METER_MAP_MAIN_LOGIC_POWER 12 #define BCMXCP_METER_MAP_SECONDARY_V_PLUS_POWER 13 #define BCMXCP_METER_MAP_SECONDARY_V_MINUS_POWER 14 #define BCMXCP_METER_MAP_INVERTER_AVG_CURRENT_PHASE_A 15 #define BCMXCP_METER_MAP_INVERTER_AVG_CURRENT_PHASE_B 16 #define BCMXCP_METER_MAP_INVERTER_AVG_CURRENT_PHASE_C 17 #define BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A 18 /* mapped */ #define BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_B 19 /* mapped */ #define BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_C 20 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_WATTS 21 #define BCMXCP_METER_MAP_INPUT_WATTS 22 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VA 23 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VA 24 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_POWER_FACTOR 25 /* mapped */ #define BCMXCP_METER_MAP_INPUT_POWER_FACTOR 26 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_FREQUENCY 27 /* mapped */ #define BCMXCP_METER_MAP_INPUT_FREQUENCY 28 /* mapped */ #define BCMXCP_METER_MAP_INVERTER_FREQUENCY 29 #define BCMXCP_METER_MAP_BYPASS_FREQUENCY 30 /* mapped */ #define BCMXCP_METER_MAP_DC_LINK_VOLTS_DC 31 #define BCMXCP_METER_MAP_BATTERY_CURRENT 32 /* mapped */ #define BCMXCP_METER_MAP_BATTERY_VOLTAGE 33 /* mapped */ #define BCMXCP_METER_MAP_PERCENT_BATTERY_LEFT 34 /* mapped */ #define BCMXCP_METER_MAP_BATTERY_TIME_REMAINING 35 /* mapped */ #define BCMXCP_METER_MAP_BATTERY_CHARGE_TIME 36 #define BCMXCP_METER_MAP_PEAK_INVERTER_CURRENT_PHASE_A 37 #define BCMXCP_METER_MAP_PEAK_INVERTER_CURRENT_PHASE_B 38 #define BCMXCP_METER_MAP_PEAK_INVERTER_CURRENT_PHASE_C 39 #define BCMXCP_METER_MAP_AVG_INPUT_CURRENT_3_PHASE_SUM 40 #define BCMXCP_METER_MAP_BATTERY_DCUV_BAR_CHART 41 /* mapped */ #define BCMXCP_METER_MAP_INPUT_CURRENT_BAR_CHART 42 #define BCMXCP_METER_MAP_LOW_BATTERY_WARNING_V_BAR_CHART 43 /* mapped */ #define BCMXCP_METER_MAP_DC_VOLTS_BAR_CHART 44 #define BCMXCP_METER_MAP_BATTERY_CHARGING_CURRENT_BAR_CHART 45 #define BCMXCP_METER_MAP_BATTERY_DISCHARGING_CURRENT_BAR_CHART 46 /* mapped */ #define BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A 47 /* mapped */ #define BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_B 48 /* mapped */ #define BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_C 49 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VA_PHASE_A 50 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VA_PHASE_B 51 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VA_PHASE_C 52 /* mapped */ #define BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A 53 /* mapped */ #define BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_B 54 /* mapped */ #define BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_C 55 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A 56 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_B 57 /* mapped */ #define BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_C 58 /* mapped */ #define BCMXCP_METER_MAP_INVERTER_VOLTS_PHASE_A 59 #define BCMXCP_METER_MAP_INVERTER_VOLTS_PHASE_B 60 #define BCMXCP_METER_MAP_INVERTER_VOLTS_PHASE_C 61 #define BCMXCP_METER_MAP_AMBIENT_TEMPERATURE 62 /* mapped */ #define BCMXCP_METER_MAP_HEATSINK_TEMPERATURE 63 /* mapped */ #define BCMXCP_METER_MAP_POWER_SUPPLY_TEMPERATURE 64 /* mapped */ #define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A 65 /* mapped */ #define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B 66 /* mapped */ #define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C 67 /* mapped */ #define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART 68 /* mapped */ #define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B_BAR_CHART 69 /* mapped */ #define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C_BAR_CHART 70 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART 71 /* mapped */ #define BCMXCP_METER_MAP_DATE 72 /* mapped */ #define BCMXCP_METER_MAP_TIME 73 /* mapped */ #define BCMXCP_METER_MAP_POSITIVE_DC_LINK_RAIL_VOLTAGE 74 #define BCMXCP_METER_MAP_NEGATIVE_DC_LINK_RAIL_VOLTAGE 75 #define BCMXCP_METER_MAP_AUTO_BALANCE_VOLTAGE_DC 76 #define BCMXCP_METER_MAP_BATTERY_TEMPERATURE 77 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VOLTS_A 78 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VOLTS_B 79 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_VOLTS_C 80 /* mapped */ #define BCMXCP_METER_MAP_NEUTRAL_CURRENT 81 #define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A 82 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_B 83 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_C 84 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A_B_C_BAR_CHART 85 /* mapped */ #define BCMXCP_METER_MAP_RECTIFIER_DC_CURRENT 86 #define BCMXCP_METER_MAP_POSITIVE_BATTERY_VOLTAGE 87 #define BCMXCP_METER_MAP_NEGATIVE_BATTERY_VOLTAGE 88 #define BCMXCP_METER_MAP_POSITIVE_BATTERY_CURRENT 89 #define BCMXCP_METER_MAP_NEGATIVE_BATTERY_CURRENT 90 #define BCMXCP_METER_MAP_LINE_EVENT_COUNTER 91 /* mapped */ #define BCMXCP_METER_MAP_OUTPUT_V1_PERCENT 92 #define BCMXCP_METER_MAP_OUTPUT_V2_PERCENT 93 #define BCMXCP_METER_MAP_OUTPUT_V3_PERCENT 94 #define BCMXCP_METER_MAP_OUTPUT_I1_PERCENT 95 #define BCMXCP_METER_MAP_OUTPUT_I2_PERCENT 96 #define BCMXCP_METER_MAP_OUTPUT_I3_PERCENT 97 #define BCMXCP_METER_MAP_INPUT_V1_PERCENT 98 #define BCMXCP_METER_MAP_INPUT_V2_PERCENT 99 #define BCMXCP_METER_MAP_INPUT_V3_PERCENT 100 #define BCMXCP_METER_MAP_INPUT_I1_PERCENT 101 #define BCMXCP_METER_MAP_INPUT_I2_PERCENT 102 #define BCMXCP_METER_MAP_INPUT_I3_PERCENT 103 #define BCMXCP_METER_MAP_GROUND_CURRENT 104 #define BCMXCP_METER_MAP_OUTPUT_CREST_FACTOR_L1 105 #define BCMXCP_METER_MAP_OUTPUT_CREST_FACTOR_L2 106 #define BCMXCP_METER_MAP_OUTPUT_CREST_FACTOR_L3 107 #define BCMXCP_METER_MAP_OUTPUT_KW_HOUR 108 #define BCMXCP_METER_MAP_INPUT_VOLTAGE_THD_LINE1 109 #define BCMXCP_METER_MAP_INPUT_VOLTAGE_THD_LINE2 110 #define BCMXCP_METER_MAP_INPUT_VOLTAGE_THD_LINE3 111 #define BCMXCP_METER_MAP_INPUT_CURRENT_THD_LINE1 112 #define BCMXCP_METER_MAP_INPUT_CURRENT_THD_LINE2 113 #define BCMXCP_METER_MAP_INPUT_CURRENT_THD_LINE3 114 #define BCMXCP_METER_MAP_OUTPUT_VOLTAGE_THD_LINE1 115 #define BCMXCP_METER_MAP_OUTPUT_VOLTAGE_THD_LINE2 116 #define BCMXCP_METER_MAP_OUTPUT_VOLTAGE_THD_LINE3 117 #define BCMXCP_METER_MAP_OUTPUT_CURRENT_THD_LINE1 118 #define BCMXCP_METER_MAP_OUTPUT_CURRENT_THD_LINE2 119 #define BCMXCP_METER_MAP_OUTPUT_CURRENT_THD_LINE3 120 #define BCMXCP_METER_MAP_INPUT_CREST_FACTOR_L1 121 #define BCMXCP_METER_MAP_INPUT_CREST_FACTOR_L2 122 #define BCMXCP_METER_MAP_INPUT_CREST_FACTOR_L3 123 #define BCMXCP_METER_MAP_INPUT_KW_HOUR 124 #define BCMXCP_METER_MAP_BATTERY_LIFE_REMAINING 125 #define BCMXCP_METER_MAP_SECONDARY_NEUTRAL_CURRENT 126 #define BCMXCP_METER_MAP_SECONDARY_GROUND_CURRENT 127 #define BCMXCP_METER_MAP_HOURS_OF_OPERATION 128 /* Indexes for alarm map */ #define BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE 0 #define BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE 1 #define BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ 2 #define BCMXCP_ALARM_BYPASS_AC_OVER_VOLTAGE 3 #define BCMXCP_ALARM_BYPASS_AC_UNDER_VOLTAGE 4 #define BCMXCP_ALARM_BYPASS_OVER_OR_UNDER_FREQ 5 #define BCMXCP_ALARM_INPUT_AC_OVER_VOLTAGE 6 #define BCMXCP_ALARM_INPUT_AC_UNDER_VOLTAGE 7 #define BCMXCP_ALARM_INPUT_UNDER_OR_OVER_FREQ 8 #define BCMXCP_ALARM_OUTPUT_OVER_VOLTAGE 9 #define BCMXCP_ALARM_OUTPUT_UNDER_VOLTAGE 10 #define BCMXCP_ALARM_OUTPUT_UNDER_OR_OVER_FREQ 11 #define BCMXCP_ALARM_REMOTE_EMERGENCY_PWR_OFF 12 #define BCMXCP_ALARM_REMOTE_GO_TO_BYPASS 13 #define BCMXCP_ALARM_BUILDING_ALARM_6 14 #define BCMXCP_ALARM_BUILDING_ALARM_5 15 #define BCMXCP_ALARM_BUILDING_ALARM_4 16 #define BCMXCP_ALARM_BUILDING_ALARM_3 17 #define BCMXCP_ALARM_BUILDING_ALARM_2 18 #define BCMXCP_ALARM_BUILDING_ALARM_1 19 #define BCMXCP_ALARM_STATIC_SWITCH_OVER_TEMP 20 #define BCMXCP_ALARM_CHARGER_OVER_TEMP 21 #define BCMXCP_ALARM_CHARGER_LOGIC_PWR_FAIL 22 #define BCMXCP_ALARM_CHARGER_OVER_VOLTAGE_OR_CURRENT 23 #define BCMXCP_ALARM_INVERTER_OVER_TEMP 24 #define BCMXCP_ALARM_OUTPUT_OVERLOAD 25 #define BCMXCP_ALARM_RECTIFIER_INPUT_OVER_CURRENT 26 #define BCMXCP_ALARM_INVERTER_OUTPUT_OVER_CURRENT 27 #define BCMXCP_ALARM_DC_LINK_OVER_VOLTAGE 28 #define BCMXCP_ALARM_DC_LINK_UNDER_VOLTAGE 29 #define BCMXCP_ALARM_RECTIFIER_FAILED 30 #define BCMXCP_ALARM_INVERTER_FAULT 31 #define BCMXCP_ALARM_BATTERY_CONNECTOR_FAIL 32 #define BCMXCP_ALARM_BYPASS_BREAKER_FAIL 33 #define BCMXCP_ALARM_CHARGER_FAIL 34 #define BCMXCP_ALARM_RAMP_UP_FAILED 35 #define BCMXCP_ALARM_STATIC_SWITCH_FAILED 36 #define BCMXCP_ALARM_ANALOG_AD_REF_FAIL 37 #define BCMXCP_ALARM_BYPASS_UNCALIBRATED 38 #define BCMXCP_ALARM_RECTIFIER_UNCALIBRATED 39 #define BCMXCP_ALARM_OUTPUT_UNCALIBRATED 40 #define BCMXCP_ALARM_INVERTER_UNCALIBRATED 41 #define BCMXCP_ALARM_DC_VOLT_UNCALIBRATED 42 #define BCMXCP_ALARM_OUTPUT_CURRENT_UNCALIBRATED 43 #define BCMXCP_ALARM_RECTIFIER_CURRENT_UNCALIBRATED 44 #define BCMXCP_ALARM_BATTERY_CURRENT_UNCALIBRATED 45 #define BCMXCP_ALARM_INVERTER_ON_OFF_STAT_FAIL 46 #define BCMXCP_ALARM_BATTERY_CURRENT_LIMIT 47 #define BCMXCP_ALARM_INVERTER_STARTUP_FAIL 48 #define BCMXCP_ALARM_ANALOG_BOARD_AD_STAT_FAIL 49 #define BCMXCP_ALARM_OUTPUT_CURRENT_OVER_100 50 #define BCMXCP_ALARM_BATTERY_GROUND_FAULT 51 #define BCMXCP_ALARM_WAITING_FOR_CHARGER_SYNC 52 #define BCMXCP_ALARM_NV_RAM_FAIL 53 #define BCMXCP_ALARM_ANALOG_BOARD_AD_TIMEOUT 54 #define BCMXCP_ALARM_SHUTDOWN_IMMINENT 55 #define BCMXCP_ALARM_BATTERY_LOW 56 #define BCMXCP_ALARM_UTILITY_FAIL 57 #define BCMXCP_ALARM_OUTPUT_SHORT_CIRCUIT 58 #define BCMXCP_ALARM_UTILITY_NOT_PRESENT 59 #define BCMXCP_ALARM_FULL_TIME_CHARGING 60 #define BCMXCP_ALARM_FAST_BYPASS_COMMAND 61 #define BCMXCP_ALARM_AD_ERROR 62 #define BCMXCP_ALARM_INTERNAL_COM_FAIL 63 #define BCMXCP_ALARM_RECTIFIER_SELFTEST_FAIL 64 #define BCMXCP_ALARM_RECTIFIER_EEPROM_FAIL 65 #define BCMXCP_ALARM_RECTIFIER_EPROM_FAIL 66 #define BCMXCP_ALARM_INPUT_LINE_VOLTAGE_LOSS 67 #define BCMXCP_ALARM_BATTERY_DC_OVER_VOLTAGE 68 #define BCMXCP_ALARM_POWER_SUPPLY_OVER_TEMP 69 #define BCMXCP_ALARM_POWER_SUPPLY_FAIL 70 #define BCMXCP_ALARM_POWER_SUPPLY_5V_FAIL 71 #define BCMXCP_ALARM_POWER_SUPPLY_12V_FAIL 72 #define BCMXCP_ALARM_HEATSINK_OVER_TEMP 73 #define BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL 74 #define BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125 75 #define BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL 76 #define BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL 77 #define BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR 78 #define BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL 79 #define BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL 80 #define BCMXCP_ALARM_INVERTER_RAM_SELFTEST_FAIL 81 #define BCMXCP_ALARM_NV_MEMORY_CHECKSUM_FAIL 82 #define BCMXCP_ALARM_PROGRAM_CHECKSUM_FAIL 83 #define BCMXCP_ALARM_INVERTER_CPU_SELFTEST_FAIL 84 #define BCMXCP_ALARM_NETWORK_NOT_RESPONDING 85 #define BCMXCP_ALARM_FRONT_PANEL_SELFTEST_FAIL 86 #define BCMXCP_ALARM_NODE_EEPROM_VERIFICATION_ERROR 87 #define BCMXCP_ALARM_OUTPUT_AC_OVER_VOLT_TEST_FAIL 88 #define BCMXCP_ALARM_OUTPUT_DC_OVER_VOLTAGE 89 #define BCMXCP_ALARM_INPUT_PHASE_ROTATION_ERROR 90 #define BCMXCP_ALARM_INVERTER_RAMP_UP_TEST_FAILED 91 #define BCMXCP_ALARM_INVERTER_OFF_COMMAND 92 #define BCMXCP_ALARM_INVERTER_ON_COMMAND 93 #define BCMXCP_ALARM_TO_BYPASS_COMMAND 94 #define BCMXCP_ALARM_FROM_BYPASS_COMMAND 95 #define BCMXCP_ALARM_AUTO_MODE_COMMAND 96 #define BCMXCP_ALARM_EMERGENCY_SHUTDOWN_COMMAND 97 #define BCMXCP_ALARM_SETUP_SWITCH_OPEN 98 #define BCMXCP_ALARM_INVERTER_OVER_VOLT_INT 99 #define BCMXCP_ALARM_INVERTER_UNDER_VOLT_INT 100 #define BCMXCP_ALARM_ABSOLUTE_DCOV_ACOV 101 #define BCMXCP_ALARM_PHASE_A_CURRENT_LIMIT 102 #define BCMXCP_ALARM_PHASE_B_CURRENT_LIMIT 103 #define BCMXCP_ALARM_PHASE_C_CURRENT_LIMIT 104 #define BCMXCP_ALARM_BYPASS_NOT_AVAILABLE 105 #define BCMXCP_ALARM_RECTIFIER_BREAKER_OPEN 106 #define BCMXCP_ALARM_BATTERY_CONTACTOR_OPEN 107 #define BCMXCP_ALARM_INVERTER_CONTACTOR_OPEN 108 #define BCMXCP_ALARM_BYPASS_BREAKER_OPEN 109 #define BCMXCP_ALARM_INV_BOARD_ACOV_INT_TEST_FAIL 110 #define BCMXCP_ALARM_INVERTER_OVER_TEMP_TRIP 111 #define BCMXCP_ALARM_INV_BOARD_ACUV_INT_TEST_FAIL 112 #define BCMXCP_ALARM_INVERTER_VOLTAGE_FEEDBACK_ERROR 113 #define BCMXCP_ALARM_DC_UNDER_VOLTAGE_TIMEOUT 114 #define BCMXCP_ALARM_AC_UNDER_VOLTAGE_TIMEOUT 115 #define BCMXCP_ALARM_DC_UNDER_VOLTAGE_WHILE_CHARGE 116 #define BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR 117 #define BCMXCP_ALARM_RECTIFIER_PHASE_ROTATION 118 #define BCMXCP_ALARM_BYPASS_PHASER_ROTATION 119 #define BCMXCP_ALARM_SYSTEM_INTERFACE_BOARD_FAIL 120 #define BCMXCP_ALARM_PARALLEL_BOARD_FAIL 121 #define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_A 122 #define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_B 123 #define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_C 124 #define BCMXCP_ALARM_DC_OVER_VOLTAGE_TIMEOUT 125 #define BCMXCP_ALARM_BATTERY_TOTALLY_DISCHARGED 126 #define BCMXCP_ALARM_INVERTER_PHASE_BIAS_ERROR 127 #define BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR_2 128 #define BCMXCP_ALARM_DC_LINK_BLEED_COMPLETE 129 #define BCMXCP_ALARM_LARGE_CHARGER_INPUT_CURRENT 130 #define BCMXCP_ALARM_INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL 131 #define BCMXCP_ALARM_LOSS_OF_REDUNDANCY 132 #define BCMXCP_ALARM_LOSS_OF_SYNC_BUS 133 #define BCMXCP_ALARM_RECTIFIER_BREAKER_SHUNT_TRIP 134 #define BCMXCP_ALARM_LOSS_OF_CHARGER_SYNC 135 #define BCMXCP_ALARM_INVERTER_LOW_LEVEL_TEST_TIMEOUT 136 #define BCMXCP_ALARM_OUTPUT_BREAKER_OPEN 137 #define BCMXCP_ALARM_CONTROL_POWER_ON 138 #define BCMXCP_ALARM_INVERTER_ON 139 #define BCMXCP_ALARM_CHARGER_ON 140 #define BCMXCP_ALARM_BYPASS_ON 141 #define BCMXCP_ALARM_BYPASS_POWER_LOSS 142 #define BCMXCP_ALARM_ON_MANUAL_BYPASS 143 #define BCMXCP_ALARM_BYPASS_MANUAL_TURN_OFF 144 #define BCMXCP_ALARM_INVERTER_BLEEDING_DC_LINK_VOLT 145 #define BCMXCP_ALARM_CPU_ISR_ERROR 146 #define BCMXCP_ALARM_SYSTEM_ISR_RESTART 147 #define BCMXCP_ALARM_PARALLEL_DC 148 #define BCMXCP_ALARM_BATTERY_NEEDS_SERVICE 149 #define BCMXCP_ALARM_BATTERY_CHARGING 150 #define BCMXCP_ALARM_BATTERY_NOT_CHARGED 151 #define BCMXCP_ALARM_DISABLED_BATTERY_TIME 152 #define BCMXCP_ALARM_SERIES_7000_ENABLE 153 #define BCMXCP_ALARM_OTHER_UPS_ON 154 #define BCMXCP_ALARM_PARALLEL_INVERTER 155 #define BCMXCP_ALARM_UPS_IN_PARALLEL 156 #define BCMXCP_ALARM_OUTPUT_BREAKER_REALY_FAIL 157 #define BCMXCP_ALARM_CONTROL_POWER_OFF 158 #define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_A 159 #define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_B 160 #define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_C 161 #define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_A 162 #define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_B 163 #define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_C 164 #define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_A 165 #define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_B 166 #define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_C 167 #define BCMXCP_ALARM_UPS_ON_BATTERY 168 #define BCMXCP_ALARM_UPS_ON_BYPASS 169 #define BCMXCP_ALARM_LOAD_DUMPED 170 #define BCMXCP_ALARM_LOAD_ON_INVERTER 171 #define BCMXCP_ALARM_UPS_ON_COMMAND 172 #define BCMXCP_ALARM_UPS_OFF_COMMAND 173 #define BCMXCP_ALARM_LOW_BATTERY_SHUTDOWN 174 #define BCMXCP_ALARM_AUTO_ON_ENABLED 175 #define BCMXCP_ALARM_SOFTWARE_INCOMPABILITY_DETECTED 176 #define BCMXCP_ALARM_INVERTER_TEMP_SENSOR_FAILED 177 #define BCMXCP_ALARM_DC_START_OCCURED 178 #define BCMXCP_ALARM_IN_PARALLEL_OPERATION 179 #define BCMXCP_ALARM_SYNCING_TO_BYPASS 180 #define BCMXCP_ALARM_RAMPING_UPS_UP 181 #define BCMXCP_ALARM_INVERTER_ON_DELAY 182 #define BCMXCP_ALARM_CHARGER_ON_DELAY 183 #define BCMXCP_ALARM_WAITING_FOR_UTIL_INPUT 184 #define BCMXCP_ALARM_CLOSE_BYPASS_BREAKER 185 #define BCMXCP_ALARM_TEMPORARY_BYPASS_OPERATION 186 #define BCMXCP_ALARM_SYNCING_TO_OUTPUT 187 #define BCMXCP_ALARM_BYPASS_FAILURE 188 #define BCMXCP_ALARM_AUTO_OFF_COMMAND_EXECUTED 189 #define BCMXCP_ALARM_AUTO_ON_COMMAND_EXECUTED 190 #define BCMXCP_ALARM_BATTERY_TEST_FAILED 191 #define BCMXCP_ALARM_FUSE_FAIL 192 #define BCMXCP_ALARM_FAN_FAIL 193 #define BCMXCP_ALARM_SITE_WIRING_FAULT 194 #define BCMXCP_ALARM_BACKFEED_CONTACTOR_FAIL 195 #define BCMXCP_ALARM_ON_BUCK 196 #define BCMXCP_ALARM_ON_BOOST 197 #define BCMXCP_ALARM_ON_DOUBLE_BOOST 198 #define BCMXCP_ALARM_BATTERIES_DISCONNECTED 199 #define BCMXCP_ALARM_UPS_CABINET_OVER_TEMP 200 #define BCMXCP_ALARM_TRANSFORMER_OVER_TEMP 201 #define BCMXCP_ALARM_AMBIENT_UNDER_TEMP 202 #define BCMXCP_ALARM_AMBIENT_OVER_TEMP 203 #define BCMXCP_ALARM_CABINET_DOOR_OPEN 204 #define BCMXCP_ALARM_CABINET_DOOR_OPEN_VOLT_PRESENT 205 #define BCMXCP_ALARM_AUTO_SHUTDOWN_PENDING 206 #define BCMXCP_ALARM_TAP_SWITCHING_REALY_PENDING 207 #define BCMXCP_ALARM_UNABLE_TO_CHARGE_BATTERIES 208 #define BCMXCP_ALARM_STARTUP_FAILURE_CHECK_EPO 209 #define BCMXCP_ALARM_AUTOMATIC_STARTUP_PENDING 210 #define BCMXCP_ALARM_MODEM_FAILED 211 #define BCMXCP_ALARM_INCOMING_MODEM_CALL_STARTED 212 #define BCMXCP_ALARM_OUTGOING_MODEM_CALL_STARTED 213 #define BCMXCP_ALARM_MODEM_CONNECTION_ESTABLISHED 214 #define BCMXCP_ALARM_MODEM_CALL_COMPLETED_SUCCESS 215 #define BCMXCP_ALARM_MODEM_CALL_COMPLETED_FAIL 216 #define BCMXCP_ALARM_INPUT_BREAKER_FAIL 217 #define BCMXCP_ALARM_SYSINIT_IN_PROGRESS 218 #define BCMXCP_ALARM_AUTOCALIBRATION_FAIL 219 #define BCMXCP_ALARM_SELECTIVE_TRIP_OF_MODULE 220 #define BCMXCP_ALARM_INVERTER_OUTPUT_FAILURE 221 #define BCMXCP_ALARM_ABNORMAL_OUTPUT_VOLT_AT_STARTUP 222 #define BCMXCP_ALARM_RECTIFIER_OVER_TEMP 223 #define BCMXCP_ALARM_CONFIG_ERROR 224 #define BCMXCP_ALARM_REDUNDANCY_LOSS_DUE_TO_OVERLOAD 225 #define BCMXCP_ALARM_ON_ALTERNATE_AC_SOURCE 226 #define BCMXCP_ALARM_IN_HIGH_EFFICIENCY_MODE 227 #define BCMXCP_ALARM_SYSTEM_NOTICE_ACTIVE 228 #define BCMXCP_ALARM_SYSTEM_ALARM_ACTIVE 229 #define BCMXCP_ALARM_ALTERNATE_POWER_SOURCE_NOT_AVAILABLE 230 #define BCMXCP_ALARM_CURRENT_BALANCE_FAILURE 231 #define BCMXCP_ALARM_CHECK_AIR_FILTER 232 #define BCMXCP_ALARM_SUBSYSTEM_NOTICE_ACTIVE 233 #define BCMXCP_ALARM_SUBSYSTEM_ALARM_ACTIVE 234 #define BCMXCP_ALARM_CHARGER_ON_COMMAND 235 #define BCMXCP_ALARM_CHARGER_OFF_COMMAND 236 #define BCMXCP_ALARM_UPS_NORMAL 237 #define BCMXCP_ALARM_INVERTER_PHASE_ROTATION 238 #define BCMXCP_ALARM_UPS_OFF 239 #define BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE 240 #define BCMXCP_ALARM_BATTERY_TEST_INPROGRESS 256 #define BCMXCP_ALARM_SYSTEM_TEST_INPROGRESS 257 #define BCMXCP_ALARM_BATTERY_TEST_ABORTED 258 #define BCMXCP_COMMAND_MAP_MAX 208 /* Max no of entries in BCM/XCP meter map (adjusted upwards to nearest multi of 8) */ #define BCMXCP_METER_MAP_MAX 136 /* Max no of entries in BCM/XCP meter map (adjusted upwards to nearest multi of 8) */ #define BCMXCP_ALARM_MAP_MAX 264 /* Max no of entries in BCM/XCP alarm map (adjusted upwards to nearest multi of 8) */ /* Return codes for XCP ACK block responses */ #define BCMXCP_RETURN_ACCEPTED 0x31 /* Accepted and executed (or execution in progress) */ #define BCMXCP_RETURN_NOT_IMPLEMENTED 0x32 /* Recognized but not implemented */ #define BCMXCP_RETURN_BUSY 0x33 /* Recognized but not currently able to execute (busy) */ #define BCMXCP_RETURN_UNRECOGNISED 0x34 /* Unrecognized -- e.g., command byte not in valid range, or command has been corrupted (bad checksum) */ #define BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE 0x35 /* Command recognized, but its Parameter value is out of range */ #define BCMXCP_RETURN_INVALID_PARAMETER 0x36 /* Command recognized, but its Parameter is invalid (e.g., no such parameter, bad Outlet number) */ #define BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST 0x37 /* Accepted, with parameter adjusted to nearest good value */ /*#define BCMXCP_RETURN_READONLY 0x38 */ /* Parameter is Read-only - cannot be written (at this privilege level) (this is not listed in spec document */ /* UPS status */ #define BCMXCP_STATUS_ONLINE 0x50 #define BCMXCP_STATUS_ONBATTERY 0xf0 #define BCMXCP_STATUS_OVERLOAD 0xe0 #define BCMXCP_STATUS_TRIM 0x63 #define BCMXCP_STATUS_BOOST1 0x61 #define BCMXCP_STATUS_BOOST2 0x62 #define BCMXCP_STATUS_BYPASS 0x60 #define BCMXCP_STATUS_OFF 0x10 /* UPS topology block info */ #define BCMXCP_TOPOLOGY_NONE 0x0000 /* None; use the Table of Elements */ #define BCMXCP_TOPOLOGY_OFFLINE_SWITCHER_1P 0x0010 /* Off-line switcher, Single Phase */ #define BCMXCP_TOPOLOGY_LINEINT_UPS_1P 0x0020 /* Line-Interactive UPS, Single Phase */ #define BCMXCP_TOPOLOGY_LINEINT_UPS_2P 0x0021 /* Line-Interactive UPS, Two Phase */ #define BCMXCP_TOPOLOGY_LINEINT_UPS_3P 0x0022 /* Line-Interactive UPS, Three Phase */ #define BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_1P 0x0030 /* Dual AC Input, On-Line UPS, Single Phase */ #define BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_2P 0x0031 /* Dual AC Input, On-Line UPS, Two Phase */ #define BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_3P 0x0032 /* Dual AC Input, On-Line UPS, Three Phase */ #define BCMXCP_TOPOLOGY_ONLINE_UPS_1P 0x0040 /* On-Line UPS, Single Phase */ #define BCMXCP_TOPOLOGY_ONLINE_UPS_2P 0x0041 /* On-Line UPS, Two Phase */ #define BCMXCP_TOPOLOGY_ONLINE_UPS_3P 0x0042 /* On-Line UPS, Three Phase */ #define BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_1P 0x0050 /* Parallel Redundant On-Line UPS, Single Phase */ #define BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_2P 0x0051 /* Parallel Redundant On-Line UPS, Two Phase */ #define BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_3P 0x0052 /* Parallel Redundant On-Line UPS, Three Phase */ #define BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_1P 0x0060 /* Parallel for Capacity On-Line UPS, Single Phase */ #define BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_2P 0x0061 /* Parallel for Capacity On-Line UPS, Two Phase */ #define BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_3P 0x0062 /* Parallel for Capacity On-Line UPS, Three Phase */ #define BCMXCP_TOPOLOGY_SYSTEM_BYPASS_MODULE_3P 0x0102 /* System Bypass Module, Three Phase */ #define BCMXCP_TOPOLOGY_HOT_TIE_CABINET_3P 0x0122 /* Hot-Tie Cabinet, Three Phase */ #define BCMXCP_TOPOLOGY_OUTLET_CONTROLLER_1P 0x0200 /* Outlet Controller, Single Phase */ #define BCMXCP_TOPOLOGY_DUAL_AC_STATIC_SWITCH_3P 0x0222 /* Dual AC Input Static Switch Module, 3 Phase */ typedef struct { /* Entry in BCM/XCP - UPS mapping table */ const char *command_desc; /* Description of this command */ unsigned char command_byte; /* The command byte for this command, 0 = not supported */ } BCMXCP_COMMAND_MAP_ENTRY_t; extern BCMXCP_COMMAND_MAP_ENTRY_t bcmxcp_command_map[BCMXCP_COMMAND_MAP_MAX]; typedef struct { /* Entry in BCM/XCP - UPS - NUT mapping table */ const char *nut_entity; /* The NUT variable name */ unsigned char format; /* The format of the data - float, long etc */ unsigned int meter_block_index; /* The position of this meter in the UPS meter block */ } BCMXCP_METER_MAP_ENTRY_t; extern BCMXCP_METER_MAP_ENTRY_t bcmxcp_meter_map[BCMXCP_METER_MAP_MAX]; typedef struct { /* Entry in BCM/XCP - UPS mapping table */ int alarm_block_index; /* Index of this alarm in alarm block. -1 = not existing */ const char *alarm_desc; /* Description of this alarm */ } BCMXCP_ALARM_MAP_ENTRY_t; extern BCMXCP_ALARM_MAP_ENTRY_t bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX]; typedef struct { /* A place to store status info and other data not for NUT */ unsigned char topology_mask; /* Configuration block byte 16, masks valid status bits */ unsigned int lowbatt; /* Seconds of runtime left left when LB alarm is set */ unsigned int shutdowndelay; /* Shutdown delay in seconds, from ups.conf */ int alarm_on_battery; /* On Battery alarm active? */ int alarm_low_battery; /* Battery Low alarm active? */ int alarm_replace_battery; /* Battery needs replacement! */ } BCMXCP_STATUS_t; extern BCMXCP_STATUS_t bcmxcp_status; int checksum_test(const unsigned char*); unsigned char calc_checksum(const unsigned char *buf); /* from usbhid-ups.h */ typedef struct { const long xcp_value; /* XCP value */ const char *nut_value; /* NUT value */ const char *(*fun)(double xcp_value); /* optional XCP to NUT mapping */ double (*nuf)(const char *nut_value); /* optional NUT to HID mapping */ } info_lkp_t; /* use explicit booleans */ #ifndef FALSE typedef enum ebool { FALSE, TRUE } bool_t; #else typedef int bool_t; #endif #endif /*_POWERWARE_H */ nut-2.7.4/drivers/belkin-hid.c0000644000175000017500000006143712640473702013123 00000000000000/* belkin-hid.c - data to monitor Belkin UPS Systems USB/HID devices with NUT * * Copyright (C) * 2003 - 2008 Arnaud Quette * 2005 Peter Selinger * 2011, 2014 Charles Lepple * * Sponsored by MGE UPS SYSTEMS * * 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 "main.h" /* for getval() */ #include "usbhid-ups.h" #include "belkin-hid.h" #include "usb-common.h" #define BELKIN_HID_VERSION "Belkin/Liebert HID 0.17" /* Belkin */ #define BELKIN_VENDORID 0x050d /* Liebert */ #define LIEBERT_VENDORID 0x10af /*! USB IDs device table. * Note that there are at least two Liebert firmware types which both report * a VID:PID of 10af:0001. The newer ones tend not to have the Belkin broken * Usage Pages (and therefore use standard HID PDC paths) but they have * incorrect exponents for some fields. */ static usb_device_id_t belkin_usb_device_table[] = { /* F6C800-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x0980), NULL }, /* F6C900-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x0900), NULL }, /* F6C100-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x0910), NULL }, /* F6C120-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x0912), NULL }, /* F6C550-AVR */ { USB_DEVICE(BELKIN_VENDORID, 0x0551), NULL }, /* F6C1250-TW-RK */ { USB_DEVICE(BELKIN_VENDORID, 0x0750), NULL }, /* F6C1500-TW-RK */ { USB_DEVICE(BELKIN_VENDORID, 0x0751), NULL }, /* F6H375-USB */ { USB_DEVICE(BELKIN_VENDORID, 0x0375), NULL }, /* Regulator PRO-USB */ { USB_DEVICE(BELKIN_VENDORID, 0x0f51), NULL }, /* F6C1100-UNV, F6C1200-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x1100), NULL }, /* Liebert PowerSure PSA UPS */ { USB_DEVICE(LIEBERT_VENDORID, 0x0001), NULL }, /* Liebert PowerSure PSI 1440 */ { USB_DEVICE(LIEBERT_VENDORID, 0x0004), NULL }, /* Liebert GXT3 */ { USB_DEVICE(LIEBERT_VENDORID, 0x0008), NULL }, /* Terminating entry */ { -1, -1, NULL } }; static const char *liebert_online_fun(double value); static const char *liebert_discharging_fun(double value); static const char *liebert_charging_fun(double value); static const char *liebert_lowbatt_fun(double value); static const char *liebert_replacebatt_fun(double value); static const char *liebert_shutdownimm_fun(double value); static const char *liebert_config_voltage_fun(double value); static const char *liebert_line_voltage_fun(double value); static info_lkp_t liebert_online_info[] = { { 0, NULL, liebert_online_fun } }; static info_lkp_t liebert_discharging_info[] = { { 0, NULL, liebert_discharging_fun } }; static info_lkp_t liebert_charging_info[] = { { 0, NULL, liebert_charging_fun } }; static info_lkp_t liebert_lowbatt_info[] = { { 0, NULL, liebert_lowbatt_fun } }; static info_lkp_t liebert_replacebatt_info[] = { { 0, NULL, liebert_replacebatt_fun } }; static info_lkp_t liebert_shutdownimm_info[] = { { 0, NULL, liebert_shutdownimm_fun } }; static info_lkp_t liebert_config_voltage_info[] = { { 0, NULL, liebert_config_voltage_fun }, }; static info_lkp_t liebert_line_voltage_info[] = { { 0, NULL, liebert_line_voltage_fun }, }; static double liebert_config_voltage_mult = 1.0; static double liebert_line_voltage_mult = 1.0; static char liebert_conversion_buf[10]; /* These lookup functions also cover the 1e-7 factor which seems to be due to a * broken report descriptor in certain Liebert units. */ static const char *liebert_online_fun(double value) { return value ? "online" : "!online"; } static const char *liebert_discharging_fun(double value) { return value ? "dischrg" : "!dischrg"; } static const char *liebert_charging_fun(double value) { return value ? "chrg" : "!chrg"; } static const char *liebert_lowbatt_fun(double value) { return value ? "lowbatt" : "!lowbatt"; } static const char *liebert_replacebatt_fun(double value) { return value ? "replacebatt" : "!replacebatt"; } static const char *liebert_shutdownimm_fun(double value) { return value ? "shutdownimm" : "!shutdownimm"; } /*! Apply heuristics to Liebert ConfigVoltage for correction of other values. * Logic is weird since the ConfigVoltage item comes after InputVoltage and * OutputVoltage. */ static const char *liebert_config_voltage_fun(double value) { if( value < 1 ) { if( abs(value - 1e-7) < 1e-9 ) { liebert_config_voltage_mult = 1e8; liebert_line_voltage_mult = 1e7; /* stomp this in case input voltage was low */ upsdebugx(2, "ConfigVoltage = %g -> assuming correction factor = %g", value, liebert_config_voltage_mult); } else { upslogx(LOG_NOTICE, "ConfigVoltage exponent looks wrong, but not correcting."); } } snprintf(liebert_conversion_buf, sizeof(liebert_conversion_buf), "%.1f", value * liebert_config_voltage_mult); return liebert_conversion_buf; } static const char *liebert_line_voltage_fun(double value) { if( value < 1 ) { if( abs(value - 1e-7) < 1e-9 ) { liebert_line_voltage_mult = 1e7; upsdebugx(2, "Input/OutputVoltage = %g -> assuming correction factor = %g", value, liebert_line_voltage_mult); } else { upslogx(LOG_NOTICE, "LineVoltage exponent looks wrong, but not correcting."); } } snprintf(liebert_conversion_buf, sizeof(liebert_conversion_buf), "%.1f", value * liebert_line_voltage_mult); return liebert_conversion_buf; } /* some conversion functions specific to Belkin */ /* returns statically allocated string - must not use it again before done with result! */ static const char *belkin_firmware_conversion_fun(double value) { static char buf[20]; snprintf(buf, sizeof(buf), "%ld", (long)value >> 4); return buf; } static info_lkp_t belkin_firmware_conversion[] = { { 0, NULL, belkin_firmware_conversion_fun } }; static const char *belkin_upstype_conversion_fun(double value) { switch ((long)value & 0x0f) { case 1: return "offline"; case 2: return "line-interactive"; case 3: return "simple online"; case 4: return "simple offline"; case 5: return "simple line-interactive"; default: return "online"; } } static info_lkp_t belkin_upstype_conversion[] = { { 0, NULL, belkin_upstype_conversion_fun } }; static const char *belkin_sensitivity_conversion_fun(double value) { switch ((long)value) { case 1: return "reduced"; case 2: return "low"; default: return "normal"; } } static info_lkp_t belkin_sensitivity_conversion[] = { { 0, NULL, belkin_sensitivity_conversion_fun } }; static info_lkp_t belkin_test_info[] = { { 0, "No test initiated", NULL }, { 1, "Done and passed", NULL }, { 2, "Done and warning", NULL }, { 3, "Done and error", NULL }, { 4, "Aborted", NULL }, { 5, "In progress", NULL }, { 0, NULL, NULL } }; static const char *belkin_overload_conversion_fun(double value) { if ((long)value & 0x0010) { return "overload"; } else { return "!overload"; } } static info_lkp_t belkin_overload_conversion[] = { { 0, NULL, belkin_overload_conversion_fun } }; static const char *belkin_overheat_conversion_fun(double value) { if ((long)value & 0x0040) { return "overheat"; } else { return "!overheat"; } } static info_lkp_t belkin_overheat_conversion[] = { { 0, NULL, belkin_overheat_conversion_fun } }; static const char *belkin_commfault_conversion_fun(double value) { if ((long)value & 0x0080) { return "commfault"; } else { return "!commfault"; } } static info_lkp_t belkin_commfault_conversion[] = { { 0, NULL, belkin_commfault_conversion_fun } }; static const char *belkin_awaitingpower_conversion_fun(double value) { if ((long)value & 0x2000) { return "awaitingpower"; } else { return "!awaitingpower"; } } static info_lkp_t belkin_awaitingpower_conversion[] = { { 0, NULL, belkin_awaitingpower_conversion_fun } }; static const char *belkin_online_conversion_fun(double value) { if ((long)value & 0x0001) { return "!online"; } else { return "online"; } } static info_lkp_t belkin_online_conversion[] = { { 0, NULL, belkin_online_conversion_fun } }; static const char *belkin_lowbatt_conversion_fun(double value) { if ((long)value & 0x0004) { return "lowbatt"; } else { return "!lowbatt"; } } static info_lkp_t belkin_lowbatt_conversion[] = { { 0, NULL, belkin_lowbatt_conversion_fun } }; static const char *belkin_depleted_conversion_fun(double value) { if ((long)value & 0x0040) { return "depleted"; } else { return "!depleted"; } } static info_lkp_t belkin_depleted_conversion[] = { { 0, NULL, belkin_depleted_conversion_fun } }; static const char *belkin_replacebatt_conversion_fun(double value) { if ((long)value & 0x0080) { return "replacebatt"; } else { return "!replacebatt"; } } static info_lkp_t belkin_replacebatt_conversion[] = { { 0, NULL, belkin_replacebatt_conversion_fun } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ /* BELKIN usage table */ /* Note: these seem to have been wrongly encoded by Belkin */ /* Pages 84 to 88 are reserved for official HID definition! */ static usage_lkp_t belkin_usage_lkp[] = { { "BELKINConfig", 0x00860026 }, { "BELKINConfigVoltage", 0x00860040 }, /* (V) */ { "BELKINConfigFrequency", 0x00860042 }, /* (Hz) */ { "BELKINConfigApparentPower", 0x00860043 }, /* (VA) */ { "BELKINConfigBatteryVoltage", 0x00860044 }, /* (V) */ { "BELKINConfigOverloadTransfer", 0x00860045 }, /* (%) */ { "BELKINLowVoltageTransfer", 0x00860053 }, /* R/W (V) */ { "BELKINHighVoltageTransfer", 0x00860054 }, /* R/W (V)*/ { "BELKINLowVoltageTransferMax", 0x0086005b }, /* (V) */ { "BELKINLowVoltageTransferMin", 0x0086005c }, /* (V) */ { "BELKINHighVoltageTransferMax", 0x0086005d }, /* (V) */ { "BELKINHighVoltageTransferMin", 0x0086005e }, /* (V) */ { "BELKINControls", 0x00860027 }, { "BELKINLoadOn", 0x00860050 }, /* R/W: write: 1=do action. Read: 0=none, 1=started, 2=in progress, 3=complete */ { "BELKINLoadOff", 0x00860051 }, /* R/W: ditto */ { "BELKINLoadToggle", 0x00860052 }, /* R/W: ditto */ { "BELKINDelayBeforeReboot", 0x00860055 }, /* R/W: write: 0=start shutdown using default delay. */ { "BELKINDelayBeforeStartup", 0x00860056 }, /* R/W (minutes) */ { "BELKINDelayBeforeShutdown", 0x00860057 }, /* R/W (seconds) */ { "BELKINTest", 0x00860058 }, /* R/W: write: 0=no test, 1=quick test, 2=deep test, 3=abort test. Read: 0=no test, 1=passed, 2=warning, 3=error, 4=abort, 5=in progress */ { "BELKINAudibleAlarmControl", 0x0086005a }, /* R/W: 1=disabled, 2=enabled, 3=muted */ { "BELKINDevice", 0x00860029 }, { "BELKINVoltageSensitivity", 0x00860074 }, /* R/W: 0=normal, 1=reduced, 2=low */ { "BELKINModelString", 0x00860075 }, { "BELKINModelStringOffset", 0x00860076 }, /* offset of Model name in Model String */ { "BELKINUPSType", 0x0086007c }, /* high nibble: firmware version. Low nibble: 0=online, 1=offline, 2=line-interactive, 3=simple online, 4=simple offline, 5=simple line-interactive */ { "BELKINPowerState", 0x0086002a }, { "BELKINInput", 0x0086001a }, { "BELKINOutput", 0x0086001c }, { "BELKINBatterySystem", 0x00860010 }, { "BELKINVoltage", 0x00860030 }, /* (0.1 Volt) */ { "BELKINFrequency", 0x00860032 }, /* (0.1 Hz) */ { "BELKINPower", 0x00860034 }, /* (Watt) */ { "BELKINPercentLoad", 0x00860035 }, /* (%) */ { "BELKINTemperature", 0x00860036 }, /* (Celsius) */ { "BELKINCharge", 0x00860039 }, /* (%) */ { "BELKINRunTimeToEmpty", 0x0086006c }, /* (minutes) */ { "BELKINStatus", 0x00860028 }, { "BELKINBatteryStatus", 0x00860022 }, /* 1 byte: bit2=low battery, bit4=charging, bit5=discharging, bit6=battery empty, bit7=replace battery */ { "BELKINPowerStatus", 0x00860021 }, /* 2 bytes: bit0=ac failure, bit4=overload, bit5=load is off, bit6=overheat, bit7=UPS fault, bit13=awaiting power, bit15=alarm status */ { NULL, 0 } }; static usage_tables_t belkin_utab[] = { belkin_usage_lkp, hid_usage_lkp, NULL, }; /* --------------------------------------------------------------- */ /* HID2NUT lookup table */ /* --------------------------------------------------------------- */ static hid_info_t belkin_hid2nut[] = { /* interpreted Belkin variables */ { "battery.charge", 0, 0, "UPS.BELKINBatterySystem.BELKINCharge", NULL, "%.0f", 0, NULL }, /* { "battery.charge.broken", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, */ { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, /* Read only */ { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, { "battery.voltage", 0, 0, "UPS.BELKINBatterySystem.BELKINVoltage", NULL, "%s", 0, divide_by_10_conversion }, { "battery.voltage.nominal", 0, 0, "UPS.BELKINConfig.BELKINConfigBatteryVoltage", NULL, "%.0f", 0, NULL }, { "input.frequency", 0, 0, "UPS.BELKINPowerState.BELKINInput.BELKINFrequency", NULL, "%s", 0, divide_by_10_conversion }, { "input.frequency.nominal", 0, 0, "UPS.BELKINConfig.BELKINConfigFrequency", NULL, "%.0f", 0, NULL }, { "input.sensitivity", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.BELKINDevice.BELKINVoltageSensitivity", NULL, "%s", 0, belkin_sensitivity_conversion }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.BELKINConfig.BELKINHighVoltageTransfer", NULL, "%.0f", 0, NULL }, { "input.transfer.high.max", 0, 0, "UPS.BELKINConfig.BELKINHighVoltageTransferMax", NULL, "%.0f", 0, NULL }, { "input.transfer.high.min", 0, 0, "UPS.BELKINConfig.BELKINHighVoltageTransferMin", NULL, "%.0f", 0, NULL }, { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.BELKINConfig.BELKINLowVoltageTransfer", NULL, "%.0f", 0, NULL }, { "input.transfer.low.max", 0, 0, "UPS.BELKINConfig.BELKINLowVoltageTransferMax", NULL, "%.0f", 0, NULL }, { "input.transfer.low.min", 0, 0, "UPS.BELKINConfig.BELKINLowVoltageTransferMin", NULL, "%.0f", 0, NULL }, { "input.voltage", 0, 0, "UPS.BELKINPowerState.BELKINInput.BELKINVoltage", NULL, "%s", 0, divide_by_10_conversion }, { "input.voltage.nominal", 0, 0, "UPS.BELKINConfig.BELKINConfigVoltage", NULL, "%.0f", 0, NULL }, { "output.frequency", 0, 0, "UPS.BELKINPowerState.BELKINOutput.BELKINFrequency", NULL, "%s", 0, divide_by_10_conversion }, { "output.voltage", 0, 0, "UPS.BELKINPowerState.BELKINOutput.BELKINVoltage", NULL, "%s", 0, divide_by_10_conversion }, { "ups.beeper.status", 0, 0, "UPS.BELKINControls.BELKINAudibleAlarmControl", NULL, "%s", 0, beeper_info }, { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.BELKINControls.BELKINDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL }, { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.BELKINControls.BELKINDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, { "ups.timer.start", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, { "ups.timer.shutdown", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, { "ups.timer.reboot", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, { "ups.firmware", 0, 0, "UPS.BELKINDevice.BELKINUPSType", NULL, "%s", 0, belkin_firmware_conversion }, { "ups.load", 0, 0, "UPS.BELKINPowerState.BELKINOutput.BELKINPercentLoad", NULL, "%.0f", 0, NULL }, { "ups.load.high", 0, 0, "UPS.BELKINConfig.BELKINConfigOverloadTransfer", NULL, "%.0f", 0, NULL }, { "ups.mfr.date", 0, 0, "UPS.PowerSummary.ManufacturerDate", NULL, "%s", 0, date_conversion }, { "ups.power.nominal", 0, 0, "UPS.BELKINConfig.BELKINConfigApparentPower", NULL, "%.0f", 0, NULL }, { "ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion }, { "ups.test.result", 0, 0, "UPS.BELKINControls.BELKINTest", NULL, "%s", 0, belkin_test_info }, { "ups.type", 0, 0, "UPS.BELKINDevice.BELKINUPSType", NULL, "%s", 0, belkin_upstype_conversion }, /* Liebert PSA: */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, /* why .broken above? */ { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%s", 0, divide_by_10_conversion }, { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, /* You would think these next two would be off by the same factor. You'd be wrong. */ { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, liebert_config_voltage_info }, { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, /* status */ { "BOOL", 0, 0, "UPS.PowerSummary.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_discharging_info }, /* might not need to be liebert_* version */ { "BOOL", 0, 0, "UPS.PowerSummary.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.ShutdownImminent", NULL, NULL, 0, liebert_shutdownimm_info }, { "BOOL", 0, 0, "UPS.PowerSummary.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, liebert_shutdownimm_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_replacebatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_lowbatt_info }, /* { "BOOL", 0, 0, "UPS.PowerSummary.BelowRemainingCapacityLimit", NULL, "%s", 0, lowbatt_info }, broken! */ { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_overload_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_overheat_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_commfault_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_awaitingpower_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, HU_FLAG_QUICK_POLL, belkin_online_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINBatteryStatus", NULL, NULL, HU_FLAG_QUICK_POLL, belkin_depleted_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINBatteryStatus", NULL, NULL, 0, belkin_replacebatt_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINBatteryStatus", NULL, NULL, HU_FLAG_QUICK_POLL, belkin_lowbatt_conversion }, /* instant commands. */ /* split into subsets while waiting for extradata support * ie: test.battery.start quick */ { "test.battery.start.quick", 0, 0, "UPS.BELKINControls.BELKINTest", NULL, "1", HU_TYPE_CMD, NULL }, { "test.battery.start.deep", 0, 0, "UPS.BELKINControls.BELKINTest", NULL, "2", HU_TYPE_CMD, NULL }, { "test.battery.stop", 0, 0, "UPS.BELKINControls.BELKINTest", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.on", 0, 0, "UPS.BELKINControls.BELKINAudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.off", 0, 0, "UPS.BELKINControls.BELKINAudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.BELKINControls.BELKINAudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.BELKINControls.BELKINAudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.BELKINControls.BELKINAudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "load.off", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeShutdown", NULL, "1", HU_TYPE_CMD, NULL }, { "load.on", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeStartup", NULL, "1", HU_TYPE_CMD, NULL }, { "load.off.delay", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.BELKINControls.BELKINDelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, /* Note: on my Belkin UPS, there is no way to implement shutdown.return or load.on, even though they should exist in principle. Hopefully other Belkin models will be better designed. Fixme: fill in the appropriate instant commands. For a more detailed description of the problem and a possible (but not yet implemented) workaround, see the belkinunv(8) man page. -PS 2005/08/28 */ #if 0 /* added for debugging Liebert GXT3 : */ { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", 0, stringid_conversion }, { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.remainingtimelimit", 0, 0, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.batterypresent", 0, 0, "UPS.PowerSummary.BatteryPresent", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL }, #endif /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; static const char *belkin_format_model(HIDDevice_t *hd) { if ((hd->Product) && (strlen(hd->Product) > 0)) { return hd->Product; } return "unknown"; } static const char *belkin_format_mfr(HIDDevice_t *hd) { const char *mfr; mfr = hd->Vendor ? hd->Vendor : "Belkin"; /* trim leading whitespace */ while (*mfr == ' ') { mfr++; } if (strlen(mfr) == 0) { mfr = "Belkin"; } return mfr; } static const char *belkin_format_serial(HIDDevice_t *hd) { char serial[64]; if (hd->Serial) { return hd->Serial; } /* try UPS.PowerSummary.iSerialNumber */ HIDGetItemString(udev, "UPS.PowerSummary.iSerialNumber", serial, sizeof(serial), belkin_utab); if (strlen(serial) < 1) { return NULL; } /* free(hd->Serial); not needed, we already know it is NULL */ hd->Serial = strdup(serial); return hd->Serial; } /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int belkin_claim(HIDDevice_t *hd) { int status = is_usb_device_supported(belkin_usb_device_table, hd); switch (status) { case POSSIBLY_SUPPORTED: switch (hd->VendorID) { case BELKIN_VENDORID: /* reject any known non-UPS */ if (hd->ProductID == 0x0218) /* F5U218-MOB 4-Port USB Hub */ return 0; /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("Belkin", hd); return 0; case LIEBERT_VENDORID: /* by default, reject, unless the productid option is given */ if (getval("productid")) { return 1; } possibly_supported("Liebert", hd); return 0; } return 0; case SUPPORTED: return 1; case NOT_SUPPORTED: default: return 0; } } subdriver_t belkin_subdriver = { BELKIN_HID_VERSION, belkin_claim, belkin_utab, belkin_hid2nut, belkin_format_model, belkin_format_mfr, belkin_format_serial, }; nut-2.7.4/drivers/apcsmart.h0000644000175000017500000001300212640473702012715 00000000000000/* * apcsmart.h - common defines for apcsmart driver * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __apcsmart_h__ #define __apcsmart_h__ #define DRIVER_NAME "APC Smart protocol driver" #define DRIVER_VERSION "3.1" #define ALT_CABLE_1 "940-0095B" /* * alerts and other stuff for quick reference: * * $ OL * ! OB * % LB * + not LB anymore * # RB * ? OVER * = not OVER anymore * * powering down now (only older models ?), handled by upsread() * otherwise ignored (it doesn't have to be in ignore sets) * * | eeprom change * & check alarm register for fail * ~ ??? */ /* * old ones for reference: * #define IGNCHARS "\015+$|!~%?=#&" * #define POLL_IGNORE "\015&|" * #define POLL_ALERT "$!%+#?=" * #define MINIGNCHARS "\015+$|!" * notice ~ that was present in IGNCHARS, but not in POLL_IGNORE - this kinda * didn't make sense (?); new versions doesn't filter ~, but keep that in mind * in case something obscure surfaces * due to switch to ICANON tty mode, we removed \015 from ignored characters, * as it's handled by IGNCR at read() level */ /* * about CR: * apparently windows is unable to ignore CRs by means of IGNCR flag (or * perhaps there is something else involved); so despite IGNCR we re-aad \015 * to ignore sets for now; see: * http://article.gmane.org/gmane.comp.monitoring.nut.user/7762 * * furthermore, since the canonical/non-canonical mode is user selectable now, * we have to ignore this character explicitly */ /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF (and CR, but it's IGNCRed) */ /* what to ignore during alert aware serial reads */ #define IGN_AACHARS "\015|&" /* what alert_handler() should care about */ #define ALERT_CHARS "$!%+#?=" /* characters ignored by alertless reads */ #define IGN_CHARS IGN_AACHARS ALERT_CHARS /* * these ones are used only during Capability Check read, due to ^Z sending * certain characters such as #; it seems it could be equal to just IGN_CHARS * w/o # old: #define IGN_CCCHARS "|$!+" */ #define IGN_CCCHARS "\015|&$!%+?=" /* capability check ignore set */ /* * Command Set 'a' reports everything - protocol number, alerts and supported * commands */ #define IGN_CSCHARS "" /* command set ignore set */ /* dangerous instant commands must be reconfirmed within a 12 second window */ #define MINCMDTIME 3 #define MAXCMDTIME 15 /* it only does two strings, and they're both the same length */ #define APC_STRLEN 8 #define SER_D0 0x001 /* 0 sec., for flushes */ #define SER_DX 0x002 /* 200 ms for long/repeated cmds, in case of unexpected NAs */ #define SER_D1 0x004 /* 1.5 sec. */ #define SER_D3 0x008 /* 3 sec. (default) */ #define SER_AA 0x010 /* Alert Aware set */ #define SER_CC 0x020 /* Capability Check ign set */ #define SER_CS 0x040 /* Command Set ign set */ #define SER_TO 0x080 /* timeout allowed */ #define SER_HA 0x100 /* handle asterisk */ /* sets of the above (don't test against them, obviously */ /* * Some cmd codes to ignore (nut doesn't expose those, though the driver might * use them internally (e.g. [a]). If you decide to support them at some * point, remember about removing them from here ! */ #define APC_UNR_CMDS "\032\177~')-+8QRYayz" /* --------------- */ /* status bits */ #define APC_STAT_CAL (1 << 0) /* calibration */ #define APC_STAT_TRIM (1 << 1) /* SmartTrim */ #define APC_STAT_BOOST (1 << 2) /* SmartBoost */ #define APC_STAT_OL (1 << 3) /* on line */ #define APC_STAT_OB (1 << 4) /* on battery */ #define APC_STAT_OVER (1 << 5) /* overload */ #define APC_STAT_LB (1 << 6) /* low battery */ #define APC_STAT_RB (1 << 7) /* replace battery */ /* * serial protocol: special commands - initialization and such * these are not exposed as instant commands */ #define APC_STATUS 'Q' #define APC_GOSMART 'Y' #define APC_GODUMB 'R' #define APC_CMDSET 'a' #define APC_CMDSET_FMT "^[0-9]\\.[^.]*\\.[^.]+(\\.[^.]+)?$" #define APC_CAPS '\032' /* ^Z */ #define APC_NEXTVAL '-' #define APC_FW_OLD 'V' #define APC_FW_NEW 'b' #define APC_LBUF 512 #define APC_SBUF 32 /* default a.w.d. value / regex format for command '@' */ #define APC_AWDFMT "^[0-9]{1,3}$" /* sdtype method regex format*/ #define APC_SDFMT "^[0-5]$" /* advorder method regex format*/ #define APC_ADVFMT "^([0-4]{1,5}|[Nn][Oo])$" /* error logging/debug related macros */ #define fatx(fmt, ...) fatalx(EXIT_FAILURE, "%s: " fmt, __func__ , ## __VA_ARGS__) #define fate(fmt, ...) fatal_with_errno(EXIT_FAILURE, "%s: " fmt, __func__ , ## __VA_ARGS__) #define logx(lev, fmt, ...) upslogx(lev, "%s: " fmt, __func__ , ## __VA_ARGS__) #define debx(lev, fmt, ...) upsdebugx(lev, "%s: " fmt, __func__ , ## __VA_ARGS__) #endif nut-2.7.4/drivers/gamatronic.h0000644000175000017500000003007712640443572013244 00000000000000/* gamatronic.h * * SEC UPS Driver ported to the new NUT API for Gamatronic UPS Usage. * * Copyright (C) * 2001 John Marley * 2002 Jules Taplin * 2002 Eric Lawson * 2005 Arnaud Quette * 2005 Nadav Moskovitch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define SEC_MSG_STARTCHAR '^' #define SEC_POLLCMD 'P' #define SEC_SETCMD 'S' #define SEC_DATAMSG 'D' #define SEC_UPSMSG '*' #define SEC_ACK '1' #define SEC_NAK '0' /* commands */ #define SEC_AVAILP1 "AP1" /* Part1 of available variables */ #define SEC_AVAILP2 "AP2" /* Part1 of available variables */ #define SEC_AUTORESTART "ATR" /* Enable/disable auto restart */ #define SEC_MFR "MAN" /* UPS Manufacturer */ #define SEC_MOD "MOD" /* UPS Model */ #define SEC_NOMINAL "NOM" /* Nominal Values */ #define SEC_SHUTDOWN "PSD" /* Shutdown after delay/cancel */ #define SEC_REBOOT "RWD" /* Reboot with duration/cancel */ #define SEC_SHUTTYPE "SDA" /* Shutdown Type */ #define SEC_BATTSTAT "ST1" /* Battery Status */ #define SEC_INPUTSTAT "ST2" /* Input Status */ #define SEC_OUTPUTSTAT "ST3" /* Output Status */ #define SEC_BYPASSSTAT "ST4" /* Bypass Status */ #define SEC_ALARMSTAT "ST5" /* UPS Alarms */ #define SEC_STARTDELAY "STD" /* Startup after delay */ #define SEC_TESTRESULT "STR" /* Test Results */ #define SEC_TEST "TST" /* UPS Test/abort */ #define SEC_BAUDRATE "UBR" /* UPS Baud Rate */ #define SEC_UPSID "UID" /* UPS Identifier */ #define SEC_VERSION "VER" /* UPS Software Version */ #define FLAG_STRING 1 #define FLAG_RW 2 #define FLAG_WONLY 3 /* Dont waste time on reading commands that are read only */ #define FLAG_ALARM 4 /* If the value of a var with this flag equals to 1, then its name added to alarms list */ #define FLAG_MULTI 5 /* Multiple UNIT value instead of Dividing It */ #define FLAG_POLL 0 /* For commands that polled normaly */ #define FLAG_POLLONCE 1 /* For commands that only polled once */ /* Some baud rates for setup_serial() */ struct { int rate; int name; } baud_rates[] = { { B1200, 1200 }, { B2400, 2400 }, { B4800, 4800 }, { B9600, 9600 }, { B19200, 19200 }, }; #define SEC_NUMVARS 89 #define SEC_MAX_VARSIZE 65 /* macro for checking whether a variable is supported */ struct { const char *setcmd; /* INFO_x define from shared.h */ const char *name; /* Human readable text (also in shared-tables.h) */ int unit; /* Variable should be divided by this */ const char *cmd; /* Command to send to pool/set variable */ int field; /* Which returned field variable corresponsd to */ int size; /* string length/integer max/enum count */ int poll; /* poll flag */ int flags; /* Flags for addinfo() */ char value[SEC_MAX_VARSIZE]; } sec_varlist[] = { { "", "", 0, "", 0, 0, 0, 0 }, /*setcmd name unit cmd field size poll flags */ { "", "Awaiting Power ", 1, SEC_ALARMSTAT, 13, 2, 0, FLAG_ALARM}, { "", "Bypass Bad ", 1, SEC_ALARMSTAT, 5, 2, 0, FLAG_ALARM}, { "", "Charger Failure ", 1, SEC_ALARMSTAT, 8, 2, 0, FLAG_ALARM}, { "", "Fan Failure ", 1, SEC_ALARMSTAT, 10, 2, 0, FLAG_ALARM}, { "", "Fuse Failure ", 1, SEC_ALARMSTAT, 11, 2, 0, FLAG_ALARM}, { "", "General Fault ", 1, SEC_ALARMSTAT, 12, 2, 0, FLAG_ALARM}, { "", "Input Bad ", 1, SEC_ALARMSTAT, 2, 2, 0, FLAG_ALARM}, { "", "Output Bad ", 1, SEC_ALARMSTAT, 3, 2, 0, FLAG_ALARM}, { "", "Output Off ", 1, SEC_ALARMSTAT, 6, 2, 0, FLAG_ALARM}, { "", "Overload ", 1, SEC_ALARMSTAT, 4, 2, 0, FLAG_ALARM}, { "", "Shutdown Imminent ", 1, SEC_ALARMSTAT, 15, 2, 0, FLAG_ALARM}, { "", "Shutdown Pending ", 1, SEC_ALARMSTAT, 14, 2, 0, FLAG_ALARM}, { "", "System Off ", 1, SEC_ALARMSTAT, 9, 2, 0, FLAG_ALARM}, { "", "Temperature ", 1, SEC_ALARMSTAT, 1, 2, 0, FLAG_ALARM}, { "", "UPS Shutdown ", 1, SEC_ALARMSTAT, 7, 2, 0, FLAG_ALARM}, { "", "Audible Alarm", 1, SEC_NOMINAL, 8, 4, FLAG_POLLONCE, FLAG_RW}, { "", "Auto Restart", 1, SEC_AUTORESTART, 1, 2, FLAG_POLLONCE, FLAG_RW}, { "", "Battery Charge", 1, SEC_BATTSTAT, 3, 4, 0, 0}, { "", "Battery Condition", 1, SEC_BATTSTAT, 1, 3, 0, 0}, { "battery.current", "Battery Current", 10, SEC_BATTSTAT, 8, 9999, 0, 0 }, { "battery.date", "Battery Installed", 1, SEC_NOMINAL, 11, 8, FLAG_POLLONCE, FLAG_STRING}, { "", "Battery Status", 1, SEC_BATTSTAT, 2, 3, 0, 0 }, { "battery.temperature", "Battery Temperature", 1, SEC_BATTSTAT, 9, 99, 0, 0 }, { "battery.voltage", "Battery Voltage", 10, SEC_BATTSTAT, 7, 9999, 0, 0 }, { "", "Bypass Current 1", 10, SEC_BYPASSSTAT, 4, 9999, 0, 0 }, { "", "Bypass Current 2", 10, SEC_BYPASSSTAT, 7, 9999, 0, 0 }, { "", "Bypass Current 3", 10, SEC_BYPASSSTAT, 10, 9999, 0, 0 }, { "", "Bypass Frequency", 10, SEC_BYPASSSTAT, 1, 999, 0, 0 }, { "", "Bypass Num Lines", 1, SEC_BYPASSSTAT, 2, 9, 0, 0 }, { "", "Bypass Power 1", 1, SEC_BYPASSSTAT, 5, 99999, 0, 0 }, { "", "Bypass Power 2", 1, SEC_BYPASSSTAT, 8, 99999, 0, 0 }, { "", "Bypass Power 3", 1, SEC_BYPASSSTAT, 11, 99999, 0, 0 }, { "", "Bypass Voltage 1", 10, SEC_BYPASSSTAT, 3, 9999, 0, 0 }, { "", "Bypass Voltage 2", 10, SEC_BYPASSSTAT, 6, 9999, 0, 0 }, { "", "Bypass Voltage 3", 10, SEC_BYPASSSTAT, 9, 9999, 0, 0 }, { "battery.charge", "Estimated Charge", 1, SEC_BATTSTAT, 6, 999, 0, 0 }, { "battery.runtime.low", "Estimated Minutes", 60, SEC_BATTSTAT, 5, 999, 0, FLAG_MULTI }, { "input.transfer.high", "High Volt Xfer Pt", 1, SEC_NOMINAL, 10, 999, FLAG_POLLONCE, FLAG_STRING}, { "ups.id", "Identification", 1, SEC_UPSID, 1, 64, FLAG_POLLONCE, FLAG_STRING}, { "", "Input Current 1", 10, SEC_INPUTSTAT, 5, 9999, 0, 0 }, { "", "Input Current 2", 10, SEC_INPUTSTAT, 9, 9999, 0, 0 }, { "", "Input Current 3", 10, SEC_INPUTSTAT, 13, 9999, 0, 0 }, { "input.frequency", "Input Frequency 1", 10, SEC_INPUTSTAT, 3, 999, 0, 0 }, { "", "Input Frequency 2", 10, SEC_INPUTSTAT, 7, 999, 0, 0 }, { "", "Input Frequency 3", 10, SEC_INPUTSTAT, 11, 999, 0, 0 }, { "", "Input Line Bads", 1, SEC_INPUTSTAT, 1, 999, 0, 0 }, { "", "Input Num Lines", 1, SEC_INPUTSTAT, 2, 9, 0, 0 }, { "", "Input Power 1", 1, SEC_INPUTSTAT, 6, 99999, 0, 0 }, { "", "Input Power 2", 1, SEC_INPUTSTAT, 10, 99999, 0, 0 }, { "", "Input Power 3", 1, SEC_INPUTSTAT, 14, 99999, 0, 0 }, { "input.voltage", "Input Voltage 1", 10, SEC_INPUTSTAT, 4, 9999, 0, 0 }, { "", "Input Voltage 2", 10, SEC_INPUTSTAT, 8, 9999, 0, 0 }, { "", "Input Voltage 3", 10, SEC_INPUTSTAT, 12, 9999, 0, 0 }, { "input.transfer.low", "Low Volt Xfer Pt", 1, SEC_NOMINAL, 9, 999, FLAG_POLLONCE, FLAG_STRING}, { "ups.mfr", "Manufacturer", 1, SEC_MFR, 1, 32, FLAG_POLLONCE, FLAG_STRING}, { "ups.model", "Model", 1, SEC_MOD, 1, 64, FLAG_POLLONCE, FLAG_STRING}, { "", "Nominal Battery Life", 1, SEC_NOMINAL, 12, 99999, FLAG_POLLONCE, FLAG_STRING}, { "", "Nominal Input Frequency", 10, SEC_NOMINAL, 2, 999, FLAG_POLLONCE, FLAG_RW}, { "input.voltage.nominal", "Nominal Input Voltage", 1, SEC_NOMINAL, 1, 999, FLAG_POLLONCE, FLAG_STRING}, { "", "Nominal Low Battery Time", 1, SEC_NOMINAL, 7, 99, FLAG_POLLONCE, FLAG_STRING}, { "", "Nominal Output Frequency", 10, SEC_NOMINAL, 4, 999, FLAG_POLLONCE, FLAG_RW}, { "", "Nominal Output Power", 1, SEC_NOMINAL, 6, 99999, FLAG_POLLONCE, FLAG_STRING}, { "", "Nominal Output Voltage", 1, SEC_NOMINAL, 3, 999, FLAG_POLLONCE, FLAG_STRING}, { "ups.power.nominal", "Nominal VA Rating", 1, SEC_NOMINAL, 5, 99999, FLAG_POLLONCE, FLAG_STRING}, { "output.current", "Output Current 1", 10, SEC_OUTPUTSTAT, 5, 9999, 0, 0 }, { "", "Output Current 2", 10, SEC_OUTPUTSTAT, 9, 9999, 0, 0 }, { "", "Output Current 3", 10, SEC_OUTPUTSTAT, 13, 9999, 0, 0 }, { "output.frequency", "Output Frequency", 10, SEC_OUTPUTSTAT, 2, 999, 0, 0 }, { "ups.load", "Output Load 1", 1, SEC_OUTPUTSTAT, 7, 999, 0, 0 }, { "", "Output Load 2", 1, SEC_OUTPUTSTAT, 11, 999, 0, 0 }, { "", "Output Load 3", 1, SEC_OUTPUTSTAT, 15, 999, 0, 0 }, { "", "Output Num Lines", 1, SEC_OUTPUTSTAT, 3, 9, 0, 0 }, { "", "Output Power 1", 1, SEC_OUTPUTSTAT, 6, 99999, 0, 0 }, { "", "Output Power 2", 1, SEC_OUTPUTSTAT, 10, 99999, 0, 0 }, { "", "Output Power 3", 1, SEC_OUTPUTSTAT, 14, 99999, 0, 0 }, { "", "Output Source", 1, SEC_OUTPUTSTAT, 1, 6, 0, 0}, { "output.voltage", "Output Voltage 1", 10, SEC_OUTPUTSTAT, 4, 9999, 0, 0 }, { "", "Output Voltage 2", 10, SEC_OUTPUTSTAT, 8, 9999, 0, 0 }, { "", "Output Voltage 3", 10, SEC_OUTPUTSTAT, 12, 9999, 0, 0 }, { "", "Reboot With Duration", 1, SEC_REBOOT, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY}, { "battery.runtime", "Seconds on Battery", 1, SEC_BATTSTAT, 4, 99999, 0, 0 }, { "", "Shutdown Type", 1, SEC_SHUTTYPE, 1, 2, FLAG_POLLONCE, FLAG_RW}, { "ups.delay.shutdown", "Shutdown After Delay", 1, SEC_STARTDELAY, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY}, { "ups.firmware", "Software Version", 1, SEC_VERSION, 1, 32, FLAG_POLLONCE, FLAG_STRING}, { "", "Startup After Delay", 1, SEC_STARTDELAY, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY}, { "", "Test Results Detail", 1, SEC_TESTRESULT, 2, 64, FLAG_POLLONCE, FLAG_STRING}, { "", "Test Results Summary", 1, SEC_TESTRESULT, 1, 6, FLAG_POLLONCE, 0}, { "", "Test Type", 1, SEC_TEST, 1, 5, FLAG_POLLONCE, FLAG_WONLY}, { "", "Baud Rate", 1, SEC_BAUDRATE, 1, 19200, FLAG_POLLONCE, FLAG_RW}, }; /* a type for the supported variables */ #define SEC_QUERYLIST_LEN 17 #define SEC_MAXFIELDS 16 #define SEC_POLL 1 #define SEC_POLLONCE 0 struct { const char *command; /* sec command */ int varnum[SEC_MAXFIELDS]; /* sec variable number for each field */ int pollflag; } sec_querylist[SEC_QUERYLIST_LEN]; #define sqv(a,b) sec_querylist[a].varnum[b] nut-2.7.4/drivers/powerp-bin.c0000644000175000017500000003626012640473702013173 00000000000000/* * powerp-bin.c - Model specific routines for CyberPower binary * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2009 Arjen de Korte * * 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 */ /* * Throughout this driver, READ and WRITE comments are shown. These are * the typical commands to and replies from the UPS that was used for * decoding the protocol (with a serial logger). */ #include "main.h" #include "serial.h" #include "powerp-bin.h" #include typedef struct { unsigned char start; unsigned char i_volt; unsigned char o_volt; unsigned char o_load; unsigned char fill_4; unsigned char b_chrg; unsigned char u_temp; unsigned char i_freq; unsigned char fill_8; unsigned char flags[4]; unsigned char stop; } status_t; typedef struct { const char *val; const char command; } valtab_t; static enum { PR = 0, OP = 1 } type = PR; static unsigned char powpan_answer[SMALLBUF]; /* PR series */ static const valtab_t tran_high_pr[] = { { "138", -9 }, { "139", -8 }, { "140", -7 }, { "141", -6 }, { "142", -5 }, { "143", -4 }, { "144", -3 }, { "145", -2 }, { "146", -1 }, { "147", 0 }, { NULL } }; /* OP series */ static const valtab_t tran_high_op[] = { { "140", -5 }, { "141", -4 }, { "142", -3 }, { "143", -2 }, { "144", -1 }, { "145", 0 }, { "146", +1 }, { "147", +2 }, { "148", +3 }, { "149", +4 }, { "150", +5 }, { NULL } }; /* PR series */ static const valtab_t tran_low_pr[] = { { "88", 0 }, { "89", +1 }, { "90", +2 }, { "91", +3 }, { "92", +4 }, { "93", +5 }, { "94", +6 }, { "95", +7 }, { "96", +8 }, { "97", +9 }, { NULL } }; /* OP series */ static const valtab_t tran_low_op[] = { { "85", -5 }, { "86", -4 }, { "87", -3 }, { "88", -2 }, { "89", -1 }, { "90", 0 }, { "91", +1 }, { "92", +2 }, { "93", +3 }, { "94", +4 }, { "95", +5 }, { NULL } }; /* PR series */ static const valtab_t batt_low_pr[] = { { "25", -6 }, { "30", -5 }, { "35", -3 }, { "40", -1 }, { "45", 0 }, { "50", +2 }, { "55", +4 }, { "60", +6 }, { NULL } }; /* OP series */ static const valtab_t batt_low_op[] = { { "15", -8 }, { "18", -7 }, { "19", -6 }, { "20", -5 }, { "22", -4 }, { "24", -3 }, { "25", -2 }, { "26", -1 }, { "28", 0 }, { "30", +1 }, { "32", +2 }, { "34", +3 }, { "35", +4 }, { "36", +5 }, { "38", +6 }, { "40", +7 }, { NULL } }; /* PR series */ static const valtab_t out_volt_pr[] = { { "110", -10 }, { "120", 0 }, { "130", +10 }, { NULL } }; /* OP series */ static const valtab_t out_volt_op[] = { { "110", -2 }, { "115", -1 }, { "120", 0 }, { "124", +1 }, { "128", +2 }, { "130", +3 }, { NULL } }; static const valtab_t yes_no_info[] = { { "yes", 2 }, { "no", 0 }, { NULL } }; /* Older models report the model in a numeric format 'rOnn' */ static const struct { const char *val; const char *model; } modeltab[] = { { "rO10", "OP1000AVR" }, { "rO27", "OP320AVR" }, { "rO29", "OP500AVR" }, { "rO31", "OP800AVR" }, { "rO33", "OP850AVR" }, { "rO37", "OP900AVR" }, { "rO39", "OP650AVR" }, { "rO41", "OP700AVR" }, { "rO43", "OP1250AVR" }, { "rO45", "OP1500AVR" }, { NULL } }; static const struct { const char *var; const char *get; const char *set; const valtab_t *map[2]; } vartab[] = { { "input.transfer.high", "R\x02\r", "Q\x02%c\r", { tran_high_pr, tran_high_op } }, { "input.transfer.low", "R\x04\r", "Q\x04%c\r", { tran_low_pr, tran_low_op } }, { "battery.charge.low", "R\x08\r", "Q\x08%c\r", { batt_low_pr, batt_low_op } }, { "ups.start.battery", "R\x0F\r", "Q\x0F%c\r", { yes_no_info, yes_no_info } }, { "output.voltage.nominal", "R\x18\r", "Q\x18%c\r", { out_volt_pr, out_volt_op } }, { NULL } }; static const struct { const char *cmd; const char *command; const int len; } cmdtab[] = { { "test.battery.start.quick", "T\230\r", 3 }, /* 20 seconds test */ { "test.battery.stop", "CT\r", 3 }, { "beeper.toggle", "B\r", 2 }, { "shutdown.reboot", "S\0\0R\0\1W\r", 8}, /* the shutdown.stayoff command behaves * as shutdown.return when on battery */ { "shutdown.stayoff", "S\0\0W\r", 5 }, { "shutdown.stop", "C\r", 2 }, { NULL } }; /* map UPS data to (approximated) input/output voltage */ static int op_volt(unsigned char in) { if (in < 26) { return 0; } return (((float)in * 200 / 230) + 6); } /* map UPS data to (approximated) load */ static int op_load(unsigned char in) { if (in > 108) { return 200; } return (in * 200) / 108; } /* map UPS data to (approximated) charge percentage */ static int op_chrg(unsigned char in) { if (in > 197) { return 100; } if (in < 151) { return 0; } return ((in - 151) * 100) / 46; } /* map UPS data to (approximated) temperature */ static float op_temp(unsigned char in) { return (pow((float)in / 32, 2) + 10); } /* map UPS data to (approximated) frequency */ static float op_freq(unsigned char in) { return (12600.0 / (in + 32)); } static int powpan_command(const char *buf, size_t bufsize) { int ret; ser_flush_io(upsfd); ret = ser_send_buf_pace(upsfd, UPSDELAY, buf, bufsize); if (ret < 0) { upsdebug_with_errno(3, "send"); return -1; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); return -1; } upsdebug_hex(3, "send", buf, bufsize); usleep(100000); ret = ser_get_buf_len(upsfd, powpan_answer, bufsize-1, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 0) { upsdebug_with_errno(3, "read"); upsdebug_hex(4, " \\_", buf, bufsize-1); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); upsdebug_hex(4, " \\_", buf, bufsize-1); return -1; } upsdebug_hex(3, "read", powpan_answer, ret); return ret; } static int powpan_instcmd(const char *cmdname, const char *extra) { int i; for (i = 0; cmdtab[i].cmd != NULL; i++) { if (strcasecmp(cmdname, cmdtab[i].cmd)) { continue; } if ((powpan_command(cmdtab[i].command, cmdtab[i].len) == cmdtab[i].len - 1) && (!memcmp(powpan_answer, cmdtab[i].command, cmdtab[i].len - 1))) { return STAT_INSTCMD_HANDLED; } upslogx(LOG_ERR, "%s: command [%s] failed", __func__, cmdname); return STAT_INSTCMD_FAILED; } upslogx(LOG_ERR, "%s: command [%s] not found", __func__, cmdname); return STAT_INSTCMD_UNKNOWN; } static int powpan_setvar(const char *varname, const char *val) { char command[SMALLBUF]; int i, j; for (i = 0; vartab[i].var != NULL; i++) { if (strcasecmp(varname, vartab[i].var)) { continue; } if (!strcasecmp(val, dstate_getinfo(varname))) { upslogx(LOG_INFO, "powpan_setvar: [%s] no change for variable [%s]", val, varname); return STAT_SET_HANDLED; } for (j = 0; vartab[i].map[type][j].val != NULL; j++) { if (strcasecmp(val, vartab[i].map[type][j].val)) { continue; } snprintf(command, sizeof(command), vartab[i].set, vartab[i].map[type][j].command); if ((powpan_command(command, 4) == 3) && (!memcmp(powpan_answer, command, 3))) { dstate_setinfo(varname, "%s", val); return STAT_SET_HANDLED; } upslogx(LOG_ERR, "powpan_setvar: setting variable [%s] to [%s] failed", varname, val); return STAT_SET_UNKNOWN; } upslogx(LOG_ERR, "powpan_setvar: [%s] is not valid for variable [%s]", val, varname); return STAT_SET_UNKNOWN; } upslogx(LOG_ERR, "powpan_setvar: variable [%s] not found", varname); return STAT_SET_UNKNOWN; } static void powpan_initinfo(void) { int i, j; char *s; dstate_setinfo("ups.delay.start", "%d", 45); dstate_setinfo("ups.delay.shutdown", "%d", 0); /* almost immediate */ /* * NOTE: The reply is already in the buffer, since the F\r command * was used for autodetection of the UPS. No need to do it again. */ if ((s = strtok((char *)&powpan_answer[1], ".")) != NULL) { for (i = 0; modeltab[i].val != NULL; i++) { if (!strncmp(s, modeltab[i].val, strlen(modeltab[i].val))) { break; } } if (modeltab[i].model) { /* model found in table */ dstate_setinfo("ups.model", "%s", modeltab[i].model); } else { /* report model value as is */ dstate_setinfo("ups.model", "%s", str_rtrim(s, ' ')); } } if ((s = strtok(NULL, ".")) != NULL) { dstate_setinfo("input.voltage.nominal", "%d", (unsigned char)s[0]); } if ((s = strtok(NULL, ".")) != NULL) { dstate_setinfo("input.frequency.nominal", "%d", (unsigned char)s[0]); } if ((s = strtok(NULL, ".")) != NULL) { dstate_setinfo("ups.firmware", "%c.%c%c%c", s[0], s[1], s[2], s[3]); } for (i = 0; cmdtab[i].cmd != NULL; i++) { dstate_addcmd(cmdtab[i].cmd); } for (i = 0; vartab[i].var != NULL; i++) { if (powpan_command(vartab[i].get, 3) < 2) { continue; } for (j = 0; vartab[i].map[type][j].val != NULL; j++) { if (vartab[i].map[type][j].command != powpan_answer[1]) { continue; } dstate_setinfo(vartab[i].var, "%s", vartab[i].map[type][j].val); break; } if (dstate_getinfo(vartab[i].var) == NULL) { upslogx(LOG_WARNING, "warning: [%d] unknown value for [%s]!", powpan_answer[1], vartab[i].var); continue; } dstate_setflags(vartab[i].var, ST_FLAG_RW); for (j = 0; vartab[i].map[type][j].val != NULL; j++) { dstate_addenum(vartab[i].var, "%s", vartab[i].map[type][j].val); } } /* * FIXME: The following commands need to be reverse engineered. It * looks like they are used in detecting the UPS model. To rule out * any incompatibilities, only use them when running in debug mode. */ if (nut_debug_level > 2) { powpan_command("R\x29\r", 3); powpan_command("R\x2B\r", 3); powpan_command("R\x3D\r", 3); } dstate_addcmd("shutdown.stayoff"); dstate_addcmd("shutdown.reboot"); } static int powpan_status(status_t *status) { int ret; ser_flush_io(upsfd); /* * WRITE D\r * READ #VVL.CTF.....\r * 01234567890123 */ ret = ser_send_pace(upsfd, UPSDELAY, "D\r"); if (ret < 0) { upsdebug_with_errno(3, "send"); return -1; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); return -1; } upsdebug_hex(3, "send", "D\r", 2); usleep(200000); ret = ser_get_buf_len(upsfd, status, sizeof(*status), SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 0) { upsdebug_with_errno(3, "read"); upsdebug_hex(4, " \\_", status, sizeof(*status)); return -1; } if (ret == 0) { upsdebugx(3, "read: timeout"); upsdebug_hex(4, " \\_", status, sizeof(*status)); return -1; } upsdebug_hex(3, "read", status, ret); if ((status->flags[0] + status->flags[1]) != 255) { upsdebugx(4, " \\_ : checksum flags[0..1] failed"); return -1; } if ((status->flags[2] + status->flags[3]) != 255) { upsdebugx(4, " \\_ : checksum flags[2..3] failed"); return -1; } return 0; } static int powpan_updateinfo(void) { status_t status; if (powpan_status(&status)) { return -1; } switch (type) { case OP: dstate_setinfo("input.voltage", "%d", op_volt(status.i_volt)); if (status.flags[0] & 0x84) { dstate_setinfo("output.voltage", "%s", dstate_getinfo("output.voltage.nominal")); } else { dstate_setinfo("output.voltage", "%d", op_volt(status.i_volt)); } dstate_setinfo("ups.load", "%d", op_load(status.o_load)); dstate_setinfo("battery.charge", "%d", op_chrg(status.b_chrg)); dstate_setinfo("ups.temperature", "%.1f", op_temp(status.u_temp)); dstate_setinfo("input.frequency", "%.1f", op_freq(status.i_freq)); break; default: dstate_setinfo("input.voltage", "%d", status.i_volt); if (status.flags[0] & 0x84) { dstate_setinfo("output.voltage", "%s", dstate_getinfo("output.voltage.nominal")); } else { dstate_setinfo("output.voltage", "%d", status.o_volt); } dstate_setinfo("ups.load", "%d", status.o_load); dstate_setinfo("battery.charge", "%d", status.b_chrg); dstate_setinfo("ups.temperature", "%d", status.u_temp); dstate_setinfo("input.frequency", "%.1f", (status.i_freq == 0) ? 0.0 : 45.0 + (float)status.i_freq / 10.0); break; } if (status.flags[0] & 0x01) { dstate_setinfo("ups.beeper.status", "enabled"); } else { dstate_setinfo("ups.beeper.status", "disabled"); } status_init(); if (status.flags[0] & 0x80) { status_set("OB"); } else { status_set("OL"); } if (status.flags[0] & 0x40) { status_set("LB"); } /* !OB && !TEST */ if (!(status.flags[0] & 0x84)) { if (status.o_volt < 0.5 * status.i_volt) { upsdebugx(2, "%s: output voltage too low", __func__); } else if (status.o_volt < 0.95 * status.i_volt) { status_set("TRIM"); } else if (status.o_volt < 1.05 * status.i_volt) { upsdebugx(2, "%s: appears to be in BYPASS state", __func__); } else if (status.o_volt < 1.5 * status.i_volt) { status_set("BOOST"); } else { upsdebugx(2, "%s: output voltage too high", __func__); } } if (status.flags[0] & 0x04) { status_set("TEST"); } if (status.flags[0] == 0) { status_set("OFF"); } status_commit(); return (status.flags[0] & 0x80) ? 1 : 0; } static int powpan_initups(void) { int ret, i; upsdebugx(1, "Trying %s protocol...", powpan_binary.version); ser_set_speed(upsfd, device_path, B1200); /* This fails for many devices, so don't bother to complain */ ser_send_pace(upsfd, UPSDELAY, "\r\r"); for (i = 0; i < MAXTRIES; i++) { ser_flush_io(upsfd); /* * WRITE F\r * READ .PR2200 .x.<.1100 * 01234567890123456789 */ ret = ser_send_pace(upsfd, UPSDELAY, "F\r"); if (ret < 0) { upsdebug_with_errno(3, "send"); continue; } if (ret == 0) { upsdebug_with_errno(3, "send: timeout"); continue; } upsdebug_hex(3, "send", "F\r", 2); usleep(200000); ret = ser_get_line(upsfd, powpan_answer, sizeof(powpan_answer), ENDCHAR, IGNCHAR, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 0) { upsdebug_with_errno(3, "read"); upsdebug_hex(4, " \\_", powpan_answer, strlen((char *)powpan_answer)); continue; } if (ret == 0) { upsdebugx(3, "read: timeout"); upsdebug_hex(4, " \\_", powpan_answer, strlen((char *)powpan_answer)); continue; } upsdebug_hex(3, "read", powpan_answer, ret); if (ret < 20) { upsdebugx(2, "Expected 20 bytes but only got %d", ret); continue; } if (powpan_answer[0] != '.') { upsdebugx(2, "Expected start character '.' but got '%c'", (char)powpan_answer[0]); continue; } /* See if we need to use the 'old' protocol for the OP series */ if (!strncmp((char *)&powpan_answer[1], "OP", 2)) { type = OP; } /* This is for an older model series, that reports the model numerically */ if (!strncmp((char *)&powpan_answer[1], "rO", 2)) { type = OP; } if (getval("ondelay")) { fatalx(EXIT_FAILURE, "Setting 'ondelay' not supported by %s driver", powpan_binary.version); } if (getval("offdelay")) { fatalx(EXIT_FAILURE, "Setting 'offdelay' not supported by %s driver", powpan_binary.version); } return ret; } return -1; } subdriver_t powpan_binary = { "binary", powpan_instcmd, powpan_setvar, powpan_initups, powpan_initinfo, powpan_updateinfo }; nut-2.7.4/drivers/nutdrv_qx_blazer-common.h0000644000175000017500000000314412640473702015770 00000000000000/* nutdrv_qx_blazer-common.h - Common functions/settings for nutdrv_qx_{mecer,megatec,megatec-old,mustek,q1,voltronic-qs,zinto}.{c,h} * * Copyright (C) * 2013 Daniele Pezzini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef NUTDRV_QX_BLAZER_COMMON_H #define NUTDRV_QX_BLAZER_COMMON_H #include "nutdrv_qx.h" /* Support functions */ void blazer_makevartable(void); void blazer_makevartable_light(void); void blazer_initups(item_t *qx2nut); void blazer_initups_light(item_t *qx2nut); int blazer_claim(void); int blazer_claim_light(void); /* Preprocess functions */ int blazer_process_command(item_t *item, char *value, const size_t valuelen); int blazer_process_setvar(item_t *item, char *value, const size_t valuelen); int blazer_process_status_bits(item_t *item, char *value, const size_t valuelen); /* Ranges */ extern info_rw_t blazer_r_ondelay[]; extern info_rw_t blazer_r_offdelay[]; #endif /* NUTDRV_QX_BLAZER_COMMON_H */ nut-2.7.4/AUTHORS0000644000175000017500000001152412640473702010333 00000000000000# The NUT AUTHORS file. Don't be shy. If you have contributed to this # project in some way, send me a patch to add yourself to this file. # # Everyone deserves credit, including those who haven't added any code. # Ideas, clues, and helping out on the mailing lists all count too. # # This is a blatant ripoff of the fields found in the Linux kernel's CREDITS # file. If we need more data, those fields can always be added later. # # N = name, E = email, W = web address, D = description, P = PGP info, # S = snailmail address, etc. # # This file is supposed to be roughly alpha-sorted by the last name, but # if you want to hide at the bottom, that's fine by me. Just clarify # your preference when submitting changes to this file. N: Stephen Brown E: steve@datalimbo.net W: http://www.datalimbo.net/ D: Hacked genericups to add TrippLite Lan2.x support (Internet Office 700) N: Bill Carlson E: wcarlson@wkks.org W: http://wkks.org/ D: Fixed the GD/configure problem N: Ben Collver E: collver@softhome.net W: http://superfluous.oddbox.org D: Beginning support for HPUX 10.20 and Windows 2000 N: Luca Filipozzi E: lfilipoz@debian.org D: Original Debian maintainer for nut package. Minor patches to source. N: Matthew Gabeler-Lee E: msg2@po.cwru.edu W: http://cheetah.cwru.edu D: Added custom formatting to upslog D: Helped get apcsmart working with old SmartUPS models N: David Goncalves E: david@lestat.st W: http://www.lestat.st D: Python client support (PyNUT module and NUT-Monitor application) N: Bruno Hall D: Contributed UPS compatibility information N: Bo Kersey - VirCIO - Managed Server Solutions E: bo@vircio.com W: http://www.vircio.com/ D: Provided a Best Fortress for development of a Best driver (bestups) N: Russell Kroll E: rkroll@exploits.org W: http://www.networkupstools.org/ D: Original NUT author and coordinator P: 1024D/9DC0E77E 6A5C 7D2D 7945 C022 6104 D421 D61D C97F 9DC0 E77E N: Rick Lyons E: rick@powerup.com.au D: Support for Liebert UPSes using MultiLink cable N: Jeremy Maccelari E: visualn@iafrica.com, jeremy@visuals.co.za W: http://www.visuals.co.za D: Support for Mustek UPSes N: Philippe Marzouk E: philm@users.sourceforge.net D: Support for MGE Pulsar Ellipse UPSes ; co author of mge-shut N: Theodor A. Milkov E: zimage@delbg.com W: http://www.delbg.com/~zimage/ D: Adding support for Repotec's RPT-800A, RPT-162A to genericups driver. N: Mark Powell E: medp@primagraphics.co.uk D: Ported to SunOS4 N: Arnaud Quette E: aquette.dev@gmail.com E: arnaud.quette@mgeups.com E: aquette@debian.org W: http://arnaud.quette.free.fr/ D: Primary coordinator ; author of snmp-ups, mge-shut, usbhid-ups ; D: co author of mge-utalk, hidups, SNMP UPS Agent ; contributor to blazer, D: bestferrups, nut core, and many others ; coordination with MGE UPS D: SYSTEMS, linux-usb developers (for hidups), Net SNMP and packagers D: (Mandriva, Debian, SuSE, ...)... P: 1024D/204DDF1B 1371 07DF 3CF3 9160 7905 144B DB64 14CA 204D DF1B N: Lars Balker Rasmussen E: lbr@mjolner.dk D: Solaris and minor patches N: David Santinoli E: david@santinoli.com W: http://www.santinoli.com D: Support for Online P-series in genericups driver N: Jacob Schmier E: j.schmier@live.com D: support for Universal-Mount ON Series UPS family in oneac driver N: Peter Selinger E: selinger@users.sourceforge.net W: http://www.mathstat.dal.ca/~selinger/ D: wrote belkinunv driver, contributions to usbhid-ups P: 1024D/CA31696A 12A2 4B3C 3790 B688 E484 7A98 A68B CC37 CA31 696A N: Kirill Smelkov E: kirr@mns.spb.ru D: Author of al175 N: John Stone E: johns@megapixel.com W: http://www.megapixel.com/ D: Support for Best MicroFerrups UPS N: Technorama Ltd. E: oss-list-ups@technorama.net D: common driver core design, redundant code elimination, security fixes D: other misc patches and improvements throughout N: Jason Thomas E: jason@topic.com.au W: http://www.topic.com.au/ D: Hacked up the UPSonic Driver. N: Simon Rozman E: simon@rozman.net W: http://simon.rozman.net/ D: Hacked powercom to add Socomec Sycon Egys 420 VA support N: Len J White E: lwhite@darkfires.net N: Walt Holman E: walt_h@lorettotel.net D: Hacked up the cpsups driver for CyberPower text protocol UPSes N: Fabio Di Niro E: blaxwan@users.sourceforge.net D: Author of metasys driver, support for Meta System UPS N: Arjen de Korte E: arjen@de-korte.org D: Author of safenet driver P: 1024R/AEF3BA11 664E 032C 9DB5 CB9B 7AFE 7EC1 EE88 BC57 N: HÃ¥vard Lygre E: hklygre@online.no D: First stab at upscode2 driver for NUT 1.4 N: Niels Baggesen E: niels@baggesen.net D: upgraded the upscode2 driver to NUT-2, and extended it heavily. N: Niklas Edmundsson E: nikke@acc.umu.se D: 3-phase work, updates for upscode2 N: Olli Savia E: ops@iki.fi D: pwmib support for snmp-ups N: Kjell Claesson E: Kjell.claesson@epost.tidanet.se D: Author of bcmxcp driver, 3-phase work. N: Giuseppe Corbelli E: giuseppe.corbelli@copanitalia.com D: Author of asem driver nut-2.7.4/data/0000755000175000017500000000000012670024741010247 500000000000000nut-2.7.4/data/cmdvartab0000644000175000017500000003304412667537407012077 00000000000000# Network UPS Tools: variable and command descriptions # # This file is optional. You may delete it to save resources, but # clients will receive "Unavailable" for any description requests. # # *** NOTE *** : When updating this file, also update docs/nut-names.txt VARDESC ups.alarm "UPS alarms" VARDESC ups.status "UPS status" VARDESC ups.time "Internal UPS clock time" VARDESC ups.date "Internal UPS clock date" VARDESC ups.efficiency "Efficiency of the UPS" VARDESC ups.model "UPS model" VARDESC ups.mfr "UPS manufacturer" VARDESC ups.mfr.date "UPS manufacturing date" VARDESC ups.serial "UPS serial number" VARDESC ups.vendorid "Vendor ID for USB devices" VARDESC ups.productid "Product ID for USB devices" VARDESC ups.firmware "UPS firmware" VARDESC ups.firmware.aux "Auxiliary device firmware" VARDESC ups.temperature "UPS temperature (degrees C)" VARDESC ups.load "Load on UPS (percent of full)" VARDESC ups.load.energysave "Load on UPS that triggers energysave (percent)" VARDESC ups.load.high "Load when UPS switches to overload condition (percent)" VARDESC ups.id "UPS system identifier" VARDESC ups.delay.start "Interval to wait before (re)starting the load (seconds)" VARDESC ups.delay.reboot "Interval to wait before rebooting the UPS (seconds)" VARDESC ups.delay.shutdown "Interval to wait after shutdown with delay command (seconds)" VARDESC ups.timer.start "Time before the load will be started (seconds)" VARDESC ups.timer.reboot "Time before the load will be rebooted (seconds)" VARDESC ups.timer.shutdown "Time before the load will be shutdown (seconds)" VARDESC ups.test.interval "Interval between self tests (seconds)" VARDESC ups.test.result "Results of last self test" VARDESC ups.display.language "Language to use on front panel" VARDESC ups.contacts "UPS external contact sensors" VARDESC ups.power "Current value of apparent power (VA)" VARDESC ups.power.nominal "UPS power rating (VA)" VARDESC ups.realpower "Current value of real power (W)" VARDESC ups.realpower.nominal "UPS real power rating (W)" VARDESC ups.beeper.status "UPS beeper status" VARDESC ups.type "UPS type" VARDESC ups.start.auto "UPS starts when mains is (re)applied" VARDESC ups.start.battery "Allow to start UPS from battery" VARDESC ups.start.reboot "UPS reboots when power returns during shutdown delay" VARDESC input.voltage "Input voltage (V)" VARDESC input.voltage.extended "Extended input voltage range" VARDESC input.voltage.maximum "Maximum incoming voltage seen (V)" VARDESC input.voltage.minimum "Minimum incoming voltage seen (V)" VARDESC input.voltage.status "Voltage status relative to the thresholds" VARDESC input.voltage.low.warning "Input voltage low warning threshold (V)" VARDESC input.voltage.low.critical "Input voltage low critical threshold (V)" VARDESC input.voltage.high.warning "Input voltage high warning threshold (V)" VARDESC input.voltage.high.critical "Input voltage high critical threshold (V)" VARDESC input.voltage.nominal "Nominal input voltage (V)" VARDESC input.transfer.reason "Reason for last transfer to battery" VARDESC input.transfer.low "Low voltage transfer point (V)" VARDESC input.transfer.high "High voltage transfer point (V)" VARDESC input.transfer.low.min "smallest settable low voltage transfer point (V)" VARDESC input.transfer.low.max "greatest settable low voltage transfer point (V)" VARDESC input.transfer.high.min "smallest settable high voltage transfer point (V)" VARDESC input.transfer.high.max "greatest settable high voltage transfer point (V)" VARDESC input.sensitivity "Input power sensitivity" VARDESC input.quality "Input power quality" VARDESC input.current "Input current (A)" VARDESC input.current.nominal "Nominal input current (A)" VARDESC input.current.status "Current status relative to the thresholds" VARDESC input.current.low.warning "Input current low warning threshold (A)" VARDESC input.current.low.critical "Input current low critical threshold (A)" VARDESC input.current.high.warning "Input current high warning threshold (A)" VARDESC input.current.high.critical "Input current high critical threshold (A)" VARDESC input.frequency "Input line frequency (Hz)" VARDESC input.frequency.extended "Extended input frequency range" VARDESC input.frequency.status "Frequency status" VARDESC input.frequency.nominal "Nominal input line frequency (Hz)" VARDESC input.frequency.low "Minimum input line frequency (Hz)" VARDESC input.frequency.high "Maximum input line frequency (Hz)" VARDESC input.transfer.boost.low "Low voltage boosting transfer point (V)" VARDESC input.transfer.boost.high "High voltage boosting transfer point (V)" VARDESC input.transfer.trim.low "Low voltage trimming transfer point (V)" VARDESC input.transfer.trim.high "High voltage trimming transfer point (V)" VARDESC input.transfer.delay "Delay before transfer to mains" VARDESC input.load "Load on (ePDU) input (percent of full)" VARDESC input.realpower "Current sum value of all (ePDU) phases real power (W)" VARDESC input.power "Current sum value of all (ePDU) phases apparent power (VA)" VARDESC input.source "The current input power source" VARDESC input.source.preferred "The prefered input power source" VARDESC output.voltage "Output voltage (V)" VARDESC output.voltage.nominal "Nominal output voltage (V)" VARDESC output.frequency "Output frequency (Hz)" VARDESC output.frequency.nominal "Nominal output frequency (Hz)" VARDESC output.current "Output current (A)" VARDESC output.current.nominal "Nominal output current (A)" VARDESC battery.charge "Battery charge (percent of full)" VARDESC battery.charge.low "Remaining battery level when UPS switches to LB (percent)" VARDESC battery.charge.restart "Minimum battery level for restart after power off (percent)" VARDESC battery.charge.warning "Battery level when UPS switches to Warning state (percent)" VARDESC battery.voltage "Battery voltage (V)" VARDESC battery.current "Battery current (A)" VARDESC battery.capacity "Battery capacity (Ah)" VARDESC battery.temperature "Battery temperature (degrees C)" VARDESC battery.voltage.nominal "Nominal battery voltage (V)" VARDESC battery.runtime "Battery runtime (seconds)" VARDESC battery.runtime.low "Remaining battery runtime when UPS switches to LB (seconds)" VARDESC battery.alarm.threshold "Battery alarm threshold" VARDESC battery.date "Battery change date" VARDESC battery.mfr.date "Battery manufacturing date" VARDESC battery.packs "Number of battery packs" VARDESC battery.packs.bad "Number of bad battery packs" VARDESC battery.type "Battery chemistry" VARDESC battery.protection "Prevent deep discharge of battery" VARDESC battery.energysave "Switch off when running on battery and no/low load" VARDESC battery.energysave.load "Switch off UPS if on battery and load level lower (percent)" VARDESC battery.energysave.delay "Delay before switch off UPS if on battery and load level low (min)" VARDESC battery.energysave.realpower "Switch off UPS if on battery and load level lower (Watts)" VARDESC battery.charger.status "Battery charger status" VARDESC ambient.temperature "Ambient temperature (degrees C)" VARDESC ambient.temperature.alarm "Ambient temperature alarm is active" VARDESC ambient.temperature.status "Ambient temperature status relative to the configured thresholds" VARDESC ambient.temperature.alarm.maximum "Maximum allowed ambient temperature (degrees C)" VARDESC ambient.temperature.alarm.minimum "Minimum allowed ambient temperature (degrees C)" VARDESC ambient.temperature.alarm.enable "Enable ambient temperature alarm" VARDESC ambient.temperature.low "Temperature threshold low (degrees C)" VARDESC ambient.temperature.low.warning "Temperature threshold low warning (degrees C)" VARDESC ambient.temperature.low.critical "Temperature threshold low critical (degrees C)" VARDESC ambient.temperature.high "Temperature threshold high (degrees C)" VARDESC ambient.temperature.high.warning "Temperature threshold high warning (degrees C)" VARDESC ambient.temperature.high.critical "Temperature threshold high critical (degrees C)" VARDESC ambient.humidity "Ambient humidity (percent)" VARDESC ambient.humidity.alarm "Ambient humidity alarm is active" VARDESC ambient.humidity.status "Ambient humidity status relative to the configured thresholds" VARDESC ambient.humidity.alarm.maximum "Maximum allowed ambient humidity (percent)" VARDESC ambient.humidity.alarm.minimum "Minimum allowed ambient humidity (percent)" VARDESC ambient.humidity.alarm.enable "Enable ambient humidity alarm" VARDESC ambient.humidity.low "Ambient humidity threshold low (percent)" VARDESC ambient.humidity.low.warning "Ambient humidity threshold low warning (percent)" VARDESC ambient.humidity.low.critical "Ambient humidity threshold low critical (percent)" VARDESC ambient.humidity.high "Ambient humidity threshold high (percent)" VARDESC ambient.humidity.high.warning "Ambient humidity threshold high warning (percent)" VARDESC ambient.humidity.high.critical "Ambient humidity threshold high critical (percent)" VARDESC ambient.present "Ambient sensor presence" VARDESC ambient.contacts.1.status "State of the dry contact sensor 1" VARDESC ambient.contacts.2.status "State of the dry contact sensor 2" # FIXME: the outlet collection is indexed - solve with regexs? # # VARDESC outlet.[[:digit:]]+.id "Outlet system identifier ()" VARDESC outlet.id "Outlet system identifier" VARDESC outlet.desc "Outlet description" VARDESC outlet.switch "Outlet switch control" VARDESC outlet.status "Outlet switch status" VARDESC outlet.switchable "Outlet switch ability" VARDESC outlet.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.delay.start "Interval to wait before restarting this outlet (seconds)" VARDESC outlet.1.id "Outlet system identifier" VARDESC outlet.1.desc "Outlet description" VARDESC outlet.1.switch "Outlet switch control" VARDESC outlet.1.status "Outlet switch status" VARDESC outlet.1.switchable "Outlet switch ability" VARDESC outlet.1.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.1.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.1.delay.start "Interval to wait before restarting this outlet (seconds)" VARDESC outlet.2.id "Outlet system identifier" VARDESC outlet.2.desc "Outlet description" VARDESC outlet.2.switch "Outlet switch control" VARDESC outlet.2.status "Outlet switch status" VARDESC outlet.2.switchable "Outlet switch ability" VARDESC outlet.2.autoswitch.charge.low "Remaining battery level to power off this outlet (percent)" VARDESC outlet.2.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.2.delay.start "Interval to wait before restarting this outlet (seconds)" VARDESC driver.name "Driver name" VARDESC driver.version "Driver version - NUT release" VARDESC driver.version.internal "Internal driver version" VARDESC device.part "Device Part Number" # FIXME: driver.parameter and driver.flag can have many possible members # # VARDESC driver.parameter.[[:alpha:]]+ "Driver parameter: " # VARDESC driver.flag.[[:alpha:]]+ "Driver flag: " VARDESC server.info "Server information" VARDESC server.version "Server version" CMDDESC load.off "Turn off the load immediately" CMDDESC load.on "Turn on the load immediately" CMDDESC shutdown.return "Turn off the load and return when power is back" CMDDESC shutdown.stayoff "Turn off the load and remain off" CMDDESC shutdown.stop "Stop a shutdown in progress" CMDDESC shutdown.reboot "Shut down the load briefly while rebooting the UPS" CMDDESC shutdown.reboot.graceful "Delay briefly then shut down the load while rebooting the UPS" CMDDESC test.panel.start "Start testing the UPS panel" CMDDESC test.panel.stop "Stop a UPS panel test" CMDDESC test.failure.start "Start a simulated power failure" CMDDESC test.failure.stop "Stop simulating a power failure" CMDDESC test.battery.start "Start a battery test" CMDDESC test.battery.start.quick "Start a quick battery test" CMDDESC test.battery.start.deep "Start a deep battery test" CMDDESC test.battery.stop "Stop the battery test" CMDDESC test.system.start "Start a system test" CMDDESC calibrate.start "Start run time calibration" CMDDESC calibrate.stop "Stop run time calibration" CMDDESC bypass.start "Put the UPS in bypass mode" CMDDESC bypass.stop "Take the UPS out of bypass mode" CMDDESC reset.input.minmax "Reset minimum and maximum input voltage status" CMDDESC reset.watchdog "Reset watchdog timer" CMDDESC beeper.on "Obsolete (use beeper.enable)" CMDDESC beeper.off "Obsolete (use beeper.disable or beeper.mute)" CMDDESC beeper.enable "Enable the UPS beeper" CMDDESC beeper.disable "Disable the UPS beeper" CMDDESC beeper.mute "Temporarily mute the UPS beeper" CMDDESC beeper.toggle "Toggle the UPS beeper" CMDDESC outlet.1.load.off "Turn off the load on outlet 1 immediately" CMDDESC outlet.1.load.on "Turn on the load on outlet 1 immediately" CMDDESC outlet.1.shutdown.return "Turn off the outlet 1 and return when power is back" CMDDESC outlet.2.load.off "Turn off the load on outlet 2 immediately" CMDDESC outlet.2.load.on "Turn on the load on outlet 2 immediately" CMDDESC outlet.2.shutdown.return "Turn off the outlet 2 and return when power is back" # The following two commands should *only* be defined when you need # to compose a 'shutdown.return' command by sending both a switch-off # with delay and a switch-on with delay command to the UPS. If this # can be done with a single command, use 'shutdown.return' instead. # Note that the switch-on with delay command *must not* turn on the # load if the UPS is on battery. # CMDDESC load.off.delay "Turn off the load with a delay (seconds)" CMDDESC load.on.delay "Turn on the load with a delay (seconds)" nut-2.7.4/data/Makefile.in0000644000175000017500000005746212667762000012255 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: data VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = data DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/driver.list.in $(dist_data_DATA) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = driver.list CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(datadir)" "$(DESTDIR)$(datadir)" DATA = $(dist_data_DATA) $(nodist_data_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ SUBDIRS = html dist_data_DATA = cmdvartab nodist_data_DATA = driver.list EXTRA_DIST = evolution500.seq epdu-managed.dev all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu data/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu data/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): driver.list: $(top_builddir)/config.status $(srcdir)/driver.list.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-dist_dataDATA: $(dist_data_DATA) @$(NORMAL_INSTALL) @list='$(dist_data_DATA)'; test -n "$(datadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(datadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(datadir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(datadir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(datadir)" || exit $$?; \ done uninstall-dist_dataDATA: @$(NORMAL_UNINSTALL) @list='$(dist_data_DATA)'; test -n "$(datadir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(datadir)'; $(am__uninstall_files_from_dir) install-nodist_dataDATA: $(nodist_data_DATA) @$(NORMAL_INSTALL) @list='$(nodist_data_DATA)'; test -n "$(datadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(datadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(datadir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(datadir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(datadir)" || exit $$?; \ done uninstall-nodist_dataDATA: @$(NORMAL_UNINSTALL) @list='$(nodist_data_DATA)'; test -n "$(datadir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(datadir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(datadir)" "$(DESTDIR)$(datadir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dist_dataDATA install-nodist_dataDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-dist_dataDATA uninstall-nodist_dataDATA .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_dataDATA install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-nodist_dataDATA \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am \ uninstall-dist_dataDATA uninstall-nodist_dataDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/data/evolution500.seq0000644000175000017500000000257212640443572013164 00000000000000# dummy-ups example power sequence file # # Base is the same as .dev files, generated using: # $ upsc ups@host > evolution500.seq # # TIMER have then been added to generate power events. battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 driver.name: usbhid-ups driver.parameter.port: auto driver.version: 2.2.0 driver.version.data: MGE HID 0.7 driver.version.internal: 0.23 input.frequency: 49.0 input.transfer.boost.low: 184.0 input.transfer.high: 294.0 input.transfer.low: 160.0 input.transfer.trim.high: 265.0 input.voltage: 230.0 outlet.desc: Main Outlet outlet.id: 0 outlet.switchable: 0 outlet.1.autoswitch.charge.low: 0 outlet.1.delay.shutdown: -1 outlet.1.delay.start: -1 outlet.1.desc: PowerShare Outlet 1 outlet.1.id: 1 outlet.1.switch: 1 outlet.1.switchable: 1 outlet.2.autoswitch.charge.low: 0 outlet.2.delay.shutdown: -1 outlet.2.delay.start: -1 outlet.2.desc: PowerShare Outlet 2 outlet.2.id: 2 outlet.2.switch: 1 outlet.2.switchable: 1 output.current: 0.00 output.frequency: 49.0 output.voltage: 230.0 output.voltage.nominal: 230.0 ups.delay.shutdown: -1 ups.delay.start: -10 ups.load: 10 ups.mfr: MGE UPS SYSTEMS ups.model: Pulsar Evolution 500 ups.power.nominal: 500 ups.serial: AV2G3300L ups.status: OL CHRG ups.test.interval: 604800 ups.test.result: Done and passed TIMER 300 ups.status: OB DISCHRG TIMER 300 ups.status: OB LB DISCHRG TIMER 60 nut-2.7.4/data/driver.list.in0000644000175000017500000022766012670024741013001 00000000000000# Network UPS Tools - @PACKAGE_VERSION@ - Hardware Compatibility List # version=2 # # This file is used for various purposes, like building the HTML compatibility # list or displaying information in NUT configuration tools. # # If you write a new driver, modify an existing one to add more support, # or just know about some equipment that isn't listed but should be, # please send us a patch to update this file. # # Format: # ======= # # # Details: # ======== # - device type: # "ups" for Uninterruptible Power Supply # "pdu" for Power Distributions Unit # "scd" for Solar Controlers Device # "ats" for Automatic Transfer Switch # # - support level: a number from "1" to "5" (stars) meaning: # * protocol based on reverse engineering # ** based on fragments of publicly available protocol # *** based on publicly available protocol # **** vendor provided protocol # ***** vendor provided protocol and hardware # # - driver [parameters]: mention the driver(s) (separated by "or" if there are # several possibilities). Any mandatory parameter(s) must be referenced, as # they will be parsed by configuration helper software. # For example, genericups entries must mention the "upstype=nn" format. # # Duplicate text in the last field will be cooked out during the conversion # to HTML with ROWSPAN magic. They must be an exact match for this to work. "Ablerex" "ups" "2" "MS-RT" "" "blazer_ser" "Ablerex" "ups" "2" "625L" "USB" "blazer_usb" "Ablerex" "ups" "2" "Hope Office 400/600" "" "blazer_ser" "ActivePower" "ups" "2" "400VA" "" "blazer_ser" "ActivePower" "ups" "2" "1400VA" "" "blazer_ser" "ActivePower" "ups" "2" "2000VA" "" "blazer_ser" "Advice" "ups" "2" "TopGuard 2000" "" "blazer_ser" "AEC" "ups" "1" "MiniGuard UPS 700" "Megatec M2501 cable" "genericups upstype=21" "AEG Power Solutions" "ups" "2" "PROTECT HOME" "" "blazer_ser or blazer_usb" "AEG Power Solutions" "ups" "3" "PROTECT NAS" "" "usbhid-ups" "AEG Power Solutions" "ups" "3" "PROTECT B" "" "usbhid-ups" "APC" "ups" "2" "Back-UPS 1200BR (Microsol)" "" "solis" "APC" "ups" "2" "Back-UPS BZ2200BI-BR (Microsol)" "" "solis" "APC" "ups" "1" "Back-UPS Pro" "" "apcsmart" "APC" "ups" "1" "Matrix-UPS" "" "apcsmart" "APC" "ups" "1" "Smart-UPS" "" "apcsmart" "APC" "ups" "1" "Smart-UPS SMT/SMX/SURTD" "Microlink models with RJ45 socket - they *require* AP9620 SmartSlot expansion card and smart cable" "apcsmart" "APC" "ups" "2" "Back-UPS Pro USB" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS USB" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS RS USB" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS LS USB" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS ES/CyberFort 350" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS BF500" "USB" "usbhid-ups" "APC" "ups" "2" "BACK-UPS XS LCD" "USB" "usbhid-ups" "APC" "ups" "2" "Smart-UPS USB" "USB" "usbhid-ups" "APC" "ups" "1" "Back-UPS" "940-0095A/C cables" "genericups upstype=1" "APC" "ups" "1" "Back-UPS" "940-0020B/C cables" "genericups upstype=2" "APC" "ups" "1" "Back-UPS" "940-0023A cable" "genericups upstype=9" "APC" "ups" "1" "Back-UPS Office" "940-0119A cable" "genericups upstype=12" "APC" "ups" "1" "Back-UPS RS 500" "custom non-USB cable" "genericups upstype=20" "APC" "ups" "3" "Smart-UPS RT XL" "AP9618 SNMP monitoring card" "snmp-ups" "APC" "ups" "3" "(various)" "AP9618 SNMP monitoring card" "snmp-ups" "APC" "ups" "3" "(various)" "AP9630 SNMP monitoring card" "snmp-ups privProtocol=AES" "APC" "ups" "3" "(various)" "APCUPSD-controlled devices" "apcupsd-ups" "APC" "pdu" "1" "Masterswitch" "940-0020 cable" "genericups upstype=12" "APC" "pdu" "1" "AP9210" "8 outlets" "powerman-pdu (experimental)" "APC" "pdu" "1" "AP79xx" "8 to 24 outlets" "powerman-pdu (experimental)" "APC" "pdu" "3" "(various)" "SNMP monitoring card" "snmp-ups (experimental)" "APC" "ats" "3" "ATS AP7724" "" "snmp-ups (experimental)" "Aphel" "pdu" "3" "various PDU" "" "snmp-ups (experimental)" "Apollo" "ups" "1" "1000A" "" "genericups upstype=4" "Apollo" "ups" "1" "1000F" "" "genericups upstype=4" "Apollo" "ups" "2" "850VA" "" "blazer_usb" "Appro" "pdu" "1" "SWPDU" "48 outlets" "powerman-pdu (experimental)" "ARTronic" "ups" "2" "ARTon Millenium 1/2/3/6/10 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Millenium 3.1 10/15/20 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Titanium 6/10 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Titanium 3.1 10/15/20 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Optimum 1/2/3/6/10 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Titanium Plus 1/2/3 kVA" "USB" "blazer_usb" "ARTronic" "ups" "2" "ARTon Platinium 6/10 kVA" "USB" "blazer_usb" "ARTronic" "ups" "2" "ARTon Platinium Combo 3.1 10/15/20 kVA" "USB" "blazer_usb" "ARTronic" "ups" "2" "ARTon Platinium RT 1/2/3/6/10 kVA" "USB" "blazer_usb" "ASEM SPA" "ups" "5" "PB1300 UPS" "i2c" "asem" "Asium" "ups" "3" "P700" "USB" "blazer_usb" # http://www.asiumpower.com/Asium-ASIUM-P700-650VA-360W/dp/B009SMEQ10 "ATEK" "ups" "2" "Defensor 1K Tower / Rack" "USB" "blazer_usb" "ATEK" "ups" "2" "Defensor 2K Tower / Rack" "USB" "blazer_usb" "ATEK" "ups" "2" "Defensor 3K Tower / Rack" "USB" "blazer_usb" "ATEK" "ups" "2" "Defensor 6K Tower / Rack" "USB" "blazer_usb" "ATEK" "ups" "2" "Defensor 10K Tower / Rack" "USB" "blazer_usb" "Atlantis Land" "ups" "2" "A03-P826" "" "blazer_ser" "Atlantis Land" "ups" "2" "A03-P551" "USB" "blazer_usb langid_fix=0x0409" # http://www.atlantisland.it/pub/prodotti.php?famiglia=1&l1=7&l2=24&articolo=QTAzLVA1NTE= "Atlantis Land" "ups" "2" "HostPower 851 (A03-HP851)" "USB" "nutdrv_qx" "Atlantis Land" "ups" "2" "LinePower 1151 (A03-OP1151)" "Serial" "nutdrv_qx" "Atlantis Land" "ups" "2" "LinePower 1151 (A03-OP1151)" "USB" "nutdrv_qx" "Atlantis Land" "ups" "2" "OnePower 841+ (A03-P841)" "USB" "nutdrv_qx" "Atlantis Land" "ups" "2" "(various)" "Serial" "nutdrv_qx" "Atlantis Land" "ups" "2" "(various)" "USB" "nutdrv_qx" "Aviem Systems" "ups" "2" "Aviem Power RT 1000-3000VA" "" "blazer_ser" "Baytech" "pdu" "1" "RPC3" "8 outlets" "powerman-pdu (experimental)" "Baytech" "pdu" "1" "RPC3-20NC" "8 outlets" "powerman-pdu (experimental)" "Baytech" "pdu" "1" "RPC28-30NC" "20 outlets" "powerman-pdu (experimental)" "Baytech" "pdu" "3" "various RPC" "" "snmp-ups" "Belkin" "ups" "2" "Active Battery Backup BU30 series" "USB" "blazer_usb" "Belkin" "ups" "1" "Home Office F6H350-SER" "" "genericups upstype=7" "Belkin" "ups" "1" "Home Office F6H500-SER" "" "genericups upstype=7" "Belkin" "ups" "1" "Home Office F6H650-SER" "" "genericups upstype=7" "Belkin" "ups" "2" "F6H375-USB" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" "Belkin" "ups" "2" "F6H375-USB" "USB (2007 models, vendor id: 0665)" "blazer_usb" "Belkin" "ups" "2" "Office Series F6C550-AVR" "USB" "usbhid-ups" "Belkin" "ups" "3" "Regulator PRO-USB" "USB (~2000, product id: 0f51)" "usbhid-ups" "Belkin" "ups" "2" "Regulator Pro" "F6C525-SER, F6C625-SER" "belkin" "Belkin" "ups" "1" "Resource" "" "genericups upstype=4" "Belkin" "ups" "2" "Small Enterprise F6C1500-TW-RK" "serial port" "belkin" "Belkin" "ups" "2" "Small Enterprise F6C1500-TW-RK" "USB" "usbhid-ups" "Belkin" "ups" "2" "Universal UPS F6C100-UNV" "USB" "usbhid-ups" "Belkin" "ups" "1" "Universal UPS F6C120-UNV" "serial port" "belkinunv" "Belkin" "ups" "2" "Universal UPS F6C120-UNV" "USB" "usbhid-ups" "Belkin" "ups" "1" "Universal UPS F6C800-UNV" "serial port" "belkinunv" "Belkin" "ups" "2" "Universal UPS F6C800-UNV" "USB" "usbhid-ups" "Belkin" "ups" "1" "Universal UPS F6C1100-UNV" "serial port (<= 2005 models)" "belkinunv" "Belkin" "ups" "2" "Universal UPS F6C1100-UNV" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" "Belkin" "ups" "2" "Universal UPS F6C1100-UNV" "USB (2007 models, vendor id: 0665)" "blazer_usb" "Belkin" "ups" "2" "Universal UPS F6C1200-UNV" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" "Belkin" "ups" "2" "Universal UPS F6C1200-UNV" "USB (2007 models, vendor id: 0665)" "blazer_usb" "Belkin" "ups" "2" "Universal UPS F6H350deUNV" "serial port" "blazer_ser" "Belkin" "ups" "2" "Universal UPS F6H350ukUNV" "serial port" "blazer_ser" "Belkin" "ups" "2" "Universal UPS F6H500ukUNV" "serial port" "blazer_ser" "Belkin" "ups" "2" "Universal UPS F6H650ukUNV" "serial port" "blazer_ser" "Belkin" "ups" "2" "F6S600auUSB" "USB" "blazer_usb" "Best Power" "ups" "1" "Fortress (older)" "" "bestfortress" "Best Power" "ups" "1" "Fortress (newer)" "" "blazer_ser or bestups" "Best Power" "ups" "1" "Fortress Telecom" "" "blazer_ser or bestups" "Best Power" "ups" "1" "Axxium Rackmount" "" "blazer_ser or bestups" "Best Power" "ups" "1" "Patriot Pro" "" "blazer_ser or bestups" "Best Power" "ups" "1" "Patriot Pro II" "" "blazer_ser or bestups" "Best Power" "ups" "1" "Patriot" "INT51 cable" "genericups upstype=6" "Best Power" "ups" "1" "Micro-Ferrups" "" "bestuferrups" "Best Power" "ups" "1" "Fortress/Ferrups" "f-command support" "bestfcom" "Borri" "ups" "2" "B400-010-B/B400-020-B/B400-030-B/B400-010-C/B400-020-C/B400-030-C" "" "blazer_usb" "Borri" "ups" "2" "B400-R010-B/B400-R020-B/B400-R030-B/B400-R010-C/B400-R020-C/B400-R030-C" "" "blazer_usb" "Borri" "ups" "2" "B500-060-B/B500-100-B/B500-060-C/B500-100-C" "" "blazer_usb" "Borri" "ups" "2" "B500-R060-B/B500-R100-B" "" "blazer_usb" "Borri" "ups" "2" "B500EVO-100-B/B500EVO-200-B" "" "blazer_usb" "CABAC" "ups" "2" "UPS-1700DV2" "" "blazer_usb" "Chloride" "ups" "2" "Desk Power 650" "serial port" "blazer_ser" "Cito Power" "ups" "2" "CPG-SR1000" "" "blazer_ser" "Clary" "ups" "4" "ST-800" "" "gamatronic" "Compaq" "ups" "4" "T1500h" "" "upscode2 use_pre_lf" "Compaq" "ups" "4" "R3000h" "" "upscode2" "Compaq" "ups" "4" "R3000 XR" "" "bcmxcp" "Compaq" "ups" "4" "R5500 XR" "" "bcmxcp" "COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K" "" "blazer_usb" "COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K EC" "" "blazer_usb" "COVER ENERGY SA" "ups" "2" "COVER PRM 6K/10K PR" "" "blazer_usb" "Cyber Power Systems" "ups" "1" "CPS320AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS500AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS650AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS700AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS800AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS850AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS900AVR" "" "powerpanel" # http://www.cyberpowersystems.com/products/ups-systems/other-ups/CPS900AVR.html "Cyber Power Systems" "ups" "1" "CPS1250AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS1500AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "Power99" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "550SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "725SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "CPS825VA" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS1100AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS1200AVR" "" "powerpanel" "Cyber Power Systems" "ups" "1" "CPS1500AVR-HO" "" "powerpanel" "Cyber Power Systems" "ups" "1" "PR2200" "" "powerpanel" "Cyber Power Systems" "ups" "2" "PR3000E" "" "powerpanel" "Cyber Power Systems" "ups" "2" "OL3000RMXL2U" "" "powerpanel" "Cyber Power Systems" "ups" "2" "CPS685AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CPS800AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "AE550" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "CP 1500C" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "CP825AVR-G / LE825G" "USB" "usbhid-ups" # http://www.cyberpowersystems.com/products/ups-systems/retail-products/LE825G.html "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "PR6000LCDRTXL5U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "Value 400E" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "Value 600E" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "Value 800E" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "Value 1500ELCD-RU" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP900AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1000AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1350AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1000PFCLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "RMCARD100" "" "snmp-ups" "Cyber Power Systems" "ups" "3" "RMCARD201" "" "snmp-ups" "Cyber Power Systems" "ups" "3" "RMCARD202" "" "snmp-ups" "Cyber Power Systems" "ups" "3" "RMCARD301" "" "snmp-ups" "Cyclades" "pdu" "1" "PM8" "8 outlets" "powerman-pdu (experimental)" "Cyclades" "pdu" "1" "PM10" "10 outlets" "powerman-pdu (experimental)" "Dell" "ups" "5" "Tower 500W LV / HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack/Tower 1000W LV / HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack/Tower 1500W LV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack/Tower 1920W LV / HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack/Tower 2300W LV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack/Tower 2700W LV / HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Short Depth Rack 2700W Online LV / HV" "High Efficiency, Serial port" "mge-shut" "Dell" "ups" "5" "Rack 3750W High Efficiency Online HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack 4200W High Efficiency Online HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Rack 5600W HV" "Serial port" "mge-shut" "Dell" "ups" "5" "Tower 500W LV / HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack/Tower 1000W LV / HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack/Tower 1500W LV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack/Tower 1920W LV / HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack/Tower 2300W LV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack/Tower 2700W LV / HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Short Depth Rack 2700W Online LV / HV" "High Efficiency, USB port" "usbhid-ups" "Dell" "ups" "5" "Rack 3750W High Efficiency Online HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack 4200W High Efficiency Online HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Rack 5600W HV" "USB port" "usbhid-ups" "Dell" "ups" "5" "Various (SNMP mode)" "UPS Network Management Card" "snmp-ups" "Dell" "ups" "5" "Various (XML/HTTP mode)" "UPS Network Management Card" "netxml-ups (experimental)" "Delta" "ups" "1" "GES602N" "" "belkin" "Deltec" "ups" "1" "PowerRite Pro II" "" "genericups upstype=15" "Deltec" "ups" "4" "PRM 450/700/1000/1500" "" "upscode2" "Digital Loggers" "pdu" "1" "LPC, EPCR2, DIN" "8 outlets" "powerman-pdu (experimental)" "Digitus" "ups" "1" "DN-170014" "USB" "richcomm_usb" # http://www.digitus.info/en/products/professional-network/security-and-surveillance/power-supply/uninterrruptable-power-supplies/ups-uninterruptible-power-systems-dn-170014/section/prof/ "Digitus" "ups" "2" "DN-170020" "" "blazer_ser" "Dynamix" "ups" "2" "UPS1700D" "" "blazer_ser" "Dynamix" "ups" "2" "UPS-650VA" "" "blazer_ser" "Dynamix" "ups" "2" "650VA/1000VA" "USB" "blazer_usb langid_fix=0x0409" "Dynex" "ups" "1" "975AVR" "" "genericups upstype=7" "Dynex" "ups" "2" "DX-800U" "USB" "usbhid-ups" "Eaton" "ups" "5" "3S" "" "usbhid-ups" "Eaton" "ups" "5" "Protection Station 500/650/800 VA" "USB" "usbhid-ups" "Eaton" "ups" "5" "Ellipse ECO 650/800/1200/1600 VA" "USB" "usbhid-ups" "Eaton" "ups" "5" "Ellipse ASR USBS 600/750/1000/1500 VA" "USB cable" "usbhid-ups" "Eaton" "ups" "5" "Ellipse MAX USBS 600/850/1100/1500 VA" "USB cable" "usbhid-ups" "Eaton" "ups" "5" "Evolution 650/850/1150/1550/2000 VA" "USB port" "usbhid-ups" "Eaton" "ups" "5" "Evolution S 1250/1750/2500/3000 VA" "USB port" "usbhid-ups" "Eaton" "ups" "5" "EX 700/1000/1500 VA" "USB port" "usbhid-ups" "Eaton" "ups" "5" "EX 2200/3000/3000 XL VA" "USB port" "usbhid-ups" "Eaton" "ups" "5" "EX 1000 RT2U / 1500 RT2U" "USB port" "usbhid-ups" "Eaton" "ups" "5" "MX 5/8/10/15/20 kVA" "USB port" "usbhid-ups" "Eaton" "ups" "5" "5 PX" "USB port" "usbhid-ups" "Eaton" "ups" "5" "Nova AVR 625/1250" "USB" "usbhid-ups" "Eaton" "ups" "5" "5E650iUSB" "USB port" "usbhid-ups" "Eaton" "ups" "5" "5E1100iUSB" "USB port" "usbhid-ups" # http://powerquality.eaton.com/5E1100iUSB.aspx?CX&GUID=8D85FE66-3102-427C-8F33-B8D56BBDD4D3 "Eaton" "ups" "5" "5S" "USB port" "usbhid-ups" "Eaton" "ups" "5" "5SC" "USB port" "usbhid-ups" "Eaton" "ups" "5" "5P" "USB port" "usbhid-ups" "Eaton" "ups" "5" "9SX" "USB port" "usbhid-ups" "Eaton" "ups" "5" "Ellipse ASR USBS 600/750/1000/1500 VA" "Serial cable" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "Ellipse MAX USBS 600/850/1100/1500 VA" "Serial cable" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "Evolution 650/850/1150/1550/2000 VA" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "Evolution S 1250/1750/2500/3000 VA" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "EX 700/1000/1500 VA" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "EX 2200/3000/3000 XL VA" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "EX 1000 RT2U / 1500 RT2U" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "MX 5/8/10/15/20 kVA" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "5 PX" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "EX RT 1:1 7/11 kVA" "" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "EX RT 3:1 5/7/11 kVA" "" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "5SC" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "5P" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "9SX" "Serial port" "mge-shut or oldmge-shut" "Eaton" "ups" "5" "EX RT (XML/HTTP)" "NMC Transverse card (ref 66074)" "netxml-ups (experimental)" "Eaton" "ups" "5" "EX RT (SNMP)" "NMC Transverse card (ref 66074)" "snmp-ups (experimental)" "Eaton" "ups" "5" "E Series NV UPS 400-2000 VA" "" "blazer_usb" "Eaton" "ups" "5" "E Series DX UPS 1-20 kVA" "" "blazer_ser" # http://www.eaton.com/Eaton/ESeriesUPS/DXUPS/ "Eaton" "ups" "4" "NetUPS SE 450/700/1000/1500" "" "upscode2" "Eaton" "ups" "5" "BladeUPS (SNMP)" "ConnectUPS Web/SNMP Card" "snmp-ups (experimental)" "Eaton" "ups" "5" "various models (SNMP mode)" "Power Xpert Gateway UPS Card" "snmp-ups (experimental)" "Eaton" "ups" "5" "various models (XML/HTTP mode)" "NMC Minislot (ref 66102)" "netxml-ups (experimental)" "Eaton" "ups" "5" "various models (SNMP mode)" "NMC Minislot (ref 66102)" "snmp-ups (experimental)" "Eaton" "ups" "5" "various models (XML/HTTP mode)" "SNMP/Web Minislot card (ref 66244)" "netxml-ups (experimental)" "Eaton" "ups" "5" "various models (SNMP mode)" "SNMP/Web Minislot card (ref 66244)" "snmp-ups (experimental)" "Eaton" "ups" "5" "various models (serial mode)" "Management Card Contact (ref 66104)" "mge-shut or mge-utalk" "Eaton" "pdu" "5" "ePDU Managed" "" "snmp-ups" "Eaton" "pdu" "5" "ePDU Switched" "" "snmp-ups" "Eaton" "pdu" "5" "ePDU Monitored" "" "snmp-ups or netxml-ups" "Eaton" "ups" "5" "Powerware 3105" "USB" "bcmxcp_usb" # http://powerquality.eaton.com/Products-services/Backup-Power-UPS/3105-eol.aspx "Eaton" "ups" "5" "Powerware 9125" "USB card" "bcmxcp_usb" "Eaton" "ups" "5" "Powerware 9130" "" "bcmxcp or usbhid-ups" "Eaton" "ups" "5" "Powerware 9140" "" "bcmxcp or usbhid-ups" "Eaton" "ups" "5" "Powerware 5130" "" "usbhid-ups" "Eaton" "ups" "5" "9395" "Serial port" "bcmxcp" "Eaton" "ups" "5" "Best Ferrups" "older ConnectUPS" "snmp-ups" "Eaton" "ups" "5" "ConnectUPS X / BD / E Slot" "Serial Pass-through mode" "bcmxcp" "Eaton" "ups" "5" "ConnectUPS X / BD / E Slot" "Network port" "snmp-ups" "Eaton" "ups" "5" "Management Card Contact" "Config 3 - Cable 66033" "genericups upstype=7" "Eaton" "ats" "5" "Eaton ATS16" "" "snmp-ups" "Effekta" "ups" "2" "MI/MT/MH" "2502 cable" "blazer_ser" "Effekta" "ups" "2" "RM2000MH" "" "blazer_ser" "Electrys" "ups" "2" "UPS 2500" "" "nutdrv_qx or blazer_ser" "Energy Sistem" "ups" "2" "(various)" "" "blazer_ser" "ETA" "ups" "1" "mini+UPS" "WinNT/Upsoft cable" "genericups upstype=7" "ETA" "ups" "1" "mini+UPS PRO" "UPS Explorer cable" "etapro" "EUROCASE" "ups" "2" "EA200N 2000VA" "USB" "nutdrv_qx" # http://partis.cz/index.php?gid=2551 "EVER" "ups" "1" "NET DPC series" "Serial port" "everups" "EVER" "ups" "1" "AP Pro series" "Serial port" "everups" "EVER" "ups" "1" "625/1000" "" "safenet" "EVER" "ups" "2" "POWERLINE RT 1-3kVA series" "Serial port" "blazer_ser" "EVER" "ups" "2" "POWERLINE RT 6-10kVA series" "Serial port" "blazer_ser" "EVER" "ups" "2" "POWERLINE 11 series" "Serial port" "blazer_ser" "EVER" "ups" "2" "POWERLINE 31 series" "Serial port" "blazer_ser" "EVER" "ups" "2" "DUO II Pro series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 1-3kVA series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 6-10kVA series" "USB port" "blazer_usb" "Exide" "ups" "1" "NetUPS SE" "" "genericups upstype=15" "Exide" "ups" "4" "NetUPS SE 450/700/1000/1500" "" "upscode2" "Fenton Technologies" "ups" "1" "PowerPal" "P-series" "safenet" "Fenton Technologies" "ups" "5" "PowerPal" "L-series" "blazer_ser" "Fenton Technologies" "ups" "5" "PowerOn" "" "blazer_ser" "Fenton Technologies" "ups" "5" "PowerPure" "" "blazer_ser" "Fairstone" "ups" "1" "L525/L625/L750" "" "safenet" "Fideltronik" "ups" "1" "Ares 700 and larger" "" "genericups upstype=6" "Fideltronik" "ups" "2" "LUPUS 500" "USB" "nutdrv_qx" "Fideltronik" "ups" "1" "Other Ares models" "" "genericups upstype=19" "Fideltronik INIGO" "ups" "2" "Viper 1200" "USB" "nutdrv_qx" # http://fideltronikinigo.com/viper/viper-1200/ "Fiskars" "ups" "4" "PowerRite MAX" "" "upscode2" "Fiskars" "ups" "4" "PowerServer 10" "" "upscode2" "Fiskars" "ups" "4" "PowerServer 30" "" "upscode2" "Fiskars" "ups" "4" "9200" "UPS Information Unit" "upscode2" "Flight Technic & International (FTUPS)" "ups" "2" "FT-1000BS" "Serial" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "FT-1000BS" "USB" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "FT-1000BS(T)" "Serial" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "FT-1000BS(T)" "USB" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "Smart On Line UPS 1KVA" "Serial" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "Smart On Line UPS 1KVA" "USB" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "(various)" "Serial" "nutdrv_qx" "Flight Technic & International (FTUPS)" "ups" "2" "(various)" "USB" "nutdrv_qx" "Forza Power Technologies" "ups" "2" "SL-1001" "USB" "blazer_usb" "Forza Power Technologies" "ups" "2" "FX-1500LCD" "USB" "blazer_usb" "FSP" "ups" "2" "EP650" "USB" "blazer_usb" "Gamatronic" "ups" "5" "All models with alarm interface" "" "genericups upstype=22" "Gamatronic" "ups" "2" "G-SmartCompact 2000" "" "blazer_ser" "Gamatronic" "ups" "5" "MP110/210" "" "gamatronic" "Gamatronic" "ups" "5" "MS-T" "" "gamatronic" "Gamatronic" "ups" "5" "MS" "" "gamatronic" "Gamatronic" "ups" "5" "µPS3/1" "" "gamatronic" "GE Digital Energy" "ups" "2" "EP Series" "" "blazer_usb" "GE Digital Energy" "ups" "2" "GT Series 1000/1500/2000/3000 VA Rack/Tower" "UL-version" "blazer_ser" "Geek Squad" "ups" "2" "GS1285U" "USB" "usbhid-ups" "Gemini" "ups" "1" "UPS625/UPS1000" "" "safenet" "Grafenthal" "ups" "2" "PR-3000-HS" "SNMP/Web Minislot card (ref 149G0006)" "snmp-ups" # http://grafenthal.de/produkte/usv/online/pr-hs-serie/pr-3000-hs/?L=3et8 "Gtec" "ups" "2" "ZP120N-1K / ZP120N-1KS / ZP120N-2K / ZP120N-2KS / ZP120N-3K / ZP120N-3KS" "" "blazer_usb" "Gtec" "ups" "2" "ZP120N-6K / ZP120N-6KS / ZP120N-10K-11 / ZP120N-10KS-11" "" "blazer_usb" "Gtec" "ups" "2" "ZP120N-10K-31-00 / ZP120N-10K-31-07 / ZP120N-10K-31-09 / ZP120N-10K-31-99 / ZP120N-20K" "USB port" "blazer_usb" "Gtec" "ups" "2" "AP160N-1K / AP160LCD-1K-KS / AP160N-2K / AP160LCD-2K-KS / AP160N-3K / AP160LCD-3K-KS / AP160N-6K-PDU / AP160N-10K-PDU" "USB port" "blazer_usb" "Gtec" "ups" "2" "ZP120N-10K-31-00 / ZP120N-10K-31-07 / ZP120N-10K-31-09 / ZP120N-10K-31-99 / ZP120N-20K" "Serial port" "blazer_ser" "Gtec" "ups" "2" "AP160N-1K / AP160LCD-1K-KS / AP160N-2K / AP160LCD-2K-KS / AP160N-3K / AP160LCD-3K-KS / AP160N-6K-PDU / AP160N-10K-PDU" "Serial port" "blazer_ser" "HP" "ups" "1" "PowerTrust 2997A" "HP 5061-2575 cable" "apcsmart" "HP" "ups" "3" "T750 G2" "Serial port" "bcmxcp" "HP" "ups" "3" "T1000 G3" "Serial port" "bcmxcp" "HP" "ups" "3" "T1500 G3" "Serial port" "bcmxcp" "HP" "ups" "3" "R1500 G2" "Serial port" "bcmxcp" "HP" "ups" "4" "R3000 XR" "" "bcmxcp" "HP" "ups" "4" "R5500 XR" "" "bcmxcp" "HP" "ups" "3" "T500 / T750" "older models, USB port" "bcmxcp_usb" "HP" "ups" "3" "R/T3000" "Serial port" "mge-shut or oldmge-shut" "HP" "ups" "3" "R5000 / R7000" "Serial port" "mge-shut or oldmge-shut" "HP" "ups" "3" "T750 INTL" "" "usbhid-ups" "HP" "ups" "3" "T1000 INTL" "" "usbhid-ups" "HP" "ups" "3" "T1500 INTL" "" "usbhid-ups" "HP" "ups" "3" "T750 G2" "USB port" "usbhid-ups" "HP" "ups" "3" "T1000 G3" "USB port" "usbhid-ups" "HP" "ups" "3" "T1500 G3" "USB port" "usbhid-ups" "HP" "ups" "3" "R1500 G2 INTL" "USB port" "usbhid-ups" "HP" "ups" "3" "R/T 2200 G2" "" "usbhid-ups" "HP" "ups" "3" "R/T3000" "USB port" "usbhid-ups" "HP" "ups" "3" "R5000 / R7000" "USB port" "usbhid-ups" "HP" "ups" "4" "Various (SNMP mode)" "HP UPS Management Module" "snmp-ups" "HP" "pdu" "1" "HP3488 Switch/Control Unit" "" "powerman-pdu (experimental)" "Huawei" "ups" "4" "UPS5000-E" "" "snmp-ups" "IBM" "pdu" "1" "Blade Center Management Module" "15 outlets" "powerman-pdu (experimental)" "ICS" "pdu" "1" "8064 Ethernet Relay Interface" "16 outlets" "powerman-pdu (experimental)" "iDowell" "ups" "2" "iBox UPS" "" "usbhid-ups" "INELT" "ups" "2" "Monolith 1000LT" "" "blazer_ser" "INELT" "ups" "2" "Monolith 3000RT" "" "blazer_ser" "Inform" "ups" "1" "GUARD" "Line Interactive AP model" "powercom" "Inform" "ups" "2" "Guard S 1500AP" "" "blazer_ser" "Inform" "ups" "2" "Informer Compact 1000-2000-3000 VA" "" "blazer_ser" "Inform" "ups" "2" "Sinus SS 210" "" "blazer_ser" "Infosec" "ups" "2" "iPEL 350" "" "blazer_ser" "Infosec" "ups" "2" "iPEL 500" "" "blazer_ser" "Infosec" "ups" "2" "iPEL 750" "" "blazer_ser" "Infosec" "ups" "2" "iPEL 1000" "" "blazer_ser" "Infosec" "ups" "2" "500XP" "" "blazer_ser" "Infosec" "ups" "2" "X2, X3, X4, E2, E3, E4" "USB" "blazer_usb" "Infosec" "ups" "2" "XP 500" "USB" "blazer_usb" "Infosec" "ups" "2" "XP 1000" "" "blazer_ser" "IPAR" "ups" "2" "Mini Energy ME 800" "" "blazer_usb" "IPMI" "pdu" "1" "" "" "powerman-pdu (experimental)" "Ippon" "ups" "2" "Back Power Pro 400/500/600/700/800" "" "blazer_ser" "Ippon" "ups" "2" "Back Power Pro 400/500/600/700/800" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "Back Comfo Pro 600/800" "" "blazer_ser" "Ippon" "ups" "2" "Back Comfo Pro 600/800" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "Smart Power Pro 1000/1400/2000" "" "blazer_ser" "Ippon" "ups" "2" "Smart Power Pro 1000/1400/2000" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "Smart Winner 750/1000/1500/2000/3000" "" "blazer_ser" "Ippon" "ups" "2" "Smart Winner 750/1000/1500/2000/3000" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "(various)" "" "blazer_ser" "Ippon" "ups" "2" "(various)" "USB" "blazer_usb" "Ippon" "ups" "2" "INNOVA RT 1K/1.5K/2K/3K" "" "blazer_usb" "IVT" "scd" "1" "SCD series" "" "ivtscd" "Jageson Technology" "ups" "1" "Jasuny USPS" "" "genericups upstype=4" "JAWAN" "ups" "2" "JW-UPSLC02" "USB" "blazer_usb" "Kanji" "ups" "1" "800 VA" "USB" "nutdrv_atcl_usb" "Kebo" "ups" "2" "1200D/D Series" "" "blazer_ser" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K TOWER" "" "blazer_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K XL TOWER" "" "blazer_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/1.5K/2K/3K/6K/10K RACK" "" "blazer_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/1.5K/2K/3K/6K/10K XL RACK" "" "blazer_usb" "Krauler" "ups" "2" "UP-D1200VA" "USB" "blazer_usb" "Krauler" "ups" "2" "UP-M500VA" "USB" "blazer_usb" "Lacerda" "ups" "2" "New Orion 800VA" "USB" "blazer_usb" "LDLC" "ups" "2" "UPS-1200D" "" "blazer_usb langid_fix=0x4095" "Legrand" "ups" "2" "Keor Multiplug" "" "nutdrv_qx" "Lestar" "ups" "2" "MD-800E" "" "blazer_ser" "Lexis" "ups" "2" "X-Power Tigra 1kVA" "" "blazer_ser or bestups" "Liebert" "ups" "2" "ITON 600VA" "" "blazer_ser" "Liebert" "ups" "5" "UPStation GXT2" "contact-closure cable" "liebert" "Liebert" "ups" "1" "GXT2-3000RT230" "" "liebert-esp2 (experimental)" "Liebert" "ups" "2" "PowerSure Personal XT" "USB" "usbhid-ups" "Liebert" "ups" "2" "PowerSure PSA" "USB" "usbhid-ups" "Liebert" "ups" "2" "PowerSure PSI 1440" "USB" "usbhid-ups" # http://www.emersonnetworkpower.com/en-US/Products/ACPower/Pages/LiebertPowerSurePSILineInteractiveUPS10003000VA.aspx "LNXI" "pdu" "1" "Icebox" "10 outlets" "powerman-pdu (experimental)" "Lyonn" "ups" "2" "CTB-800V" "" "nutdrv_qx" "Lyonn" "ups" "2" "CTB-1200" "" "blazer_usb" "Masterguard" "ups" "1" "(various)" "" "masterguard" "Maxxtro" "ups" "2" "UPS 600 VA" "serial port" "blazer_ser" "Mecer" "ups" "2" "ME-1000-WTU" "USB" "nutdrv_qx" # http://www.comx-computers.co.za/download/mecer/ME-1000-WTU.pdf "Mecer" "ups" "2" "ME-2000" "" "blazer_ser" "Meta System" "ups" "1" "HF Line" "1..4 boards" "metasys" "Meta System" "ups" "1" "HF Line \/2" "5..8 boards" "metasys" "Meta System" "ups" "1" "HF Millennium 810" "" "metasys" "Meta System" "ups" "1" "HF Millennium 820" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 910" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 920" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 930" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 940" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 950" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 960" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 970" "" "metasys" "Meta System" "ups" "1" "HF TOP Line 980" "" "metasys" "Meta System" "ups" "1" "ECO Network 750" "" "metasys" "Meta System" "ups" "1" "ECO Network 1000" "" "metasys" "Meta System" "ups" "1" "ECO Network 1050" "" "metasys" "Meta System" "ups" "1" "ECO Network 1500" "" "metasys" "Meta System" "ups" "1" "ECO Network 1800" "" "metasys" "Meta System" "ups" "1" "ECO Network 2000" "" "metasys" "Meta System" "ups" "1" "ECO Network 2100" "" "metasys" "Meta System" "ups" "1" "ECO Network 2500" "" "metasys" "Meta System" "ups" "1" "ECO Network 3000" "" "metasys" "Meta System" "ups" "1" "ECO 305" "" "metasys" "Meta System" "ups" "1" "ECO 308" "" "metasys" "Meta System" "ups" "1" "ECO 311" "" "metasys" "Meta System" "ups" "1" "ECO 511" "" "metasys" "Meta System" "ups" "1" "ECO 516" "" "metasys" "Meta System" "ups" "1" "ECO 519" "" "metasys" "Meta System" "ups" "1" "ECO 522" "" "metasys" "Meta System" "ups" "1" "ally HF 800" "" "metasys" "Meta System" "ups" "1" "ally HF 1000" "" "metasys" "Meta System" "ups" "1" "ally HF 1250" "" "metasys" "Meta System" "ups" "1" "ally HF 1600" "" "metasys" "Meta System" "ups" "1" "ally HF 2000" "" "metasys" "Meta System" "ups" "1" "ally HF 2500" "" "metasys" "Meta System" "ups" "1" "Megaline 1250" "" "metasys" "Meta System" "ups" "1" "Megaline 2500" "" "metasys" "Meta System" "ups" "1" "Megaline 3750" "" "metasys" "Meta System" "ups" "1" "Megaline 5000" "" "metasys" "Meta System" "ups" "1" "Megaline 6250" "" "metasys" "Meta System" "ups" "1" "Megaline 7500" "" "metasys" "Meta System" "ups" "1" "Megaline 8750" "" "metasys" "Meta System" "ups" "1" "Megaline 10000" "" "metasys" "MGE Office Protection Systems" "ups" "5" "Protection Center 500/675 VA" "USB" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Protection Station 500/650/800 VA" "USB" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Ellipse ASR USBS 600/750/1000/1500 VA" "USB cable" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Ellipse MAX USBS 600/850/1100/1500 VA" "USB cable" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Evolution 650/850/1150/1550/2000 VA" "USB port" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Evolution S 1250/1750/2500/3000 VA" "USB port" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Pulsar 700/1000/1500 VA" "USB port" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Pulsar M 2200/3000 VA" "USB port" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Pulsar MX 5/8/10/15/20 kVA" "USB port" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Ellipse ASR USBS 600/750/1000/1500 VA" "Serial cable" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Ellipse MAX USBS 600/850/1100/1500 VA" "Serial cable" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Evolution 650/850/1150/1550/2000 VA" "Serial port" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Evolution S 1250/1750/2500/3000 VA" "Serial port" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Pulsar 700/1000/1500 VA" "Serial port" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Pulsar M 2200/3000 VA" "Serial port" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Pulsar MX 5/8/10/15/20 kVA" "Serial port" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Comet EX RT 1:1 7/11 kVA" "" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Comet EX RT 3:1 5/7/11 kVA" "" "mge-shut or oldmge-shut" "MGE Office Protection Systems" "ups" "5" "Comet EX RT (XML/HTTP)" "NMC Transverse card (ref 66074)" "netxml-ups (experimental)" "MGE Office Protection Systems" "ups" "5" "Comet EX RT (SNMP)" "NMC Transverse card (ref 66074)" "snmp-ups (experimental)" "MGE Office Protection Systems" "ups" "5" "various models (XML/HTTP mode)" "NMC Minislot (Ref 66102)" "netxml-ups (experimental)" "MGE Office Protection Systems" "ups" "5" "various models (SNMP mode)" "NMC Minislot (Ref 66102)" "snmp-ups (experimental)" "MGE Office Protection Systems" "ups" "5" "various models (XML/HTTP mode)" "SNMP/Web Minislot card (ref 66244)" "netxml-ups (experimental)" "MGE Office Protection Systems" "ups" "5" "various models (SNMP mode)" "SNMP/Web Minislot card (ref 66244)" "snmp-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "Comet EX RT" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Comet EX RT 3:1" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Protection Center 420" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Protection Center 500" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Protection Center 675" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "NOVA AVR 600 USB" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "NOVA AVR 1100 USB" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse USBS" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse USB" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse Premium USBS" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse Premium USB" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 600" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 750" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 1000" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 1500" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 600" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 850" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 1100" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 1500" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Evolution" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution 650" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution 850" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution 1150" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 1250" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution 1550" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 1750" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution 2000" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 2500" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 3000" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar M 2200" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar M 3000" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar M 3000 XL" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 700" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1000" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1500" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1000 RT2U" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1500 RT2U" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar MX 4000 RT" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Pulsar MX 5000 RT" "USB" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "Comet / Galaxy (USB)" "USB Card (ref 66067)" "usbhid-ups" "MGE UPS SYSTEMS" "ups" "5" "NOVA AVR 600 Serial" "" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "NOVA AVR 1100 Serial" "" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse USBS" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse S" "" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse Premium USBS" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Ellipse Premium S" "" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 600" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 750" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 1000" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse Office 1500" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 600" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 850" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 1100" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Ellipse MAX 1500" "Serial cable" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar EXtreme C / EX RT" "" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Comet EX RT" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Comet EX RT 3:1" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Esprit" "" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution 650" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution 850" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution 1150" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 1250" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution 1550" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 1750" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution 2000" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 2500" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Evolution S 3000" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar M 2200" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar M 3000" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar M 3000 XL" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 700" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1000" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1500" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1000 RT2U" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar 1500 RT2U" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar MX 4000 RT" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar MX 5000 RT" "Serial port" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar Evolution" "Serial port" "mge-shut or oldmge-shut or mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar EXtreme C" "" "mge-shut or oldmge-shut or mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar ES+" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar ESV+" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar SV" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar ESV" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar EX" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar EXL" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar PSX" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar SX" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Pulsar EXtreme" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Comet EXtreme" "" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Comet / Galaxy (Serial)" "Utalk Serial Card (ref 66060)" "mge-utalk" "MGE UPS SYSTEMS" "ups" "5" "Comet / Galaxy (Serial)" "HID COM Serial Card (ref 66066)" "mge-shut or oldmge-shut" "MGE UPS SYSTEMS" "ups" "5" "Pulsar / Comet / Galaxy (SNMP)" "SNMP/Web Transverse card (ref 66074)" "snmp-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "various models (XML/HTTP mode)" "NMC Minislot (Ref 66102)" "netxml-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "various models (SNMP mode)" "NMC Minislot (Ref 66102)" "snmp-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "Pulsar (XML/HTTP mode)" "SNMP/Web Minislot card (ref 66244)" "netxml-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "Pulsar (SNMP mode)" "SNMP/Web Minislot card (ref 66244)" "snmp-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "Pulsar / Comet / Galaxy (SNMP)" "SNMP card (ref 66062)" "snmp-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "Pulsar (SNMP)" "SNMP card (ref 66045)" "snmp-ups (experimental)" "MGE UPS SYSTEMS" "ups" "5" "UM-Link (SNMP)" "Not a UPS (ref 66850)" "snmp-ups (experimental)" "MicroDowell" "ups" "1" "B.Box BP 500" "" "powerpanel" "MicroDowell" "ups" "1" "B.Box BP 750" "" "powerpanel" "MicroDowell" "ups" "1" "B.Box BP 1000" "" "powerpanel" "MicroDowell" "ups" "1" "B.Box BP 1500" "" "powerpanel" "MicroDowell" "ups" "1" "B.Box LP 500" "" "genericups upstype=7" "MicroDowell" "ups" "5" "Enterprise B8" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise B10" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N8" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N11" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N15" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N20" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N22" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N30" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N40" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N50" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise N60" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise HiBox ST" "" "microdowell" "Microline" "ups" "2" "C-Lion Innova RT 2K/3K" "" "blazer_usb" "Microline" "ups" "2" "C-Lion Innova RT 6K/10K (Parallel)" "" "blazer_usb" "Microline" "ups" "2" "C-Lion Innova Tower 6K/10K" "" "blazer_usb" "Microline" "ups" "2" "C-Lion Innova Combo 10K/20K (3/1)" "" "blazer_usb" "Micropower" "ups" "2" "LCD 1000" "USB" "blazer_usb" "Microsol" "ups" "4" "Solis 1.0" "1000VA" "solis" "Microsol" "ups" "4" "Solis 1.5" "1500VA" "solis" "Microsol" "ups" "4" "Solis 2.0" "2000VA" "solis" "Microsol" "ups" "4" "Solis 3.0" "3000VA" "solis" "Microsol" "ups" "5" "Rhino 6.0" "6000VA" "rhino" "Microsol" "ups" "5" "Rhino 7.5" "7500VA" "rhino" "Microsol" "ups" "5" "Rhino 10.0" "10000VA" "rhino" "Microsol" "ups" "5" "Rhino 20.0" "20000VA" "rhino" "Minibox" "ups" "5" "openUPS Intelligent UPS" "USB port" "usbhid-ups" "Mustek" "ups" "2" "Powermust" "400VA Plus" "blazer_ser" "Mustek" "ups" "2" "Powermust" "600VA Plus" "blazer_ser" "Mustek" "ups" "2" "Powermust" "800VA Pro" "blazer_ser" "Mustek" "ups" "2" "Powermust" "1000VA Plus" "blazer_ser" "Mustek" "ups" "2" "Powermust" "1000VA USB" "blazer_usb" "Mustek" "ups" "2" "Powermust" "1400VA Plus" "blazer_ser" "Mustek" "ups" "2" "Powermust" "2000VA USB" "blazer_ser" "Mustek" "ups" "2" "Powermust Office 650" "USB" "blazer_usb" "Mustek" "ups" "2" "PowerMust 424 / 636 / 848" "USB" "blazer_usb" "Mustek" "ups" "2" "Yukai PowerMust" "1000 USB (PID: 5161)" "blazer_usb" "Mustek" "ups" "2" "Various" "" "blazer_ser" "Neus" "ups" "2" "400va / 600va" "" "blazer_ser" "NHS Sistemas de Energia" "ups" "5" "Expert C Online 6000" "" "gamatronic" # http://www.nhs.com.br/produtos_interna/id/TWpFeQ== "NHS Sistemas de Energia" "ups" "5" "Expert C Online 8000" "" "gamatronic" "NHS Sistemas de Energia" "ups" "5" "Expert C Online 10000" "" "gamatronic" "NHS Sistemas de Energia" "ups" "5" "Expert S Online 6000" "" "gamatronic" "NHS Sistemas de Energia" "ups" "5" "Expert S Online 8000" "" "gamatronic" "NHS Sistemas de Energia" "ups" "5" "Expert S Online 10000" "" "gamatronic" "NHS Sistemas de Energia" "ups" "5" "Expert S Online 10000" "" "gamatronic" "NHS Sistemas de Energia" "ups" "5" "Laser Senoidal 5000VA" "USB" "gamatronic" # http://www.nhs.com.br/produtos_interna/id/T0RrPQ== "Nitram" "ups" "1" "Elite 500" "" "genericups upstype=8" "Nitram" "ups" "1" "Elite 2002" "" "genericups upstype=16" "Nitram" "ups" "1" "Elite 2005" "" "powerpanel" "Novex" "ups" "1" "NUPS-650" "USB" "blazer_usb protocol=megatec" # http://komp.1k.by/periphery-ups/novex/Novex_NUPS_650-130052.html "Numeric" "ups" "2" "3000 SW" "" "blazer_ser" "Numeric" "ups" "2" "Digital 800 plus" "USB" "blazer_usb" "Oneac" "ups" "1" "ON400" "advanced interface" "oneac" "Oneac" "ups" "1" "ON600" "advanced interface" "oneac" "Oneac" "ups" "1" "ON900" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1300" "advanced interface" "oneac" "Oneac" "ups" "1" "EG Series" "advanced interface" "oneac" "Oneac" "ups" "1" "ON700" "advanced interface" "oneac" "Oneac" "ups" "1" "ON700XAU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON700XIU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1000" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1000XAU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1000XIU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1500" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1500XAU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON1500XIU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON2000" "advanced interface" "oneac" "Oneac" "ups" "1" "ON2000XAU" "advanced interface" "oneac" "Oneac" "ups" "1" "ON2000XIU" "advanced interface" "oneac" "Online" "ups" "1" "P-Series" "" "genericups upstype=14" "Online" "ups" "2" "Zinto A" "" "blazer_usb" "Online" "ups" "1" "Zinto D" "" "optiups" "Online" "ups" "2" "Yunto YQ450" "" "blazer_usb" "OnLite" "ups" "2" "AQUA" "50" "blazer_ser" "Opti-UPS" "ups" "1" "PowerES" "420E" "optiups" "Opti-UPS" "ups" "1" "VS 575C" "type=OPTI" "powercom" "Orvaldi Power Protection" "ups" "2" "various" "not 400 or 600" "blazer_ser" "Orvaldi Power Protection" "ups" "2" "750 / 900SP" "" "blazer_usb" "Phasak" "ups" "2" "400VA / 600VA" "" "blazer_ser" "Plexus" "ups" "2" "500VA" "USB" "blazer_usb" "Plexus" "ups" "2" "1000VA Pro" "USB" "blazer_usb" "Plexus" "ups" "1" "800 VA" "USB" "nutdrv_atcl_usb" "Powercom" "ups" "4" "SMK" "" "blazer_ser" "Powercom" "ups" "4" "SXL" "" "blazer_ser" "Powercom" "ups" "4" "ULT" "" "blazer_ser" "Powercom" "ups" "4" "Trust 425/625" "" "powercom" "Powercom" "ups" "4" "Advice Partner/King Pr750" "" "powercom" "Powercom" "ups" "4" "Black Knight PRO" "" "powercom" "Powercom" "ups" "5" "Black Knight PRO" "USB (2009 models, product id: 00a6)" "usbhid-ups (experimental)" "Powercom" "ups" "4" "Smart KING Pro (all Smart series)" "" "powercom" "Powercom" "ups" "5" "Smart KING Pro (all Smart series)" "USB (2009 models, product id: 00a3)" "usbhid-ups (experimental)" "Powercom" "ups" "4" "Imperial" "" "powercom" "Powercom" "ups" "5" "Imperial" "USB (2009 models, product id: 00a2)" "usbhid-ups (experimental)" "Powercom" "ups" "4" "Vanguard" "" "blazer_ser" "Powercom" "ups" "5" "Vanguard" "USB (2009 models, product id: 0004 or 00a5)" "usbhid-ups (experimental)" "Powercom" "ups" "4" "WOW" "USB (<= 2009 models, product id: 0002)" "powercom (requires 'usbserial' kernel module)" "Powercom" "ups" "5" "WOW" "USB (2009 models, product id: 00a4)" "usbhid-ups (experimental)" "Powercom" "ups" "4" "(various)" "USB (<= 2009 models, product id: 0002)" "powercom (requires 'usbserial' kernel module)" "Powercom" "ups" "5" "(various)" "USB (2009 models, product id: 00a?)" "usbhid-ups (experimental)" "Powercom" "ups" "5" "BNT-xxxAP" "USB (product id: 0004)" "usbhid-ups (experimental)" "Powercom" "ups" "1" "BNT-xxxAP" "USB (product id: 0001)" "usbhid-ups (experimental)" "POWEREX" "ups" "2" "VI 1000 LED" "" "blazer_usb" "PowerGuard" "ups" "2" "PG-600" "" "blazer_ser" "PowerKinetics" "ups" "1" "9001" "" "genericups upstype=17" "PowerKinetics" "ups" "2" "BlackOut Buster" "" "blazer_ser" "PowerMan" "ups" "2" "RealSmart 800" "" "blazer_ser" "PowerMan" "ups" "2" "RealSmart 1000" "" "blazer_ser" "PowerMan" "ups" "1" "BackPro" "" "genericups upstype=4" "PowerShield" "ups" "2" "Defender 1200VA" "" "blazer_usb" "PowerTech" "ups" "1" "Comp1000" "DTR cable power" "genericups upstype=3" "PowerTech" "ups" "2" "SMK-800" "" "blazer_ser" "PowerWalker" "ups" "2" "Line-Interactive VI 1000" "" "blazer_ser" "PowerWalker" "ups" "2" "Line-Interactive VI 400/800" "" "blazer_ser" "PowerWalker" "ups" "2" "Line-Interactive VI 600" "" "blazer_ser" "PowerWalker" "ups" "2" "Line-Interactive VI 600 SE" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 800 SE" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 1400" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 2000" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 850 LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "Online VFI 1000RT/1500RT/2000RT/3000RT/6000RT/10000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 1000RT/1500RT/2000RT/3000RT LCD" "" "blazer_usb" "Powerware" "ups" "4" "3110" "" "genericups upstype=7" "Powerware" "ups" "4" "3115" "" "genericups upstype=11" "Powerware" "ups" "4" "5119, 5125" "" "genericups upstype=15" "Powerware" "ups" "4" "5119 RM" "" "genericups upstype=20" "Powerware" "ups" "5" "5119 RM" "" "upscode2" "Powerware" "ups" "5" "PW3105" "" "bcmxcp_usb" "Powerware" "ups" "5" "PW5110" "" "bcmxcp_usb" "Powerware" "ups" "5" "PW5115" "Serial port" "bcmxcp" "Powerware" "ups" "5" "PW5115" "USB port" "bcmxcp_usb" "Powerware" "ups" "5" "PW5125" "" "bcmxcp" "Powerware" "ups" "5" "PW9120" "Serial port" "bcmxcp" "Powerware" "ups" "5" "PW9120" "USB port" "bcmxcp_usb" "Powerware" "ups" "5" "PW9125" "" "bcmxcp" "Powerware" "ups" "5" "PW9315" "3-phase" "bcmxcp" "Powerware" "ups" "5" "9110" "" "upscode2" "Powerware" "ups" "5" "9120" "" "upscode2" "Powerware" "ups" "5" "9150" "" "upscode2" "Powerware" "ups" "5" "9305" "" "upscode2" "Powerware" "ups" "5" "BladeUPS (SNMP)" "ConnectUPS Web/SNMP Card" "snmp-ups (experimental)" "Powerware" "ups" "5" "(various)" "ConnectUPS Web/SNMP card" "snmp-ups (experimental)" "Powerwell" "ups" "1" "PM525A/-625A/-800A/-1000A/-1250A" "" "safenet" "Phantom" "pdu" "1" "Rackable Systems" "1 outlets" "powerman-pdu (experimental)" "Raritan" "pdu" "3" "Intelligent PDU - Dominion PX" "no report, but should be supported" "snmp-ups (experimental)" "Raritan" "pdu" "3" "Metered PDU - Raritan PM" "no report, but should be supported" "snmp-ups (experimental)" "Raritan" "pdu" "3" "Switched PDU - Raritan RPC" "no report, but should be supported" "snmp-ups (experimental)" "REDi" "ups" "2" "Blazer 400VA / 600VA / 800VA" "" "blazer_ser" "Repotec" "ups" "1" "RPF525/625/800/1000" "" "safenet" "Repotec" "ups" "1" "RPT-800A" "" "genericups upstype=13" "Repotec" "ups" "1" "RPT-162A" "" "genericups upstype=13" "Riello" "ups" "3" "Riello Sentinel SDL 6000-7" "Netman Plus 102 SNMP Card" "snmp-ups" "Riello" "ups" "3" "Riello Sentinel Dual SDH 1000-7" "Netman Plus 102 SNMP Card" "snmp-ups" "Riello" "ups" "5" "IDG 400/600/800/1200/1600" "" "riello_usb" "Riello" "ups" "5" "IPG 600/800" "" "riello_usb" "Riello" "ups" "5" "WPG 400/600/800" "" "riello_usb" "Riello" "ups" "5" "NPW 600/800/1000/1500/2000" "" "riello_usb" "Riello" "ups" "5" "NDG 800/1000/1500/2000" "" "riello_usb" "Riello" "ups" "5" "DVT 500/800/1100/1500/2000" "" "riello_usb" "Riello" "ups" "5" "DVR 500/800/1100" "" "riello_usb" "Riello" "ups" "5" "DVD 1500/2200/3000" "" "riello_usb" "Riello" "ups" "5" "VST 800/1100/1500/2000" "" "riello_usb" "Riello" "ups" "5" "VSD 1100/1500/2200/3000" "" "riello_usb" "Riello" "ups" "5" "SEP 700/1000/1500/2200/3000" "" "riello_usb" "Riello" "ups" "5" "SDH 1000/1500/2200/3000" "" "riello_usb" "Riello" "ups" "5" "SDL 3300/4000/5000/6000/6500/8000/10000" "" "riello_usb" "Riello" "ups" "5" "SPW" "" "riello_usb" "Riello" "ups" "5" "SPT" "" "riello_usb" "Riello" "ups" "5" "NDG 800/1000/1500/2000" "" "riello_ser" "Riello" "ups" "5" "DVT 500/800/1100/1500/2000" "" "riello_ser" "Riello" "ups" "5" "DVR 500/800/1100" "" "riello_ser" "Riello" "ups" "5" "DVD 1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "VST 800/1100/1500/2000" "" "riello_ser" "Riello" "ups" "5" "VSD 1100/1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "SEP 700/1000/1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "SDH 1000/1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "SDL 3300/4000/5000/6000/6500/8000/10000" "" "riello_ser" "Riello" "ups" "5" "SPW" "" "riello_ser" "Riello" "ups" "5" "SPT" "" "riello_ser" "Riello" "ups" "5" "MCT" "" "riello_ser" "Riello" "ups" "5" "MST" "" "riello_ser" "Riello" "ups" "5" "MCM" "" "riello_ser" "Riello" "ups" "5" "MCT" "" "riello_ser" "Riello" "ups" "5" "MHT" "" "riello_ser" "Riello" "ups" "5" "MPT" "" "riello_ser" "Riello" "ups" "5" "MPM" "" "riello_ser" "Riello" "ups" "3" "(various)" "Netman Plus 101 SNMP Box" "snmp-ups" "Riello" "ups" "3" "(various)" "Netman Plus 102 SNMP Card" "snmp-ups" "Riello" "ups" "3" "(various)" "Netman Plus 202 SNMP Card" "snmp-ups" "Rocketfish" "ups" "2" "RF-1000VA / RF-1025VA" "" "usbhid-ups" "Rucelf" "ups" "2" "Rucelf UPOII-3000-96-EL" "" "blazer_ser" # http://www.rucelf.ua/en/catalog/upoii-3000-96-el/ "SmartLabs" "pdu" "1" "2412S Power Line Modem" "for X10/Insteon" "powerman-pdu (experimental)" "SMS (Brazil)" "ups" "2" "Manager III" "" "blazer_ser" "SOLA" "ups" "1" "305" "cable INT-0025C" "genericups upstype=7" "SOLA" "ups" "1" "325" "" "blazer_ser or bestups" "SOLA" "ups" "1" "520" "" "blazer_ser or bestups" "SOLA" "ups" "1" "610" "use ID= in ups.conf" "blazer_ser or bestups" "SOLA" "ups" "1" "620" "" "blazer_ser or bestups" "SOLA" "ups" "4" "330" "" "blazer_ser" "SOLA/BASIC Mexico" "ups" "1" "various" "ISBMEX protocol" "isbmex" "Socomec Sicon" "ups" "2" "NeTYS-PE 600VA" "" "blazer_ser" "Socomec Sicon" "ups" "1" "Egys" "420 VA" "powercom" "Socomec Sicon" "ups" "3" "MASTERYS 3/3 SYSTEM 60 kVA" "Net Vision v5.05 SNMP card" "snmp-ups (experimental)" "Socomec Sicon" "ups" "3" "NETYS RT 1/1" "Net Vision SNMP card" "snmp-ups (experimental)" "Socomec Sicon" "ups" "3" "Netvision" "UPS equipped with Netvision WEB/SNMP card/external box" "snmp-ups (experimental)" "Soltec" "ups" "1" "Winmate 525/625/800/1000" "" "safenet" "Soyntec" "ups" "2" "Sekury C 500" "" "blazer_ser" "Soyntec" "ups" "2" "Sekury C 800" "" "blazer_ser" "SquareOne Power" "ups" "2" "QP1000" "" "blazer_ser" "StarPower" "ups" "1" "PCF-800VA" "USB" "richcomm_usb" # http://www.sako.com.cn/products01.asp?Id=29&proid=53 "Sun" "pdu" "1" "ILOM Management Module" "1 outlet" "powerman-pdu (experimental)" "SuperPower" "ups" "2" "HP360, Hope-550" "" "blazer_ser" "SVEN" "ups" "2" "Power Pro+ series" "USB" "blazer_usb (USB ID ffff:0000)" "SVEN" "ups" "2" "Power Pro+ series" "USB" "blazer_usb (USB ID 05b8:0000)" "SVEN" "ups" "1" "Power Pro+ series" "USB" "richcomm_usb (USB ID 0925:1234)" "SVEN" "ups" "2" "Power Smart RM 2000" "USB" "blazer_usb (USB ID 05b8:0000)" "Sweex" "ups" "1" "500/1000" "smart - shipped with SafeNet" "safenet" "Sweex" "ups" "1" "500/1000" "" "genericups upstype=7" "Sweex" "ups" "1" "1000" "USB" "richcomm_usb" "Sweex" "ups" "2" "(various)" "" "blazer_ser" "Sweex" "ups" "2" "INTELLIGENT UPS 1500VA P220" "USB" "blazer_usb (USB ID 0665:5161)" # http://www.sweex.com/en/notebook-pc-accessoires/ups/PP220/ "Syndome" "ups" "2" "Era 500VA" "USB" "blazer_usb" "Sysgration" "ups" "2" "UPGUARDS Pro650" "" "blazer_ser" "Tecnoware" "ups" "2" "Easy Power 1200" "" "blazer_ser" "Tecnoware" "ups" "2" "UPS ERA LCD 0.65" "" "blazer_usb langid_fix=0x409" "Tripp Lite" "ups" "1" "(various)" "Lan 2.2 interface - black 73-0844 cable" "genericups upstype=5" "Tripp Lite" "ups" "2" "1500 LCD" "USB" "usbhid-ups" "Tripp Lite" "ups" "3" "AVR550U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3090 "Tripp Lite" "ups" "3" "AVR700U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=4785 "Tripp Lite" "ups" "3" "AVR750U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3141 "Tripp Lite" "ups" "3" "AVR900U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3649 "Tripp Lite" "ups" "3" "AVR900UTAA" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4375 "Tripp Lite" "ups" "3" "AVRX550U" "USB (protocol 2009)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3079 "Tripp Lite" "ups" "3" "AVRX550UD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=4620 "Tripp Lite" "ups" "3" "AVRX750U" "USB (protocol 2009)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3190 "Tripp Lite" "ups" "3" "AVRX750UD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=4755 "Tripp Lite" "ups" "3" "AVRX750UF" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=4756 "Tripp Lite" "ups" "3" "BC600SINE" "USB (protocol 2011)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4863 "Tripp Lite" "ups" "3" "BCPERS450" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4 "Tripp Lite" "ups" "3" "BCPRO600" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=931&txtModelID=5 "Tripp Lite" "ups" "3" "ECO350UPS" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=743&txtModelID=4149 "Tripp Lite" "ups" "3" "ECO550UPS" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=743&txtModelID=4148 "Tripp Lite" "ups" "3" "ECO650LCD" "USB (protocol 2011)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4861 "Tripp Lite" "ups" "3" "ECO750UPS" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=743&txtModelID=4147 "Tripp Lite" "ups" "3" "ECO850LCD" "USB (protocol 2011)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=743&txtModelID=4862 "Tripp Lite" "ups" "3" "HT850UPS" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3197 "Tripp Lite" "ups" "3" "HTR05-1U" "USB (protocol 3005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3202 "Tripp Lite" "ups" "3" "HTR10-2U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3343 "Tripp Lite" "ups" "3" "INTERNET350U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2663 "Tripp Lite" "ups" "3" "INTERNET550U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3063 "Tripp Lite" "ups" "3" "INTERNET600U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3033 "Tripp Lite" "ups" "3" "INTERNET750U" "USB (protocol 1007)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3142 "Tripp Lite" "ups" "3" "INTERNET900U" "USB (protocol 1007)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3657 "Tripp Lite" "ups" "3" "INTERNETOFFICE500" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=11 "Tripp Lite" "ups" "1" "INTERNETOFFICE700" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "INTERNETOFFICE700" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=14 "Tripp Lite" "ups" "3" "OMNI650LCD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3195 "Tripp Lite" "ups" "3" "OMNI900LCD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=3082 "Tripp Lite" "ups" "2" "OMNI1000LCD" "USB" "usbhid-ups" "Tripp Lite" "ups" "3" "OMNISMART300PNP" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=19 "Tripp Lite" "ups" "1" "OMNISMART500" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "OMNISMART500" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=21 "Tripp Lite" "ups" "3" "OMNISMART700" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=23 "Tripp Lite" "ups" "2" "OMNIVSINT800" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "OMNIVS800" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=2729 "Tripp Lite" "ups" "2" "OMNIVS1000" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "OMNIVS1000" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=2656 "Tripp Lite" "ups" "1" "OMNIVS1500XL" "USB" "tripplite_usb" "Tripp Lite" "ups" "3" "POS500" "USB (protocol 2007)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3742 "Tripp Lite" "ups" "3" "SM2200RMDVTAA" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4648 "Tripp Lite" "ups" "1" "SmartUPS" "" "tripplite" "Tripp Lite" "ups" "1" "SmartOnline" "" "tripplitesu" "Tripp Lite" "ups" "1" "SMART550USB" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "SMART550USB" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=2002 "Tripp Lite" "ups" "3" "SMART550USBWD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=2591 "Tripp Lite" "ups" "1" "SMART700USB" "USB" "tripplite_usb" "Tripp Lite" "ups" "3" "SMART750RMXL2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3020 "Tripp Lite" "ups" "3" "SMART750SLT" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3007 "Tripp Lite" "ups" "3" "SMART750USB" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=2679 "Tripp Lite" "ups" "3" "SMART750XLA" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3820 "Tripp Lite" "ups" "3" "SMART1000LCD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=3071 "Tripp Lite" "ups" "3" "SMART1000RM2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2657 "Tripp Lite" "ups" "3" "SMART1000RMXL2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5262 "Tripp Lite" "ups" "3" "SMART1050SLT" "USB (protocol 3012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3253 "Tripp Lite" "ups" "3" "SMART1050SLTAA" "USB (protocol 3012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4389 "Tripp Lite" "ups" "3" "SMART1200LCD" "USB (protocol 2009)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3193 "Tripp Lite" "ups" "1" "SMART1500RM2U" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "SMART1500CRMXL" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3826 "Tripp Lite" "ups" "3" "SMART1500LCD" "USB (protocol 2009)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=3151 "Tripp Lite" "ups" "3" "SMART1500LCDXL" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=4978 "Tripp Lite" "ups" "3" "SMART1500RM2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=2658 "Tripp Lite" "ups" "3" "SMART1500RM2UN" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5424 "Tripp Lite" "ups" "3" "SMART1500RMXL2UA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3982 "Tripp Lite" "ups" "3" "SMART1500RMXLN" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5429 "Tripp Lite" "ups" "3" "SMART1500SLT" "USB (protocol 3012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3254 "Tripp Lite" "ups" "3" "SMART2200CRMXL" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3825 "Tripp Lite" "ups" "3" "SMART2200RM2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=4403 "Tripp Lite" "ups" "3" "SMART2200RM2UN" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5428 "Tripp Lite" "ups" "1" "SMART2200RMXL2U" "USB (older; product ID: 0001)" "tripplite_usb (experimental)" "Tripp Lite" "ups" "3" "SMART2200RMXL2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3264 "Tripp Lite" "ups" "3" "SMART2200RMXL2UP" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=4672 "Tripp Lite" "ups" "3" "SMART2200RMXLN" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5427 "Tripp Lite" "ups" "3" "SMART2200SLT" "USB (protocol 3013)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3489 "Tripp Lite" "ups" "3" "SMART2200SLTAA" "USB (protocol 3013)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4390 "Tripp Lite" "ups" "3" "SMART2500XLHG" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5440 "Tripp Lite" "ups" "3" "SMART2600RM2U" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3024 "Tripp Lite" "ups" "3" "SMART3000CRMXL" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3824 "Tripp Lite" "ups" "3" "SMART3000RM2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3025 "Tripp Lite" "ups" "3" "SMART3000RM2UN" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5426 "Tripp Lite" "ups" "3" "SMART3000RMOD2U" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=4249 "Tripp Lite" "ups" "3" "SMART3000RMXL2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=4418 "Tripp Lite" "ups" "3" "SMART3000RMXLN" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=5425 "Tripp Lite" "ups" "3" "SMART3000SLT" "USB (protocol 3013)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3490 "Tripp Lite" "ups" "2" "SMART500RT1U" "USB (older; product ID 0001, protocol 3005)" "tripplite_usb" "Tripp Lite" "ups" "3" "SMART500RT1U" "USB (newer; protocol/product ID 3005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2853 "Tripp Lite" "ups" "3" "SMX1000LCD" "USB (protocol 2005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3200 "Tripp Lite" "ups" "3" "SMX1000RT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2798 "Tripp Lite" "ups" "3" "SMX1050SLT" "USB (protocol 3012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3249 "Tripp Lite" "ups" "3" "SMX1500LCD" "USB (protocol 2009)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=3303 "Tripp Lite" "ups" "3" "SMX1500SLT" "USB (protocol 3012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3248 "Tripp Lite" "ups" "3" "SMX1500XLRT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=2687 "Tripp Lite" "ups" "3" "SMX2200XLRT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3251 "Tripp Lite" "ups" "3" "SMX3000RT2UTAA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4396 "Tripp Lite" "ups" "3" "SMX3000XLRT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=2694 "Tripp Lite" "ups" "3" "SMX3000XLRT2UA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5658 "Tripp Lite" "ups" "3" "SMX500RT1U" "USB (protocol 3005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2691 "Tripp Lite" "ups" "3" "SMX750SLT" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3021 "Tripp Lite" "ups" "3" "SU750RTXL2U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3194 "Tripp Lite" "ups" "3" "SU750RTXLCD2U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5070 "Tripp Lite" "ups" "3" "SU750XL" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3299 "Tripp Lite" "ups" "3" "SU1000RTXL2UA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=2948 "Tripp Lite" "ups" "3" "SU1000RTXLCD2U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=745&txtModelID=4980 "Tripp Lite" "ups" "3" "SU1000XLA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3180 "Tripp Lite" "ups" "3" "SU1000XLCD" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5320 "Tripp Lite" "ups" "3" "SU1500RTXL2UA (SmartOnline)" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=2949 "Tripp Lite" "ups" "3" "SU1500RTXLCD2U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=745&txtModelID=5037 "Tripp Lite" "ups" "3" "SU1500XL" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3300 "Tripp Lite" "ups" "3" "SU1500XLCD" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=744&txtModelID=5339 "Tripp Lite" "ups" "3" "SU2200RTXL2UA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=2950 "Tripp Lite" "ups" "3" "SU2200RTXLCD2U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=745&txtModelID=5069 "Tripp Lite" "ups" "3" "SU2200XLA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3594 "Tripp Lite" "ups" "3" "SU2200XLCD" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=744&txtModelID=5340 "Tripp Lite" "ups" "3" "SU3000RTXL2U" "USB (protocol 4005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4210 "Tripp Lite" "ups" "3" "SU3000RTXL3U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3023 "Tripp Lite" "ups" "3" "SU3000RTXLCD3U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=745&txtModelID=5071 "Tripp Lite" "ups" "3" "SU3000RTXR3U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3080 "Tripp Lite" "ups" "3" "SU3000RTXR3UHW" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=4976 "Tripp Lite" "ups" "3" "SU3000XL" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3601 "Tripp Lite" "ups" "3" "SU3000XLCD" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=744&txtModelID=5342 "Tripp Lite" "ups" "3" "SUINT1000RTXL2UA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3983 "Tripp Lite" "ups" "3" "SUINT1500RTXL2UA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=2720 "Tripp Lite" "ups" "3" "SUINT2200RTXL2UA" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=934&txtModelID=3970 "Tripp Lite" "ups" "3" "SUINT3000RTXL2U" "USB (protocol 4005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4523 "Trust" "ups" "2" "UPS 1000 Management PW-4105" "" "blazer_ser" "Trust" "ups" "2" "UPS 1200VA Management PW-4120M" "" "blazer_ser" "Trust" "ups" "2" "UPS 1300VA Management PW-4130M" "" "blazer_ser" "TS Shara" "ups" "4" "(various)" "" "nutdrv_qx" "UNITEK" "ups" "2" "ALPHA 500 IC" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha 1000is" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha 500" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha 500 ipE" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha650ipF" "USB" "blazer_usb" "UNITEK" "ups" "2" "Alpha650ipE" "USB" "blazer_usb" "UNITEK" "ups" "2" "Alpha 2600" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha 1200Sx" "USB" "blazer_usb" "UNITEK" "ups" "2" "Alpha 1250xD" "USB" "blazer_usb" "UPSonic" "ups" "2" "CXR1000" "" "blazer_ser" "UPSonic" "ups" "1" "LAN Saver 600" "" "genericups upstype=0" "UPSonic" "ups" "1" "Power Guardian" "" "genericups upstype=7" "UPSonic" "ups" "2" "PrOffice 650" "USB" "blazer_usb" "UPSonic" "ups" "2" "DS-800" "USB" "blazer_usb" "Various" "ups" "4" "(various)" "SEC protocol" "gamatronic" "Various" "ups" "1" "(various)" "Generic RUPS model" "genericups upstype=4" "Various" "ups" "1" "(various)" "Generic RUPS 2000 (Megatec M2501 cable)" "genericups upstype=21" "Various" "ups" "2" "(various serial)" "Q1 / Megatec protocol" "blazer_ser" "Various" "ups" "2" "(various USB)" "Q1 / Megatec protocol" "blazer_usb" "Various" "ups" "2" "(various)" "PhoenixTec protocol" "blazer_ser or bestups" "Various" "ups" "3" "(various)" "SNMP - RFC 1628" "snmp-ups (experimental)" "Various" "ups" "1" "(various)" "Safenet software" "safenet" "Various" "ups" "2" "(Various USB)" "UPSilon 2000 software" "blazer_usb langid_fix=0x409" "Various" "ups" "2" "(Various serial)" "ViewPower software" "nutdrv_qx" "Various" "ups" "2" "(Various USB)" "ViewPower software" "nutdrv_qx" "Various" "ups" "2" "(Various serial)" "WinPower software" "blazer_ser" "Various" "ups" "2" "(Various USB)" "WinPower software" "blazer_usb" "Various" "ups" "2" "Various Innova T" "(USB ID 06da:0201)" "blazer_ser or blazer_usb" "Various" "ups" "2" "Various Innova RT" "(USB ID 06da:0005)" "blazer_ser or blazer_usb" "Various" "ups" "2" "Various Innova 3/1 T" "(USB ID 06da:0004)" "blazer_ser or blazer_usb" "Victron/IMV" "ups" "1" "(various)" "" "victronups" "Victron/IMV" "ups" "1" "Lite" "crack cable" "genericups upstype=10" "Viewsonic" "ups" "1" "PowerES" "420E" "optiups" "Vivaldi" "ups" "1" "EA200 LED" "USB" "richcomm_usb" "Voltronic Power" "ups" "2" "Apex 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Apex 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Frigate TX 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Frigate TX 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Galleon 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Galleon 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Imperial 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Imperial 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Prosine 800" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Prosine 800" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Vesta LED 850VA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "(various)" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "(various)" "USB" "nutdrv_qx" "WinPower" "ups" "2" "CPM-800" "" "blazer_ser" "WTI" "pdu" "1" "RPS-10" "10 outlets" "powerman-pdu (experimental)" "WTI" "pdu" "1" "NPS" "8 outlets" "powerman-pdu (experimental)" nut-2.7.4/data/Makefile.am0000644000175000017500000000022412640443572012225 00000000000000# Network UPS Tools: data SUBDIRS = html dist_data_DATA = cmdvartab nodist_data_DATA = driver.list EXTRA_DIST = evolution500.seq epdu-managed.dev nut-2.7.4/data/html/0000755000175000017500000000000012670024740011212 500000000000000nut-2.7.4/data/html/bottom.html0000644000175000017500000000007612640443572013334 00000000000000 nut-2.7.4/data/html/README0000644000175000017500000000524512640443572012025 00000000000000Desc: NUT HTML complementary information File: README Date: 27 Jul 2005 Auth: Arnaud Quette Dave Breiland This file provides some complementary information about the use and integration of NUT HTML pages. 1) Introduction --------------- NUT HTML pages have been created as a central point that ease the access to the various CGI scripts providing the NUT web interface. It consists of three .html files: - index.html: defines the two container frames, topFrame and mainFrame - header.html: contain the header including links to NUT website, and upsstat.cgi/upsset.cgi - bottom.html: empty frame that will be replaced with the content of upsstat.cgi or upsset.cgi. 2) Integration -------------- You first need to install NUT CGI (ie using ./configure --with-cgi). Refer to the README file for more information There are two ways to integrate NUT HTML with your webserver, with the same results: a) take advantage of the existing tree ====================================== - the cgi are for example installed in /usr/lib/cgi-bin, which is already configured in your webserver as the default CGI path - in the same spirit, we will use the existing DocumentRoot and create a "nut" subdirectory, and copy the three .html files (index, header and bottom) Note that the links to cgi scripts in header.html are pre configured to work in this situation, which ease the packagers work. b) configure manually ===================== - copy the data/html directory to somepath (ie /usr/local/nut for a standard installation from source) -Now edit your webserver configuration file, adding for example (for Apache): #Begin Section ScriptAlias /nut/cgi-bin/ /usr/local/nut/cgi-bin/ AllowOverride AuthConfig Options ExecCGI Order allow,deny Allow from all Alias /nut/ /usr/local/nut/html/ Options None AllowOverride AuthConfig Order allow,deny Allow from all #End Section -Make sure to change the links path in header.html according to your configuration and installation. 3) Conclusion ============= - Make sure to restart your webserver. -Configure the CGI scripts. Manpages can be found from: --prompt> man -M /usr/local/nut/man/ upsstats.cgi --prompt> man -M /usr/local/nut/man/ upsset.cgi --prompt> man -M /usr/local/nut/man/ upsimage.cgi --prompt> man -M /usr/local/nut/man/ hosts.conf -It is recommended that you use .htaccess files in the cgi-bin folder and the html folder. Please reference: http://httpd.apache.org/docs/howto/htaccess.html - You will then be able to access the NUT HTML page at: http://localhost/nut nut-2.7.4/data/html/Makefile.in0000644000175000017500000004343112667762000013210 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = data/html DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/header.html.in $(am__dist_html_DATA_DIST) README ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = header.html CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__dist_html_DATA_DIST = index.html bottom.html nut-banner.png am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(htmldir)" "$(DESTDIR)$(htmldir)" DATA = $(dist_html_DATA) $(nodist_html_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # Network UPS Tools: data/html # install these only if configured --with-cgi @WITH_CGI_TRUE@dist_html_DATA = index.html bottom.html nut-banner.png @WITH_CGI_TRUE@nodist_html_DATA = header.html EXTRA_DIST = README all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu data/html/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu data/html/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): header.html: $(top_builddir)/config.status $(srcdir)/header.html.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-dist_htmlDATA: $(dist_html_DATA) @$(NORMAL_INSTALL) @list='$(dist_html_DATA)'; test -n "$(htmldir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ done uninstall-dist_htmlDATA: @$(NORMAL_UNINSTALL) @list='$(dist_html_DATA)'; test -n "$(htmldir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(htmldir)'; $(am__uninstall_files_from_dir) install-nodist_htmlDATA: $(nodist_html_DATA) @$(NORMAL_INSTALL) @list='$(nodist_html_DATA)'; test -n "$(htmldir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)'"; \ $(MKDIR_P) "$(DESTDIR)$(htmldir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \ done uninstall-nodist_htmlDATA: @$(NORMAL_UNINSTALL) @list='$(nodist_html_DATA)'; test -n "$(htmldir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(htmldir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(htmldir)" "$(DESTDIR)$(htmldir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dist_htmlDATA install-nodist_htmlDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_htmlDATA uninstall-nodist_htmlDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-dist_htmlDATA install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-nodist_htmlDATA \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am uninstall-dist_htmlDATA \ uninstall-nodist_htmlDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/data/html/nut-banner.png0000644000175000017500000000527612640443572013730 00000000000000‰PNG  IHDR("±S$PLTEÀÿ€ÿ?€8›ÿUªÿÿªÔÿÿÿÿÆÿâðÿq¸ÿÆâÿØl tEXtSoftwaregif2png 1.1.1,¦!q¨tEXtmimencode_-d_gzip_-dc H4sICOfSNEAAA3owAKVUS2/jNhA+h79ijglq0ZQc+aFsF039wLpYZQPLLQoUPdDy2GZXJgWS ipOiP75D2Umd3Q1aoLyIEGe+x8yQM2v2MJHWouZ5eYeqqn6QdVnykv4vdw3McAXxCMQgi9NM JJAIcc0W6Buro3vpdxm8eyP9PYWVqB5wncEm0GgX4R7l3kv+QnH5Wz/h8YAnaY+PRr9fsYvV E2xR2pV55PhYV0Z5x43dwuWQxxQquqfvFRyU38G0yJf3oNag4p8ms9jNhYiHaZ9dbIyFd/az NSTpHOj9TfDVCaaCt+Dn2V0MkegL8aXuUre6tdtLVfGwDfpP8uN0xJM4JUGCXwcD8LLIyTct fzS+cTAxe6UNLLBC6RD6XPDeefZrd0GmSOJRnKTDdCDSKIn7AwE3ZwlfuYqTLB1m6QC+E7SA /RrlZABtBkcFd8ajexGQkoABQC5tuYMk7gSMmOXzfBr9gtYpozOIuWBFs/oDS5/B7f0YSrmq ENboCdixpcm+XXKWo3Nyi9F8QhGfZv3r3nT843jCR4NBcivGw+jT3VAkaX/am3GSnl4Pr3tn gzSjNmRvjSmbSI/Zv9kP7snL0srycwYFWiUr9SeGJi1M49GC0ZArrc2hO82nt12yN7581Z2/ Cqw97lcUm/Tb+vSuQHpGrF2RdF+RdtjFnGJPDG0XifPhSBPw82VLQZ//SEIMxPPa2X1OPG94 +b8kIj7diS9IqOJ1hR4p/o1wNjbao/aRf6qpMR4ffbeupNI3UO6kdei//7mIbovxfM4KL2kU M1iwl/VBdRibE4JF55XegjdgUa6DKb9DOFCDn0xjn8cO5IpMt0dhJIu9tB4KtdWyqiidtTPK GZs+oIa9sQjqDPwgHaWSF4IEWfqGsp6gblaVcjtc05lyZ8wdkI6F/TO52bRHx4sgbaiP3qg1 2ad6cZpK/ErRKVgdgT6qEnWJEN6roN+ZjT8QUId2pBRws6Hr1srbyQdKI4xtK7I0tcI1q44A rkUIdamtWTclXT3GiN79o7XNXyGVoZbOBXOmFW9pNqg4nhgbS0iUuMCttGtHnTjeOjjdOiqj K2UlPb0HUDR1bcjaVG+VRrShezvv66zbPRwOz48k/fwb3RDCHGYGAAA= NGÄå~IDATxœíYMOWòžfì1öŽ` É‚…úՊ¡]Y¢žÀÎæb{e%Û%"ɤé á¦;¤T£ª®çÜ7cŠä©pdqEŒß™÷î¹÷Ü!Ož<ʃ’§_Û€EÉê×6`Aò”@ÌÈê#&@š|9 ÍúÿºÏ—²v ÝEì³ÊY®óYk4Æ8ø òžïŠÈõdÕlp‡>ªÜ«ñLµÎUçÒ^ç•êí}ŠØ§ëÄ<@BìèžùÙâÎàãQQpmî%ò!0¦ãË »ÙÊð_û|'r‘ëÀ\ y„ó\.ƒ‰nt2ç¡YÔͱô‘y[]‹%JDÞÑ=¡R”÷"ðG¹y½—+Gä Ç ¦g^À€êtU“S|h8_ˆ±,J„PEz¹"A!+"î·¥ }.É.<6f%šjöf2àN¹i€Ïç.`¤Ú6ÄÔ?ÖËÞÀxPï°¹¸P 2"™==u»ó I˜n§ 7?3å—0Ðlt±4…­¶“¼ƒx¢¼ŸnÒjS¢MäÚ‰Ó§gˆ_°13q|‰ ÊEˆXè>ùXXD²×”KMúqÈìkñ@ñ‡Q•>¢öÖ³þ‘È_Ú;TVl MÚi“ï`áÓL+wm'Q ¤¬¸„_nù8ÇÆ\@@ ¸èƒüîY__ÈßQ›‡[ Yÿ@ C6¤âÙQ„f%²eòI&m5%Ÿ*°—Æ–+ÛØ‰þÏ!%y߳݌2V @æ>€8 ˆñ´;kÛ„›NIzºz–Ô}h˜ô´ï)„]°DØPâ&á½K‰^”•ÑÌè®dï(5µÇÛF#¾³@´Æ©+j‘nsÞ±bç@­ïuFéBîîÃsó| ¤hÞ×3àÙaj"P°nlöQÏ<Ûh—Vj,¦uª¦Ú´Ð)+žÄÌVuX îľâ6õ=Ôy£µÓÕµI}úÞš ¿köuµIõng3Ûc£ó’”r¦ï³Îô*hèêöðk®¸ëFgŽÇ?=8yòÐd¹€¬.…ð?{–B–% «ËÕÖýìàhbPIEND®B`‚nut-2.7.4/data/html/index.html0000644000175000017500000000076712640443572013146 00000000000000 Network UPS Tools -- http://www.networkupstools.org <body> </body> nut-2.7.4/data/html/Makefile.am0000644000175000017500000000037512640443572013200 00000000000000# Network UPS Tools: data/html # install these only if configured --with-cgi if WITH_CGI dist_html_DATA = index.html bottom.html nut-banner.png nodist_html_DATA = header.html endif EXTRA_DIST = README nut-2.7.4/data/html/header.html.in0000644000175000017500000000115712640443572013666 00000000000000 Network UPS Tools

    Statistics Settings
    nut-2.7.4/data/epdu-managed.dev0000644000175000017500000000317212640443572013225 00000000000000# dummy-ups example device definition file # # Generated using: # $ upsc ups@host > epdu-managed.dev device.mfr: EATON | Powerware device.model: DBQ10634/5 device.serial: ADO6750531 device.type: pdu driver.name: snmp-ups driver.parameter.pollinterval: 2 driver.parameter.port: somewhere.org driver.version: 2.3.0-1540MS driver.version.internal: 0.44 (mib: aphel_revelation 0.2) outlet.1.current: 0.00 outlet.1.current.maximum: 0.00 outlet.1.desc: Outlet 1 outlet.1.id: 1 outlet.1.power: 0.00 outlet.1.powerfactor: 0.05 outlet.1.realpower: 0.00 outlet.1.status: on outlet.1.switchable: 0.00 outlet.1.voltage: 247.00 outlet.2.current: 0.00 outlet.2.current.maximum: 0.16 outlet.2.desc: Outlet 2 outlet.2.id: 2 outlet.2.power: 0.00 outlet.2.powerfactor: 0.01 outlet.2.realpower: 0.00 outlet.2.status: on outlet.2.switchable: 1.00 outlet.2.voltage: 247.00 outlet.3.current: 0.00 outlet.3.current.maximum: 0.16 outlet.3.desc: Outlet 3 outlet.3.id: 3 outlet.3.power: 0.00 outlet.3.powerfactor: 0.13 outlet.3.realpower: 0.00 outlet.3.status: on outlet.3.switchable: 2.00 outlet.3.voltage: 247.00 outlet.4.current: 0.19 outlet.4.current.maximum: 0.56 outlet.4.desc: Outlet 4 outlet.4.id: 2 outlet.4.power: 46.00 outlet.4.powerfactor: 0.60 outlet.4.realpower: 28.00 outlet.4.status: on outlet.4.switchable: 3.00 outlet.4.voltage: 247.00 outlet.count: 4.00 outlet.current: 0.19 outlet.desc: All outlets outlet.id: 0 outlet.power: 46.00 outlet.realpower: 28.00 outlet.voltage: 247.00 ups.firmware: 01.01.00 ups.id: my_device234 ups.macaddr: ups.mfr: EATON | Powerware ups.model: DBQ10634/5 ups.serial: ADO6750531 ups.status: ups.temperature: 49.00 device.type: pdu nut-2.7.4/ltmain.sh0000644000175000017500000105203012640476414011104 00000000000000 # libtool (GNU libtool) 2.4.2 # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, # 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool 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. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that 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 GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, # or obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Usage: $progname [OPTION]... [MODE-ARG]... # # Provide generalized library-building support services. # # --config show all configuration variables # --debug enable verbose shell tracing # -n, --dry-run display commands without modifying any files # --features display basic configuration information and exit # --mode=MODE use operation mode MODE # --preserve-dup-deps don't remove duplicate dependency libraries # --quiet, --silent don't print informational messages # --no-quiet, --no-silent # print informational messages (default) # --no-warn don't display warning messages # --tag=TAG use configuration variables from tag TAG # -v, --verbose print more informational messages than default # --no-verbose don't print the extra informational messages # --version print version information # -h, --help, --help-all print short, long, or detailed help message # # MODE must be one of the following: # # clean remove files from the build directory # compile compile a source file into a libtool object # execute automatically set library path, then run a program # finish complete the installation of libtool libraries # install install libraries or executables # link create a library or an executable # uninstall remove libraries from an installed directory # # MODE-ARGS vary depending on the MODE. When passed as first option, # `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. # Try `$progname --help --mode=MODE' for a more detailed description of MODE. # # When reporting a bug, please describe a test case to reproduce it and # include the following information: # # host-triplet: $host # shell: $SHELL # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) # $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.11 # automake: $automake_version # autoconf: $autoconf_version # # Report bugs to . # GNU libtool home page: . # General help using GNU software: . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.2 Debian-2.4.2-1.11" TIMESTAMP="" package_revision=1.3337 # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # NLS nuisances: We save the old values to restore during execute mode. lt_user_locale= lt_safe_locale= for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${$lt_var+set}\" = set; then save_$lt_var=\$$lt_var $lt_var=C export $lt_var lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" fi" done LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL $lt_unset CDPATH # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath="$0" : ${CP="cp -f"} test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} : ${Xsed="$SED -e 1s/^X//"} # Global variables: EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. exit_status=$EXIT_SUCCESS # Make sure IFS has a sensible default lt_nl=' ' IFS=" $lt_nl" dirname="s,/[^/]*$,," basename="s,^.*/,," # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_dirname may be replaced by extended shell implementation # func_basename file func_basename () { func_basename_result=`$ECHO "${1}" | $SED "$basename"` } # func_basename may be replaced by extended shell implementation # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` } # func_dirname_and_basename may be replaced by extended shell implementation # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname may be replaced by extended shell implementation # These SED scripts presuppose an absolute path with a trailing slash. pathcar='s,^/\([^/]*\).*$,\1,' pathcdr='s,^/[^/]*,,' removedotparts=':dotsl s@/\./@/@g t dotsl s,/\.$,/,' collapseslashes='s@/\{1,\}@/@g' finalslash='s,/*$,/,' # func_normal_abspath PATH # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. # value returned in "$func_normal_abspath_result" func_normal_abspath () { # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` while :; do # Processed it all yet? if test "$func_normal_abspath_tpath" = / ; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result" ; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_relative_path SRCDIR DSTDIR # generates a relative path from SRCDIR to DSTDIR, with a trailing # slash if non-empty, suitable for immediately appending a filename # without needing to append a separator. # value returned in "$func_relative_path_result" func_relative_path () { func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=${func_dirname_result} if test "x$func_relative_path_tlibdir" = x ; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test "x$func_stripname_result" != x ; then func_relative_path_result=${func_relative_path_result}/${func_stripname_result} fi # Normalisation. If bindir is libdir, return empty string, # else relative path ending with a slash; either way, target # file name can be directly appended. if test ! -z "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result/" func_relative_path_result=$func_stripname_result fi } # The name of this program: func_dirname_and_basename "$progpath" progname=$func_basename_result # Make sure we have an absolute path for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=$func_dirname_result progdir=`cd "$progdir" && pwd` progpath="$progdir/$progname" ;; *) save_IFS="$IFS" IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS="$save_IFS" test -x "$progdir/$progname" && break done IFS="$save_IFS" test -n "$progdir" || progdir=`pwd` progpath="$progdir/$progname" ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed="${SED}"' -e 1s/^X//' sed_quote_subst='s/\([`"$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' # Sed substitution that converts a w32 file name or path # which contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-`\' parameter expansions in output of double_quote_subst that were # `\'-ed in input to the same. If an odd number of `\' preceded a '$' # in input to double_quote_subst, that '$' was protected from expansion. # Since each input `\' is now two `\'s, look for any number of runs of # four `\'s followed by two `\'s and then a '$'. `\' that '$'. bs='\\' bs2='\\\\' bs4='\\\\\\\\' dollar='\$' sed_double_backslash="\ s/$bs4/&\\ /g s/^$bs2$dollar/$bs&/ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g s/\n//g" # Standard options: opt_dry_run=false opt_help=false opt_quiet=false opt_verbose=false opt_warning=: # func_echo arg... # Echo program name prefixed message, along with the current mode # name if it has been set yet. func_echo () { $ECHO "$progname: ${opt_mode+$opt_mode: }$*" } # func_verbose arg... # Echo program name prefixed message in verbose mode only. func_verbose () { $opt_verbose && func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_error arg... # Echo program name prefixed message to standard error. func_error () { $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 } # func_warning arg... # Echo program name prefixed warning message to standard error. func_warning () { $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 # bash bug again: : } # func_fatal_error arg... # Echo program name prefixed message to standard error, and exit. func_fatal_error () { func_error ${1+"$@"} exit $EXIT_FAILURE } # func_fatal_help arg... # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { func_error ${1+"$@"} func_fatal_error "$help" } help="Try \`$progname --help' for more information." ## default # func_grep expression filename # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $GREP "$1" "$2" >/dev/null 2>&1 } # func_mkdir_p directory-path # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { my_directory_path="$1" my_dir_list= if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then # Protect directory names starting with `-' case $my_directory_path in -*) my_directory_path="./$my_directory_path" ;; esac # While some portion of DIR does not yet exist... while test ! -d "$my_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. my_dir_list="$my_directory_path:$my_dir_list" # If the last portion added has no slash in it, the list is done case $my_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` done my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` save_mkdir_p_IFS="$IFS"; IFS=':' for my_dir in $my_dir_list; do IFS="$save_mkdir_p_IFS" # mkdir can fail with a `File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$my_dir" 2>/dev/null || : done IFS="$save_mkdir_p_IFS" # Bail out if we (or some other process) failed to create a directory. test -d "$my_directory_path" || \ func_fatal_error "Failed to create \`$1'" fi } # func_mktempdir [string] # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, STRING is the basename for that directory. func_mktempdir () { my_template="${TMPDIR-/tmp}/${1-$progname}" if test "$opt_dry_run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else # If mktemp works, use that first and foremost my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race my_tmpdir="${my_template}-${RANDOM-0}$$" save_mktempdir_umask=`umask` umask 0077 $MKDIR "$my_tmpdir" umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$my_tmpdir" || \ func_fatal_error "cannot create temporary directory \`$my_tmpdir'" fi $ECHO "$my_tmpdir" } # func_quote_for_eval arg # Aesthetically quote ARG to be evaled later. # This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT # is double-quoted, suitable for a subsequent eval, whereas # FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters # which are still active within double quotes backslashified. func_quote_for_eval () { case $1 in *[\\\`\"\$]*) func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; *) func_quote_for_eval_unquoted_result="$1" ;; esac case $func_quote_for_eval_unquoted_result in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and and variable # expansion for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" ;; *) func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" esac } # func_quote_for_expand arg # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { case $1 in *[\\\`\"]*) my_arg=`$ECHO "$1" | $SED \ -e "$double_quote_subst" -e "$sed_double_backslash"` ;; *) my_arg="$1" ;; esac case $my_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") my_arg="\"$my_arg\"" ;; esac func_quote_for_expand_result="$my_arg" } # func_show_eval cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$my_cmd" my_status=$? if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_show_eval_locale cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$lt_user_locale $my_cmd" my_status=$? eval "$lt_safe_locale" if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_tr_sh # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_version # Echo version message to standard output and exit. func_version () { $opt_debug $SED -n '/(C)/!b go :more /\./!{ N s/\n# / / b more } :go /^# '$PROGRAM' (GNU /,/# warranty; / { s/^# // s/^# *$// s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ p }' < "$progpath" exit $? } # func_usage # Echo short help message to standard output and exit. func_usage () { $opt_debug $SED -n '/^# Usage:/,/^# *.*--help/ { s/^# // s/^# *$// s/\$progname/'$progname'/ p }' < "$progpath" echo $ECHO "run \`$progname --help | more' for full usage" exit $? } # func_help [NOEXIT] # Echo long help message to standard output and exit, # unless 'noexit' is passed as argument. func_help () { $opt_debug $SED -n '/^# Usage:/,/# Report bugs to/ { :print s/^# // s/^# *$// s*\$progname*'$progname'* s*\$host*'"$host"'* s*\$SHELL*'"$SHELL"'* s*\$LTCC*'"$LTCC"'* s*\$LTCFLAGS*'"$LTCFLAGS"'* s*\$LD*'"$LD"'* s/\$with_gnu_ld/'"$with_gnu_ld"'/ s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ p d } /^# .* home page:/b print /^# General help using/b print ' < "$progpath" ret=$? if test -z "$1"; then exit $ret fi } # func_missing_arg argname # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $opt_debug func_error "missing argument for $1." exit_cmd=exit } # func_split_short_opt shortopt # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. func_split_short_opt () { my_sed_short_opt='1s/^\(..\).*$/\1/;q' my_sed_short_rest='1s/^..\(.*\)$/\1/;q' func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` } # func_split_short_opt may be replaced by extended shell implementation # func_split_long_opt longopt # Set func_split_long_opt_name and func_split_long_opt_arg shell # variables after splitting LONGOPT at the `=' sign. func_split_long_opt () { my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' my_sed_long_arg='1s/^--[^=]*=//' func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` } # func_split_long_opt may be replaced by extended shell implementation exit_cmd=: magic="%%%MAGIC variable%%%" magic_exe="%%%MAGIC EXE variable%%%" # Global variables. nonopt= preserve_args= lo2o="s/\\.lo\$/.${objext}/" o2lo="s/\\.${objext}\$/.lo/" extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "${1}=\$${1}\${2}" } # func_append may be replaced by extended shell implementation # func_append_quoted var value # Quote VALUE and append to the end of shell variable VAR, separated # by a space. func_append_quoted () { func_quote_for_eval "${2}" eval "${1}=\$${1}\\ \$func_quote_for_eval_result" } # func_append_quoted may be replaced by extended shell implementation # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "${@}"` } # func_arith may be replaced by extended shell implementation # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` } # func_len may be replaced by extended shell implementation # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` } # func_lo2o may be replaced by extended shell implementation # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` } # func_xform may be replaced by extended shell implementation # func_fatal_configuration arg... # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_error ${1+"$@"} func_error "See the $PACKAGE documentation for more information." func_fatal_error "Fatal configuration error." } # func_config # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # Display the features supported by this script. func_features () { echo "host: $host" if test "$build_libtool_libs" = yes; then echo "enable shared libraries" else echo "disable shared libraries" fi if test "$build_old_libs" = yes; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag tagname # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname="$1" re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf="/$re_begincf/,/$re_endcf/p" # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Option defaults: opt_debug=: opt_dry_run=false opt_config=false opt_preserve_dup_deps=false opt_features=false opt_finish=false opt_help=false opt_help_all=false opt_silent=: opt_warning=: opt_verbose=: opt_silent=false opt_verbose=false # Parse options once, thoroughly. This comes as soon as possible in the # script to make things like `--version' happen as quickly as we can. { # this just eases exit handling while test $# -gt 0; do opt="$1" shift case $opt in --debug|-x) opt_debug='set -x' func_echo "enabling shell trace mode" $opt_debug ;; --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) opt_config=: func_config ;; --dlopen|-dlopen) optarg="$1" opt_dlopen="${opt_dlopen+$opt_dlopen }$optarg" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) opt_features=: func_features ;; --finish) opt_finish=: set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help_all=: opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $opt && break optarg="$1" opt_mode="$optarg" case $optarg in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_silent=false func_append preserve_args " $opt" ;; --no-warning|--no-warn) opt_warning=false func_append preserve_args " $opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $opt" ;; --silent|--quiet) opt_silent=: func_append preserve_args " $opt" opt_verbose=false ;; --verbose|-v) opt_verbose=: func_append preserve_args " $opt" opt_silent=false ;; --tag) test $# = 0 && func_missing_arg $opt && break optarg="$1" opt_tag="$optarg" func_append preserve_args " $opt $optarg" func_enable_tag "$optarg" shift ;; -\?|-h) func_usage ;; --help) func_help ;; --version) func_version ;; # Separate optargs to long options: --*=*) func_split_long_opt "$opt" set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-n*|-v*) func_split_short_opt "$opt" set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognized option \`$opt'" ;; *) set dummy "$opt" ${1+"$@"}; shift; break ;; esac done # Validate options: # save first non-option argument if test "$#" -gt 0; then nonopt="$opt" shift fi # preserve --debug test "$opt_debug" = : || func_append preserve_args " --debug" case $host in *cygwin* | *mingw* | *pw32* | *cegcc*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then func_fatal_configuration "not configured to build any kind of library" fi # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test "$opt_mode" != execute; then func_error "unrecognized option \`-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" help="Try \`$progname --help --mode=$opt_mode' for more information." } # Bail if the options were screwed $exit_cmd $EXIT_FAILURE } ## ----------- ## ## Main. ## ## ----------- ## # func_lalib_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null \ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_unsafe_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if `file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case "$lalib_p_line" in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test "$lalib_p" = yes } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { func_lalib_p "$1" } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $opt_debug save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$save_ifs eval cmd=\"$cmd\" func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. func_source () { $opt_debug case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case "$lt_sysroot:$1" in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result="=$func_stripname_result" ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $opt_debug if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with \`--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=${1} if test "$build_libtool_libs" = yes; then write_lobj=\'${2}\' else write_lobj=none fi if test "$build_old_libs" = yes; then write_oldobj=\'${3}\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$lt_sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $opt_debug # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result="" if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result" ; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $opt_debug if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $opt_debug # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $opt_debug if test -z "$2" && test -n "$1" ; then func_error "Could not determine host file name corresponding to" func_error " \`$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result="$1" fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $opt_debug if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " \`$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result="$3" fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $opt_debug case $4 in $1 ) func_to_host_path_result="$3$func_to_host_path_result" ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via `$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $opt_debug $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $opt_debug case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result="$1" } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result="$func_convert_core_msys_to_w32_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result="$func_cygpath_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $opt_debug func_to_host_file_result="$1" if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result="$func_cygpath_result" fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via `$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $opt_debug if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd="func_convert_path_${func_stripname_result}" fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $opt_debug func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result="$1" } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result="$func_convert_core_msys_to_w32_result" func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result="$func_cygpath_result" func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $opt_debug func_to_host_path_result="$1" if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result="$func_cygpath_result" func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_mode_compile arg... func_mode_compile () { $opt_debug # Get the compilation command and the source file. base_compile= srcfile="$nonopt" # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg="$arg" arg_mode=normal ;; target ) libobj="$arg" arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify \`-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs="$IFS"; IFS=',' for arg in $args; do IFS="$save_ifs" func_append_quoted lastarg "$arg" done IFS="$save_ifs" func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg="$srcfile" srcfile="$arg" ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with \`-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj="$func_basename_result" } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from \`$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name \`$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname="$func_basename_result" xdir="$func_dirname_result" lobj=${xdir}$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test "$build_old_libs" = yes; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test "$build_libtool_libs" = yes; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test "$pic_mode" != no; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test "$suppress_opt" = yes; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test "$build_old_libs" = yes; then if test "$pic_mode" != yes; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test "$compiler_c_o" = yes; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test "$need_locks" != no; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test "$opt_mode" = compile && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a \`.o' file suitable for static linking -static only build a \`.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a \`standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix \`.c' with the library object suffix, \`.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to \`-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the \`--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the \`install' or \`cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE Use a list of object files found in FILE to specify objects -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with \`-') are ignored. Every other argument is treated as a filename. Files ending in \`.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only library objects (\`.lo' files) may be specified, and \`-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created using \`ar' and \`ranlib', or on Windows using \`lib'. If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode \`$opt_mode'" ;; esac echo $ECHO "Try \`$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test "$opt_help" = :; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | sed -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | sed '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $opt_debug # The first argument is the command name. cmd="$nonopt" test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "\`$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "\`$file' was not linked with \`-export-dynamic'" continue fi func_dirname "$file" "" "." dir="$func_dirname_result" if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir="$func_dirname_result" ;; *) func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir="$absdir" # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic="$magic" # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file="$progdir/$program" elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file="$progdir/$program" fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if test "X$opt_dry_run" = Xfalse; then if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd="\$cmd$args" else # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS fi } test "$opt_mode" = execute && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $opt_debug libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "\`$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument \`$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and \`=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_silent && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the \`-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the \`$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the \`$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the \`$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test "$opt_mode" = finish && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $opt_debug # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac; then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=no stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=yes ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test "x$prev" = x-m && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the \`$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=yes if test "$isdir" = yes; then destdir="$dest" destname= else func_dirname_and_basename "$dest" "" "." destdir="$func_dirname_result" destname="$func_basename_result" # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "\`$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "\`$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir="$func_dirname_result" func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking \`$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname="$1" shift srcname="$realname" test -n "$relink_command" && srcname="$realname"T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme="$stripme" case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme="" ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try `ln -sf' first, because the `ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib="$destdir/$realname" func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name="$func_basename_result" instname="$dir/$name"i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest="$destfile" destfile= ;; *) func_fatal_help "cannot copy a libtool object to \`$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test "$build_old_libs" = yes; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext="" case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=".exe" fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script \`$wrapper'" finalize=yes for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test if test -n "$libdir" && test ! -f "$libfile"; then func_warning "\`$lib' has not been installed in \`$libdir'" finalize=no fi done relink_command= func_source "$wrapper" outputname= if test "$fast_install" = no && test -n "$relink_command"; then $opt_dry_run || { if test "$finalize" = yes; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file="$func_basename_result" outputname="$tmpdir/$file" # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_silent || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink \`$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file="$outputname" else func_warning "cannot relink \`$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name="$func_basename_result" # Set up the ranlib parameters. oldlib="$destdir/$name" func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run \`$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test "$opt_mode" = install && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $opt_debug my_outputname="$1" my_originator="$2" my_pic_p="${3-no}" my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms="${my_outputname}S.c" else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist="$output_objdir/${my_outputname}.nm" func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif /* External symbol declarations for the compiler. */\ " if test "$dlself" = yes; then func_verbose "generating symbol list for \`$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols="$output_objdir/$outputname.exp" $opt_dry_run || { $RM $export_symbols eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from \`$dlprefile'" func_basename "$dlprefile" name="$func_basename_result" case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename="" if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname" ; then func_basename "$dlprefile_dlname" dlprefile_dlbasename="$func_basename_result" else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename" ; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[]; LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = {\ { \"$my_originator\", (void *) 0 }," case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) if test "X$my_pic_p" != Xno; then pic_flag_for_symtable=" $pic_flag" fi ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' # Transform the symbol file into the correct name. symfileobj="$output_objdir/${my_outputname}S.$objext" case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for \`$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $opt_debug win32_libid_type="unknown" win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s,.*,import, p q } }'` case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $opt_debug sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $opt_debug match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive which possess that section. Heuristic: eliminate # all those which have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $opt_debug func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $opt_debug func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $opt_debug if func_cygming_gnu_implib_p "$1" ; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1" ; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result="" fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $opt_debug f_ex_an_ar_dir="$1"; shift f_ex_an_ar_oldlib="$1" if test "$lock_old_archive_extraction" = yes; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test "$lock_old_archive_extraction" = yes; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $opt_debug my_gentop="$1"; shift my_oldlibs=${1+"$@"} my_oldobjs="" my_xlib="" my_xabs="" my_xdir="" for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib="$func_basename_result" my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir="$my_gentop/$my_xlib_u" func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` darwin_base_archive=`basename "$darwin_archive"` darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches ; do func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" func_extract_an_archive "`pwd`" "${darwin_base_archive}" cd "$darwin_curdir" $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result="$my_oldobjs" } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory in which it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ which is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options which match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test "$fast_install" = yes; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else $ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include /* declarations of non-ANSI functions */ #if defined(__MINGW32__) # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined(__CYGWIN__) # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined (other platforms) ... */ #endif /* portability defines, excluding path handling macros */ #if defined(_MSC_VER) # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC # ifndef _INTPTR_T_DEFINED # define _INTPTR_T_DEFINED # define intptr_t int # endif #elif defined(__MINGW32__) # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined(__CYGWIN__) # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined (other platforms) ... */ #endif #if defined(PATH_MAX) # define LT_PATHMAX PATH_MAX #elif defined(MAXPATHLEN) # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ defined (__OS2__) # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free ((void *) stale); stale = 0; } \ } while (0) #if defined(LT_DEBUGWRAPPER) static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; int tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined (HAVE_DOS_BASED_FILE_SYSTEM) if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined (HAVE_DOS_BASED_FILE_SYSTEM) } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = q - p; p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (strcmp (str, pat) == 0) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else int len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { int orig_value_len = strlen (orig_value); int add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ int len = strlen (new_value); while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[len-1] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $opt_debug case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_mode_link arg... func_mode_link () { $opt_debug case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # which system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll which has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=no prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module="${wl}-single_module" func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg="$1" shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir="$arg" prev= continue ;; dlfiles|dlprefiles) if test "$preload" = no; then # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=yes fi case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test "$dlself" = no; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test "$prev" = dlprefiles; then dlself=yes elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test "$prev" = dlfiles; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols="$arg" test -f "$arg" \ || func_fatal_error "symbol file \`$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex="$arg" prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir="$arg" prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file \`$arg' does not exist" fi arg=$save_arg prev= continue ;; precious_regex) precious_files_regex="$arg" prev= continue ;; release) release="-$arg" prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test "$prev" = rpath; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds="$arg" prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg="$arg" case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "\`-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test "X$arg" = "X-export-symbols"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between \`-L' and \`$1'" else func_fatal_error "need path for \`-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of \`$dir'" dir="$absdir" ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test "X$arg" = "X-lc" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. test "X$arg" = "X-lc" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test "X$arg" = "X-lc" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test "X$arg" = "X-lc" && continue ;; esac elif test "X$arg" = "X-lc_r"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module="${wl}-multi_module" continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "\`-no-install' is ignored for $host" func_warning "assuming \`-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-flto*|-fwhopr*|-fuse-linker-plugin) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test "$prev" = dlfiles; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test "$prev" = dlprefiles; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the \`$prevarg' option requires an argument" if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname="$func_basename_result" libobjs_save="$libobjs" if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" func_dirname "$output" "/" "" output_objdir="$func_dirname_result$objdir" func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps ; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test "$linkmode" = lib; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=no newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test "$linkmode,$pass" = "lib,link"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs="$tmp_deplibs" fi if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan"; then libs="$deplibs" deplibs= fi if test "$linkmode" = prog; then case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test "$linkmode,$pass" = "lib,dlpreopen"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs="$dlprefiles" fi if test "$pass" = dlopen; then # Collect dlpreopened libraries save_deplibs="$deplibs" deplibs= fi for deplib in $libs; do lib= found=no case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test "$linkmode" != lib && test "$linkmode" != prog; then func_warning "\`-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test "$linkmode" = lib; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" if test -f "$lib"; then if test "$search_ext" = ".la"; then found=yes else found=no fi break 2 fi done done if test "$found" != yes; then # deplib doesn't seem to be a libtool library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue else # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll="$l" done if test "X$ll" = "X$old_library" ; then # only static version available found=no func_dirname "$lib" "" "." ladir="$func_dirname_result" lib=$ladir/$old_library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi fi ;; # -l *.ltframework) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test "$pass" = conv && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi if test "$pass" = scan; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "\`-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test "$pass" = link; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=no case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=yes fi ;; pass_all) valid_a_lib=yes ;; esac if test "$valid_a_lib" != yes; then echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." else echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" fi ;; esac continue ;; prog) if test "$pass" != link; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test "$pass" = conv; then deplibs="$deplib $deplibs" elif test "$linkmode" = prog; then if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=yes continue ;; esac # case $deplib if test "$found" = yes || test -f "$lib"; then : else func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" fi # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "\`$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir="$func_dirname_result" dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan" || { test "$linkmode" != prog && test "$linkmode" != lib; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test "$pass" = conv; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test "$linkmode" != prog && test "$linkmode" != lib; then func_fatal_error "\`$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test "$prefer_static_libs" = yes || test "$prefer_static_libs,$installed" = "built,no"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib="$l" done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # This library was specified with -dlopen. if test "$pass" = dlopen; then if test -z "$libdir"; then func_fatal_error "cannot -dlopen a convenience library: \`$lib'" fi if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of \`$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir="$ladir" fi ;; esac func_basename "$lib" laname="$func_basename_result" # Find the relevant object directory and library name. if test "X$installed" = Xyes; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library \`$lib' was moved." dir="$ladir" absdir="$abs_ladir" libdir="$abs_ladir" else dir="$lt_sysroot$libdir" absdir="$lt_sysroot$libdir" fi test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir="$ladir" absdir="$abs_ladir" # Remove this search path later func_append notinst_path " $abs_ladir" else dir="$ladir/$objdir" absdir="$abs_ladir/$objdir" # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test "$pass" = dlpreopen; then if test -z "$libdir" && test "$linkmode" = prog; then func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" fi case "$host" in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test "$linkmode" = lib; then deplibs="$dir/$old_library $deplibs" elif test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test "$linkmode" = prog && test "$pass" != link; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=no if test "$link_all_deplibs" != no || test -z "$library_names" || test "$build_libtool_libs" = no; then linkalldeplibs=yes fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if test "$linkalldeplibs" = yes; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test "$linkmode,$pass" = "prog,link"; then if test -n "$library_names" && { { test "$prefer_static_libs" = no || test "$prefer_static_libs,$installed" = "built,yes"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then # Make sure the rpath contains only unique directories. case "$temp_rpath:" in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if test "$alldeplibs" = yes && { test "$deplibs_check_method" = pass_all || { test "$build_libtool_libs" = yes && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test "$use_static_libs" = built && test "$installed" = yes; then use_static_libs=no fi if test -n "$library_names" && { test "$use_static_libs" = no || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test "$installed" = no; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule="" for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule="$dlpremoduletest" break fi done if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then echo if test "$linkmode" = prog; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test "$linkmode" = lib && test "$hardcode_into_libs" = yes; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname="$1" shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname="$dlname" elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc*) func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; esac eval soname=\"$soname_spec\" else soname="$realname" fi # Make a new name for the extract_expsyms_cmds to use soroot="$soname" func_basename "$soroot" soname="$func_basename_result" func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from \`$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for \`$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test "$linkmode" = prog || test "$opt_mode" != relink; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test "$hardcode_direct" = no; then add="$dir/$linklib" case $host in *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; *-*-sysv4*uw2*) add_dir="-L$dir" ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir="-L$dir" ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we can not # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null ; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library" ; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add="$dir/$old_library" fi elif test -n "$old_library"; then add="$dir/$old_library" fi fi esac elif test "$hardcode_minus_L" = no; then case $host in *-*-sunos*) add_shlibpath="$dir" ;; esac add_dir="-L$dir" add="-l$name" elif test "$hardcode_shlibpath_var" = no; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; relink) if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$dir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$absdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; *) lib_linked=no ;; esac if test "$lib_linked" != yes; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test "$linkmode" = prog; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test "$hardcode_direct" != yes && test "$hardcode_minus_L" != yes && test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test "$linkmode" = prog || test "$opt_mode" = relink; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$libdir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$libdir" add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add="-l$name" elif test "$hardcode_automatic" = yes; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then add="$inst_prefix_dir$libdir/$linklib" else add="$libdir/$linklib" fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir="-L$libdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" fi if test "$linkmode" = prog; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test "$linkmode" = prog; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test "$hardcode_direct" != unsupported; then test -n "$old_library" && linklib="$old_library" compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test "$build_libtool_libs" = yes; then # Not a shared library if test "$deplibs_check_method" != pass_all; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system can not link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test "$module" = yes; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using \`nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test "$linkmode" = lib; then if test -n "$dependency_libs" && { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || test "$link_static" = yes; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs="$temp_deplibs" fi func_append newlib_search_path " $absdir" # Link against this library test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps ; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test "$link_all_deplibs" != no; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path="$deplib" ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of \`$dir'" absdir="$dir" fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl" ; then depdepl="$absdir/$objdir/$depdepl" darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" path= fi fi ;; *) path="-L$absdir/$objdir" ;; esac else eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "\`$deplib' seems to be moved" path="-L$absdir" fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test "$pass" = link; then if test "$linkmode" = "prog"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs="$newdependency_libs" if test "$pass" = dlpreopen; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test "$pass" != dlopen; then if test "$pass" != conv; then # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= fi if test "$linkmode,$pass" != "prog,link"; then vars="deplibs" else vars="compile_deplibs finalize_deplibs" fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs ; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i="" ;; esac if test -n "$i" ; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test "$linkmode" = prog; then dlfiles="$newdlfiles" fi if test "$linkmode" = prog || test "$linkmode" = lib; then dlprefiles="$newdlprefiles" fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "\`-R' is ignored for archives" test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "\`-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "\`-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs="$output" func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form `libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test "$module" = no && \ func_fatal_help "libtool library \`$output' must begin with \`lib'" if test "$need_lib_prefix" != no; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test "$deplibs_check_method" != pass_all; then func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test "$dlself" != no && \ func_warning "\`-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test "$#" -gt 1 && \ func_warning "ignoring multiple \`-rpath's for a libtool library" install_libdir="$1" oldlibs= if test -z "$rpath"; then if test "$build_libtool_libs" = yes; then # Building a libtool convenience library. # Some compilers have problems with a `.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "\`-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs="$IFS"; IFS=':' set dummy $vinfo 0 0 0 shift IFS="$save_ifs" test -n "$7" && \ func_fatal_help "too many parameters to \`-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major="$1" number_minor="$2" number_revision="$3" # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # which has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_revision" ;; freebsd-aout|freebsd-elf|qnx|sunos) current="$number_major" revision="$number_minor" age="0" ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_minor" lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type \`$version_type'" ;; esac ;; no) current="$1" revision="$2" age="$3" ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT \`$current' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION \`$revision' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE \`$age' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE \`$age' is greater than the current interface number \`$current'" func_fatal_error "\`$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; freebsd-aout) major=".$current" versuffix=".$current.$revision"; ;; freebsd-elf) major=".$current" versuffix=".$current" ;; irix | nonstopux) if test "X$lt_irix_increment" = "Xno"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring="$verstring_prefix$major.$revision" # Add in all the interfaces that we are compatible with. loop=$revision while test "$loop" -ne 0; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring_prefix$major.$iface:$verstring" done # Before this point, $major must not contain `.'. major=.$major versuffix="$major.$revision" ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=".$current.$age.$revision" verstring="$current.$age.$revision" # Add in all the interfaces that we are compatible with. loop=$age while test "$loop" -ne 0; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring:${iface}.0" done # Make executables depend on our current version. func_append verstring ":${current}.0" ;; qnx) major=".$current" versuffix=".$current" ;; sunos) major=".$current" versuffix=".$current.$revision" ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 filesystems. func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; *) func_fatal_configuration "unknown library version type \`$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring="0.0" ;; esac if test "$need_version" = no; then versuffix= else versuffix=".0.0" fi fi # Remove version info from name if versioning should be avoided if test "$avoid_version" = yes && test "$need_version" = no; then major= versuffix= verstring="" fi # Check to see if the archive will have undefined symbols. if test "$allow_undefined" = yes; then if test "$allow_undefined_flag" = unsupported; then func_warning "undefined symbols not allowed in $host shared libraries" build_libtool_libs=no build_old_libs=yes fi else # Don't allow undefined symbols. allow_undefined_flag="$no_undefined_flag" fi fi func_generate_dlsyms "$libname" "$libname" "yes" func_append libobjs " $symfileobj" test "X$libobjs" = "X " && libobjs= if test "$opt_mode" != relink; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) if test "X$precious_files_regex" != "X"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles="$dlfiles" dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles="$dlprefiles" dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test "$build_libtool_libs" = yes; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test "$build_libtool_need_lc" = "yes"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release="" versuffix="" major="" newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib="$potent_lib" while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib="" ;; esac fi if test -n "$a_deplib" ; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib="$potent_lib" # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs="" tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then for i in $predeps $postdeps ; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test "X$deplibs_check_method" = "Xnone"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test "$droppeddeps" = yes; then if test "$module" = yes; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using \`nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test "$allow_undefined" = no; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs="$new_libs" # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test "$build_libtool_libs" = yes; then # Remove ${wl} instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test "$hardcode_into_libs" = yes; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath="$finalize_rpath" test "$opt_mode" != relink && rpath="$compile_rpath$rpath" for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath="$finalize_shlibpath" test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname="$1" shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname="$realname" fi if test -z "$dlname"; then dlname=$soname fi lib="$output_objdir/$realname" linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols="$output_objdir/$libname.uexp" func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile if test "x`$SED 1q $export_symbols`" != xEXPORTS; then # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols="$export_symbols" export_symbols= always_export_symbols=yes fi fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs="$IFS"; IFS='~' for cmd1 in $cmds; do IFS="$save_ifs" # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test "$try_normal_branch" = yes \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=${output_objdir}/${output_la}.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS="$save_ifs" if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs="$tmp_deplibs" if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test "$compiler_needs_object" = yes && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test "$opt_mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test "X$skipped_export" != "X:" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then output=${output_objdir}/${output_la}.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then output=${output_objdir}/${output_la}.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test "$compiler_needs_object" = yes; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-${k}.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test "X$objlist" = X || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test "$k" -eq 1 ; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-${k}.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-${k}.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\${concat_cmds}$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi if ${skipped_export-false}; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi fi test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs="$IFS"; IFS='~' for cmd in $concat_cmds; do IFS="$save_ifs" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$opt_mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi if ${skipped_export-false}; then if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi fi libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$opt_mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" # Restore the uninstalled library and exit if test "$opt_mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test "$module" = yes || test "$export_dynamic" = yes; then # On all known operating systems, these are identical. dlname="$soname" fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "\`-R' is ignored for objects" test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for objects" test -n "$release" && \ func_warning "\`-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object \`$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj="$output" ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # reload_cmds runs $LD directly, so let us get rid of # -Wl from whole_archive_flag_spec and hope we can get by with # turning comma into space.. wl= if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` else gentop="$output_objdir/${obj}x" func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" # Create the old-style object. reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test output="$obj" func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi if test "$build_libtool_libs" != yes; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS fi if test -n "$pic_flag" || test "$pic_mode" != default; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output="$libobj" func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for programs" test -n "$release" && \ func_warning "\`-release' is ignored for programs" test "$preload" = yes \ && test "$dlopen_support" = unknown \ && test "$dlopen_self" = unknown \ && test "$dlopen_self_static" = unknown && \ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test "$tagname" = CXX ; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " ${wl}-bind_at_load" func_append finalize_command " ${wl}-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs="$new_libs" func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath="$rpath" rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath="$rpath" if test -n "$libobjs" && test "$build_old_libs" = yes; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" "no" # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=yes case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=no ;; *cygwin* | *mingw* ) if test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; *) if test "$need_relink" = no || test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; esac if test "$wrappers_required" = no; then # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command="$compile_command$compile_rpath" # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.${objext}"; then func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' fi exit $exit_status fi if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test "$no_install" = yes; then # We don't need to create a wrapper script. link_command="$compile_var$compile_command$compile_rpath" # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi if test "$hardcode_action" = relink; then # Fast installation is not supported link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" func_warning "this platform does not like uninstalled shared libraries" func_warning "\`$output' will be relinked during installation" else if test "$fast_install" != no; then link_command="$finalize_var$compile_command$finalize_rpath" if test "$fast_install" = yes; then relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` else # fast_install is set to needless relink_command= fi else link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" fi fi # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource="$output_path/$objdir/lt-$output_name.c" cwrapper="$output_path/$output_name.exe" $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host" ; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do if test "$build_libtool_libs" = convenience; then oldobjs="$libobjs_save $symfileobj" addlibs="$convenience" build_libtool_libs=no else if test "$build_libtool_libs" = module; then oldobjs="$libobjs_save" build_libtool_libs=no else oldobjs="$old_deplibs $non_pic_objects" if test "$preload" = yes && test -f "$symfileobj"; then func_append oldobjs " $symfileobj" fi fi addlibs="$old_convenience" fi if test -n "$addlibs"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop="$output_objdir/${outputname}x" func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase="$func_basename_result" case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj" ; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test "X$oldobjs" = "X" ; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test "$build_old_libs" = yes && old_library="$libname.$libext" func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test "$hardcode_automatic" = yes ; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test "$installed" = yes; then if test -z "$install_libdir"; then break fi output="$output_objdir/$outputname"i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name="$func_basename_result" func_resolve_sysroot "$deplib" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs="$newdependency_libs" newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles="$newdlprefiles" else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles="$newdlprefiles" fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test "x$bindir" != x ; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that can not go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test "$installed" = no && test "$need_relink" = yes; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } { test "$opt_mode" = link || test "$opt_mode" = relink; } && func_mode_link ${1+"$@"} # func_mode_uninstall arg... func_mode_uninstall () { $opt_debug RM="$nonopt" files= rmforce= exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" for arg do case $arg in -f) func_append RM " $arg"; rmforce=yes ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir="$func_dirname_result" if test "X$dir" = X.; then odir="$objdir" else odir="$dir/$objdir" fi func_basename "$file" name="$func_basename_result" test "$opt_mode" = uninstall && odir="$dir" # Remember odir for removal later, being careful to avoid duplicates if test "$opt_mode" = clean; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif test "$rmforce" = yes; then continue fi rmfiles="$file" case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case "$opt_mode" in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test "$pic_object" != none; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test "$non_pic_object" != none; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test "$opt_mode" = clean ; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.${objext}" if test "$fast_install" = yes && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name" ; then func_append rmfiles " $odir/lt-${noexename}.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } { test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && func_mode_uninstall ${1+"$@"} test -z "$opt_mode" && { help="$generic_help" func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode \`$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: # vi:sw=2 nut-2.7.4/missing0000755000175000017500000001533012640476420010661 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2013 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: nut-2.7.4/MAINTAINERS0000644000175000017500000000336412640473702010763 00000000000000In the tradition of stealing ideas for top-level files from the Linux kernel tree, here is the NUT MAINTAINERS file. This file is intended to help patch contributors route their changes to the right people. Note: just because something isn't listed in here doesn't mean it's not being maintained. It just means that the maintainer hasn't sent me a patch to update this file yet. Note 2: there are always other people who work on a project beyond those who are listed here. Omission from this list should not be taken as a slight. Those who are listed below are merely volunteering to be the "lightning rods" for patches to certain parts of the tree. Fields ====== P: Person M: Mail patches to this address W: Web address S: Status: Supported = someone is paid to do this Maintained = someone keeps it running (add others as necessary) In the case of drivers, "maintained" should only be used if you have access to the hardware in question for testing. Drivers ======= P: Russell Kroll M: rkroll@exploits.org S: Maintained: apcsmart, belkin, bestups, cyberpower, dummycons, fentonups, driver core (main.c), upsdrvctl P: Arnaud Quette M: aquette.dev@gmail.com M: ArnaudQuette@eaton.com S: Maintained or Supported: dummy-ups, usbhid-ups, mge-shut, newmge-shut mge-utalk, snmp-ups, ... P: Fabio Di Niro M: blaxwan@users.sourceforge.net S: Maintained: metasys P: Kirill Smelkov M: kirr@mns.spb.ru S: Maintained: al175 Packaging ========= P: Nigel Metheringham M: Nigel.Metheringham@dev.InTechnology.co.uk S: Maintained: nut.spec.in (for Red Hat RPM builds) P: Arnaud Quette M: aquette@debian.org S: Maintained: Official Debian packages Everything else =============== No other categories have been created yet. nut-2.7.4/lib/0000755000175000017500000000000012670024741010104 500000000000000nut-2.7.4/lib/README0000644000175000017500000001067212667574712010730 00000000000000ifndef::external_title[] NUT libraries complementary information ======================================= endif::external_title[] This chapter provides some complementary information about the creation process of NUT libraries, and using these in your program. Introduction ------------ NUT provides several libraries, to ease interfacing with 3rd party programs: - *libupsclient*, to interact with NUT server (upsd), - *libnutclient*, to interact with NUT server at high level, - *libnutscan*, to discover NUT supported devices. External applications, such as asapm-ups, wmnut, and others, currently use it. But it is also used internally (by upsc, upsrw, upscmd, upsmon and dummy-ups) to reduce storage footprint and memory consumption. The runtime libraries are installed by default. However, to install other development files (header, additional static and shared libraries, and compilation helpers below), you will have to provide the '--with-dev' flag to the 'configure' script. libupsclient-config ------------------- In case pkgconfig is not available on the system, an alternate helper script is provided: 'libupsclient-config'. It will be installed in the same directory as NUT client programs (BINDIR), providing that you have enabled the '--with-dev' flag to the 'configure' script. The usage is about the same as pkg-config and similar tools. To get CFLAGS, use: $ libupsclient-config --cflags To get LD_FLAGS, use: $ libupsclient-config --libs References: linkman:libupsclient-config[1] manual page, NOTE: this feature may evolve (name change), or even disapear in the future. pkgconfig support ----------------- pkgconfig enables a high level of integration with minimal effort. There is no more needs to handle hosts of possible NUT installation path in your configure script ! To check if NUT is available, use: $ pkg-config --exists libupsclient --silence-errors To get CFLAGS, use: $ pkg-config --cflags libupsclient To get LD_FLAGS, use: $ pkg-config --libs libupsclient pkgconfig support ('.pc') files are provided in the present directory of the source distribution ('nut-X.Y.Z/lib/'), and installed in the suitable system directory if you have enabled '--with-dev'. The default installation directory ("/usr/lib/pkgconfig/") can be changed with the following command: ./configure --with-pkgconfig-dir=PATH You can also use this if you are sure that pkg-config is installed: PKG_CHECK_MODULES(LIBUPSCLI, libupsclient >= 2.4.0) PKG_CHECK_MODULES(LIBNUTSCAN, libnutscan >= 2.6.2) Example configure script ------------------------ To use NUT libraries in your program, use the following code in your configure (.in or .ac) script: AC_MSG_CHECKING(for NUT client library (libupsclient)) pkg-config --exists libupsclient --silence-errors if test $? -eq 0 then AC_MSG_RESULT(found (using pkg-config)) LDFLAGS="$LDFLAGS `pkg-config --libs libupsclient`" NUT_HEADER="`pkg-config --cflags libupsclient`" else libupsclient-config --version if test $? -eq 0 then AC_MSG_RESULT(found (using libupsclient-config)) LDFLAGS="$LDFLAGS `libupsclient-config --libs`" NUT_HEADER="`libupsclient-config --cflags`" else AC_MSG_ERROR("libupsclient not found") fi fi This code will test for pkgconfig support for NUT client library, and fall back to libupsclient-config if not available. It will issue an error if none is found! The same is also valid for other NUT libraries, such as libnutscan. Simply replace 'libupsclient' occurences in the above example, by the name of the desired library (for example, 'libnutscan'). NOTE: this is an alternate method. Use of PKG_CHECK_MODULES macro should be preferred. Future consideration -------------------- We are considering the following items: - provide libupsclient-config support for libnutscan, and libnutconfig when available. This requires to rename and rewrite the script in a more generic way (libnut-config), with options to address specific libraries. - provide pkgconfig support for libnutconfig, when available. Libtool information ------------------- NUT libraries are built using Libtool. This tool is integrated with automake, and can create static and dynamic libraries for a variety of platforms in a transparent way. References: - link:http://www.gnu.org/software/libtool/[libtool] - link:http://sources.redhat.com/autobook/autobook/autobook.html[David MacKenzie's Autobook (RedHat)] - link:http://debianlinux.net/~jama/howto/gnu_build_steps.html[DebianLinux.Net, The GNU Build System] nut-2.7.4/lib/libupsclient.pc.in0000644000175000017500000000051212640443572013454 00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ sysconfdir=@CONFPATH@ statepath=@STATEPATH@ nutuser=@RUN_AS_USER@ Name: libupsclient Description: UPS monitoring with Network UPS Tools Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lupsclient @LIBSSL_LIBS@ Cflags: -I${includedir} @LIBSSL_CFLAGS@ nut-2.7.4/lib/Makefile.in0000644000175000017500000004551612667762001012110 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: lib VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = lib DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/libupsclient-config.in $(srcdir)/libupsclient.pc.in \ $(srcdir)/libnutclient.pc.in $(srcdir)/libnutscan.pc.in README ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = libupsclient-config libupsclient.pc \ libnutclient.pc libnutscan.pc CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)" SCRIPTS = $(bin_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac DATA = $(pkgconfig_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ @WITH_DEV_TRUE@@WITH_PKG_CONFIG_TRUE@pkgconfig_DATA = libupsclient.pc libnutscan.pc libnutclient.pc @WITH_DEV_TRUE@@WITH_PKG_CONFIG_FALSE@bin_SCRIPTS = libupsclient-config all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): libupsclient-config: $(top_builddir)/config.status $(srcdir)/libupsclient-config.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ libupsclient.pc: $(top_builddir)/config.status $(srcdir)/libupsclient.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ libnutclient.pc: $(top_builddir)/config.status $(srcdir)/libnutclient.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ libnutscan.pc: $(top_builddir)/config.status $(srcdir)/libnutscan.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(SCRIPTS) $(DATA) installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkgconfigDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binSCRIPTS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binSCRIPTS uninstall-pkgconfigDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-binSCRIPTS install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-pkgconfigDATA install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-binSCRIPTS uninstall-pkgconfigDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/lib/libnutscan.pc.in0000644000175000017500000000035712640443572013130 00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libnutscan Description: Power devices discovery with Network UPS Tools Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnutscan Cflags: -I${includedir} nut-2.7.4/lib/libnutclient.pc.in0000644000175000017500000000045412640444140013450 00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ sysconfdir=@CONFPATH@ statepath=@STATEPATH@ nutuser=@RUN_AS_USER@ Name: libnutclient Description: UPS monitoring with Network UPS Tools Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnutclient Cflags: -I${includedir} nut-2.7.4/lib/libupsclient-config.in0000644000175000017500000000142412640443572014321 00000000000000#!/bin/sh # **********************************************************# # libupsclient-config: helper script for NUT libupsclient # # **********************************************************# # Copyright 2003 - Arnaud Quette # # Distributed under the GNU GPL v2 # # See the distribution lib/README for usage information # # **********************************************************# Version="@PACKAGE_VERSION@" prefix=@prefix@ exec_prefix=@exec_prefix@ Libs="-L@libdir@ -lupsclient @LIBSSL_LIBS@" Cflags="-I@includedir@ @LIBSSL_CFLAGS@" case "$1" in --cflags) echo "$Cflags" ;; --libs) echo "$Libs" ;; --version) echo $Version ;; *) echo "Usage: libupsclient-config {--cflags|--libs|--version}" exit 1 esac nut-2.7.4/lib/Makefile.am0000644000175000017500000000025512640444140012056 00000000000000# Network UPS Tools: lib if WITH_DEV if WITH_PKG_CONFIG pkgconfig_DATA = libupsclient.pc libnutscan.pc libnutclient.pc else bin_SCRIPTS = libupsclient-config endif endif nut-2.7.4/clients/0000755000175000017500000000000012670024740010776 500000000000000nut-2.7.4/clients/upsstats.c0000644000175000017500000005076212640443572012767 00000000000000/* upsstats - cgi program to generate the main ups info page Copyright (C) 1998 Russell Kroll Copyright (C) 2005 Arnaud Quette 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 "common.h" #include "upsclient.h" #include "status.h" #include "cgilib.h" #include "parseconf.h" #include "timehead.h" #include "upsstats.h" #include "upsimagearg.h" #define MAX_CGI_STRLEN 128 #define MAX_PARSE_ARGS 16 static char *monhost = NULL; static int use_celsius = 1, refreshdelay = -1, treemode = 0; /* from cgilib's checkhost() */ static char *monhostdesc = NULL; static int port; static char *upsname, *hostname; static char *upsimgpath="upsimage.cgi", *upsstatpath="upsstats.cgi"; static UPSCONN_t ups; static FILE *tf; static long forofs = 0; static ulist_t *ulhead = NULL, *currups = NULL; static int skip_clause = 0, skip_block = 0; void parsearg(char *var, char *value) { /* avoid bogus junk from evil people */ if ((strlen(var) > MAX_CGI_STRLEN) || (strlen(value) > MAX_CGI_STRLEN)) return; if (!strcmp(var, "host")) { free(monhost); monhost = xstrdup(value); return; } if (!strcmp(var, "refresh")) refreshdelay = (int) strtol(value, (char **) NULL, 10); if (!strcmp(var, "treemode")) { /* FIXME: Validate that treemode is allowed */ treemode = 1; } } static void report_error(void) { if (upscli_upserror(&ups) == UPSCLI_ERR_VARNOTSUPP) printf("Not supported\n"); else printf("[error: %s]\n", upscli_strerror(&ups)); } /* make sure we're actually connected to upsd */ static int check_ups_fd(int do_report) { if (upscli_fd(&ups) == -1) { if (do_report) report_error(); return 0; } /* also check for insanity in currups */ if (!currups) { if (do_report) printf("No UPS specified for monitoring\n"); return 0; } /* must be OK */ return 1; } static int get_var(const char *var, char *buf, size_t buflen, int verbose) { int ret; unsigned int numq, numa; const char *query[4]; char **answer; if (!check_ups_fd(1)) return 0; if (!upsname) { if (verbose) printf("[No UPS name specified]\n"); return 0; } query[0] = "VAR"; query[1] = upsname; query[2] = var; numq = 3; ret = upscli_get(&ups, numq, query, &numa, &answer); if (ret < 0) { if (verbose) report_error(); return 0; } if (numa < numq) { if (verbose) printf("[Invalid response]\n"); return 0; } snprintf(buf, buflen, "%s", answer[3]); return 1; } static void parse_var(const char *var) { char answer[SMALLBUF]; if (!get_var(var, answer, sizeof(answer), 1)) return; printf("%s", answer); } static void do_status(void) { int i; char status[SMALLBUF], *ptr, *last = NULL; if (!get_var("ups.status", status, sizeof(status), 1)) { return; } for (ptr = strtok_r(status, " \n", &last); ptr != NULL; ptr = strtok_r(NULL, " \n", &last)) { /* expand from table in status.h */ for (i = 0; stattab[i].name != NULL; i++) { if (!strcasecmp(ptr, stattab[i].name)) { printf("%s
    ", stattab[i].desc); } } } } static void do_runtime(void) { int total, hours, minutes, seconds; char runtime[SMALLBUF]; if (!get_var("battery.runtime", runtime, sizeof(runtime), 1)) return; total = (int) strtol(runtime, (char **) NULL, 10); hours = total / 3600; minutes = (total - (hours * 3600)) / 60; seconds = total % 60; printf("%02d:%02d:%02d", hours, minutes, seconds); } static int do_date(const char *buf) { char datebuf[SMALLBUF]; time_t tod; time(&tod); if (strftime(datebuf, sizeof(datebuf), buf, localtime(&tod))) { printf("%s", datebuf); return 1; } return 0; } static int get_img_val(const char *var, const char *desc, const char *imgargs) { char answer[SMALLBUF]; if (!get_var(var, answer, sizeof(answer), 1)) return 1; printf("sys, var); if ((imgargs) && (strlen(imgargs) > 0)) printf("&%s", imgargs); printf("\" ALT=\"%s: %s\">", desc, answer); return 1; } /* see if is valid - table from upsimagearg.h */ static void check_imgarg(char *arg, char *out, size_t outlen) { int i; char *ep; ep = strchr(arg, '='); if (!ep) return; *ep++= '\0'; /* if it's allowed, append it so it can become part of the URL */ for (i = 0; imgarg[i].name != NULL; i++) { if (!strcmp(imgarg[i].name, arg)) { if (strlen(out) == 0) snprintf(out, outlen, "%s=%s", arg, ep); else snprintfcat(out, outlen, "&%s=%s", arg, ep); return; } } } /* split out the var=val commands from the IMG line */ static void split_imgarg(char *in, char *out, size_t outlen) { char *ptr, *sp; if (strlen(in) < 3) return; ptr = in; sp = strchr(ptr, ' '); /* split by spaces, then check each one (can't use parseconf...) */ while (sp) { *sp++ = '\0'; check_imgarg(ptr, out, outlen); ptr = sp; sp = strchr(ptr, ' '); } check_imgarg(ptr, out, outlen); } /* IMG [==] ... */ static int do_img(char *buf) { char *type, *ptr, imgargs[SMALLBUF]; memset(imgargs, '\0', sizeof(imgargs)); type = buf; ptr = strchr(buf, ' '); if (ptr) { *ptr++ = '\0'; split_imgarg(ptr, imgargs, sizeof(imgargs)); } /* only allow known types through */ if (!strcmp(type, "input.voltage") || !strcmp(type, "input.L1-N.voltage") || !strcmp(type, "input.L2-N.voltage") || !strcmp(type, "input.L3-N.voltage") || !strcmp(type, "input.L1-L2.voltage") || !strcmp(type, "input.L2-L3.voltage") || !strcmp(type, "input.L3-L1.voltage")) { return get_img_val(type, "Input voltage", imgargs); } if (!strcmp(type, "battery.voltage")) return get_img_val(type, "Battery voltage", imgargs); if (!strcmp(type, "battery.charge")) return get_img_val(type, "Battery charge", imgargs); if (!strcmp(type, "output.voltage") || !strcmp(type, "output.L1-N.voltage") || !strcmp(type, "output.L2-N.voltage") || !strcmp(type, "output.L3-N.voltage") || !strcmp(type, "output.L1-L2.voltage") || !strcmp(type, "output.L2-L3.voltage") || !strcmp(type, "output.L3-L1.voltage")) { return get_img_val(type, "Output voltage", imgargs); } if (!strcmp(type, "ups.load") || !strcmp(type, "output.L1.power.percent") || !strcmp(type, "output.L2.power.percent") || !strcmp(type, "output.L3.power.percent") || !strcmp(type, "output.L1.realpower.percent") || !strcmp(type, "output.L2.realpower.percent") || !strcmp(type, "output.L3.realpower.percent")) { return get_img_val(type, "UPS load", imgargs); } if (!strcmp(type, "input.frequency")) return get_img_val(type, "Input frequency", imgargs); if (!strcmp(type, "output.frequency")) return get_img_val(type, "Output frequency", imgargs); if (!strcmp(type, "ups.temperature")) return get_img_val(type, "UPS temperature", imgargs); if (!strcmp(type, "ambient.temperature")) return get_img_val(type, "Ambient temperature", imgargs); if (!strcmp(type, "ambient.humidity")) return get_img_val(type, "Ambient humidity", imgargs); return 0; } static void ups_connect(void) { static ulist_t *lastups = NULL; char *newups, *newhost; int newport; /* try to minimize reconnects */ if (lastups) { /* don't reconnect if these are both the same UPS */ if (!strcmp(lastups->sys, currups->sys)) { lastups = currups; return; } /* see if it's just on the same host */ newups = newhost = NULL; if (upscli_splitname(currups->sys, &newups, &newhost, &newport) != 0) { printf("Unusable UPS definition [%s]\n", currups->sys); fprintf(stderr, "Unusable UPS definition [%s]\n", currups->sys); exit(EXIT_FAILURE); } if ((!strcmp(newhost, hostname)) && (port == newport)) { free(upsname); upsname = newups; free(newhost); lastups = currups; return; } /* not the same upsd, so disconnect */ free(newups); free(newhost); } upscli_disconnect(&ups); free(upsname); free(hostname); if (upscli_splitname(currups->sys, &upsname, &hostname, &port) != 0) { printf("Unusable UPS definition [%s]\n", currups->sys); fprintf(stderr, "Unusable UPS definition [%s]\n", currups->sys); exit(EXIT_FAILURE); } if (upscli_connect(&ups, hostname, port, 0) < 0) fprintf(stderr, "UPS [%s]: can't connect to server: %s\n", currups->sys, upscli_strerror(&ups)); lastups = currups; } static void do_hostlink(void) { if (!currups) { return; } printf("
    sys); if (refreshdelay > 0) { printf("&refresh=%d", refreshdelay); } printf("\">%s", currups->desc); } static void do_treelink(void) { if (!currups) { return; } printf("All data", upsstatpath, currups->sys); } /* see if the UPS supports this variable - skip to the next ENDIF if not */ /* if val is not null, value returned by var must be equal to val to match */ static void do_ifsupp(const char *var, const char *val) { char dummy[SMALLBUF]; /* if not connected, act like it's not supported and skip the rest */ if (!check_ups_fd(0)) { skip_clause = 1; return; } if (!get_var(var, dummy, sizeof(dummy), 0)) { skip_clause = 1; return; } if(!val) { return; } if(strcmp(dummy, val)) { skip_clause = 1; return; } } static int breakargs(char *s, char **aargs) { char *p; int i=0; aargs[i]=NULL; for(p=s; *p && i<(MAX_PARSE_ARGS-1); p++) { if(aargs[i] == NULL) { aargs[i] = p; aargs[i+1] = NULL; } if(*p==' ') { *p='\0'; i++; } } /* Check how many valid args we got */ for(i=0; aargs[i]; i++); return i; } static void do_ifeq(const char *s) { char var[SMALLBUF]; char *aa[MAX_PARSE_ARGS]; int nargs; strcpy(var, s); nargs = breakargs(var, aa); if(nargs != 2) { printf("upsstats: IFEQ: Argument error!\n"); return; } do_ifsupp(aa[0], aa[1]); } /* IFBETWEEN var1 var2 var3. Skip if var3 not between var1 * and var2 */ static void do_ifbetween(const char *s) { char var[SMALLBUF]; char *aa[MAX_PARSE_ARGS]; char tmp[SMALLBUF]; int nargs; long v1, v2, v3; char *isvalid=NULL; strcpy(var, s); nargs = breakargs(var, aa); if(nargs != 3) { printf("upsstats: IFBETWEEN: Argument error!\n"); return; } if (!check_ups_fd(0)) { return; } if (!get_var(aa[0], tmp, sizeof(tmp), 0)) { return; } v1 = strtol(tmp, &isvalid, 10); if(tmp == isvalid) { return; } if (!get_var(aa[1], tmp, sizeof(tmp), 0)) { return; } v2 = strtol(tmp, &isvalid, 10); if(tmp == isvalid) { return; } if (!get_var(aa[2], tmp, sizeof(tmp), 0)) { return; } v3 = strtol(tmp, &isvalid, 10); if(tmp == isvalid) { return; } if(v1 > v3 || v2 < v3) { skip_clause = 1; return; } } static void do_upsstatpath(const char *s) { if(strlen(s)) { upsstatpath = strdup(s); } } static void do_upsimgpath(const char *s) { if(strlen(s)) { upsimgpath = strdup(s); } } static void do_temp(const char *var) { char tempc[SMALLBUF]; float tempf; if (!get_var(var, tempc, sizeof(tempc), 1)) return; if (use_celsius) { printf("%s", tempc); return; } tempf = (strtod(tempc, (char **) NULL) * 1.8) + 32; printf("%.1f", tempf); } static void do_degrees(void) { printf("°"); if (use_celsius) printf("C"); else printf("F"); } /* plug in the right color string (like #FF0000) for the UPS status */ static void do_statuscolor(void) { int severity, i; char stat[SMALLBUF], *sp, *ptr; if (!check_ups_fd(0)) { /* can't print the warning here - give a red error condition */ printf("#FF0000"); return; } if (!get_var("ups.status", stat, sizeof(stat), 0)) { /* status not available - give yellow as a warning */ printf("#FFFF00"); return; } severity = 0; sp = stat; while (sp) { ptr = strchr(sp, ' '); if (ptr) *ptr++ = '\0'; /* expand from table in status.h */ for (i = 0; stattab[i].name != NULL; i++) if (!strcmp(stattab[i].name, sp)) if (stattab[i].severity > severity) severity = stattab[i].severity; sp = ptr; } switch(severity) { case 0: printf("#00FF00"); break; /* green : OK */ case 1: printf("#FFFF00"); break; /* yellow : warning */ default: printf("#FF0000"); break; /* red : error */ } } static int do_command(char *cmd) { /* ending an if block? */ if (!strcmp(cmd, "ENDIF")) { skip_clause = 0; skip_block = 0; return 1; } /* Skipping a block means skip until ENDIF, so... */ if (skip_block) { return 1; } /* Toggle state when we run across ELSE */ if (!strcmp(cmd, "ELSE")) { if (skip_clause) { skip_clause = 0; } else { skip_block = 1; } return 1; } /* don't do any commands if skipping a section */ if (skip_clause == 1) { return 1; } if (!strncmp(cmd, "VAR ", 4)) { parse_var(&cmd[4]); return 1; } if (!strcmp(cmd, "HOST")) { printf("%s", currups->sys); return 1; } if (!strcmp(cmd, "HOSTDESC")) { printf("%s", currups->desc); return 1; } if (!strcmp(cmd, "RUNTIME")) { do_runtime(); return 1; } if (!strcmp(cmd, "STATUS")) { do_status(); return 1; } if (!strcmp(cmd, "STATUSCOLOR")) { do_statuscolor(); return 1; } if (!strcmp(cmd, "TEMPF")) { use_celsius = 0; return 1; } if (!strcmp(cmd, "TEMPC")) { use_celsius = 1; return 1; } if (!strncmp(cmd, "DATE ", 5)) { return do_date(&cmd[5]); } if (!strncmp(cmd, "IMG ", 4)) { return do_img(&cmd[4]); } if (!strcmp(cmd, "VERSION")) { printf("%s", UPS_VERSION); return 1; } if (!strcmp(cmd, "REFRESH")) { if (refreshdelay > 0) { printf("", refreshdelay); } return 1; } if (!strcmp(cmd, "FOREACHUPS")) { forofs = ftell(tf); currups = ulhead; ups_connect(); return 1; } if (!strcmp(cmd, "ENDFOR")) { /* if not in a for, ignore this */ if (forofs == 0) { return 1; } currups = currups->next; if (currups) { fseek(tf, forofs, SEEK_SET); ups_connect(); } return 1; } if (!strcmp(cmd, "HOSTLINK")) { do_hostlink(); return 1; } if (!strcmp(cmd, "TREELINK")) { do_treelink(); return 1; } if (!strncmp(cmd, "IFSUPP ", 7)) { do_ifsupp(&cmd[7], NULL); return 1; } if (!strcmp(cmd, "UPSTEMP")) { do_temp("ups.temperature"); return 1; } if (!strcmp(cmd, "BATTTEMP")) { do_temp("battery.temperature"); return 1; } if (!strcmp(cmd, "AMBTEMP")) { do_temp("ambient.temperature"); return 1; } if (!strcmp(cmd, "DEGREES")) { do_degrees(); return 1; } if (!strncmp(cmd, "IFEQ ", 5)) { do_ifeq(&cmd[5]); return 1; } if (!strncmp(cmd, "IFBETWEEN ", 10)) { do_ifbetween(&cmd[10]); return 1; } if (!strncmp(cmd, "UPSSTATSPATH ", 13)) { do_upsstatpath(&cmd[13]); return 1; } if (!strncmp(cmd, "UPSIMAGEPATH ", 13)) { do_upsimgpath(&cmd[13]); return 1; } return 0; } static void parse_line(const char *buf) { char cmd[SMALLBUF]; int i, len, do_cmd = 0; for (i = 0; buf[i]; i += len) { len = strcspn(&buf[i], "@"); if (len == 0) { if (do_cmd) { do_command(cmd); do_cmd = 0; } else { cmd[0] = '\0'; do_cmd = 1; } i++; /* skip over the '@' character */ continue; } if (do_cmd) { snprintf(cmd, sizeof(cmd), "%.*s", len, &buf[i]); continue; } if (skip_clause || skip_block) { /* ignore this */ continue; } /* pass it trough */ printf("%.*s", len, &buf[i]); } } static void display_template(const char *tfn) { char fn[SMALLBUF], buf[LARGEBUF]; snprintf(fn, sizeof(fn), "%s/%s", confpath(), tfn); tf = fopen(fn, "r"); if (!tf) { fprintf(stderr, "upsstats: Can't open %s: %s\n", fn, strerror(errno)); printf("Error: can't open template file (%s)\n", tfn); exit(EXIT_FAILURE); } while (fgets(buf, sizeof(buf), tf)) { parse_line(buf); } fclose(tf); } static void display_tree(int verbose) { unsigned int numq, numa; const char *query[4]; char **answer; if (!upsname) { if (verbose) printf("[No UPS name specified]\n"); return; } query[0] = "VAR"; query[1] = upsname; numq = 2; if (upscli_list_start(&ups, numq, query) < 0) { if (verbose) report_error(); return; } printf("\n"); printf("\n"); printf("upsstat: data tree of %s\n", currups->desc); printf("\n"); printf("\n"); printf("
    \n"); printf("\n"); /* include the description from checkhost() if present */ printf("\n"); printf("\n"); while (upscli_list_next(&ups, numq, query, &numa, &answer) == 1) { /* VAR */ if (numa < 4) { if (verbose) printf("[Invalid response]\n"); return; } printf("\n"); printf("\n", answer[2]); printf("\n"); printf("\n", answer[3]); printf("\n"); } printf("
    \n"); printf("%s\n", currups->desc); printf("
    %s:%s
    \n"); printf("
    \n"); /* FIXME (AQ): add a save button (?), and a checkbt for showing var.desc */ printf("\n"); } static void add_ups(char *sys, char *desc) { ulist_t *tmp, *last; tmp = last = ulhead; while (tmp) { last = tmp; tmp = tmp->next; } tmp = xmalloc(sizeof(ulist_t)); tmp->sys = xstrdup(sys); tmp->desc = xstrdup(desc); tmp->next = NULL; if (last) last->next = tmp; else ulhead = tmp; } /* called for fatal errors in parseconf like malloc failures */ static void upsstats_hosts_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf(hosts.conf): %s", errmsg); } static void load_hosts_conf(void) { char fn[SMALLBUF]; PCONF_CTX_t ctx; snprintf(fn, sizeof(fn), "%s/hosts.conf", CONFPATH); pconf_init(&ctx, upsstats_hosts_err); if (!pconf_file_begin(&ctx, fn)) { pconf_finish(&ctx); printf("\n"); printf("\n"); printf("Error: can't open hosts.conf\n"); printf("\n"); printf("Error: can't open hosts.conf\n"); printf("\n"); /* leave something for the admin */ fprintf(stderr, "upsstats: %s\n", ctx.errmsg); exit(EXIT_FAILURE); } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); continue; } if (ctx.numargs < 3) continue; /* MONITOR */ if (!strcmp(ctx.arglist[0], "MONITOR")) add_ups(ctx.arglist[1], ctx.arglist[2]); } pconf_finish(&ctx); if (!ulhead) { printf("\n"); printf("\n"); printf("Error: no hosts to monitor\n"); printf("\n"); printf("Error: no hosts to monitor (check hosts.conf)\n"); printf("\n"); /* leave something for the admin */ fprintf(stderr, "upsstats: no hosts to monitor\n"); exit(EXIT_FAILURE); } } static void display_single(void) { if (!checkhost(monhost, &monhostdesc)) { printf("Access to that host [%s] is not authorized.\n", monhost); exit(EXIT_FAILURE); } add_ups(monhost, monhostdesc); currups = ulhead; ups_connect(); /* switch between data tree view and standard single view */ if (treemode) display_tree(1); else display_template("upsstats-single.html"); upscli_disconnect(&ups); } int main(int argc, char **argv) { extractcgiargs(); printf("Content-type: text/html\n"); printf("Pragma: no-cache\n"); printf("\n"); /* if a host is specified, use upsstats-single.html instead */ if (monhost) { display_single(); exit(EXIT_SUCCESS); } /* default: multimon replacement mode */ load_hosts_conf(); currups = ulhead; display_template("upsstats.html"); upscli_disconnect(&ups); return 0; } nut-2.7.4/clients/upscmd.c0000644000175000017500000001752212640473702012367 00000000000000/* upscmd - simple "client" to test instant commands via upsd Copyright (C) 2000 Russell Kroll 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 "common.h" #include "nut_platform.h" #include #include #include #include #include #include "upsclient.h" static char *upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; struct list_t { char *name; struct list_t *next; }; static void usage(const char *prog) { printf("Network UPS Tools upscmd %s\n\n", UPS_VERSION); printf("usage: %s [-h]\n", prog); printf(" %s [-l ]\n", prog); printf(" %s [-u ] [-p ] []\n\n", prog); printf("Administration program to initiate instant commands on UPS hardware.\n"); printf("\n"); printf(" -h display this help text\n"); printf(" -l show available commands on UPS \n"); printf(" -u set username for command authentication\n"); printf(" -p set password for command authentication\n"); printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf(" Valid instant command - test.panel.start, etc.\n"); printf(" [] Additional data for command - number of seconds, etc.\n"); } static void print_cmd(char *cmdname) { int ret; unsigned int numq, numa; const char *query[4]; char **answer; query[0] = "CMDDESC"; query[1] = upsname; query[2] = cmdname; numq = 3; ret = upscli_get(ups, numq, query, &numa, &answer); if ((ret < 0) || (numa < numq)) { printf("%s\n", cmdname); return; } /* CMDDESC */ printf("%s - %s\n", cmdname, answer[3]); } static void listcmds(void) { int ret; unsigned int numq, numa; const char *query[4]; char **answer; struct list_t *lhead = NULL, *llast = NULL, *ltmp, *lnext; query[0] = "CMD"; query[1] = upsname; numq = 2; ret = upscli_list_start(ups, numq, query); if (ret < 0) { /* old upsd = no way to continue */ if (upscli_upserror(ups) == UPSCLI_ERR_UNKCOMMAND) { fatalx(EXIT_FAILURE, "Error: upsd is too old to support this query"); } fatalx(EXIT_FAILURE, "Error: %s", upscli_strerror(ups)); } while (upscli_list_next(ups, numq, query, &numa, &answer) == 1) { /* CMD */ if (numa < 3) { fatalx(EXIT_FAILURE, "Error: insufficient data (got %d args, need at least 3)", numa); } /* we must first read the entire list of commands, before we can start reading the descriptions */ ltmp = xcalloc(1, sizeof(*ltmp)); ltmp->name = xstrdup(answer[2]); if (llast) { llast->next = ltmp; } else { lhead = ltmp; } llast = ltmp; } /* walk the list and try to get descriptions, freeing as we go */ printf("Instant commands supported on UPS [%s]:\n\n", upsname); for (ltmp = lhead; ltmp; ltmp = lnext) { lnext = ltmp->next; print_cmd(ltmp->name); free(ltmp->name); free(ltmp); } } static void do_cmd(char **argv, const int argc) { char buf[SMALLBUF]; if (argc > 1) { snprintf(buf, sizeof(buf), "INSTCMD %s %s %s\n", upsname, argv[0], argv[1]); } else { snprintf(buf, sizeof(buf), "INSTCMD %s %s\n", upsname, argv[0]); } if (upscli_sendline(ups, buf, strlen(buf)) < 0) { fatalx(EXIT_FAILURE, "Can't send instant command: %s", upscli_strerror(ups)); } if (upscli_readline(ups, buf, sizeof(buf)) < 0) { fatalx(EXIT_FAILURE, "Instant command failed: %s", upscli_strerror(ups)); } /* FUTURE: status cookies will tie in here */ if (strncmp(buf, "OK", 2) != 0) { fatalx(EXIT_FAILURE, "Unexpected response from upsd: %s", buf); } fprintf(stderr, "%s\n", buf); } static void clean_exit(void) { if (ups) { upscli_disconnect(ups); } free(upsname); free(hostname); free(ups); } int main(int argc, char **argv) { int i, ret, port; int have_un = 0, have_pw = 0, cmdlist = 0; char buf[SMALLBUF], username[SMALLBUF], password[SMALLBUF]; const char *prog = xbasename(argv[0]); while ((i = getopt(argc, argv, "+lhu:p:V")) != -1) { switch (i) { case 'l': cmdlist = 1; break; case 'u': snprintf(username, sizeof(username), "%s", optarg); have_un = 1; break; case 'p': snprintf(password, sizeof(password), "%s", optarg); have_pw = 1; break; case 'V': fatalx(EXIT_SUCCESS, "Network UPS Tools upscmd %s", UPS_VERSION); case 'h': default: usage(prog); exit(EXIT_SUCCESS); } } argc -= optind; argv += optind; if (argc < 1) { usage(prog); exit(EXIT_SUCCESS); } /* be a good little client that cleans up after itself */ atexit(clean_exit); if (upscli_splitname(argv[0], &upsname, &hostname, &port) != 0) { fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]"); } ups = xcalloc(1, sizeof(*ups)); if (upscli_connect(ups, hostname, port, 0) < 0) { fatalx(EXIT_FAILURE, "Error: %s", upscli_strerror(ups)); } if (cmdlist) { listcmds(); exit(EXIT_SUCCESS); } if (argc < 2) { usage(prog); exit(EXIT_SUCCESS); } /* also fallback for old command names */ if (!strchr(argv[1], '.')) { fatalx(EXIT_FAILURE, "Error: old command names are not supported"); } if (!have_un) { struct passwd *pw; memset(username, '\0', sizeof(username)); pw = getpwuid(getuid()); if (pw) { printf("Username (%s): ", pw->pw_name); } else { printf("Username: "); } if (!fgets(username, sizeof(username), stdin)) { fatalx(EXIT_FAILURE, "Error reading from stdin!"); } /* deal with that pesky newline */ if (strlen(username) > 1) { username[strlen(username) - 1] = '\0'; } else { if (!pw) { fatalx(EXIT_FAILURE, "No username available - even tried getpwuid"); } snprintf(username, sizeof(username), "%s", pw->pw_name); } } /* getpass leaks slightly - use -p when testing in valgrind */ if (!have_pw) { /* using getpass or getpass_r might not be a good idea here (marked obsolete in POSIX) */ char *pwtmp = GETPASS("Password: "); if (!pwtmp) { fatalx(EXIT_FAILURE, "getpass failed: %s", strerror(errno)); } snprintf(password, sizeof(password), "%s", pwtmp); } snprintf(buf, sizeof(buf), "USERNAME %s\n", username); if (upscli_sendline(ups, buf, strlen(buf)) < 0) { fatalx(EXIT_FAILURE, "Can't set username: %s", upscli_strerror(ups)); } ret = upscli_readline(ups, buf, sizeof(buf)); if (ret < 0) { if (upscli_upserror(ups) != UPSCLI_ERR_UNKCOMMAND) { fatalx(EXIT_FAILURE, "Set username failed: %s", upscli_strerror(ups)); } fatalx(EXIT_FAILURE, "Set username failed due to an unknown command.\n" "You probably need to upgrade upsd."); } snprintf(buf, sizeof(buf), "PASSWORD %s\n", password); if (upscli_sendline(ups, buf, strlen(buf)) < 0) { fatalx(EXIT_FAILURE, "Can't set password: %s", upscli_strerror(ups)); } if (upscli_readline(ups, buf, sizeof(buf)) < 0) { fatalx(EXIT_FAILURE, "Set password failed: %s", upscli_strerror(ups)); } do_cmd(&argv[1], argc - 1); exit(EXIT_SUCCESS); } /* Formal do_upsconf_args implementation to satisfy linker on AIX */ #if (defined NUT_PLATFORM_AIX) void do_upsconf_args(char *upsname, char *var, char *val) { fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called"); } #endif /* end of #if (defined NUT_PLATFORM_AIX) */ nut-2.7.4/clients/upslog.c0000644000175000017500000003045412640473702012404 00000000000000/* upslog - log ups values to a file for later collection and analysis Copyright (C) 1998 Russell Kroll 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 */ /* Basic theory of operation: * * First we go through and parse as much of the status format string as * possible. We used to do this parsing run every time, but that's a * waste of CPU since it can't change during the program's run. * * This version does the parsing pass once, and creates a linked list of * pointers to the functions that do the work and the arg they get. * * That means the main loop just has to run the linked list and call * anything it finds in there. Everything happens from there, and we * don't have to pointlessly reparse the string every time around. */ #include "common.h" #include "nut_platform.h" #include "upsclient.h" #include "config.h" #include "timehead.h" #include "upslog.h" static int port, reopen_flag = 0, exit_flag = 0; static char *upsname, *hostname; static UPSCONN_t ups; static FILE *logfile; static const char *logfn, *monhost; static sigset_t nut_upslog_sigmask; static char logbuffer[LARGEBUF], *logformat; static flist_t *fhead = NULL; #define DEFAULT_LOGFORMAT "%TIME @Y@m@d @H@M@S% %VAR battery.charge% " \ "%VAR input.voltage% %VAR ups.load% [%VAR ups.status%] " \ "%VAR ups.temperature% %VAR input.frequency%" static void reopen_log(void) { if (logfile == stdout) { upslogx(LOG_INFO, "logging to stdout"); return; } fclose(logfile); logfile = fopen(logfn, "a"); if (logfile == NULL) fatal_with_errno(EXIT_FAILURE, "could not reopen logfile %s", logfn); } static void set_reopen_flag(int sig) { reopen_flag = sig; } static void set_exit_flag(int sig) { exit_flag = sig; } static void set_print_now_flag(int sig) { /* no need to do anything, the signal will cause sleep to be interrupted */ } /* handlers: reload on HUP, exit on INT/QUIT/TERM */ static void setup_signals(void) { struct sigaction sa; sigemptyset(&nut_upslog_sigmask); sigaddset(&nut_upslog_sigmask, SIGHUP); sa.sa_mask = nut_upslog_sigmask; sa.sa_handler = set_reopen_flag; sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGHUP handler"); sa.sa_handler = set_exit_flag; if (sigaction(SIGINT, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGINT handler"); if (sigaction(SIGQUIT, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGQUIT handler"); if (sigaction(SIGTERM, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGTERM handler"); sa.sa_handler = set_print_now_flag; if (sigaction(SIGUSR1, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGUSR1 handler"); } static void help(const char *prog) { printf("UPS status logger.\n"); printf("\nusage: %s [OPTIONS]\n", prog); printf("\n"); printf(" -f - Log format. See below for details.\n"); printf(" - Use -f \"\" so your shell doesn't break it up.\n"); printf(" -i - Time between updates, in seconds\n"); printf(" -l - Log file name, or - for stdout\n"); printf(" -p - Base name for PID file (defaults to \"%s\")\n", prog); printf(" -s - Monitor UPS - @[:]\n"); printf(" - Example: -s myups@server\n"); printf(" -u - Switch to if started as root\n"); printf("\n"); printf("Some valid format string escapes:\n"); printf("\t%%%% insert a single %%\n"); printf("\t%%TIME format%% insert the time with strftime formatting\n"); printf("\t%%HOST%% insert the local hostname\n"); printf("\t%%UPSHOST%% insert the host of the ups being monitored\n"); printf("\t%%PID%% insert the pid of upslog\n"); printf("\t%%VAR varname%% insert the value of ups variable varname\n\n"); printf("format string defaults to:\n"); printf("%s\n", DEFAULT_LOGFORMAT); printf("\n"); printf("See the upslog(8) man page for more information.\n"); exit(EXIT_SUCCESS); } /* print current host name */ static void do_host(const char *arg) { int ret; char hn[LARGEBUF]; ret = gethostname(hn, sizeof(hn)); if (ret != 0) { upslog_with_errno(LOG_ERR, "gethostname failed"); return; } snprintfcat(logbuffer, sizeof(logbuffer), "%s", hn); } static void do_upshost(const char *arg) { snprintfcat(logbuffer, sizeof(logbuffer), "%s", monhost); } static void do_pid(const char *arg) { snprintfcat(logbuffer, sizeof(logbuffer), "%ld", (long)getpid()); } static void do_time(const char *arg) { unsigned int i; char timebuf[SMALLBUF], *format; time_t tod; format = xstrdup(arg); /* @s are used on the command line since % is taken */ for (i = 0; i < strlen(format); i++) if (format[i] == '@') format[i] = '%'; time(&tod); strftime(timebuf, sizeof(timebuf), format, localtime(&tod)); snprintfcat(logbuffer, sizeof(logbuffer), "%s", timebuf); free(format); } static void getvar(const char *var) { int ret; unsigned int numq, numa; const char *query[4]; char **answer; query[0] = "VAR"; query[1] = upsname; query[2] = var; numq = 3; ret = upscli_get(&ups, numq, query, &numa, &answer); if ((ret < 0) || (numa < numq)) { snprintfcat(logbuffer, sizeof(logbuffer), "NA"); return; } snprintfcat(logbuffer, sizeof(logbuffer), "%s", answer[3]); } static void do_var(const char *arg) { if ((!arg) || (strlen(arg) < 1)) { snprintfcat(logbuffer, sizeof(logbuffer), "INVALID"); return; } /* old variable names are no longer supported */ if (!strchr(arg, '.')) { snprintfcat(logbuffer, sizeof(logbuffer), "INVALID"); return; } /* a UPS name is now required */ if (!upsname) { snprintfcat(logbuffer, sizeof(logbuffer), "INVALID"); return; } getvar(arg); } static void do_etime(const char *arg) { time_t tod; time(&tod); snprintfcat(logbuffer, sizeof(logbuffer), "%ld", (unsigned long) tod); } static void print_literal(const char *arg) { snprintfcat(logbuffer, sizeof(logbuffer), "%s", arg); } /* register another parsing function to be called later */ static void add_call(void (*fptr)(const char *arg), const char *arg) { flist_t *tmp, *last; tmp = last = fhead; while (tmp) { last = tmp; tmp = tmp->next; } tmp = xmalloc(sizeof(flist_t)); tmp->fptr = fptr; if (arg) tmp->arg = xstrdup(arg); else tmp->arg = NULL; tmp->next = NULL; if (last) last->next = tmp; else fhead = tmp; } /* turn the format string into a list of function calls with args */ static void compile_format(void) { unsigned int i; int j, found, ofs; char *cmd, *arg, *ptr; for (i = 0; i < strlen(logformat); i++) { /* if not a % sequence, append character and start over */ if (logformat[i] != '%') { char buf[4]; /* we have to stuff it into a string first */ snprintf(buf, sizeof(buf), "%c", logformat[i]); add_call(print_literal, buf); continue; } /* if a %%, append % and start over */ if (logformat[i+1] == '%') { add_call(print_literal, "%"); /* make sure we don't parse the second % next time */ i++; continue; } /* it must start with a % now - %[ ]%*/ cmd = xstrdup(&logformat[i+1]); ptr = strchr(cmd, '%'); /* no trailing % = broken */ if (!ptr) { add_call(print_literal, "INVALID"); free(cmd); continue; } *ptr = '\0'; /* remember length (plus first %) so we can skip over it */ ofs = strlen(cmd) + 1; /* jump out to argument (if any) */ arg = strchr(cmd, ' '); if (arg) *arg++ = '\0'; found = 0; /* see if we know how to handle this command */ for (j = 0; logcmds[j].name != NULL; j++) { if (strncasecmp(cmd, logcmds[j].name, strlen(logcmds[j].name)) == 0) { add_call(logcmds[j].func, arg); found = 1; break; } } free(cmd); if (!found) add_call(print_literal, "INVALID"); /* now do the skip ahead saved from before */ i += ofs; } /* for (i = 0; i < strlen(logformat); i++) */ } /* go through the list of functions and call them in order */ static void run_flist(void) { flist_t *tmp; tmp = fhead; memset(logbuffer, 0, sizeof(logbuffer)); while (tmp) { tmp->fptr(tmp->arg); tmp = tmp->next; } fprintf(logfile, "%s\n", logbuffer); fflush(logfile); } /* -s * -l * -i * -f * -u */ int main(int argc, char **argv) { int interval = 30, i; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; struct passwd *new_uid = NULL; const char *pidfilebase = prog; logformat = DEFAULT_LOGFORMAT; user = RUN_AS_USER; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:")) != -1) { switch(i) { case 'h': help(prog); break; case 's': monhost = optarg; break; case 'l': logfn = optarg; break; case 'i': interval = atoi(optarg); break; case 'f': logformat = optarg; break; case 'u': user = optarg; break; case 'V': exit(EXIT_SUCCESS); case 'p': pidfilebase = optarg; break; } } argc -= optind; argv += optind; /* not enough args for the old way? */ if ((argc == 1) || (argc == 2)) help(prog); /* see if it's being called in the old style - 3 or 4 args */ /* [] */ if (argc >= 3) { monhost = argv[0]; logfn = argv[1]; interval = atoi(argv[2]); } if (argc >= 4) { /* read out the remaining argv entries to the format string */ logformat = xmalloc(LARGEBUF); memset(logformat, '\0', LARGEBUF); for (i = 3; i < argc; i++) snprintfcat(logformat, LARGEBUF, "%s ", argv[i]); } if (!monhost) fatalx(EXIT_FAILURE, "No UPS defined for monitoring - use -s "); if (!logfn) fatalx(EXIT_FAILURE, "No filename defined for logging - use -l "); /* shouldn't happen */ if (!logformat) fatalx(EXIT_FAILURE, "No format defined - but this should be impossible"); printf("logging status of %s to %s (%is intervals)\n", monhost, logfn, interval); if (upscli_splitname(monhost, &upsname, &hostname, &port) != 0) { fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]\n"); } if (upscli_connect(&ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) fprintf(stderr, "Warning: initial connect failed: %s\n", upscli_strerror(&ups)); if (strcmp(logfn, "-") == 0) logfile = stdout; else logfile = fopen(logfn, "a"); if (logfile == NULL) fatal_with_errno(EXIT_FAILURE, "could not open logfile %s", logfn); /* now drop root if we have it */ new_uid = get_user_pwent(user); open_syslog(prog); if (logfile != stdout) background(); setup_signals(); writepid(pidfilebase); become_user(new_uid); compile_format(); while (exit_flag == 0) { time(&now); if (nextpoll > now) { /* there is still time left, so sleep it off */ sleep(difftime(nextpoll, now)); nextpoll += interval; } else { /* we spent more time in polling than the interval allows */ nextpoll = now + interval; } if (reopen_flag) { upslogx(LOG_INFO, "Signal %d: reopening log file", reopen_flag); reopen_log(); reopen_flag = 0; } /* reconnect if necessary */ if (upscli_fd(&ups) < 0) { upscli_connect(&ups, hostname, port, 0); } run_flist(); /* don't keep connection open if we don't intend to use it shortly */ if (interval > 30) { upscli_disconnect(&ups); } } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); if (logfile != stdout) fclose(logfile); upscli_disconnect(&ups); exit(EXIT_SUCCESS); } /* Formal do_upsconf_args implementation to satisfy linker on AIX */ #if (defined NUT_PLATFORM_AIX) void do_upsconf_args(char *upsname, char *var, char *val) { fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called"); } #endif /* end of #if (defined NUT_PLATFORM_AIX) */ nut-2.7.4/clients/Makefile.in0000644000175000017500000012216712667762000013000 00000000000000# Makefile.in generated by automake 1.14.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Network UPS Tools: clients VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_SSL_TRUE@am__append_1 = $(LIBSSL_LIBS) @WITH_SSL_TRUE@am__append_2 = $(LIBSSL_CFLAGS) @WITH_CGI_TRUE@am__append_3 = $(LIBGD_CFLAGS) bin_PROGRAMS = upsc$(EXEEXT) upslog$(EXEEXT) upsrw$(EXEEXT) \ upscmd$(EXEEXT) sbin_PROGRAMS = upsmon$(EXEEXT) upssched$(EXEEXT) @WITH_CGI_TRUE@cgiexec_PROGRAMS = upsstats.cgi$(EXEEXT) \ @WITH_CGI_TRUE@ upsimage.cgi$(EXEEXT) upsset.cgi$(EXEEXT) @WITH_SSL_TRUE@am__append_4 = $(LIBSSL_LIBS) subdir = clients DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(dist_bin_SCRIPTS) $(top_srcdir)/depcomp \ $(am__include_HEADERS_DIST) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ $(top_srcdir)/m4/nut_check_libnss.m4 \ $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(cgiexecdir)" "$(DESTDIR)$(sbindir)" \ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) libnutclient_la_LIBADD = am_libnutclient_la_OBJECTS = nutclient.lo libnutclient_la_OBJECTS = $(am_libnutclient_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libnutclient_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ $(AM_CXXFLAGS) $(CXXFLAGS) $(libnutclient_la_LDFLAGS) \ $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = @WITH_SSL_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) libupsclient_la_DEPENDENCIES = ../common/libcommonclient.la \ $(am__DEPENDENCIES_2) am_libupsclient_la_OBJECTS = upsclient.lo libupsclient_la_OBJECTS = $(am_libupsclient_la_OBJECTS) libupsclient_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libupsclient_la_LDFLAGS) $(LDFLAGS) \ -o $@ PROGRAMS = $(bin_PROGRAMS) $(cgiexec_PROGRAMS) $(sbin_PROGRAMS) am_upsc_OBJECTS = upsc.$(OBJEXT) upsc_OBJECTS = $(am_upsc_OBJECTS) upsc_LDADD = $(LDADD) upsc_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_upscmd_OBJECTS = upscmd.$(OBJEXT) upscmd_OBJECTS = $(am_upscmd_OBJECTS) upscmd_LDADD = $(LDADD) upscmd_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_upsimage_cgi_OBJECTS = upsimage.$(OBJEXT) cgilib.$(OBJEXT) upsimage_cgi_OBJECTS = $(am_upsimage_cgi_OBJECTS) am__DEPENDENCIES_3 = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) upsimage_cgi_DEPENDENCIES = $(am__DEPENDENCIES_3) \ $(am__DEPENDENCIES_1) am_upslog_OBJECTS = upslog.$(OBJEXT) upslog_OBJECTS = $(am_upslog_OBJECTS) upslog_LDADD = $(LDADD) upslog_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_upsmon_OBJECTS = upsmon.$(OBJEXT) upsmon_OBJECTS = $(am_upsmon_OBJECTS) upsmon_LDADD = $(LDADD) upsmon_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_upsrw_OBJECTS = upsrw.$(OBJEXT) upsrw_OBJECTS = $(am_upsrw_OBJECTS) upsrw_LDADD = $(LDADD) upsrw_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_upssched_OBJECTS = upssched.$(OBJEXT) upssched_OBJECTS = $(am_upssched_OBJECTS) upssched_DEPENDENCIES = ../common/libcommon.la \ ../common/libparseconf.la $(am__DEPENDENCIES_1) am_upsset_cgi_OBJECTS = upsset.$(OBJEXT) cgilib.$(OBJEXT) upsset_cgi_OBJECTS = $(am_upsset_cgi_OBJECTS) upsset_cgi_LDADD = $(LDADD) upsset_cgi_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am_upsstats_cgi_OBJECTS = upsstats.$(OBJEXT) cgilib.$(OBJEXT) upsstats_cgi_OBJECTS = $(am_upsstats_cgi_OBJECTS) upsstats_cgi_LDADD = $(LDADD) upsstats_cgi_DEPENDENCIES = ../common/libcommon.la libupsclient.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) SCRIPTS = $(dist_bin_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CXXFLAGS) $(CXXFLAGS) AM_V_CXX = $(am__v_CXX_@AM_V@) am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; am__v_CXX_1 = CXXLD = $(CXX) CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = SOURCES = $(libnutclient_la_SOURCES) $(libupsclient_la_SOURCES) \ $(upsc_SOURCES) $(upscmd_SOURCES) $(upsimage_cgi_SOURCES) \ $(upslog_SOURCES) $(upsmon_SOURCES) $(upsrw_SOURCES) \ $(upssched_SOURCES) $(upsset_cgi_SOURCES) \ $(upsstats_cgi_SOURCES) DIST_SOURCES = $(libnutclient_la_SOURCES) $(libupsclient_la_SOURCES) \ $(upsc_SOURCES) $(upscmd_SOURCES) $(upsimage_cgi_SOURCES) \ $(upslog_SOURCES) $(upsmon_SOURCES) $(upsrw_SOURCES) \ $(upssched_SOURCES) $(upsset_cgi_SOURCES) \ $(upsstats_cgi_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__include_HEADERS_DIST = upsclient.h ../include/parseconf.h \ nutclient.h HEADERS = $(include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ ASPELL = @ASPELL@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ CPPUNIT_LIBS = @CPPUNIT_LIBS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ LIBOBJS = @LIBOBJS@ LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ LIBTOOL = @LIBTOOL@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ XMLLINT = @XMLLINT@ XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ hotplugdir = @hotplugdir@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemshutdowndir = @systemdsystemshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ # by default, link programs in this directory with libcommon.a LDADD = ../common/libcommon.la libupsclient.la $(NETLIBS) \ $(am__append_1) # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include $(am__append_2) $(am__append_3) dist_bin_SCRIPTS = upssched-cmd lib_LTLIBRARIES = libupsclient.la libnutclient.la @WITH_DEV_TRUE@include_HEADERS = upsclient.h ../include/parseconf.h nutclient.h upsc_SOURCES = upsc.c upsclient.h upscmd_SOURCES = upscmd.c upsclient.h upsrw_SOURCES = upsrw.c upsclient.h upslog_SOURCES = upslog.c upsclient.h upslog.h upsmon_SOURCES = upsmon.c upsmon.h upsclient.h upssched_SOURCES = upssched.c upssched.h upssched_LDADD = ../common/libcommon.la ../common/libparseconf.la $(NETLIBS) upsimage_cgi_SOURCES = upsimage.c upsclient.h upsimagearg.h cgilib.c cgilib.h upsimage_cgi_LDADD = $(LDADD) $(LIBGD_LDFLAGS) upsset_cgi_SOURCES = upsset.c upsclient.h cgilib.c cgilib.h upsstats_cgi_SOURCES = upsstats.c upsclient.h status.h upsstats.h \ upsimagearg.h cgilib.c cgilib.h # not LDADD. libupsclient_la_SOURCES = upsclient.c upsclient.h libupsclient_la_LIBADD = ../common/libcommonclient.la $(am__append_4) # libupsclient version information # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html libupsclient_la_LDFLAGS = -version-info 4:0:0 libnutclient_la_SOURCES = nutclient.h nutclient.cpp libnutclient_la_LDFLAGS = -version-info 0:0:0 all: all-am .SUFFIXES: .SUFFIXES: .c .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu clients/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libnutclient.la: $(libnutclient_la_OBJECTS) $(libnutclient_la_DEPENDENCIES) $(EXTRA_libnutclient_la_DEPENDENCIES) $(AM_V_CXXLD)$(libnutclient_la_LINK) -rpath $(libdir) $(libnutclient_la_OBJECTS) $(libnutclient_la_LIBADD) $(LIBS) libupsclient.la: $(libupsclient_la_OBJECTS) $(libupsclient_la_DEPENDENCIES) $(EXTRA_libupsclient_la_DEPENDENCIES) $(AM_V_CCLD)$(libupsclient_la_LINK) -rpath $(libdir) $(libupsclient_la_OBJECTS) $(libupsclient_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-cgiexecPROGRAMS: $(cgiexec_PROGRAMS) @$(NORMAL_INSTALL) @list='$(cgiexec_PROGRAMS)'; test -n "$(cgiexecdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(cgiexecdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(cgiexecdir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cgiexecdir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cgiexecdir)$$dir" || exit $$?; \ } \ ; done uninstall-cgiexecPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(cgiexec_PROGRAMS)'; test -n "$(cgiexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(cgiexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(cgiexecdir)" && rm -f $$files clean-cgiexecPROGRAMS: @list='$(cgiexec_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list upsc$(EXEEXT): $(upsc_OBJECTS) $(upsc_DEPENDENCIES) $(EXTRA_upsc_DEPENDENCIES) @rm -f upsc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsc_OBJECTS) $(upsc_LDADD) $(LIBS) upscmd$(EXEEXT): $(upscmd_OBJECTS) $(upscmd_DEPENDENCIES) $(EXTRA_upscmd_DEPENDENCIES) @rm -f upscmd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upscmd_OBJECTS) $(upscmd_LDADD) $(LIBS) upsimage.cgi$(EXEEXT): $(upsimage_cgi_OBJECTS) $(upsimage_cgi_DEPENDENCIES) $(EXTRA_upsimage_cgi_DEPENDENCIES) @rm -f upsimage.cgi$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsimage_cgi_OBJECTS) $(upsimage_cgi_LDADD) $(LIBS) upslog$(EXEEXT): $(upslog_OBJECTS) $(upslog_DEPENDENCIES) $(EXTRA_upslog_DEPENDENCIES) @rm -f upslog$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upslog_OBJECTS) $(upslog_LDADD) $(LIBS) upsmon$(EXEEXT): $(upsmon_OBJECTS) $(upsmon_DEPENDENCIES) $(EXTRA_upsmon_DEPENDENCIES) @rm -f upsmon$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsmon_OBJECTS) $(upsmon_LDADD) $(LIBS) upsrw$(EXEEXT): $(upsrw_OBJECTS) $(upsrw_DEPENDENCIES) $(EXTRA_upsrw_DEPENDENCIES) @rm -f upsrw$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsrw_OBJECTS) $(upsrw_LDADD) $(LIBS) upssched$(EXEEXT): $(upssched_OBJECTS) $(upssched_DEPENDENCIES) $(EXTRA_upssched_DEPENDENCIES) @rm -f upssched$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upssched_OBJECTS) $(upssched_LDADD) $(LIBS) upsset.cgi$(EXEEXT): $(upsset_cgi_OBJECTS) $(upsset_cgi_DEPENDENCIES) $(EXTRA_upsset_cgi_DEPENDENCIES) @rm -f upsset.cgi$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsset_cgi_OBJECTS) $(upsset_cgi_LDADD) $(LIBS) upsstats.cgi$(EXEEXT): $(upsstats_cgi_OBJECTS) $(upsstats_cgi_DEPENDENCIES) $(EXTRA_upsstats_cgi_DEPENDENCIES) @rm -f upsstats.cgi$(EXEEXT) $(AM_V_CCLD)$(LINK) $(upsstats_cgi_OBJECTS) $(upsstats_cgi_LDADD) $(LIBS) install-dist_binSCRIPTS: $(dist_bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgilib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutclient.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsclient.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upscmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsimage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upslog.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsmon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsrw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upssched.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsset.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsstats.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cgiexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-cgiexecPROGRAMS clean-generic \ clean-libLTLIBRARIES clean-libtool clean-sbinPROGRAMS \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-cgiexecPROGRAMS \ install-dist_binSCRIPTS install-libLTLIBRARIES \ install-sbinPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-cgiexecPROGRAMS \ uninstall-dist_binSCRIPTS uninstall-includeHEADERS \ uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ clean-binPROGRAMS clean-cgiexecPROGRAMS clean-generic \ clean-libLTLIBRARIES clean-libtool clean-sbinPROGRAMS \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-cgiexecPROGRAMS install-data \ install-data-am install-dist_binSCRIPTS install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-includeHEADERS install-info \ install-info-am install-libLTLIBRARIES install-man install-pdf \ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-cgiexecPROGRAMS \ uninstall-dist_binSCRIPTS uninstall-includeHEADERS \ uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: nut-2.7.4/clients/status.h0000644000175000017500000000253612640444140012415 00000000000000/* status.h - translation of status abbreviations to descriptions Copyright (C) 1999 Russell Kroll 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 */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif struct { char *name; char *desc; int severity; } stattab[] = { { "OFF", "OFF", 1 }, { "OL", "ONLINE", 0 }, { "OB", "ON BATTERY", 2 }, { "LB", "LOW BATTERY", 2 }, { "RB", "REPLACE BATTERY", 2 }, { "OVER", "OVERLOAD", 2 }, { "TRIM", "VOLTAGE TRIM", 1 }, { "BOOST", "VOLTAGE BOOST", 1 }, { "CAL", "CALIBRATION", 1 }, { "BYPASS", "BYPASS", 2 }, { NULL, NULL, 0 } }; #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif nut-2.7.4/clients/upsmon.c0000644000175000017500000012626312640473702012420 00000000000000/* upsmon - monitor power status over the 'net (talks to upsd via TCP) Copyright (C) 1998 Russell Kroll 2012 Arnaud Quette 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 "common.h" #include #include #include #include "upsclient.h" #include "upsmon.h" #include "parseconf.h" #include "timehead.h" #ifdef HAVE_STDARG_H #include #endif static char *shutdowncmd = NULL, *notifycmd = NULL; static char *powerdownflag = NULL, *configfile = NULL; static int minsupplies = 1, sleepval = 5, deadtime = 15; /* default polling interval = 5 sec */ static int pollfreq = 5, pollfreqalert = 5; /* slave hosts are given 15 sec by default to logout from upsd */ static int hostsync = 15; /* sum of all power values from config file */ static int totalpv = 0; /* default replace battery warning interval (seconds) */ static int rbwarntime = 43200; /* default "all communications down" warning interval (seconds) */ static int nocommwarntime = 300; /* default interval between the shutdown warning and the shutdown */ static int finaldelay = 5; /* set by SIGHUP handler, cleared after reload finishes */ static int reload_flag = 0; /* set after SIGINT, SIGQUIT, or SIGTERM */ static int exit_flag = 0; /* userid for unprivileged process when using fork mode */ static char *run_as_user = NULL; /* SSL details - where to find certs, whether to use them */ static char *certpath = NULL; static char *certname = NULL; static char *certpasswd = NULL; static int certverify = 0; /* don't verify by default */ static int forcessl = 0; /* don't require ssl by default */ static int userfsd = 0, use_pipe = 1, pipefd[2]; static utype_t *firstups = NULL; static int opt_af = AF_UNSPEC; /* signal handling things */ static struct sigaction sa; static sigset_t nut_upsmon_sigmask; static void setflag(int *val, int flag) { *val |= flag; } static void clearflag(int *val, int flag) { *val ^= (*val & flag); } static int flag_isset(int num, int flag) { return ((num & flag) == flag); } static void wall(const char *text) { FILE *wf; wf = popen("wall", "w"); if (!wf) { upslog_with_errno(LOG_NOTICE, "Can't invoke wall"); return; } fprintf(wf, "%s\n", text); pclose(wf); } static void notify(const char *notice, int flags, const char *ntype, const char *upsname) { char exec[LARGEBUF]; int ret; if (flag_isset(flags, NOTIFY_IGNORE)) return; if (flag_isset(flags, NOTIFY_SYSLOG)) upslogx(LOG_NOTICE, "%s", notice); /* fork here so upsmon doesn't get wedged if the notifier is slow */ ret = fork(); if (ret < 0) { upslog_with_errno(LOG_ERR, "Can't fork to notify"); return; } if (ret != 0) /* parent */ return; /* child continues and does all the work */ if (flag_isset(flags, NOTIFY_WALL)) wall(notice); if (flag_isset(flags, NOTIFY_EXEC)) { if (notifycmd != NULL) { snprintf(exec, sizeof(exec), "%s \"%s\"", notifycmd, notice); if (upsname) setenv("UPSNAME", upsname, 1); else setenv("UPSNAME", "", 1); setenv("NOTIFYTYPE", ntype, 1); if (system(exec) == -1) { upslog_with_errno(LOG_ERR, "%s", __func__); } } } exit(EXIT_SUCCESS); } static void do_notify(const utype_t *ups, int ntype) { int i; char msg[SMALLBUF], *upsname = NULL; /* grab this for later */ if (ups) upsname = ups->sys; for (i = 0; notifylist[i].name != NULL; i++) { if (notifylist[i].type == ntype) { upsdebugx(2, "%s: ntype 0x%04x (%s)", __func__, ntype, notifylist[i].name); snprintf(msg, sizeof(msg), notifylist[i].msg ? notifylist[i].msg : notifylist[i].stockmsg, ups ? ups->sys : ""); notify(msg, notifylist[i].flags, notifylist[i].name, upsname); return; } } /* not found ?! */ } /* check for master permissions on the server for this ups */ static int checkmaster(utype_t *ups) { char buf[SMALLBUF]; /* don't bother if we're not configured as a master for this ups */ if (!flag_isset(ups->status, ST_MASTER)) return 1; /* this shouldn't happen (LOGIN checks it earlier) */ if ((ups->upsname == NULL) || (strlen(ups->upsname) == 0)) { upslogx(LOG_ERR, "Set master on UPS [%s] failed: empty upsname", ups->sys); return 0; } snprintf(buf, sizeof(buf), "MASTER %s\n", ups->upsname); if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { upslogx(LOG_ALERT, "Can't set master mode on UPS [%s] - %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } if (upscli_readline(&ups->conn, buf, sizeof(buf)) == 0) { if (!strncmp(buf, "OK", 2)) return 1; /* not ERR, but not caught by readline either? */ upslogx(LOG_ALERT, "Master privileges unavailable on UPS [%s]", ups->sys); upslogx(LOG_ALERT, "Response: [%s]", buf); } else { /* something caught by readraw's parsing call */ upslogx(LOG_ALERT, "Master privileges unavailable on UPS [%s]", ups->sys); upslogx(LOG_ALERT, "Reason: %s", upscli_strerror(&ups->conn)); } return 0; } /* authenticate to upsd, plus do LOGIN and MASTER if applicable */ static int do_upsd_auth(utype_t *ups) { char buf[SMALLBUF]; if (!ups->un) { upslogx(LOG_ERR, "UPS [%s]: no username defined!", ups->sys); return 0; } snprintf(buf, sizeof(buf), "USERNAME %s\n", ups->un); if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { upslogx(LOG_ERR, "Can't set username on [%s]: %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } if (upscli_readline(&ups->conn, buf, sizeof(buf)) < 0) { upslogx(LOG_ERR, "Set username on [%s] failed: %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } /* authenticate first */ snprintf(buf, sizeof(buf), "PASSWORD %s\n", ups->pw); if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { upslogx(LOG_ERR, "Can't set password on [%s]: %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } if (upscli_readline(&ups->conn, buf, sizeof(buf)) < 0) { upslogx(LOG_ERR, "Set password on [%s] failed: %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } /* catch insanity from the server - not ERR and not OK either */ if (strncmp(buf, "OK", 2) != 0) { upslogx(LOG_ERR, "Set password on [%s] failed - got [%s]", ups->sys, buf); return 0; } /* we require a upsname now */ if ((ups->upsname == NULL) || (strlen(ups->upsname) == 0)) { upslogx(LOG_ERR, "Login to UPS [%s] failed: empty upsname", ups->sys); return 0; } /* password is set, let's login */ snprintf(buf, sizeof(buf), "LOGIN %s\n", ups->upsname); if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { upslogx(LOG_ERR, "Login to UPS [%s] failed: %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } if (upscli_readline(&ups->conn, buf, sizeof(buf)) < 0) { upslogx(LOG_ERR, "Can't login to UPS [%s]: %s", ups->sys, upscli_strerror(&ups->conn)); return 0; } /* catch insanity from the server - not ERR and not OK either */ if (strncmp(buf, "OK", 2) != 0) { upslogx(LOG_ERR, "Login on UPS [%s] failed - got [%s]", ups->sys, buf); return 0; } /* finally - everything is OK */ upsdebugx(1, "Logged into UPS %s", ups->sys); setflag(&ups->status, ST_LOGIN); /* now see if we also need to test master permissions */ return checkmaster(ups); } /* set flags and make announcements when a UPS has been checked successfully */ static void ups_is_alive(utype_t *ups) { time_t now; time(&now); ups->lastpoll = now; if (ups->commstate == 1) /* already known */ return; /* only notify for 0->1 transitions (to ignore the first connect) */ if (ups->commstate == 0) do_notify(ups, NOTIFY_COMMOK); ups->commstate = 1; } /* handle all the notifications for a missing UPS in one place */ static void ups_is_gone(utype_t *ups) { time_t now; /* first time: clear the flag and throw the first notifier */ if (ups->commstate != 0) { ups->commstate = 0; /* COMMBAD is the initial loss of communications */ do_notify(ups, NOTIFY_COMMBAD); return; } time(&now); /* first only act if we're seconds past the last poll */ if ((now - ups->lastpoll) < nocommwarntime) return; /* now only complain if we haven't lately */ if ((now - ups->lastncwarn) > nocommwarntime) { /* NOCOMM indicates a persistent condition */ do_notify(ups, NOTIFY_NOCOMM); ups->lastncwarn = now; } } static void ups_on_batt(utype_t *ups) { if (flag_isset(ups->status, ST_ONBATT)) { /* no change */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); return; } sleepval = pollfreqalert; /* bump up polling frequency */ ups->linestate = 0; upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); /* must have changed from OL to OB, so notify */ do_notify(ups, NOTIFY_ONBATT); setflag(&ups->status, ST_ONBATT); clearflag(&ups->status, ST_ONLINE); } static void ups_on_line(utype_t *ups) { if (flag_isset(ups->status, ST_ONLINE)) { /* no change */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); return; } sleepval = pollfreq; upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); /* ignore the first OL at startup, otherwise send the notifier */ if (ups->linestate != -1) do_notify(ups, NOTIFY_ONLINE); ups->linestate = 1; setflag(&ups->status, ST_ONLINE); clearflag(&ups->status, ST_ONBATT); } /* create the flag file if necessary */ static void set_pdflag(void) { FILE *pdf; if (!powerdownflag) return; pdf = fopen(powerdownflag, "w"); if (!pdf) { upslogx(LOG_ERR, "Failed to create power down flag!"); return; } fprintf(pdf, "%s", SDMAGIC); fclose(pdf); } /* the actual shutdown procedure */ static void doshutdown(void) { int ret; /* this should probably go away at some point */ upslogx(LOG_CRIT, "Executing automatic power-fail shutdown"); wall("Executing automatic power-fail shutdown\n"); do_notify(NULL, NOTIFY_SHUTDOWN); sleep(finaldelay); /* in the pipe model, we let the parent do this for us */ if (use_pipe) { char ch; ch = 1; ret = write(pipefd[1], &ch, 1); } else { /* one process model = we do all the work here */ if (geteuid() != 0) upslogx(LOG_WARNING, "Not root, shutdown may fail"); set_pdflag(); ret = system(shutdowncmd); if (ret != 0) upslogx(LOG_ERR, "Unable to call shutdown command: %s", shutdowncmd); } exit(EXIT_SUCCESS); } /* set forced shutdown flag so other upsmons know what's going on here */ static void setfsd(utype_t *ups) { char buf[SMALLBUF]; int ret; /* this shouldn't happen */ if (!ups->upsname) { upslogx(LOG_ERR, "setfsd: programming error: no UPS name set [%s]", ups->sys); return; } upsdebugx(2, "Setting FSD on UPS %s", ups->sys); snprintf(buf, sizeof(buf), "FSD %s\n", ups->upsname); ret = upscli_sendline(&ups->conn, buf, strlen(buf)); if (ret < 0) { upslogx(LOG_ERR, "FSD set on UPS %s failed: %s", ups->sys, upscli_strerror(&ups->conn)); return; } ret = upscli_readline(&ups->conn, buf, sizeof(buf)); if (ret < 0) { upslogx(LOG_ERR, "FSD set on UPS %s failed: %s", ups->sys, upscli_strerror(&ups->conn)); return; } if (!strncmp(buf, "OK", 2)) return; /* protocol error: upsd said something other than "OK" */ upslogx(LOG_ERR, "FSD set on UPS %s failed: %s", ups->sys, buf); } static void set_alarm(void) { alarm(NET_TIMEOUT); } static void clear_alarm(void) { signal(SIGALRM, SIG_IGN); alarm(0); } static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize) { int ret; unsigned int numq, numa; const char *query[4]; char **answer; /* this shouldn't happen */ if (!ups->upsname) { upslogx(LOG_ERR, "get_var: programming error: no UPS name set [%s]", ups->sys); return -1; } numq = 0; if (!strcmp(var, "numlogins")) { query[0] = "NUMLOGINS"; query[1] = ups->upsname; numq = 2; } if (!strcmp(var, "status")) { query[0] = "VAR"; query[1] = ups->upsname; query[2] = "ups.status"; numq = 3; } if (numq == 0) { upslogx(LOG_ERR, "get_var: programming error: var=%s", var); return -1; } upsdebugx(3, "%s: %s / %s", __func__, ups->sys, var); ret = upscli_get(&ups->conn, numq, query, &numa, &answer); if (ret < 0) { /* detect old upsd */ if (upscli_upserror(&ups->conn) == UPSCLI_ERR_UNKCOMMAND) { upslogx(LOG_ERR, "UPS [%s]: Too old to monitor", ups->sys); return -1; } /* some other error */ return -1; } if (numa < numq) { upslogx(LOG_ERR, "%s: Error: insufficient data " "(got %d args, need at least %d)", var, numa, numq); return -1; } snprintf(buf, bufsize, "%s", answer[numq]); return 0; } static void slavesync(void) { utype_t *ups; char temp[SMALLBUF]; time_t start, now; int maxlogins, logins; time(&start); for (;;) { maxlogins = 0; for (ups = firstups; ups != NULL; ups = ups->next) { /* only check login count on our master(s) */ if (!flag_isset(ups->status, ST_MASTER)) continue; set_alarm(); if (get_var(ups, "numlogins", temp, sizeof(temp)) >= 0) { logins = strtol(temp, (char **)NULL, 10); if (logins > maxlogins) maxlogins = logins; } clear_alarm(); } /* if no UPS has more than 1 login (us), then slaves are gone */ if (maxlogins <= 1) return; /* after HOSTSYNC seconds, assume slaves are stuck and bail */ time(&now); if ((now - start) > hostsync) { upslogx(LOG_INFO, "Host sync timer expired, forcing shutdown"); return; } usleep(250000); } } static void forceshutdown(void) { utype_t *ups; int isamaster = 0; upsdebugx(1, "Shutting down any UPSes in MASTER mode..."); /* set FSD on any "master" UPS entries (forced shutdown in progress) */ for (ups = firstups; ups != NULL; ups = ups->next) if (flag_isset(ups->status, ST_MASTER)) { isamaster = 1; setfsd(ups); } /* if we're not a master on anything, we should shut down now */ if (!isamaster) doshutdown(); /* must be the master now */ upsdebugx(1, "This system is a master... waiting for slave logout..."); /* wait up to HOSTSYNC seconds for slaves to logout */ slavesync(); /* time expired or all the slaves are gone, so shutdown */ doshutdown(); } static int is_ups_critical(utype_t *ups) { time_t now; /* FSD = the master is forcing a shutdown */ if (flag_isset(ups->status, ST_FSD)) return 1; /* not OB or not LB = not critical yet */ if ((!flag_isset(ups->status, ST_ONBATT)) || (!flag_isset(ups->status, ST_LOWBATT))) return 0; /* must be OB+LB now */ /* if we're a master, declare it critical so we set FSD on it */ if (flag_isset(ups->status, ST_MASTER)) return 1; /* must be a slave now */ /* FSD isn't set, so the master hasn't seen it yet */ time(&now); /* give the master up to HOSTSYNC seconds before shutting down */ if ((now - ups->lastnoncrit) > hostsync) { upslogx(LOG_WARNING, "Giving up on the master for UPS [%s]", ups->sys); return 1; } /* there's still time left */ return 0; } /* recalculate the online power value and see if things are still OK */ static void recalc(void) { utype_t *ups; int val_ol = 0; time_t now; time(&now); ups = firstups; while (ups != NULL) { /* promote dead UPSes that were last known OB to OB+LB */ if ((now - ups->lastpoll) > deadtime) if (flag_isset(ups->status, ST_ONBATT)) { upsdebugx(1, "Promoting dead UPS: %s", ups->sys); setflag(&ups->status, ST_LOWBATT); } /* note: we assume that a UPS that isn't critical must be OK * * * * this means a UPS we've never heard from is assumed OL * * whether this is really the best thing to do is undecided */ /* crit = (FSD) || (OB & LB) > HOSTSYNC seconds */ if (is_ups_critical(ups)) upsdebugx(1, "Critical UPS: %s", ups->sys); else val_ol += ups->pv; ups = ups->next; } upsdebugx(3, "Current power value: %d", val_ol); upsdebugx(3, "Minimum power value: %d", minsupplies); if (val_ol < minsupplies) forceshutdown(); } static void ups_low_batt(utype_t *ups) { if (flag_isset(ups->status, ST_LOWBATT)) { /* no change */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); return; } upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); /* must have changed from !LB to LB, so notify */ do_notify(ups, NOTIFY_LOWBATT); setflag(&ups->status, ST_LOWBATT); } static void upsreplbatt(utype_t *ups) { time_t now; time(&now); if ((now - ups->lastrbwarn) > rbwarntime) { do_notify(ups, NOTIFY_REPLBATT); ups->lastrbwarn = now; } } static void ups_fsd(utype_t *ups) { if (flag_isset(ups->status, ST_FSD)) { /* no change */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); return; } upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); /* must have changed from !FSD to FSD, so notify */ do_notify(ups, NOTIFY_FSD); setflag(&ups->status, ST_FSD); } /* cleanly close the connection to a given UPS */ static void drop_connection(utype_t *ups) { upsdebugx(2, "Dropping connection to UPS [%s]", ups->sys); ups->commstate = 0; ups->linestate = 0; clearflag(&ups->status, ST_LOGIN); clearflag(&ups->status, ST_CONNECTED); upscli_disconnect(&ups->conn); } /* change some UPS parameters during reloading */ static void redefine_ups(utype_t *ups, int pv, const char *un, const char *pw, const char *master) { ups->retain = 1; if (ups->pv != pv) { upslogx(LOG_INFO, "UPS [%s]: redefined power value to %d", ups->sys, pv); ups->pv = pv; } totalpv += ups->pv; if (ups->un) { if (strcmp(ups->un, un) != 0) { upslogx(LOG_INFO, "UPS [%s]: redefined username", ups->sys); free(ups->un); ups->un = xstrdup(un); /* * if not logged in force a reconnection since this * may have been redefined to make a login work */ if (!flag_isset(ups->status, ST_LOGIN)) { upslogx(LOG_INFO, "UPS [%s]: retrying connection", ups->sys); drop_connection(ups); } } /* if (strcmp(ups->un, un) != 0) { */ } else { /* adding a username? (going to new style MONITOR line) */ if (un) { upslogx(LOG_INFO, "UPS [%s]: defined username", ups->sys); ups->un = xstrdup(un); /* possibly force reconnection - see above */ if (!flag_isset(ups->status, ST_LOGIN)) { upslogx(LOG_INFO, "UPS [%s]: retrying connection", ups->sys); drop_connection(ups); } } /* if (un) */ } /* paranoia */ if (!ups->pw) ups->pw = xstrdup(""); /* give it a bogus, but non-NULL one */ /* obviously don't put the new password in the syslog... */ if (strcmp(ups->pw, pw) != 0) { upslogx(LOG_INFO, "UPS [%s]: redefined password", ups->sys); free(ups->pw); ups->pw = xstrdup(pw); /* possibly force reconnection - see above */ if (!flag_isset(ups->status, ST_LOGIN)) { upslogx(LOG_INFO, "UPS [%s]: retrying connection", ups->sys); drop_connection(ups); } } /* slave -> master */ if ((!strcasecmp(master, "master")) && (!flag_isset(ups->status, ST_MASTER))) { upslogx(LOG_INFO, "UPS [%s]: redefined as master", ups->sys); setflag(&ups->status, ST_MASTER); /* reset connection to ensure master mode gets checked */ drop_connection(ups); return; } /* master -> slave */ if ((!strcasecmp(master, "slave")) && (flag_isset(ups->status, ST_MASTER))) { upslogx(LOG_INFO, "UPS [%s]: redefined as slave", ups->sys); clearflag(&ups->status, ST_MASTER); return; } } static void addups(int reloading, const char *sys, const char *pvs, const char *un, const char *pw, const char *master) { int pv; utype_t *tmp, *last; /* the username is now required - no more host-based auth */ if ((!sys) || (!pvs) || (!pw) || (!master) || (!un)) { upslogx(LOG_WARNING, "Ignoring invalid MONITOR line in %s!", configfile); upslogx(LOG_WARNING, "MONITOR configuration directives require five arguments."); return; } pv = strtol(pvs, (char **) NULL, 10); if (pv < 0) { upslogx(LOG_WARNING, "UPS [%s]: ignoring invalid power value [%s]", sys, pvs); return; } last = tmp = firstups; while (tmp) { last = tmp; /* check for duplicates */ if (!strcmp(tmp->sys, sys)) { if (reloading) redefine_ups(tmp, pv, un, pw, master); else upslogx(LOG_WARNING, "Warning: ignoring duplicate" " UPS [%s]", sys); return; } tmp = tmp->next; } tmp = xmalloc(sizeof(utype_t)); tmp->sys = xstrdup(sys); tmp->pv = pv; /* build this up so the user doesn't run with bad settings */ totalpv += tmp->pv; if (un) tmp->un = xstrdup(un); else tmp->un = NULL; tmp->pw = xstrdup(pw); tmp->status = 0; tmp->retain = 1; /* ignore initial COMMOK and ONLINE by default */ tmp->commstate = -1; tmp->linestate = -1; tmp->lastpoll = 0; tmp->lastnoncrit = 0; tmp->lastrbwarn = 0; tmp->lastncwarn = 0; if (!strcasecmp(master, "master")) setflag(&tmp->status, ST_MASTER); tmp->next = NULL; if (last) last->next = tmp; else firstups = tmp; if (tmp->pv) upslogx(LOG_INFO, "UPS: %s (%s) (power value %d)", tmp->sys, flag_isset(tmp->status, ST_MASTER) ? "master" : "slave", tmp->pv); else upslogx(LOG_INFO, "UPS: %s (monitoring only)", tmp->sys); tmp->upsname = tmp->hostname = NULL; if (upscli_splitname(tmp->sys, &tmp->upsname, &tmp->hostname, &tmp->port) != 0) { upslogx(LOG_ERR, "Error: unable to split UPS name [%s]", tmp->sys); } if (!tmp->upsname) upslogx(LOG_WARNING, "Warning: UPS [%s]: no upsname set!", tmp->sys); } static void set_notifymsg(const char *name, const char *msg) { int i; for (i = 0; notifylist[i].name != NULL; i++) { if (!strcasecmp(notifylist[i].name, name)) { free(notifylist[i].msg); notifylist[i].msg = xstrdup(msg); return; } } upslogx(LOG_WARNING, "'%s' is not a valid notify event name", name); } static void set_notifyflag(const char *ntype, char *flags) { int i, pos; char *ptr, *tmp; /* find ntype */ pos = -1; for (i = 0; notifylist[i].name != NULL; i++) { if (!strcasecmp(notifylist[i].name, ntype)) { pos = i; break; } } if (pos == -1) { upslogx(LOG_WARNING, "Warning: invalid notify type [%s]", ntype); return; } ptr = flags; /* zero existing flags */ notifylist[pos].flags = 0; while (ptr) { int newflag; tmp = strchr(ptr, '+'); if (tmp) *tmp++ = '\0'; newflag = 0; if (!strcmp(ptr, "SYSLOG")) newflag = NOTIFY_SYSLOG; if (!strcmp(ptr, "WALL")) newflag = NOTIFY_WALL; if (!strcmp(ptr, "EXEC")) newflag = NOTIFY_EXEC; if (!strcmp(ptr, "IGNORE")) newflag = NOTIFY_IGNORE; if (newflag) notifylist[pos].flags |= newflag; else upslogx(LOG_WARNING, "Invalid notify flag: [%s]", ptr); ptr = tmp; } } /* in split mode, the parent doesn't hear about reloads */ static void checkmode(char *cfgentry, char *oldvalue, char *newvalue, int reloading) { /* nothing to do if in "all as root" mode */ if (use_pipe == 0) return; /* it's ok if we're not reloading yet */ if (reloading == 0) return; /* also nothing to do if it didn't change */ if ((oldvalue) && (newvalue)) { if (!strcmp(oldvalue, newvalue)) return; } /* otherwise, yell at them */ upslogx(LOG_WARNING, "Warning: %s redefined in split-process mode!", cfgentry); upslogx(LOG_WARNING, "You must restart upsmon for this change to work"); } /* returns 1 if used, 0 if not, so we can complain about bogus configs */ static int parse_conf_arg(int numargs, char **arg) { /* using up to arg[1] below */ if (numargs < 2) return 0; /* SHUTDOWNCMD */ if (!strcmp(arg[0], "SHUTDOWNCMD")) { checkmode(arg[0], shutdowncmd, arg[1], reload_flag); free(shutdowncmd); shutdowncmd = xstrdup(arg[1]); return 1; } /* POWERDOWNFLAG */ if (!strcmp(arg[0], "POWERDOWNFLAG")) { checkmode(arg[0], powerdownflag, arg[1], reload_flag); free(powerdownflag); powerdownflag = xstrdup(arg[1]); if (!reload_flag) upslogx(LOG_INFO, "Using power down flag file %s", arg[1]); return 1; } /* NOTIFYCMD */ if (!strcmp(arg[0], "NOTIFYCMD")) { free(notifycmd); notifycmd = xstrdup(arg[1]); return 1; } /* POLLFREQ */ if (!strcmp(arg[0], "POLLFREQ")) { pollfreq = atoi(arg[1]); return 1; } /* POLLFREQALERT */ if (!strcmp(arg[0], "POLLFREQALERT")) { pollfreqalert = atoi(arg[1]); return 1; } /* HOSTSYNC */ if (!strcmp(arg[0], "HOSTSYNC")) { hostsync = atoi(arg[1]); return 1; } /* DEADTIME */ if (!strcmp(arg[0], "DEADTIME")) { deadtime = atoi(arg[1]); return 1; } /* MINSUPPLIES */ if (!strcmp(arg[0], "MINSUPPLIES")) { minsupplies = atoi(arg[1]); return 1; } /* RBWARNTIME */ if (!strcmp(arg[0], "RBWARNTIME")) { rbwarntime = atoi(arg[1]); return 1; } /* NOCOMMWARNTIME */ if (!strcmp(arg[0], "NOCOMMWARNTIME")) { nocommwarntime = atoi(arg[1]); return 1; } /* FINALDELAY */ if (!strcmp(arg[0], "FINALDELAY")) { finaldelay = atoi(arg[1]); return 1; } /* RUN_AS_USER */ if (!strcmp(arg[0], "RUN_AS_USER")) { free(run_as_user); run_as_user = xstrdup(arg[1]); return 1; } /* CERTPATH */ if (!strcmp(arg[0], "CERTPATH")) { free(certpath); certpath = xstrdup(arg[1]); return 1; } /* CERTVERIFY (0|1) */ if (!strcmp(arg[0], "CERTVERIFY")) { certverify = atoi(arg[1]); return 1; } /* FORCESSL (0|1) */ if (!strcmp(arg[0], "FORCESSL")) { forcessl = atoi(arg[1]); return 1; } /* using up to arg[2] below */ if (numargs < 3) return 0; /* NOTIFYMSG */ if (!strcmp(arg[0], "NOTIFYMSG")) { set_notifymsg(arg[1], arg[2]); return 1; } /* NOTIFYFLAG */ if (!strcmp(arg[0], "NOTIFYFLAG")) { set_notifyflag(arg[1], arg[2]); return 1; } /* CERTIDENT */ if (!strcmp(arg[0], "CERTIDENT")) { free(certname); certname = xstrdup(arg[1]); free(certpasswd); certpasswd = xstrdup(arg[2]); return 1; } /* using up to arg[4] below */ if (numargs < 5) return 0; /* CERTHOST (0|1) (0|1) */ if (!strcmp(arg[0], "CERTHOST")) { upscli_add_host_cert(arg[1], arg[2], atoi(arg[3]), atoi(arg[4])); return 1; } if (!strcmp(arg[0], "MONITOR")) { /* original style: no username (only 5 args) */ if (numargs == 5) { upslogx(LOG_ERR, "Unable to use old-style MONITOR line without a username"); upslogx(LOG_ERR, "Convert it and add a username to upsd.users - see the documentation"); fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } /* ("master" | "slave") */ addups(reload_flag, arg[1], arg[2], arg[3], arg[4], arg[5]); return 1; } /* didn't parse it at all */ return 0; } /* called for fatal errors in parseconf like malloc failures */ static void upsmon_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf(%s): %s", configfile, errmsg); } static void loadconfig(void) { PCONF_CTX_t ctx; pconf_init(&ctx, upsmon_err); if (!pconf_file_begin(&ctx, configfile)) { pconf_finish(&ctx); if (reload_flag == 1) { upslog_with_errno(LOG_ERR, "Reload failed: %s", ctx.errmsg); return; } fatalx(EXIT_FAILURE, "%s", ctx.errmsg); } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", configfile, ctx.linenum, ctx.errmsg); continue; } if (ctx.numargs < 1) continue; if (!parse_conf_arg(ctx.numargs, ctx.arglist)) { unsigned int i; char errmsg[SMALLBUF]; snprintf(errmsg, sizeof(errmsg), "%s line %d: invalid directive", configfile, ctx.linenum); for (i = 0; i < ctx.numargs; i++) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); upslogx(LOG_WARNING, "%s", errmsg); } } pconf_finish(&ctx); } /* SIGPIPE handler */ static void sigpipe(int sig) { upsdebugx(1, "SIGPIPE: dazed and confused, but continuing..."); } /* SIGQUIT, SIGTERM handler */ static void set_exit_flag(int sig) { exit_flag = sig; } static void ups_free(utype_t *ups) { free(ups->sys); free(ups->upsname); free(ups->hostname); free(ups->un); free(ups->pw); free(ups); } static void upsmon_cleanup(void) { int i; utype_t *utmp, *unext; /* close all fds */ utmp = firstups; while (utmp) { unext = utmp->next; drop_connection(utmp); ups_free(utmp); utmp = unext; } free(run_as_user); free(shutdowncmd); free(notifycmd); free(powerdownflag); for (i = 0; notifylist[i].name != NULL; i++) { free(notifylist[i].msg); } upscli_cleanup(); } static void user_fsd(int sig) { upslogx(LOG_INFO, "Signal %d: User requested FSD", sig); userfsd = 1; } static void set_reload_flag(int sig) { reload_flag = 1; } /* handler for alarm when getupsvarfd times out */ static void read_timeout(int sig) { /* don't do anything here, just return */ } /* install handlers for a few signals */ static void setup_signals(void) { sigemptyset(&nut_upsmon_sigmask); sa.sa_mask = nut_upsmon_sigmask; sa.sa_flags = 0; sa.sa_handler = sigpipe; sigaction(SIGPIPE, &sa, NULL); sa.sa_handler = set_exit_flag; sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* handle timeouts */ sa.sa_handler = read_timeout; sigaction(SIGALRM, &sa, NULL); /* deal with the ones from userspace as well */ sa.sa_handler = user_fsd; sigaction(SIGCMD_FSD, &sa, NULL); sa.sa_handler = set_reload_flag; sigaction(SIGCMD_RELOAD, &sa, NULL); } /* remember the last time the ups was not critical (OB + LB) */ static void update_crittimer(utype_t *ups) { /* if !OB or !LB, then it's not critical, so log the time */ if ((!flag_isset(ups->status, ST_ONBATT)) || (!flag_isset(ups->status, ST_LOWBATT))) { time(&ups->lastnoncrit); return; } /* fallthrough: let the timer age */ } /* handle connecting to upsd, plus get SSL going too if possible */ static int try_connect(utype_t *ups) { int flags = 0, ret; upsdebugx(1, "Trying to connect to UPS [%s]", ups->sys); clearflag(&ups->status, ST_CONNECTED); /* force it if configured that way, just try it otherwise */ if (forcessl == 1) flags |= UPSCLI_CONN_REQSSL; else flags |= UPSCLI_CONN_TRYSSL; if (opt_af == AF_INET) flags |= UPSCLI_CONN_INET; if (opt_af == AF_INET6) flags |= UPSCLI_CONN_INET6; if (!certpath) { if (certverify == 1) { upslogx(LOG_ERR, "Configuration error: " "CERTVERIFY is set, but CERTPATH isn't"); upslogx(LOG_ERR, "UPS [%s]: Connection impossible, " "dropping link", ups->sys); ups_is_gone(ups); drop_connection(ups); return 0; /* failed */ } } if (certverify == 1) { flags |= UPSCLI_CONN_CERTVERIF; } ret = upscli_connect(&ups->conn, ups->hostname, ups->port, flags); if (ret < 0) { upslogx(LOG_ERR, "UPS [%s]: connect failed: %s", ups->sys, upscli_strerror(&ups->conn)); ups_is_gone(ups); return 0; } /* we're definitely connected now */ setflag(&ups->status, ST_CONNECTED); /* now try to authenticate to upsd */ ret = do_upsd_auth(ups); if (ret == 1) return 1; /* everything is happy */ /* something failed in the auth so we may not be completely logged in */ /* FUTURE: do something beyond the error msgs from do_upsd_auth? */ return 0; } /* deal with the contents of STATUS or ups.status for this ups */ static void parse_status(utype_t *ups, char *status) { char *statword, *ptr; clear_alarm(); upsdebugx(2, "%s: [%s]", __func__, status); /* empty response is the same as a dead ups */ if (!strcmp(status, "")) { ups_is_gone(ups); return; } ups_is_alive(ups); /* clear these out early if they disappear */ if (!strstr(status, "LB")) clearflag(&ups->status, ST_LOWBATT); if (!strstr(status, "FSD")) clearflag(&ups->status, ST_FSD); statword = status; /* split up the status words and parse each one separately */ while (statword != NULL) { ptr = strchr(statword, ' '); if (ptr) *ptr++ = '\0'; upsdebugx(3, "parsing: [%s]", statword); if (!strcasecmp(statword, "OL")) ups_on_line(ups); if (!strcasecmp(statword, "OB")) ups_on_batt(ups); if (!strcasecmp(statword, "LB")) ups_low_batt(ups); if (!strcasecmp(statword, "RB")) upsreplbatt(ups); /* do it last to override any possible OL */ if (!strcasecmp(statword, "FSD")) ups_fsd(ups); update_crittimer(ups); statword = ptr; } } /* see what the status of the UPS is and handle any changes */ static void pollups(utype_t *ups) { char status[SMALLBUF]; /* try a reconnect here */ if (!flag_isset(ups->status, ST_CONNECTED)) if (try_connect(ups) != 1) return; if (upscli_ssl(&ups->conn) == 1) upsdebugx(2, "%s: %s [SSL]", __func__, ups->sys); else upsdebugx(2, "%s: %s", __func__, ups->sys); set_alarm(); if (get_var(ups, "status", status, sizeof(status)) == 0) { clear_alarm(); parse_status(ups, status); return; } /* fallthrough: no communications */ clear_alarm(); /* try to make some of these a little friendlier */ switch (upscli_upserror(&ups->conn)) { case UPSCLI_ERR_UNKNOWNUPS: upslogx(LOG_ERR, "Poll UPS [%s] failed - [%s] " "does not exist on server %s", ups->sys, ups->upsname, ups->hostname); break; default: upslogx(LOG_ERR, "Poll UPS [%s] failed - %s", ups->sys, upscli_strerror(&ups->conn)); break; } /* throw COMMBAD or NOCOMM as conditions may warrant */ ups_is_gone(ups); /* if upsclient lost the connection, clean up things on our side */ if (upscli_fd(&ups->conn) == -1) { drop_connection(ups); return; } } /* see if the powerdownflag file is there and proper */ static int pdflag_status(void) { FILE *pdf; char buf[SMALLBUF]; if (!powerdownflag) return 0; /* unusable */ pdf = fopen(powerdownflag, "r"); if (pdf == NULL) return 0; /* not there */ /* if it exists, see if it has the right text in it */ if (fgets(buf, sizeof(buf), pdf) == NULL) { upslog_with_errno(LOG_ERR, "'%s' exists, but we can't read from it", powerdownflag); } fclose(pdf); /* reasoning: say upsmon.conf is world-writable (!) and some nasty * user puts something "important" as the power flag file. This * keeps upsmon from utterly trashing it when starting up or powering * down at the expense of not shutting down the UPS. * * solution: don't let mere mortals edit that configuration file. */ if (!strncmp(buf, SDMAGIC, strlen(SDMAGIC))) return 1; /* exists and looks good */ return -1; /* error: something else is in there */ } /* only remove the flag file if it's actually from us */ static void clear_pdflag(void) { int ret; ret = pdflag_status(); if (ret == -1) { upslogx(LOG_ERR, "POWERDOWNFLAG (%s) does not contain" "the upsmon magic string - disabling!", powerdownflag); powerdownflag = NULL; return; } /* it's from us, so we can remove it */ if (ret == 1) unlink(powerdownflag); } /* exit with success only if it exists and is proper */ static int check_pdflag(void) { int ret; ret = pdflag_status(); if (ret == -1) { upslogx(LOG_ERR, "POWERDOWNFLAG (%s) does not contain " "the upsmon magic string", powerdownflag); return EXIT_FAILURE; } if (ret == 0) { /* not there - this is not a shutdown event */ upslogx(LOG_ERR, "Power down flag is not set"); return EXIT_FAILURE; } if (ret != 1) { upslogx(LOG_ERR, "Programming error: pdflag_status returned %d", ret); return EXIT_FAILURE; } /* only thing left - must be time for a shutdown */ upslogx(LOG_INFO, "Power down flag is set"); return EXIT_SUCCESS; } static void help(const char *progname) { printf("Monitors UPS servers and may initiate shutdown if necessary.\n\n"); printf("usage: %s [OPTIONS]\n\n", progname); printf(" -c send command to running process\n"); printf(" commands:\n"); printf(" - fsd: shutdown all master UPSes (use with caution)\n"); printf(" - reload: reread configuration\n"); printf(" - stop: stop monitoring and exit\n"); printf(" -D raise debugging level\n"); printf(" -h display this help\n"); printf(" -K checks POWERDOWNFLAG, sets exit code to 0 if set\n"); printf(" -p always run privileged (disable privileged parent)\n"); printf(" -u run child as user (ignored when using -p)\n"); printf(" -4 IPv4 only\n"); printf(" -6 IPv6 only\n"); exit(EXIT_SUCCESS); } static void runparent(int fd) { int ret; char ch; /* handling signals is the child's job */ signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); ret = read(fd, &ch, 1); if (ret < 1) { if (errno == ENOENT) fatalx(EXIT_FAILURE, "upsmon parent: exiting (child exited)"); fatal_with_errno(EXIT_FAILURE, "upsmon parent: read"); } if (ch != 1) fatalx(EXIT_FAILURE, "upsmon parent: got bogus pipe command %c", ch); /* have to do this here - child is unprivileged */ set_pdflag(); ret = system(shutdowncmd); if (ret != 0) upslogx(LOG_ERR, "parent: Unable to call shutdown command: %s", shutdowncmd); close(fd); exit(EXIT_SUCCESS); } /* fire up the split parent/child scheme */ static void start_pipe(void) { int ret; ret = pipe(pipefd); if (ret) fatal_with_errno(EXIT_FAILURE, "pipe creation failed"); ret = fork(); if (ret < 0) fatal_with_errno(EXIT_FAILURE, "fork failed"); /* start the privileged parent */ if (ret != 0) { close(pipefd[1]); runparent(pipefd[0]); exit(EXIT_FAILURE); /* NOTREACHED */ } close(pipefd[0]); } static void delete_ups(utype_t *target) { utype_t *ptr, *last; if (!target) return; ptr = last = firstups; while (ptr) { if (ptr == target) { upslogx(LOG_NOTICE, "No longer monitoring UPS [%s]", target->sys); /* disconnect cleanly */ drop_connection(ptr); /* about to delete the first ups? */ if (ptr == last) firstups = ptr->next; else last->next = ptr->next; /* release memory */ ups_free(ptr); return; } last = ptr; ptr = ptr->next; } /* shouldn't happen */ upslogx(LOG_ERR, "delete_ups: UPS not found"); } /* see if we can open a file */ static int check_file(const char *fn) { FILE *f; f = fopen(fn, "r"); if (!f) { upslog_with_errno(LOG_ERR, "Reload failed: can't open %s", fn); return 0; /* failed */ } fclose(f); return 1; /* OK */ } static void reload_conf(void) { utype_t *tmp, *next; upslogx(LOG_INFO, "Reloading configuration"); /* sanity check */ if (!check_file(configfile)) { reload_flag = 0; return; } /* flip through ups list, clear retain value */ tmp = firstups; while (tmp) { tmp->retain = 0; tmp = tmp->next; } /* reset paranoia checker */ totalpv = 0; /* reread upsmon.conf */ loadconfig(); /* go through the utype_t struct again */ tmp = firstups; while (tmp) { next = tmp->next; /* !retain means it wasn't in the .conf this time around */ if (tmp->retain == 0) delete_ups(tmp); tmp = next; } /* see if the user just blew off a foot */ if (totalpv < minsupplies) { upslogx(LOG_CRIT, "Fatal error: total power value (%d) less " "than MINSUPPLIES (%d)", totalpv, minsupplies); fatalx(EXIT_FAILURE, "Impossible power configuation, unable to continue"); } /* finally clear the flag */ reload_flag = 0; } /* make sure the parent is still alive */ static void check_parent(void) { int ret; fd_set rfds; struct timeval tv; time_t now; static time_t lastwarn = 0; FD_ZERO(&rfds); FD_SET(pipefd[1], &rfds); tv.tv_sec = 0; tv.tv_usec = 0; ret = select(pipefd[1] + 1, &rfds, NULL, NULL, &tv); if (ret == 0) return; /* this should never happen, but we MUST KNOW if it ever does */ time(&now); /* complain every 2 minutes */ if ((now - lastwarn) < 120) return; lastwarn = now; do_notify(NULL, NOTIFY_NOPARENT); /* also do this in case the notifier isn't being effective */ upslogx(LOG_ALERT, "Parent died - shutdown impossible"); } int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); int i, cmd = 0, checking_flag = 0; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); /* if no configuration file is specified on the command line, use default */ configfile = xmalloc(SMALLBUF); snprintf(configfile, SMALLBUF, "%s/upsmon.conf", confpath()); configfile = xrealloc(configfile, strlen(configfile) + 1); run_as_user = xstrdup(RUN_AS_USER); while ((i = getopt(argc, argv, "+Dhic:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) cmd = SIGCMD_FSD; if (!strncmp(optarg, "stop", strlen(optarg))) cmd = SIGCMD_STOP; if (!strncmp(optarg, "reload", strlen(optarg))) cmd = SIGCMD_RELOAD; /* bad command name given */ if (cmd == 0) help(argv[0]); break; case 'D': nut_debug_level++; break; case 'f': free(configfile); configfile = xstrdup(optarg); break; case 'h': help(argv[0]); break; case 'K': checking_flag = 1; break; case 'p': use_pipe = 0; break; case 'u': free(run_as_user); run_as_user = xstrdup(optarg); break; case 'V': /* just show the banner */ exit(EXIT_SUCCESS); case '4': opt_af = AF_INET; break; case '6': opt_af = AF_INET6; break; default: help(argv[0]); break; } } if (cmd) { sendsignal(prog, cmd); exit(EXIT_SUCCESS); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ if (sendsignal(prog, 0) == 0) { printf("Fatal error: A previous upsmon instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); } argc -= optind; argv += optind; open_syslog(prog); loadconfig(); if (checking_flag) exit(check_pdflag()); if (shutdowncmd == NULL) printf("Warning: no shutdown command defined!\n"); /* we may need to get rid of a flag from a previous shutdown */ if (powerdownflag != NULL) clear_pdflag(); /* FIXME (else): POWERDOWNFLAG is not defined!! * => fallback to a default value */ if (totalpv < minsupplies) { printf("\nFatal error: insufficient power configured!\n\n"); printf("Sum of power values........: %d\n", totalpv); printf("Minimum value (MINSUPPLIES): %d\n", minsupplies); printf("\nEdit your upsmon.conf and change the values.\n"); exit(EXIT_FAILURE); } if (nut_debug_level < 1) { background(); } else { upsdebugx(1, "debug level is '%d'", nut_debug_level); } /* only do the pipe stuff if the user hasn't disabled it */ if (use_pipe) { struct passwd *new_uid = get_user_pwent(run_as_user); /* === root parent and unprivileged child split here === */ start_pipe(); /* write the pid file now, as we will soon lose root */ writepid(prog); become_user(new_uid); } else { upslogx(LOG_INFO, "Warning: running as one big root process by request (upsmon -p)"); writepid(prog); } if (upscli_init(certverify, certpath, certname, certpasswd) < 0) { exit(EXIT_FAILURE); } /* prep our signal handlers */ setup_signals(); /* reopen the log for the child process */ closelog(); open_syslog(prog); while (exit_flag == 0) { utype_t *ups; /* check flags from signal handlers */ if (userfsd) forceshutdown(); if (reload_flag) reload_conf(); for (ups = firstups; ups != NULL; ups = ups->next) pollups(ups); recalc(); /* make sure the parent hasn't died */ if (use_pipe) check_parent(); /* reap children that have exited */ waitpid(-1, NULL, WNOHANG); sleep(sleepval); } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); upsmon_cleanup(); exit(EXIT_SUCCESS); } nut-2.7.4/clients/upssched.c0000644000175000017500000004443312640473702012713 00000000000000/* upssched.c - upsmon's scheduling helper for offset timers Copyright (C) 2000 Russell Kroll 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 */ /* design notes for the curious: * * 1. we get called with a upsname and notifytype from upsmon * 2. the config file is searched for an AT condition that matches * 3. the conditions on any matching lines are parsed * * starting a timer: the timer is added to the daemon's timer queue * cancelling a timer: the timer is removed from that queue * execute a command: the command is passed straight to the cmdscript * * if the daemon is not already running and is required (to start a timer) * it will be started automatically * * when the time arrives, the command associated with a timer will be * executed by the daemon (via the cmdscript) * * timers can be cancelled at any time before they trigger * * the daemon will shut down automatically when no more timers are active * */ #include "common.h" #include #include #include #include #include #include "upssched.h" #include "timehead.h" typedef struct ttype_s { char *name; time_t etime; struct ttype_s *next; } ttype_t; ttype_t *thead = NULL; static conn_t *connhead = NULL; char *cmdscript = NULL, *pipefn = NULL, *lockfn = NULL; int verbose = 0; /* use for debugging */ /* ups name and notify type (string) as received from upsmon */ const char *upsname, *notify_type; #define PARENT_STARTED -2 #define PARENT_UNNECESSARY -3 #define MAX_TRIES 30 #define EMPTY_WAIT 15 /* min passes with no timers to exit */ #define US_LISTEN_BACKLOG 16 #define US_SOCK_BUF_LEN 256 #define US_MAX_READ 128 /* --- server functions --- */ static void exec_cmd(const char *cmd) { int err; char buf[LARGEBUF]; snprintf(buf, sizeof(buf), "%s %s", cmdscript, cmd); err = system(buf); if (WIFEXITED(err)) { if (WEXITSTATUS(err)) { upslogx(LOG_INFO, "exec_cmd(%s) returned %d", buf, WEXITSTATUS(err)); } } else { if (WIFSIGNALED(err)) { upslogx(LOG_WARNING, "exec_cmd(%s) terminated with signal %d", buf, WTERMSIG(err)); } else { upslogx(LOG_ERR, "Execute command failure: %s", buf); } } return; } static void removetimer(ttype_t *tfind) { ttype_t *tmp, *last; last = NULL; tmp = thead; while (tmp) { if (tmp == tfind) { /* found it */ if (last == NULL) /* deleting first */ thead = tmp->next; else last->next = tmp->next; free(tmp->name); free(tmp); return; } last = tmp; tmp = tmp->next; } /* this one should never happen */ upslogx(LOG_ERR, "removetimer: failed to locate target at %p", (void *)tfind); } static void checktimers(void) { ttype_t *tmp, *tmpnext; time_t now; static int emptyctr = 0; /* if the queue is empty we might be ready to exit */ if (!thead) { emptyctr++; /* wait a little while in case someone wants us again */ if (emptyctr < EMPTY_WAIT) return; if (verbose) upslogx(LOG_INFO, "Timer queue empty, exiting"); #ifdef UPSSCHED_RACE_TEST upslogx(LOG_INFO, "triggering race: sleeping 15 sec before exit"); sleep(15); #endif unlink(pipefn); exit(EXIT_SUCCESS); } emptyctr = 0; /* flip through LL, look for activity */ tmp = thead; time(&now); while (tmp) { tmpnext = tmp->next; if (now >= tmp->etime) { if (verbose) upslogx(LOG_INFO, "Event: %s ", tmp->name); exec_cmd(tmp->name); /* delete from queue */ removetimer(tmp); } tmp = tmpnext; } } static void start_timer(const char *name, const char *ofsstr) { time_t now; int ofs; ttype_t *tmp, *last; /* get the time */ time(&now); /* add an event for +