pax_global_header00006660000000000000000000000064150170204060014505gustar00rootroot0000000000000052 comment=fda92d915119950864f2ab2bd9ea709884d5252b lcagent/000077500000000000000000000000001501702040600124665ustar00rootroot00000000000000lcagent/.gitignore000066400000000000000000000003311501702040600144530ustar00rootroot00000000000000lcagent *.o *.a *.so* *.swp cov-int/ *.tgz net-setup *.log autom4te.cache/ Makefile src/Makefile test/Makefile man/Makefile libs/Makefile config.status configure~ src/config.h y.tab.[ch] lex.yy.[ch] lex.h test/*.test lcagent/CHANGELOG.md000066400000000000000000000021531501702040600143000ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.1.0] - 2025-06-01 This release updates the server to wait for at least one network interface to become available and for IPv6 DAD to finished before running onstart events, or commencing operation. Other bugfixes and additions are listed in the CHANGELOG. ### Added - lcagent key show - systemd unit file - add some DEBUG for channel tokens ### Changed - check IPv6 DAD is finished before starting server - wait for an interface on startup in server mode - only log timestamp on terminal or to logfile - require network for test 0028 ### Fixed - fix uninstall target for man pages - configure: require flex - configure: require librecast headers - configure: use noyywrap in flex check - man page: fix spelling - fixed parser race affecting tests 0018,0021,0026,0030 ## [0.0.1] - 2025-05-19 ### Fixed - various bug fixes ## [0.0.0] - 2025-05-18 - Initial release. lcagent/CODE_OF_CONDUCT.md000066400000000000000000000064251501702040600152740ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at coc@librecast.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq lcagent/CONTRIBUTING.md000066400000000000000000000024561501702040600147260ustar00rootroot00000000000000# CONTRIBUTING Contributions are very welcome, both large and small. Correct patches (see Patch Requirements) will generally be accepted. This is a small project, so I don't want to get bogged down in too much formality, but in general I'm striving to follow Pieter Hintjens' Collective Code Construction Contract (C4) as used by the ZeroMQ project: https://rfc.zeromq.org/spec:42/C4/ ## Patch Requirements Maintainers and Contributors MUST have a Platform account and SHOULD use their real names or a well-known alias. A patch SHOULD be a minimal and accurate answer to exactly one identified and agreed problem. A patch MUST adhere to the code style guidelines of the project (see below) A patch SHALL NOT include non-trivial code from other projects unless the Contributor is the original author of that code. A patch MUST compile cleanly and pass project self-tests on at least the principal target platform. A patch commit message SHOULD consist of a single short (less than 50 character) line summarizing the change, optionally followed by a blank line and then a more thorough description. A "Correct Patch" is one that satisfies the above requirements. ## Coding Style This project uses the Linux kernel coding style: See https://www.kernel.org/doc/html/latest/process/coding-style.html lcagent/COPYING000066400000000000000000000000661501702040600135230ustar00rootroot00000000000000SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only lcagent/LICENSE.GPL-2.0-only000066400000000000000000000444511501702040600154000ustar00rootroot00000000000000Valid-License-Identifier: GPL-2.0 Valid-License-Identifier: GPL-2.0-only Valid-License-Identifier: GPL-2.0+ Valid-License-Identifier: GPL-2.0-or-later SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX tag/value pairs into a comment according to the placement guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 or SPDX-License-Identifier: GPL-2.0-only For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ or SPDX-License-Identifier: GPL-2.0-or-later License-Text: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. lcagent/LICENSE.GPL-3.0-only000066400000000000000000001045131501702040600153750ustar00rootroot00000000000000 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 . lcagent/Makefile.in000066400000000000000000000052651501702040600145430ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only # Copyright (c) 2024 Brett Sheffield PREFIX ?= @prefix@ export PREFIX PROGRAM := @PACKAGE_NAME@ VERSION := @PACKAGE_VERSION@ COVERITY_DIR := cov-int COVERITY_TGZ := $(PROGRAM)-coverity-$(VERSION).tgz COVERITY_PATH=$(PATH):../../coverity/bin/ OSNAME := $(shell uname -s) export OSNAME export SOURCEDIR=$(shell pwd) ifeq ($(origin CC),default) CC = @CC@ endif export CC CFLAGS ?= @CFLAGS@ ifdef DEBUG CFLAGS += -Og -g -Wall -Wextra -pedantic endif export CFLAGS CPPFLAGS += @CPPFLAGS@ export CPPFLAGS LDFLAGS ?= @LDFLAGS@ export LDFLAGS all: src install: all $(MAKE) -C src $@ $(MAKE) -C man $@ uninstall: $(MAKE) -C src $@ $(MAKE) -C man $@ .PHONY: clean realclean src test testloop sparse doc netcheck net-teardown testshell update newtest src: $(MAKE) -C $@ doc: $(MAKE) -C doc $@ fixme: grep -n FIXME src/*.{c,h} test/*.{c,h} todo: grep -n TODO src/*.{c,h} test/*.{c,h} clean: $(MAKE) -C src $@ $(MAKE) -C test $@ $(MAKE) -C man $@ realclean: clean $(MAKE) -C src $@ $(MAKE) -C test $@ $(MAKE) -C man $@ $(RM) -r ./$(COVERITY_DIR) $(RM) $(COVERITY_TGZ) $(RM) Makefile $(RM) config.{log,status} $(RM) include/librecast/crypto.h sparse: clean CC=cgcc $(MAKE) src clang: clean CC=clang $(MAKE) src clangtest: clean CC=clang $(MAKE) test gcc: clean all netcheck: $(MAKE) -C test $@ test memcheck: src netcheck $(MAKE) -C test $@/all newtest: $(MAKE) -C test $@ LOOPBANNER ?= echo testloop: num_ok=0; \ while $(MAKE) $(subst testloop,test,$@); do \ num_ok=$$(( $$num_ok + 1 )); \ make -C test logclean; \ echo; \ echo; \ $(LOOPBANNER) "OK $$num_ok"; \ echo; \ sleep 1; \ done; \ $(LOOPBANNER) "OK $$num_ok" testloop/%: num_ok=0; \ while $(MAKE) $(subst testloop,test,$@); do \ num_ok=$$(( $$num_ok + 1 )); \ make -C test logclean; \ echo; \ echo; \ $(LOOPBANNER) "OK $$num_ok"; \ echo; \ done; \ $(LOOPBANNER) "OK $$num_ok" test/% memcheck/%: .FORCE src $(MAKE) -C test $@ .FORCE: # always recompile coverity: clean PATH=$(COVERITY_PATH) cov-build --dir cov-int $(MAKE) src tar czvf $(COVERITY_TGZ) $(COVERITY_DIR) net-setup: ip link add veth0 type veth peer name veth1 ip netns add vnet0 ip netns add vnet1 ip link set veth0 netns vnet0 ip link set veth1 netns vnet1 ip -n vnet0 link set veth0 up ip -n vnet1 link set veth1 up ip netns show touch net-setup net-teardown: ip -n vnet0 link set veth0 down ip -n vnet1 link set veth1 down ip -n vnet1 link set veth1 netns vnet0 ip -n vnet0 link del veth0 type veth peer name veth1 ip netns del vnet0 ip netns del vnet1 ip netns show rm -f net-setup testshell: @echo "sudo ip netns exec vnet0 sudo -u `id -un` /bin/bash" lcagent/README.md000066400000000000000000000263741501702040600137610ustar00rootroot00000000000000# lcagent - Librecast Multicast Agent ![Librecast Hex Logo](https://nlnet.nl/project/Librecast-OverlayMulticast/librecast.hex.svg)

Coverity Scan Build Status

**lcagent** is the [Librecast](https://librecast.net) multicast agent. In server mode, lcagent can be configured to listen on Librecast Channels (multicast groups) and execute programs in response to packet data received on those Channels. Packets must be accompanied by an authorized token and signed by the matching key or they will be silently dropped. Data is encoded with RaptorQ using Librecast’s [liblcrq](https://librecast.net/lcrq.html) library to provide forward error correction in the event of packet loss. Any data received in addition to the required token will be piped to stdin of the configured commands after they are executed. Multiple commands can be executed sequentially. lcagent can be used to send and receive data over multicast and to pipe data between programs on one computer and as many receivers as the multicast network can support simultaneously. lcagent can be used for running CI builds on multiple build servers simultaneously, triggered by multicasting the patch to all servers. Can also be useful for monitoring and configuration management etc. When an agent receives a command, it checks the supplied capability token against its known trusted authorities before executing the command. ### CI Example Each time a commit is made to a git repository, a patch can be generated and sent to lcagent running on different CI test machines. The patch will be applied and the tests run. This is a simple example, but very complex CI systems can be set up and controlled in this way. Example `.lcagentrc`: ``` # this channel will apply a git patch sent to it which was created # with git-format-patch(1). # If the patch applies without error, ‘gmake test‘ is run channel "lcagent patchtest" directory /home/lcagent/src/lcagent command "git am --committer-date-is-author-date" command "gmake test" ``` An example post-commit git hook which can be used to generate a patch suitable for multicasting to the above Channel: ``` #!/bin/bash -eu # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only # Copyright (c) 2025 Brett Sheffield FILE=`git format-patch HEAD~ --base HEAD~` lcagent send "lcagent patchtest" - < $FILE exit 0 ``` ## Options \-d | \-\-debug : Enable debug mode to print additional debugging information. \-\-bpslimit rate[KMGT] : When sending, this option limits the sending speed to rate. Rate is a number in bits per second (bps) and can be followed by a capital letter representing SI units. eg. 100M limits the sending rate to 100 megabits per second (Mbps). \-\-expires seconds : For the sign command, this sets the validity of the token in seconds. \-f, \-\-follow : In recv mode, do not stop after first data object received. Keep blocking and receiving data until interrupted. eg. ˆC is pressed or SIGINT is received by the process. \-l, \-\-loopback : Enable multicast loopback when sending. This will allow packets to be received by the same host that sent them. By default multicast packets are not received on the sending host. \-\-overhead packets : When sending, this option sets the number of additional repair packets sent to aid with recovery of lost packets with RaptorQ encoding. Default: 5 \-v, \-\-verbose : Print more verbose output. ## Commands ### exec `lcagent exec [options] channel [data] [-]` The **exec** command executes the commands configured for *channel* locally, without requiring data to be received over the network. This is a good way to test the configured commands that will be executed before making them accessible on the network. The *nojoin* configuration directive can be used to ensure that a channel is not network-executable, by preventing lcagent from joining the associated Librecast channel. See lcagentrc(1). ### help `lcagent help` Print some (possibly) helpful instructions for lcagent. ### key `lcagent key [options] add|del key` Add or delete a key from the `authorized_keys` file. Packets that do not include a token signed by a key in the `authorized_keys` file are silently dropped. ### recv `lcagent recv [options] channel` Block and wait for data to be received on channel, where channel is a string that will be hashed to create a Librecast channel. Data received will be written to stdout. If expecting binary data, be sure to redirect stdout somewhere appropriate. By default, once EOF is received from the sender, the program will exit. This behaviour can be changed by specifying the *\-\-follow* option. ### reload `lcagent reload [options]` Reload the lcagent configuration. Sends a SIGHUP to the daemon process. Has no effect unless lcagent has been started using the start command. Service files should simply send SIGHUP to the process. ### send `lcagent send [options] channel [payload] [-]` Send data to channel, where channel is a string that will be hashed to create a Librecast channel. If payload is specified, this string will be sent as data to the channel. If no payload is specified, an NULL (empty) payload of zero bytes will be sent to the channel. A hyphen (-) at the end of the command indicates that the payload should instead be read from stdin. ### server `lcagent [server] [options]` Starting lcagent with no command starts it in server mode. The `.lcagentrc` file will be parsed and lcagent will join the configured channels and wait for data, executing the configured commands when packets are received on one of the configured channels and writing any payload data to `stdin` of the command when executing it. See lcagentrc(1) for details of the runtime control (configuration) file. ### sign `lcagent sign [options] [--expires seconds] bearer_key channel` Create and sign a token for the given *bearer\_key* and *channel* and write to disk. The resulting token will be written to a file in the user’s state directory, and the file pathname will be printed. If the token is copied to the state directory of the bearer it will be used when sending to channel. A signed token means that packets sent by the bearer will be accepted by any receivers on chan‐ nel which have the signer’s key added to their `authorized_keys` file. Tokens enable delegation of authority, which is important for scaling. Using pre-signed tokens allows, say, a host to issue tokens for virtual machines it creates without the need to add individual keys for every machine to all receivers on the network. In the absence of a signed token, packets will be sent with a self-signed token, requiring that all receivers have the sender’s key installed in their `authorized_keys` file. If the expires option is supplied, this will be the validity in seconds of the generated token. A value of zero (0) means that the token will never expire. By default tokens do not expire. ### start `lcagent start [options]` Start lcagent in server mode and fork it as a daemon. Not intended for use with daemon supervising programs. If you are running lcagent as a service, just start lcagent with no command and let the init system or daemon supervisor handle forking and pidfiles. If you are NOT running lcagent as a service and want it to daemonize, use this command. ### stop `lcagent stop [options]` Stop the lcagent daemon. Has no effect unless lcagent has been started using the start com‐ mand. Sends SIGINT to the running daemon process. ### version `lcagent version` Print the version of lcagent and exit. ### whoami `lcagent whoami [options]` Print the active key to stdout and exit. This key can be added to the `authorized_keys` file of another host with: `lcagent key add key` ## Configuration See [lcagentrc](doc/lcagentrc.5.md) for details of the rcfile format. ## Mailing List Join the low-traffic Librecast announce mailing list to stay up to date on Librecast related news: https://lists.sr.ht/~librecast ## Supported Systems lcagent and its Librecast dependencies are expected to build and run on: - GNU/Linux (any distribution) - FreeBSD - NetBSD - OpenBSD If you encounter problems building or running on these systems, please send a bug report. See *Bugs*. If you get lcagent running on something else, please drop us an email and let us know. We'd love to know what you are using lcagent for and any feedback you have. ## Dependencies **lcagent** requires liblibrecast (>= 0.11) to be installed and configured with liblcrq (>= 0.2.4) and libsodium. You will also need a C toolchain (lcagent is tested with gcc and clang). gnumake is required on \*BSD systems (sorry). Additional build dependencies: - flex - bison - gmake ## Obtaining the source lcagent (and the Librecast dependencies liblcrq and liblibrecast) can be downloaded from Codeberg or SourceHut: - https://codeberg.org/librecast/lcagent - https://git.sr.ht/~librecast/lcagent ## Installation ``` git clone https://git.sr.ht/~librecast/lcagent cd lcagent ./configure make make test make install ``` ## Files ˜/.lcagentrc : This is the runtime control file for lcagent. This is where global settings, channels and commands are defined that affect the runtime operation of the program. See lcagentrc(5) for more information. ˜/.local/state/lcagent/authorized\_keys : This file contains the keys which are authorized to issue tokens which will be accepted by the program when receiving data. One key is stored per line. It can be managed with the lcagent key command. ## Security Authenticity of data received is established by checking that each packet received contains a token signed by an authority with their public key installed in the `authorized_keys` file. If the token is valid, the packet must be signed by the bearer key contained in the token. Cryptographic signatures use the Ed25519ph algorithm provided by the libsodium library. Data can be encrypted using the seed configuration directive in `.lcagentrc`. See [lcagentrc(5)](doc/lcagentrc.5.md). The symmetric encryption key is generated from seed using BLAKE2b. Symmetric encryption uses the XSalsa20 stream cipher from libsodium using a Poly1305 MAC for authentication and random nonce. If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error. ## License GPLv2 or (at your option) GPLv3 ## Bugs If you find one, email bugs@librecast.net or raise an issue in our online bug tracker at: https://bugs.librecast.net/lcagent including as much relevant information as you can. ## See Also - [lcagent(1)](doc/lcagent.1.md) - [lcagentrc(5)](doc/lcagentrc.5.md) ## Funding This project was funded through [NGI Zero Core](https://nlnet.nl/core) and [NGI Assure](https://nlnet.nl/assure), funds established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/Librecast-OverlayMulticast). [NLnet foundation logo](https://nlnet.nl) [NGI Zero Core Logo](https://nlnet.nl/core/) lcagent/configure000077500000000000000000005267631501702040600144200ustar00rootroot00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.72 for lcagent 0.1.0. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # # Copyright (c) 2024-2025 Brett Sheffield # See COPYING for license details. # ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case e in #( e) case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else case e in #( e) exitcode=1; echo positional parameters were not saved. ;; esac fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else case e in #( e) as_have_required=no ;; esac fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi ;; esac fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed 'exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and bugs@librecast.net $0: about your system, including any error possibly output $0: before this message. Then install a modern shell, or $0: manually run the script under such a shell if you do $0: have one." fi exit 1 fi ;; esac fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' t clear :clear s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='lcagent' PACKAGE_TARNAME='lcagent' PACKAGE_VERSION='0.1.0' PACKAGE_STRING='lcagent 0.1.0' PACKAGE_BUGREPORT='bugs@librecast.net' PACKAGE_URL='' ac_unique_file="src/" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS LIBOBJS ROUTE IPROUTE iproute IFCONFIG ifconfig FALSE TESTRUNNER testrunner YFLAGS YACC LEXLIB LEX_OUTPUT_ROOT LEX LN_S INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS YACC YFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: '$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: '$ac_option' Try '$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: '$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: '$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF 'configure' configures lcagent 0.1.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print 'checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for '--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or '..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, 'make install' will install all the files in '$ac_default_prefix/bin', '$ac_default_prefix/lib' etc. You can specify an installation prefix other than '$ac_default_prefix' using '--prefix', for instance '--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/lcagent] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of lcagent 0.1.0:";; esac cat <<\_ACEOF Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory YACC The 'Yet Another Compiler Compiler' implementation to use. Defaults to the first program found out of: 'bison -y', 'byacc', 'yacc'. YFLAGS The list of arguments that will be passed by default to $YACC. This script will default YFLAGS to the empty string to avoid a default value of '-d' given by some make applications. Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF lcagent configure 0.1.0 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Copyright (c) 2024-2025 Brett Sheffield See COPYING for license details. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 ;; esac fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) eval "$3=yes" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_find_intX_t LINENO BITS VAR # ----------------------------------- # Finds a signed integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_intX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 printf %s "checking for int$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in int$2_t 'int' 'long int' \ 'long long int' 'short int' 'signed char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main (void) { static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main (void) { static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) case $ac_type in #( int$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else case e in #( e) break ;; esac fi done ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_intX_t # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 printf %s "checking for uint$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else case e in #( e) break ;; esac fi done ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (void); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (void); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else case e in #( e) eval "$3=no" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ;; esac fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by lcagent $as_me 0.1.0, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` 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 || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See 'config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (char **p, int i) { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* C89 style stringification. */ #define noexpand_stringify(a) #a const char *stringified = noexpand_stringify(arbitrary+token=sequence); /* C89 style token pasting. Exercises some of the corner cases that e.g. old MSVC gets wrong, but not very hard. */ #define noexpand_concat(a,b) a##b #define expand_concat(a,b) noexpand_concat(a,b) extern int vA; extern int vbee; #define aye A #define bee B int *pvA = &expand_concat(v,aye); int *pvbee = &noexpand_concat(v,bee); /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' /* Does the compiler advertise C99 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif // See if C++-style comments work. #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); extern void free (void *); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Work around memory leak warnings. free (ia); // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' /* Does the compiler advertise C11 conformance? */ #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="install-sh" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else case e in #( e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;; esac fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: '$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers src/config.h" # Checks for programs. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" fi fi fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. set dummy ${ac_tool_prefix}clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "clang", so it can be a program name with args. set dummy clang; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="clang" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See 'config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'. # So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an '-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else case e in #( e) ac_file='' ;; esac fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See 'config.log' for more details" "$LINENO" 5; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both 'conftest.exe' and 'conftest' are 'present' (well, observable) # catch 'conftest.exe'. For instance with Cygwin, 'ls conftest' will # work properly (i.e., refer to 'conftest.exe'), while it won't with # 'rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See 'config.log' for more details" "$LINENO" 5; } ;; esac fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); if (!f) return 1; return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use '--host'. See 'config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext \ conftest.o conftest.obj conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else case e in #( e) printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See 'config.log' for more details" "$LINENO" 5; } ;; esac fi rm -f conftest.$ac_cv_objext conftest.$ac_ext ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else case e in #( e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else case e in #( e) ac_compiler_gnu=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else case e in #( e) CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else case e in #( e) ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 ;; esac fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ;; esac fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else case e in #( e) if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" ;; esac fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 ;; esac fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else case e in #( e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir ;; esac fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi for ac_prog in flex lex do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_LEX+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$LEX"; then ac_cv_prog_LEX="$LEX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_LEX="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi LEX=$ac_cv_prog_LEX if test -n "$LEX"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LEX" >&5 printf "%s\n" "$LEX" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$LEX" && break done test -n "$LEX" || LEX=":" if test "x$LEX" != "x:"; then cat >conftest.l <<_ACEOF %{ #ifdef __cplusplus extern "C" #endif int yywrap(void); %} %% a { ECHO; } b { REJECT; } c { yymore (); } d { yyless (1); } e { /* IRIX 6.5 flex 2.5.4 underquotes its yyless argument. */ #ifdef __cplusplus yyless ((yyinput () != 0)); #else yyless ((input () != 0)); #endif } f { unput (yytext[0]); } . { BEGIN INITIAL; } %% #ifdef YYTEXT_POINTER extern char *yytext; #endif int yywrap (void) { return 1; } int main (void) { return ! yylex (); } _ACEOF { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for lex output file root" >&5 printf %s "checking for lex output file root... " >&6; } if test ${ac_cv_prog_lex_root+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_prog_lex_root=unknown { { ac_try="$LEX conftest.l" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$LEX conftest.l") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && if test -f lex.yy.c; then ac_cv_prog_lex_root=lex.yy elif test -f lexyy.c; then ac_cv_prog_lex_root=lexyy fi ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_root" >&5 printf "%s\n" "$ac_cv_prog_lex_root" >&6; } if test "$ac_cv_prog_lex_root" = unknown then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cannot find output from $LEX; giving up on $LEX" >&5 printf "%s\n" "$as_me: WARNING: cannot find output from $LEX; giving up on $LEX" >&2;} LEX=: LEXLIB= fi LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root if test ${LEXLIB+y} then : else case e in #( e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for lex library" >&5 printf %s "checking for lex library... " >&6; } if test ${ac_cv_lib_lex+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_save_LIBS="$LIBS" ac_found=false for ac_cv_lib_lex in 'none needed' -lfl -ll 'not found'; do case $ac_cv_lib_lex in #( 'none needed') : ;; #( 'not found') : break ;; #( *) : LIBS="$ac_cv_lib_lex $ac_save_LIBS" ;; #( *) : ;; esac cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ `cat $LEX_OUTPUT_ROOT.c` _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_found=: fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if $ac_found; then break fi done LIBS="$ac_save_LIBS" ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lex" >&5 printf "%s\n" "$ac_cv_lib_lex" >&6; } if test "$ac_cv_lib_lex" = 'not found' then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: required lex library not found; giving up on $LEX" >&5 printf "%s\n" "$as_me: WARNING: required lex library not found; giving up on $LEX" >&2;} LEX=: LEXLIB= elif test "$ac_cv_lib_lex" = 'none needed' then : LEXLIB='' else case e in #( e) LEXLIB=$ac_cv_lib_lex ;; esac fi ;; esac fi if test "$LEX" != : then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether yytext is a pointer" >&5 printf %s "checking whether yytext is a pointer... " >&6; } if test ${ac_cv_prog_lex_yytext_pointer+y} then : printf %s "(cached) " >&6 else case e in #( e) # POSIX says lex can declare yytext either as a pointer or an array; the # default is implementation-dependent. Figure out which it is, since # not all implementations provide the %pointer and %array declarations. ac_cv_prog_lex_yytext_pointer=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define YYTEXT_POINTER 1 `cat $LEX_OUTPUT_ROOT.c` _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_lex_yytext_pointer=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_yytext_pointer" >&5 printf "%s\n" "$ac_cv_prog_lex_yytext_pointer" >&6; } if test $ac_cv_prog_lex_yytext_pointer = yes; then printf "%s\n" "#define YYTEXT_POINTER 1" >>confdefs.h fi fi rm -f conftest.l $LEX_OUTPUT_ROOT.c fi if test "x$LEX" != xflex then : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "flex is required See 'config.log' for more details" "$LINENO" 5; } fi for ac_prog in 'bison -y' byacc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_YACC+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$YACC"; then ac_cv_prog_YACC="$YACC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_YACC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi YACC=$ac_cv_prog_YACC if test -n "$YACC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5 printf "%s\n" "$YACC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$YACC" && break done test -n "$YACC" || YACC="yacc" # Extract the first word of "lctest", so it can be a program name with args. set dummy lctest; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_testrunner+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$testrunner"; then ac_cv_prog_testrunner="$testrunner" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_testrunner="lctest" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_testrunner" && ac_cv_prog_testrunner="maketest" fi ;; esac fi testrunner=$ac_cv_prog_testrunner if test -n "$testrunner"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $testrunner" >&5 printf "%s\n" "$testrunner" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi TESTRUNNER=$testrunner for ac_prog in false do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_FALSE+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$FALSE"; then ac_cv_prog_FALSE="$FALSE" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_FALSE="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi FALSE=$ac_cv_prog_FALSE if test -n "$FALSE"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FALSE" >&5 printf "%s\n" "$FALSE" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$FALSE" && break done test -n "$FALSE" || FALSE="/usr/bin/env false" # Extract the first word of "ifconfig", so it can be a program name with args. set dummy ifconfig; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ifconfig+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ifconfig"; then ac_cv_prog_ifconfig="$ifconfig" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ifconfig="ifconfig" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ifconfig=$ac_cv_prog_ifconfig if test -n "$ifconfig"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ifconfig" >&5 printf "%s\n" "$ifconfig" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi IFCONFIG=$ifconfig # Extract the first word of "ip", so it can be a program name with args. set dummy ip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_iproute+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$iproute"; then ac_cv_prog_iproute="$iproute" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_iproute="iproute" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi iproute=$ac_cv_prog_iproute if test -n "$iproute"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $iproute" >&5 printf "%s\n" "$iproute" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi IPROUTE=$iproute # Extract the first word of "route", so it can be a program name with args. set dummy route; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ROUTE+y} then : printf %s "(cached) " >&6 else case e in #( e) if test -n "$ROUTE"; then ac_cv_prog_ROUTE="$ROUTE" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ROUTE="route" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi ;; esac fi ROUTE=$ac_cv_prog_ROUTE if test -n "$ROUTE"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ROUTE" >&5 printf "%s\n" "$ROUTE" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Checks for libraries. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for rq_init in -llcrq" >&5 printf %s "checking for rq_init in -llcrq... " >&6; } if test ${ac_cv_lib_lcrq_rq_init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-llcrq $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char rq_init (void); int main (void) { return rq_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_lcrq_rq_init=yes else case e in #( e) ac_cv_lib_lcrq_rq_init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lcrq_rq_init" >&5 printf "%s\n" "$ac_cv_lib_lcrq_rq_init" >&6; } if test "x$ac_cv_lib_lcrq_rq_init" = xyes then : printf "%s\n" "#define HAVE_LIBLCRQ 1" >>confdefs.h LIBS="-llcrq $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for lc_channel_rq_overhead in -llibrecast" >&5 printf %s "checking for lc_channel_rq_overhead in -llibrecast... " >&6; } if test ${ac_cv_lib_librecast_lc_channel_rq_overhead+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-llibrecast $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char lc_channel_rq_overhead (void); int main (void) { return lc_channel_rq_overhead (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_librecast_lc_channel_rq_overhead=yes else case e in #( e) ac_cv_lib_librecast_lc_channel_rq_overhead=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_librecast_lc_channel_rq_overhead" >&5 printf "%s\n" "$ac_cv_lib_librecast_lc_channel_rq_overhead" >&6; } if test "x$ac_cv_lib_librecast_lc_channel_rq_overhead" = xyes then : printf "%s\n" "#define HAVE_LIBLIBRECAST 1" >>confdefs.h LIBS="-llibrecast $LIBS" else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "liblibrecast >= 0.11 is required See 'config.log' for more details" "$LINENO" 5; } ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sodium_init in -lsodium" >&5 printf %s "checking for sodium_init in -lsodium... " >&6; } if test ${ac_cv_lib_sodium_sodium_init+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_check_lib_save_LIBS=$LIBS LIBS="-lsodium $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char sodium_init (void); int main (void) { return sodium_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sodium_sodium_init=yes else case e in #( e) ac_cv_lib_sodium_sodium_init=no ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sodium_sodium_init" >&5 printf "%s\n" "$ac_cv_lib_sodium_sodium_init" >&6; } if test "x$ac_cv_lib_sodium_sodium_init" = xyes then : printf "%s\n" "#define HAVE_LIBSODIUM 1" >>confdefs.h LIBS="-lsodium $LIBS" else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "libsodium is required See 'config.log' for more details" "$LINENO" 5; } ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5 printf %s "checking for library containing pthread_create... " >&6; } if test ${ac_cv_search_pthread_create+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char pthread_create (void); int main (void) { return pthread_create (); ; return 0; } _ACEOF for ac_lib in '' pthread do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_pthread_create=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_pthread_create+y} then : break fi done if test ${ac_cv_search_pthread_create+y} then : else case e in #( e) ac_cv_search_pthread_create=no ;; esac fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5 printf "%s\n" "$ac_cv_search_pthread_create" >&6; } ac_res=$ac_cv_search_pthread_create if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing arc4random_uniform" >&5 printf %s "checking for library containing arc4random_uniform... " >&6; } if test ${ac_cv_search_arc4random_uniform+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. The 'extern "C"' is for builds by C++ compilers; although this is not generally supported in C code supporting it here has little cost and some practical benefit (sr 110532). */ #ifdef __cplusplus extern "C" #endif char arc4random_uniform (void); int main (void) { return arc4random_uniform (); ; return 0; } _ACEOF for ac_lib in '' bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_arc4random_uniform=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_arc4random_uniform+y} then : break fi done if test ${ac_cv_search_arc4random_uniform+y} then : else case e in #( e) ac_cv_search_arc4random_uniform=no ;; esac fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_arc4random_uniform" >&5 printf "%s\n" "$ac_cv_search_arc4random_uniform" >&6; } ac_res=$ac_cv_search_arc4random_uniform if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Checks for header files. ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default" if test "x$ac_cv_header_sys_ioctl_h" = xyes then : printf "%s\n" "#define HAVE_SYS_IOCTL_H 1" >>confdefs.h fi for ac_header in fcntl.h inttypes.h libintl.h netinet/in.h stdint.h sys/file.h sys/param.h sys/socket.h unistd.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | sed "$as_sed_sh"` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | sed "$as_sed_cpp"` 1 _ACEOF else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "required header not found See 'config.log' for more details" "$LINENO" 5; } ;; esac fi done for ac_header in librecast/types.h librecast/net.h librecast/crypto.h librecast/key.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | sed "$as_sed_sh"` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | sed "$as_sed_cpp"` 1 _ACEOF else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "required header not found See 'config.log' for more details" "$LINENO" 5; } ;; esac fi done # Checks for typedefs, structures, and compiler characteristics. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 printf %s "checking for inline... " >&6; } if test ${ac_cv_c_inline+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo (void) {return 0; } $ac_kw foo_t foo (void) {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 printf %s "checking for C/C++ restrict keyword... " >&6; } if test ${ac_cv_c_restrict+y} then : printf %s "(cached) " >&6 else case e in #( e) ac_cv_c_restrict=no # Put '__restrict__' first, to avoid problems with glibc and non-GCC; see: # https://lists.gnu.org/archive/html/bug-autoconf/2016-02/msg00006.html # Put 'restrict' last, because C++ lacks it. for ac_kw in __restrict__ __restrict _Restrict restrict; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ typedef int *int_ptr; int foo (int_ptr $ac_kw ip) { return ip[0]; } int bar (int [$ac_kw]); /* Catch GCC bug 14050. */ int bar (int ip[$ac_kw]) { return ip[0]; } int main (void) { int s[1]; int *$ac_kw t = s; t[0] = 0; return foo (t) + bar (t); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_restrict=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_restrict" != no && break done ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 printf "%s\n" "$ac_cv_c_restrict" >&6; } case $ac_cv_c_restrict in restrict) ;; no) printf "%s\n" "#define restrict /**/" >>confdefs.h ;; *) printf "%s\n" "#define restrict $ac_cv_c_restrict" >>confdefs.h ;; esac ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" if test "x$ac_cv_type_ptrdiff_t" = xyes then : printf "%s\n" "#define HAVE_PTRDIFF_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : else case e in #( e) printf "%s\n" "#define size_t unsigned int" >>confdefs.h ;; esac fi ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" if test "x$ac_cv_type_ssize_t" = xyes then : else case e in #( e) printf "%s\n" "#define ssize_t int" >>confdefs.h ;; esac fi ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" if test "x$ac_cv_type_mode_t" = xyes then : else case e in #( e) printf "%s\n" "#define mode_t int" >>confdefs.h ;; esac fi ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" if test "x$ac_cv_type_off_t" = xyes then : else case e in #( e) printf "%s\n" "#define off_t long int" >>confdefs.h ;; esac fi ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" case $ac_cv_c_int8_t in #( no|yes) ;; #( *) printf "%s\n" "#define int8_t $ac_cv_c_int8_t" >>confdefs.h ;; esac ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t" case $ac_cv_c_int16_t in #( no|yes) ;; #( *) printf "%s\n" "#define int16_t $ac_cv_c_int16_t" >>confdefs.h ;; esac ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" case $ac_cv_c_int32_t in #( no|yes) ;; #( *) printf "%s\n" "#define int32_t $ac_cv_c_int32_t" >>confdefs.h ;; esac ac_fn_c_check_type "$LINENO" "uid_t" "ac_cv_type_uid_t" "$ac_includes_default" if test "x$ac_cv_type_uid_t" = xyes then : else case e in #( e) printf "%s\n" "#define uid_t int" >>confdefs.h ;; esac fi ac_fn_c_check_type "$LINENO" "gid_t" "ac_cv_type_gid_t" "$ac_includes_default" if test "x$ac_cv_type_gid_t" = xyes then : else case e in #( e) printf "%s\n" "#define gid_t int" >>confdefs.h ;; esac fi ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT8_T 1" >>confdefs.h printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" case $ac_cv_c_uint16_t in #( no|yes) ;; #( *) printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT32_T 1" >>confdefs.h printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT8_T 1" >>confdefs.h printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h ;; esac # Checks for library functions. ac_fn_c_check_func "$LINENO" "lc_channel_get_hash" "ac_cv_func_lc_channel_get_hash" if test "x$ac_cv_func_lc_channel_get_hash" = xyes then : printf "%s\n" "#define HAVE_LC_CHANNEL_GET_HASH 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" if test "x$ac_cv_func_getcwd" = xyes then : printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath" if test "x$ac_cv_func_realpath" = xyes then : printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h fi # test/testsync.c ac_fn_c_check_func "$LINENO" "arc4random_uniform" "ac_cv_func_arc4random_uniform" if test "x$ac_cv_func_arc4random_uniform" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM_UNIFORM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" if test "x$ac_cv_func_getpagesize" = xyes then : printf "%s\n" "#define HAVE_GETPAGESIZE 1" >>confdefs.h fi # test/testdata.c for ac_func in memset setlocale strdup strerror do : as_ac_var=`printf "%s\n" "ac_cv_func_$ac_func" | sed "$as_sed_sh"` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_func" | sed "$as_sed_cpp"` 1 _ACEOF else case e in #( e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} as_fn_error $? "required function not found See 'config.log' for more details" "$LINENO" 5; } ;; esac fi done ac_config_files="$ac_config_files Makefile doc/Makefile man/Makefile src/Makefile test/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # 'ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* 'ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # 'set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # 'set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case e in #( e) case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as 'sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else case e in #( e) as_fn_append () { eval $1=\$$1\$2 } ;; esac fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else case e in #( e) as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } ;; esac fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable. # In both cases, we have to default to 'cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated # Sed expression to map a string onto a valid variable name. as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g" as_tr_sh="eval sed '$as_sed_sh'" # deprecated exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by lcagent $as_me 0.1.0, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ '$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ lcagent config.status 0.1.0 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" Copyright (C) 2023 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: '$1' Try '$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: '$1' Try '$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to '$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with './config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with './config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script 'defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain ':'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is 'configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when '$srcdir' = '.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi lcagent/configure.ac000066400000000000000000000044131501702040600147560ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.71]) AC_INIT([lcagent], [0.1.0], [bugs@librecast.net]) AC_COPYRIGHT(Copyright (c) 2024-2025 Brett Sheffield See COPYING for license details. ) AC_CONFIG_SRCDIR([src/]) AC_CONFIG_HEADERS([src/config.h]) # Checks for programs. AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_LEX(noyywrap) AS_IF([test "x$LEX" != xflex], AC_MSG_FAILURE([flex is required])) AC_PROG_YACC AC_CHECK_PROG([testrunner], [lctest], [lctest], [maketest]) AC_SUBST([TESTRUNNER],[$testrunner]) AC_CHECK_PROGS([FALSE], [false], [/usr/bin/env false]) AC_CHECK_PROG([ifconfig], [ifconfig], [ifconfig]) AC_SUBST([IFCONFIG],[$ifconfig]) AC_CHECK_PROG([iproute], [ip], [iproute]) AC_SUBST([IPROUTE],[$iproute]) AC_CHECK_PROG([ROUTE], [route], [route]) # Checks for libraries. AC_CHECK_LIB([lcrq], [rq_init]) AC_CHECK_LIB([librecast], [lc_channel_rq_overhead],,AC_MSG_FAILURE([liblibrecast >= 0.11 is required])) AC_CHECK_LIB([sodium], [sodium_init],,AC_MSG_FAILURE([libsodium is required])) AC_SEARCH_LIBS([pthread_create], [pthread]) AC_SEARCH_LIBS([arc4random_uniform], [bsd]) # Checks for header files. AC_CHECK_HEADERS([sys/ioctl.h]) AC_CHECK_HEADERS([fcntl.h inttypes.h libintl.h netinet/in.h stdint.h sys/file.h sys/param.h sys/socket.h unistd.h],, AC_MSG_FAILURE([required header not found])) AC_CHECK_HEADERS([librecast/types.h librecast/net.h librecast/crypto.h librecast/key.h],, AC_MSG_FAILURE([required header not found])) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_C_RESTRICT AC_CHECK_TYPES([ptrdiff_t]) AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_UID_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. AC_CHECK_FUNCS([lc_channel_get_hash]) AC_CHECK_FUNCS([getcwd realpath]) # test/testsync.c AC_CHECK_FUNCS([arc4random_uniform getpagesize]) # test/testdata.c AC_CHECK_FUNCS([memset setlocale strdup strerror],, AC_MSG_FAILURE([required function not found])) AC_CONFIG_FILES([Makefile doc/Makefile man/Makefile src/Makefile test/Makefile ]) AC_OUTPUT lcagent/doc/000077500000000000000000000000001501702040600132335ustar00rootroot00000000000000lcagent/doc/Makefile.in000066400000000000000000000005111501702040600152750ustar00rootroot00000000000000doc: man2html man2md man2html: lcagent.1.html lcagentrc.5.html lcagent.1.html: ../man/man1/lcagent.1 pandoc -o $@ $< lcagentrc.5.html: ../man/man5/lcagentrc.5 pandoc -o $@ $< man2md: lcagent.1.md lcagentrc.5.md lcagent.1.md: ../man/man1/lcagent.1 pandoc -o $@ $< lcagentrc.5.md: ../man/man5/lcagentrc.5 pandoc -o $@ $< lcagent/doc/lcagent.1.html000066400000000000000000000244111501702040600156770ustar00rootroot00000000000000

NAME

lcagent - the Librecast multicast agent

SYNOPSIS

lcagent [-dflv] [--bpslimit limit] [--expires seconds] [--overhead packets] [command [argument ...]]

DESCRIPTION

lcagent is the Librecast multicast agent.

In server mode lcagent can be configured to listen on Librecast Channels (multicast groups) and execute programs in response to packet data received on those Channels.

Packets must be accompanied by an authorized token and signed by the matching key or they will be silently dropped.

Data is encoded with RaptorQ using Librecast's liblcrq library to provide forward error correction in the event of packet loss.

Any data received in addition to the required token will be piped to stdin of the configured commands after they are executed. Multiple commands can be executed sequentially.

lcagent can be used to send and receive data over multicast and to pipe data between programs on one computer and as many receivers as the multicast network can support simultaneously.

OPTIONS

-d, --debug

Enable debug mode to print additional debugging information.

--bpslimit rate[KMGT]

When sending, this option limits the sending speed to rate. Rate is a number in bits per second (bps) and can be followed by a capital letter representing SI units. eg. 100M limits the sending rate to 100 megabits per second (Mbps).

--expires seconds

For the sign command, this sets the validity of the token in seconds.

-f, --follow

In recv mode, do not stop after first data object received. Keep blocking and receiving data until interrupted. eg. ^C is pressed or SIGINT is received by the process.

-l, --loopback

Enable multicast loopback when sending. This will allow packets to be received by the same host that sent them. By default multicast packets are not received on the sending host.

--overhead packets

When sending, this option sets the number of additional repair packets sent to aid with recovery of lost packets with RaptorQ encoding. Default: 5

-v, --verbose

Print more verbose output.

COMMANDS

exec

lcagent exec [options] channel [data] [-]

The exec command executes the commands configured for channel locally, without requiring data to be received over the network.

This is a good way to test the configured commands that will be executed before making them accessible on the network.

The nojoin configuration directive can be used to ensure that a channel is not network-executable, by preventing lcagent from joining the associated Librecast channel. See lcagentrc(1).

help

lcagent help

Print some (possibly) helpful instructions for lcagent.

key

lcagent key [options] add|del key

Add or delete a key from the authorized_keys file. Packets that do not include a token signed by a key in the authorized_keys file are silently dropped.

recv

lcagent recv [options] channel

Block and wait for data to be received on channel, where channel is a string that will be hashed to create a Librecast channel.

Data received will be written to stdout. If expecting binary data, be sure to redirect stdout somewhere appropriate.

By default, once EOF is received from the sender, the program will exit. This behaviour can be changed by specifying the --follow option.

reload

lcagent reload [options]

Reload the lcagent configuration. Sends a SIGHUP to the daemon process. Has no effect unless lcagent has been started using the start command. Service files should simply send SIGHUP to the process.

send

lcagent send [options] channel [payload] [-]

Send data to channel, where channel is a string that will be hashed to create a Librecast channel.

If payload is specified, this string will be sent as data to the channel. If no payload is specified, an NULL (empty) payload of zero bytes will be sent to the channel.

A hyphen (-) at the end of the command indicates that the payload should instead be read from stdin.

server

lcagent [server] [options]

Starting lcagent with no command starts it in server mode. The .lcagentrc file will be parsed and lcagent will join the configured channels and wait for data, executing the configured commands when packets are received on one of the configured channels and writing any payload data to stdin of the command when executing it.

See lcagentrc(1) for details of the runtime control (configuration) file.

sign

lcagent sign [options] [--expires seconds] bearer_key channel

Create and sign a token for the given bearer_key and channel and write to disk. The resulting token will be written to a file in the user's state directory, and the file pathname will be printed. If the token is copied to the state directory of the bearer it will be used when sending to channel.

A signed token means that packets sent by the bearer will be accepted by any receivers on channel which have the signer's key added to their authorized_keys file. Tokens enable delegation of authority, which is important for scaling. Using pre-signed tokens allows, say, a host to issue tokens for virtual machines it creates without the need to add individual keys for every machine to all receivers on the network.

In the absence of a signed token, packets will be sent with a self-signed token, requiring that all receivers have the sender's key installed in their authorized_keys file.

If the expires option is supplied, this will be the validity in seconds of the generated token. A value of zero (0) means that the token will never expire. By default tokens do not expire.

start

lcagent start [options]

Start lcagent in server mode and fork it as a daemon.

Not intended for use with daemon supervising programs. If you are running lcagent as a service, just start lcagent with no command and let the init system or daemon supervisor handle forking and pidfiles. If you are NOT running lcagent as a service and want it to daemonize, use this command.

stop

lcagent stop [options]

Stop the lcagent daemon. Has no effect unless lcagent has been started using the start command. Sends SIGINT to the running daemon process.

version

lcagent version

Print the version of lcagent and exit.

whoami

lcagent whoami [options]

Print the active key to stdout and exit. This key can be added to the authorized_keys file of another host with:

lcagent key add key

EXIT STATUS

lcagent exits with status 0 (EXIT_SUCCESS) if the command was successful, or 1 (EXIT_FAILURE) if an error occurred.

ENVIRONMENT

HOME

Set to the path of the user’s home directory.

FILES

~/.lcagentrc

This is the runtime control file for lcagent. This is where global settings, channels and commands are defined that affect the runtime operation of the program. See lcagentrc(5) for more information.

~/.local/state/lcagent/authorized_keys

This file contains the keys which are authorized to issue tokens which will be accepted by the program when receiving data. One key is stored per line. It can be managed with the lcagent key command.

SECURITY

Authenticity of data received is established by checking that each packet received contains a token signed by an authority with their public key installed in the authorized_keys file. If the token is valid, the packet must be signed by the bearer key contained in the token.

Cryptographic signatures use the Ed25519ph algorithm provided by the libsodium library.

Data can be encrypted using the seed configuration directive in .lcagentrc. See lcagentrc(5).

The symmetric encryption key is generated from seed using BLAKE2b.

Symmetric encryption uses the XSalsa20 stream cipher from libsodium using a Poly1305 MAC for authentication and random nonce.

If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error.

AUTHOR

lcagent was written by Brett Sheffield <bacs@librecast.net> and released under the terms of the GPL-2 or (at your option) GPL-3.

https://librecast.net/lcagent.html

BUGS

If you find one, email bugs@librecast.net or raise an issue in our online bug tracker at: https://bugs.librecast.net/lcagent

SEE ALSO

lcagentrc(1), lcsync(1), librecast(7)

lcagent/doc/lcagent.1.md000066400000000000000000000207261501702040600153400ustar00rootroot00000000000000# NAME lcagent - the Librecast multicast agent # SYNOPSIS **lcagent** \[**-dflv**\] \[**\--bpslimit** *limit*\] \[**\--expires** *seconds*\] \[**\--overhead** *packets*\] \[*command* \[*argument* *\...*\]\] # DESCRIPTION **lcagent** is the Librecast multicast agent. In server mode **lcagent** can be configured to listen on Librecast Channels (multicast groups) and execute programs in response to packet data received on those Channels. Packets must be accompanied by an authorized token and signed by the matching key or they will be silently dropped. Data is encoded with RaptorQ using Librecast\'s *liblcrq* library to provide forward error correction in the event of packet loss. Any data received in addition to the required token will be piped to *stdin* of the configured commands after they are executed. Multiple commands can be executed sequentially. **lcagent** can be used to send and receive data over multicast and to pipe data between programs on one computer and as many receivers as the multicast network can support simultaneously. # OPTIONS **-d**, **\--debug** : Enable debug mode to print additional debugging information. **\--bpslimit** ***rate\[KMGT\]*** : When sending, this option limits the sending speed to *rate*. Rate is a number in bits per second (bps) and can be followed by a capital letter representing SI units. eg. 100M limits the sending rate to 100 megabits per second (Mbps). **\--expires** ***seconds*** : For the *sign* command, this sets the validity of the token in seconds. **-f**, **\--follow** : In **recv** mode, do not stop after first data object received. Keep blocking and receiving data until interrupted. eg. \^C is pressed or SIGINT is received by the process. **-l**, **\--loopback** : Enable multicast loopback when sending. This will allow packets to be received by the same host that sent them. By default multicast packets are not received on the sending host. **\--overhead** ***packets*** : When sending, this option sets the number of additional repair packets sent to aid with recovery of lost packets with RaptorQ encoding. Default: 5 **-v**, **\--verbose** : Print more verbose output. # COMMANDS ## exec **lcagent** exec \[*options*\] channel \[*data*\] \[*-*\] The **exec** command executes the commands configured for *channel* locally, without requiring data to be received over the network. This is a good way to test the configured commands that will be executed before making them accessible on the network. The *nojoin* configuration directive can be used to ensure that a channel is not network-executable, by preventing **lcagent** from joining the associated Librecast channel. See **lcagentrc**(1). ## help **lcagent** help Print some (possibly) helpful instructions for **lcagent**. ## key **lcagent** key \[*options*\] add\|del *key* Add or delete a key from the authorized_keys file. Packets that do not include a token signed by a key in the authorized_keys file are silently dropped. ## recv **lcagent** recv \[*options*\] channel Block and wait for data to be received on *channel*, where *channel* is a string that will be hashed to create a Librecast channel. Data received will be written to *stdout*. If expecting binary data, be sure to redirect *stdout* somewhere appropriate. By default, once EOF is received from the sender, the program will exit. This behaviour can be changed by specifying the *\--follow* option. ## reload **lcagent** reload \[*options*\] Reload the **lcagent** configuration. Sends a SIGHUP to the daemon process. Has no effect unless **lcagent** has been started using the *start* command. Service files should simply send SIGHUP to the process. ## send **lcagent** send \[*options*\] *channel* \[*payload*\] \[-\] Send data to *channel*, where *channel* is a string that will be hashed to create a Librecast channel. If *payload* is specified, this string will be sent as data to the channel. If no payload is specified, an NULL (empty) payload of zero bytes will be sent to the channel. A hyphen (*-*) at the end of the command indicates that the payload should instead be read from *stdin*. ## server **lcagent** \[*server*\] \[*options*\] Starting lcagent with no command starts it in server mode. The *.lcagentrc* file will be parsed and **lcagent** will join the configured channels and wait for data, executing the configured commands when packets are received on one of the configured channels and writing any payload data to *stdin* of the command when executing it. See **lcagentrc**(1) for details of the runtime control (configuration) file. ## sign **lcagent** sign \[*options*\] \[**\--expires** *seconds*\] *bearer_key* *channel* Create and sign a token for the given *bearer_key* and *channel* and write to disk. The resulting token will be written to a file in the user\'s state directory, and the file pathname will be printed. If the token is copied to the state directory of the bearer it will be used when sending to *channel*. A signed token means that packets sent by the bearer will be accepted by any receivers on *channel* which have the signer\'s key added to their *authorized_keys* file. Tokens enable delegation of authority, which is important for scaling. Using pre-signed tokens allows, say, a host to issue tokens for virtual machines it creates without the need to add individual keys for every machine to all receivers on the network. In the absence of a signed token, packets will be sent with a self-signed token, requiring that all receivers have the sender\'s key installed in their *authorized_keys* file. If the *expires* option is supplied, this will be the validity in seconds of the generated token. A value of zero (0) means that the token will never expire. By default tokens do not expire. ## start **lcagent** start \[*options*\] Start **lcagent** in server mode and fork it as a daemon. Not intended for use with daemon supervising programs. If you are running **lcagent** as a service, just start **lcagent** with no command and let the init system or daemon supervisor handle forking and pidfiles. If you are NOT running **lcagent** as a service and want it to daemonize, use this command. ## stop **lcagent** stop \[*options*\] Stop the **lcagent** daemon. Has no effect unless **lcagent** has been started using the *start* command. Sends SIGINT to the running daemon process. ## version **lcagent** version Print the version of **lcagent** and exit. ## whoami **lcagent** whoami \[*options*\] Print the active key to *stdout* and exit. This key can be added to the *authorized_keys* file of another host with: **lcagent** key add *key* # EXIT STATUS **lcagent** exits with status 0 (EXIT_SUCCESS) if the command was successful, or 1 (EXIT_FAILURE) if an error occurred. # ENVIRONMENT HOME : Set to the path of the user's home directory. # FILES *\~/.lcagentrc* : This is the runtime control file for **lcagent.** This is where global settings, channels and commands are defined that affect the runtime operation of the program. See **lcagentrc**(5) for more information. *\~/.local/state/lcagent/authorized_keys* : This file contains the keys which are authorized to issue tokens which will be accepted by the program when receiving data. One key is stored per line. It can be managed with the **lcagent** key command. # SECURITY Authenticity of data received is established by checking that each packet received contains a token signed by an authority with their public key installed in the *authorized_keys* file. If the token is valid, the packet must be signed by the bearer key contained in the token. Cryptographic signatures use the Ed25519ph algorithm provided by the libsodium library. Data can be encrypted using the *seed* configuration directive in *.lcagentrc*. See **lcagentrc(5).** The symmetric encryption key is generated from *seed* using BLAKE2b. Symmetric encryption uses the XSalsa20 stream cipher from libsodium using a Poly1305 MAC for authentication and random nonce. If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error. # AUTHOR **lcagent** was written by Brett Sheffield \ and released under the terms of the GPL-2 or (at your option) GPL-3. *https://librecast.net/lcagent.html* # BUGS If you find one, email *bugs@librecast.net* or raise an issue in our online bug tracker at: *https://bugs.librecast.net/lcagent* # SEE ALSO **lcagentrc**(1), **lcsync**(1), **librecast**(7) lcagent/doc/lcagentrc.5.html000066400000000000000000000110541501702040600162270ustar00rootroot00000000000000

NAME

lcagentrc - lcagent rcfile

DESCRIPTION

The rcfile consists of some global options followed by a list of channels with their settings and commands.

Comments

A comment begins with # and all the following characters up to a NEWLINE are ignored.

OPTIONS

Global Options

debug

Enable debug logging. The same as setting --debug on the command line.

directory directory

Set the default directory for channels. Change to this directory before executing any commands.

enable

Enable channels. The default. Can be overridden by noenable per channel.

join

Join channels. The default. Can be overridden by nojoin per channel.

logfile

Set the path to the logfile. Logs will be written here instead of to stderr.

noenable | disable

Do not enable channels. Can be overridden by enable per channel.

nojoin

Do not join channels. Can be overridden by join per channel.

seed seed

Generate a symmetric key from seed and use it to encrypt and decrypt data sent and received on all channels.

If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error.

Channel Options

enable

Enable this channel. The default, unless noenable is set globally.

directory

Change to this directory before executing any commands for this channel.

join

Join channel. The default, unless nojoin is set globally.

noenable | disable

The channel is not enabled. The channel will still be joined (unless nojoin is also set, and attempts to execute it will be logged, but no commands will execute. This also prevents execution with lcagent exec.

nojoin

Do not join the Librecast Channel. The channel commands can still be executed by calling lcagent exec.

command command

Execute command. If it fails, do not execute any following commands. The channel will continue listening.

try command

Execute command. If it fails, continue executing any subsequent commands anyway.

EXAMPLE

# This is an example rcfile for lcagent(1)
# Comments begin with a hash and run until the end of the line.
# Whitespace, including blank lines are ignored.
# Indenting channel settings is optional, but is recommended for readability.
# Leaving a blank line between channels is also advised.

# enable debug logging and set logfile
debug
logfile /var/log/lcagent.log
seed somepassword

# set any global options
# these can be overridden by individual channel options
# these are the defaults:
join
enable


channel "my very first channel"
	# we usually indent the settings for each channel
	noenable             # this channel will not be executed
	command "/this/command/not/executed"

# this channel will not be joined, but can still be executed
# with `lcagent exec`
channel 'single quotes are valid string delimiters too'
	nojoin               # this channel will not be joined
	command "cat > /tmp/somefile"
	command "bzip2 /tmp/somefile"
	reload               # reload lcagent config on success

channel unquoted-string-channel
	try "/bin/false"     # this can fail, and we still continue
	command "/bin/true"  # this command must succeed, or we stop
	command "/usr/local/bin/someplugin"
	restart              # restart lcagent config on success

# this channel will apply a git patch sent to it which was created
# with git-format-patch(1).
# If the patch applies without error, `gmake test` is run
channel "lcagent patchtest"
        directory /home/lcagent/src/lcagent
        command "git am --committer-date-is-author-date"
        command "gmake test"

SEE ALSO

lcagent(1), librecast(7)

lcagent/doc/lcagentrc.5.md000066400000000000000000000075471501702040600156770ustar00rootroot00000000000000# NAME lcagentrc - lcagent rcfile # DESCRIPTION The rcfile consists of some global options followed by a list of channels with their settings and commands. ## Comments A comment begins with \# and all the following characters up to a NEWLINE are ignored. # OPTIONS ## Global Options **debug** : Enable debug logging. The same as setting \--debug on the command line. **directory *directory*** : Set the default directory for channels. Change to this *directory* before executing any commands. **enable** : Enable channels. The default. Can be overridden by *noenable* per channel. **join** : Join channels. The default. Can be overridden by *nojoin* per channel. **logfile** : Set the path to the logfile. Logs will be written here instead of to *stderr*. **noenable \| disable** : Do not enable channels. Can be overridden by *enable* per channel. **nojoin** : Do not join channels. Can be overridden by *join* per channel. **seed *seed*** : Generate a symmetric key from *seed* and use it to encrypt and decrypt data sent and received on all channels. If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error. ## Channel Options **enable** : Enable this channel. The default, unless *noenable* is set globally. **directory** : Change to this *directory* before executing any commands for this channel. **join** : Join channel. The default, unless *nojoin* is set globally. **noenable \| disable** : The channel is not enabled. The channel will still be joined (unless *nojoin* is also set, and attempts to execute it will be logged, but no commands will execute. This also prevents execution with **lcagent** exec. **nojoin** : Do not join the Librecast Channel. The channel commands can still be executed by calling **lcagent** exec. **command *command*** : Execute *command*. If it fails, do not execute any following commands. The channel will continue listening. **try *command*** : Execute *command*. If it fails, continue executing any subsequent commands anyway. # EXAMPLE # This is an example rcfile for lcagent(1) # Comments begin with a hash and run until the end of the line. # Whitespace, including blank lines are ignored. # Indenting channel settings is optional, but is recommended for readability. # Leaving a blank line between channels is also advised. # enable debug logging and set logfile debug logfile /var/log/lcagent.log seed somepassword # set any global options # these can be overridden by individual channel options # these are the defaults: join enable channel "my very first channel" # we usually indent the settings for each channel noenable # this channel will not be executed command "/this/command/not/executed" # this channel will not be joined, but can still be executed # with `lcagent exec` channel 'single quotes are valid string delimiters too' nojoin # this channel will not be joined command "cat > /tmp/somefile" command "bzip2 /tmp/somefile" reload # reload lcagent config on success channel unquoted-string-channel try "/bin/false" # this can fail, and we still continue command "/bin/true" # this command must succeed, or we stop command "/usr/local/bin/someplugin" restart # restart lcagent config on success # this channel will apply a git patch sent to it which was created # with git-format-patch(1). # If the patch applies without error, `gmake test` is run channel "lcagent patchtest" directory /home/lcagent/src/lcagent command "git am --committer-date-is-author-date" command "gmake test" # SEE ALSO **lcagent**(1), **librecast**(7) lcagent/install-sh000066400000000000000000001051161501702040600144730ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2022 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2022-01-03' # 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 to . # # 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: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # 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. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -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-2022 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 ;; *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 # Split fields of configuration type # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | 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* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # 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) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x$basic_os != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read kernel os <&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os in linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ | linux-musl* | linux-relibc* | linux-uclibc* ) ;; uclinux-uclibc* ) ;; -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 exit 1 ;; kfreebsd*-gnu* | kopensolaris*-gnu*) ;; vxworks-simlinux | vxworks-simwindows | vxworks-spe) ;; nto-qnx*) ;; os2-emx) ;; *-eabi* | *-gnueabi*) ;; -*) # Blank kernel with real OS is always fine. ;; *-*) echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$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 ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) 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 ;; esac echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: lcagent/lcagent.service000066400000000000000000000003541501702040600154670ustar00rootroot00000000000000[Unit] Description=lcagent daemon Documentation=man:lcagent(1) Requires=network-online.target [Service] Type=simple User=lcagent Group=lcagent ExecStart=lcagent ExecReload=/usr/bin/kill -HUP $MAINPID [Install] WantedBy=default.target lcagent/man/000077500000000000000000000000001501702040600132415ustar00rootroot00000000000000lcagent/man/Makefile.in000066400000000000000000000006621501702040600153120ustar00rootroot00000000000000PREFIX ?= @prefix@ .PHONY: clean doc install realclean man: install: install -d $(DESTDIR)$(PREFIX)/share/man/man1 install man1/*.1 $(DESTDIR)$(PREFIX)/share/man/man1/ install -d $(DESTDIR)$(PREFIX)/share/man/man5 install man5/*.5 $(DESTDIR)$(PREFIX)/share/man/man5/ uninstall: $(RM) $(DESTDIR)$(PREFIX)/share/man/man1/lcagent.1 $(RM) $(DESTDIR)$(PREFIX)/share/man/man5/lcagentrc.5 clean: realclean: clean $(RM) Makefile lcagent/man/man1/000077500000000000000000000000001501702040600140755ustar00rootroot00000000000000lcagent/man/man1/lcagent.1000066400000000000000000000214231501702040600155760ustar00rootroot00000000000000.TH LCAGENT 1 2025-05-16 Librecast "Librecast Commands Manual" .SH NAME lcagent \- the Librecast multicast agent .SH SYNOPSIS .B lcagent [\fB\-dflv\fR] [\fB\-\-bpslimit\fR \fIlimit\fR] [\fB\-\-expires\fR \fIseconds\fR] [\fB\-\-overhead\fR \fIpackets\fR] [\fIcommand\fR [\fIargument\fR \fI...\fR]] .SH DESCRIPTION .B lcagent is the Librecast multicast agent. .PP In server mode .B lcagent can be configured to listen on Librecast Channels (multicast groups) and execute programs in response to packet data received on those Channels. .PP Packets must be accompanied by an authorized token and signed by the matching key or they will be silently dropped. .PP Data is encoded with RaptorQ using Librecast's .I liblcrq library to provide forward error correction in the event of packet loss. .PP Any data received in addition to the required token will be piped to .I stdin of the configured commands after they are executed. Multiple commands can be executed sequentially. .PP .B lcagent can be used to send and receive data over multicast and to pipe data between programs on one computer and as many receivers as the multicast network can support simultaneously. .SH OPTIONS .TP .BR \-d ", " \-\-debug Enable debug mode to print additional debugging information. .TP .BR \-\-bpslimit " " \fIrate[KMGT]\fR When sending, this option limits the sending speed to \fIrate\fR. Rate is a number in bits per second (bps) and can be followed by a capital letter representing SI units. eg. 100M limits the sending rate to 100 megabits per second (Mbps). .TP .BR \-\-expires " " \fIseconds\fR For the \fIsign\fR command, this sets the validity of the token in seconds. .TP .BR \-f ", " \-\-follow In .B recv mode, do not stop after first data object received. Keep blocking and receiving data until interrupted. eg. ^C is pressed or SIGINT is received by the process. .TP .BR \-l ", " \-\-loopback Enable multicast loopback when sending. This will allow packets to be received by the same host that sent them. By default multicast packets are not received on the sending host. .TP .BR \-\-overhead " " \fIpackets\fR When sending, this option sets the number of additional repair packets sent to aid with recovery of lost packets with RaptorQ encoding. Default: 5 .TP .BR \-v ", " \-\-verbose Print more verbose output. .SH COMMANDS .SS exec .B lcagent exec [\fIoptions\fR] channel [\fIdata\fR] [\fI\-\fR] .PP The .B exec command executes the commands configured for \fIchannel\fR locally, without requiring data to be received over the network. .PP This is a good way to test the configured commands that will be executed before making them accessible on the network. .PP The \fInojoin\fR configuration directive can be used to ensure that a channel is not network-executable, by preventing \fBlcagent\fR from joining the associated Librecast channel. See .BR lcagentrc (1). .SS help .B lcagent help .PP Print some (possibly) helpful instructions for \fBlcagent\fR. .SS key .B lcagent key [\fIoptions\fR] add|del \fIkey\fR .PP Add or delete a key from the authorized_keys file. Packets that do not include a token signed by a key in the authorized_keys file are silently dropped. .SS recv .B lcagent recv [\fIoptions\fR] channel .PP Block and wait for data to be received on \fIchannel\fR, where \fIchannel\fR is a string that will be hashed to create a Librecast channel. .PP Data received will be written to \fIstdout\fR. If expecting binary data, be sure to redirect \fIstdout\fR somewhere appropriate. .PP By default, once EOF is received from the sender, the program will exit. This behaviour can be changed by specifying the \fI\-\-follow\fR option. .SS reload .B lcagent reload [\fIoptions\fR] .PP Reload the \fBlcagent\fR configuration. Sends a SIGHUP to the daemon process. Has no effect unless \fBlcagent\fR has been started using the \fIstart\fR command. Service files should simply send SIGHUP to the process. .SS send .B lcagent send [\fIoptions\fR] \fIchannel\fR [\fIpayload\fR] [\-] .PP Send data to \fIchannel\fR, where \fIchannel\fR is a string that will be hashed to create a Librecast channel. .PP If \fIpayload\fR is specified, this string will be sent as data to the channel. If no payload is specified, an NULL (empty) payload of zero bytes will be sent to the channel. .PP A hyphen (\fI\-\fR) at the end of the command indicates that the payload should instead be read from \fIstdin\fR. .SS server .B lcagent [\fIserver\fR] [\fIoptions\fR] .PP Starting lcagent with no command starts it in server mode. The \fI.lcagentrc\fR file will be parsed and \fBlcagent\fR will join the configured channels and wait for data, executing the configured commands when packets are received on one of the configured channels and writing any payload data to \fIstdin\fR of the command when executing it. .PP See .BR lcagentrc (1) for details of the runtime control (configuration) file. .SS sign .B lcagent sign [\fIoptions\fR] [\fB\-\-expires\fR \fIseconds\fR] \fIbearer_key\fR \fIchannel\fR .PP Create and sign a token for the given \fIbearer_key\fR and \fIchannel\fR and write to disk. The resulting token will be written to a file in the user's state directory, and the file pathname will be printed. If the token is copied to the state directory of the bearer it will be used when sending to \fIchannel\fR. .PP A signed token means that packets sent by the bearer will be accepted by any receivers on \fIchannel\fR which have the signer's key added to their \fIauthorized_keys\fR file. Tokens enable delegation of authority, which is important for scaling. Using pre-signed tokens allows, say, a host to issue tokens for virtual machines it creates without the need to add individual keys for every machine to all receivers on the network. .PP In the absence of a signed token, packets will be sent with a self-signed token, requiring that all receivers have the sender's key installed in their \fIauthorized_keys\fR file. .PP If the \fIexpires\fR option is supplied, this will be the validity in seconds of the generated token. A value of zero (0) means that the token will never expire. By default tokens do not expire. .SS start .B lcagent start [\fIoptions\fR] .PP Start \fBlcagent\fR in server mode and fork it as a daemon. .PP Not intended for use with daemon supervising programs. If you are running \fBlcagent\fR as a service, just start \fBlcagent\fR with no command and let the init system or daemon supervisor handle forking and pidfiles. If you are NOT running \fBlcagent\fR as a service and want it to daemonize, use this command. .SS stop .B lcagent stop [\fIoptions\fR] .PP Stop the \fBlcagent\fR daemon. Has no effect unless \fBlcagent\fR has been started using the \fIstart\fR command. Sends SIGINT to the running daemon process. .SS version .B lcagent version .PP Print the version of .B lcagent and exit. .SS whoami .B lcagent whoami [\fIoptions\fR] .PP Print the active key to \fIstdout\fR and exit. This key can be added to the \fIauthorized_keys\fR file of another host with: .PP .B lcagent key add \fIkey\fR .PP .SH EXIT STATUS .B lcagent exits with status 0 (EXIT_SUCCESS) if the command was successful, or 1 (EXIT_FAILURE) if an error occurred. .SH ENVIRONMENT .TP HOME Set to the path of the user’s home directory. .SH FILES .TP .IR ~/.lcagentrc This is the runtime control file for .B lcagent. This is where global settings, channels and commands are defined that affect the runtime operation of the program. See .BR lcagentrc (5) for more information. .TP .IR ~/.local/state/lcagent/authorized_keys This file contains the keys which are authorized to issue tokens which will be accepted by the program when receiving data. One key is stored per line. It can be managed with the .B lcagent key command. .SH SECURITY Authenticity of data received is established by checking that each packet received contains a token signed by an authority with their public key installed in the \fIauthorized_keys\fR file. If the token is valid, the packet must be signed by the bearer key contained in the token. .PP Cryptographic signatures use the Ed25519ph algorithm provided by the libsodium library. .PP Data can be encrypted using the \fIseed\fR configuration directive in \fI.lcagentrc\fR. See .BR lcagentrc(5). .PP The symmetric encryption key is generated from \fIseed\fR using BLAKE2b. .PP Symmetric encryption uses the XSalsa20 stream cipher from libsodium using a Poly1305 MAC for authentication and random nonce. .PP If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error. .SH AUTHOR .B lcagent was written by Brett Sheffield and released under the terms of the GPL-2 or (at your option) GPL-3. .PP .I https://librecast.net/lcagent.html .SH BUGS If you find one, email \fIbugs@librecast.net\fR or raise an issue in our online bug tracker at: \fIhttps://bugs.librecast.net/lcagent\fR .SH SEE ALSO .BR lcagentrc (1), .BR lcsync (1), .BR librecast (7) lcagent/man/man5/000077500000000000000000000000001501702040600141015ustar00rootroot00000000000000lcagent/man/man5/lcagentrc.5000066400000000000000000000074051501702040600161370ustar00rootroot00000000000000.TH LCAGENTRC 5 2025-05-16 Librecast "Librecast File Formats Manual" .SH NAME lcagentrc - lcagent rcfile .SH DESCRIPTION The rcfile consists of some global options followed by a list of channels with their settings and commands. .SS Comments A comment begins with # and all the following characters up to a NEWLINE are ignored. .SH OPTIONS .SS Global Options .TP .B debug Enable debug logging. The same as setting \-\-debug on the command line. .TP .B directory \fIdirectory\fR Set the default directory for channels. Change to this \fIdirectory\fR before executing any commands. .TP .B enable Enable channels. The default. Can be overridden by \fInoenable\fR per channel. .TP .B join Join channels. The default. Can be overridden by \fInojoin\fR per channel. .TP .B logfile Set the path to the logfile. Logs will be written here instead of to \fIstderr\fR. .TP .B noenable | disable Do not enable channels. Can be overridden by \fIenable\fR per channel. .TP .B nojoin Do not join channels. Can be overridden by \fIjoin\fR per channel. .TP .B seed \fIseed\fR Generate a symmetric key from \fIseed\fR and use it to encrypt and decrypt data sent and received on all channels. .PP If an encryption seed is used, the permissions for the rcfile must not be world readable or lcagent will refuse to run and print an error. .SS Channel Options .TP .B enable Enable this channel. The default, unless \fInoenable\fR is set globally. .TP .B directory Change to this \fIdirectory\fR before executing any commands for this channel. .TP .B join Join channel. The default, unless \fInojoin\fR is set globally. .TP .B noenable | disable The channel is not enabled. The channel will still be joined (unless \fInojoin\fR is also set, and attempts to execute it will be logged, but no commands will execute. This also prevents execution with .B lcagent exec. .TP .B nojoin Do not join the Librecast Channel. The channel commands can still be executed by calling .B lcagent exec. .TP .B command \fIcommand\fR Execute \fIcommand\fR. If it fails, do not execute any following commands. The channel will continue listening. .TP .B try \fIcommand\fR Execute \fIcommand\fR. If it fails, continue executing any subsequent commands anyway. .SH EXAMPLE .EX # This is an example rcfile for lcagent(1) # Comments begin with a hash and run until the end of the line. # Whitespace, including blank lines are ignored. # Indenting channel settings is optional, but is recommended for readability. # Leaving a blank line between channels is also advised. # enable debug logging and set logfile debug logfile /var/log/lcagent.log seed somepassword # set any global options # these can be overridden by individual channel options # these are the defaults: join enable channel "my very first channel" # we usually indent the settings for each channel noenable # this channel will not be executed command "/this/command/not/executed" # this channel will not be joined, but can still be executed # with `lcagent exec` channel 'single quotes are valid string delimiters too' nojoin # this channel will not be joined command "cat > /tmp/somefile" command "bzip2 /tmp/somefile" reload # reload lcagent config on success channel unquoted-string-channel try "/bin/false" # this can fail, and we still continue command "/bin/true" # this command must succeed, or we stop command "/usr/local/bin/someplugin" restart # restart lcagent config on success # this channel will apply a git patch sent to it which was created # with git-format-patch(1). # If the patch applies without error, `gmake test` is run channel "lcagent patchtest" directory /home/lcagent/src/lcagent command "git am --committer-date-is-author-date" command "gmake test" .EE .SH SEE ALSO .BR lcagent (1), .BR librecast (7) lcagent/src/000077500000000000000000000000001501702040600132555ustar00rootroot00000000000000lcagent/src/Makefile.in000066400000000000000000000027241501702040600153270ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only # Copyright (c) 2024-2025 Brett Sheffield SHELL = /bin/sh PROGRAM := @PACKAGE_NAME@ VERSION := @PACKAGE_VERSION@ .SUFFIXES: .SUFFIXES: .c .o CFLAGS := @CFLAGS@ -I. LDFLAGS := @LDFLAGS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ LEX = @LEX@ YACC = @YACC@ PREFIX = @prefix@ OBJECTS := lex.yy.o y.tab.o agent.o key.o lcagent.o log.o state.o HEADERS := agent.h state.h log.h lex.h LDLIBS += @LIBS@ bindir = @bindir@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ exec_prefix = @exec_prefix@ mandir = @mandir@ prefix = @prefix@ srcdir = @srcdir@ all: $(PROGRAM) $(PROGRAM): $(OBJECTS) agent.h $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LDLIBS) %.o: %.c %.h $(HEADERS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< lex.yy.o: lex.yy.c agent.h $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< y.tab.o: y.tab.c y.tab.h $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< y.tab.h: grammar.y agent.h $(YACC) -d $< y.tab.c: grammar.y agent.h lex.h: lex.yy.c agent.h lex.yy.c: lexer.l y.tab.h agent.h $(LEX) --header-file=lex.h lexer.l install: all $(INSTALL) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) $(PROGRAM) $(DESTDIR)$(bindir) .PHONY: clean realclean uninstall uninstall: $(RM) $(DESTDIR)$(bindir)/$(PROGRAM) clean: $(RM) *.o $(RM) -f y.tab.c y.tab.h lex.yy.c lex.h realclean: clean $(RM) Makefile config.h lcagent/src/agent.c000066400000000000000000000537621501702040600145340ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett Sheffield */ #define _GNU_SOURCE /* for in6_pktinfo */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define unused (void) char *agent_dir_home = NULL; char *agent_dir_cache = NULL; char *agent_dir_config = NULL; char *agent_dir_data = NULL; char *agent_dir_seq = NULL; char *agent_dir_state = NULL; int lineno = 1; int yyparse(state_t *state); static volatile int g_reload; static volatile int g_stop; static void handle_signal(int signo, siginfo_t *info, void *context) { (void)signo, (void) info, (void)context; switch (signo) { case SIGINT: g_stop++; break; case SIGHUP: g_reload++; break; } } static int agent_print_version(state_t *state) { (void)state; fprintf(stderr, "%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); return 0; } static int agent_key_add(state_t *state) { char *key; if (!(key = arg_pop(state))) { fprintf(stderr, "%s\n", AGENT_ERR_NOKEY); return (errno = EINVAL), -1; } return keyring_add(state, key); } static int agent_key_del(state_t *state) { char *key; if (!(key = arg_pop(state))) { fprintf(stderr, "%s\n", AGENT_ERR_NOKEY); return (errno = EINVAL), -1; } return keyring_del(state, key); } static int agent_key(state_t *state) { char *act; if (!(act = arg_pop(state))) { act = "show"; } if (!strcmp(act, "add")) { return agent_key_add(state); } else if (!strcmp(act, "del")) { return agent_key_del(state); } else if (!strcmp(act, "show")) { return keyring_show(state); } return -1; } static int agent_sign(state_t *state) { lc_ctx_t *lctx; lc_channel_t *chan; lc_keypair_t *signing_key = &state->defaults.keyring.s; char *bearer_key, *chanstr; int flags = KEY_CAP_SAVE | KEY_CAP_PATH; int rc = -1; uint8_t capbits = 0; if (!(bearer_key = arg_pop(state))) { fprintf(stderr, "%s\n", AGENT_ERR_NOKEY); return (errno = EINVAL), -1; } if (!(chanstr = arg_pop(state))) { fprintf(stderr, "%s\n", AGENT_ERR_NOCHANNEL); return (errno = EINVAL), -1; } /* ensure own key loaded */ if (strlen(state->defaults.keyring.shex) < 1) return (errno = ENOKEY), -1; lctx = lc_ctx_new(); if (!lctx) return -1; chan = lc_channel_new(lctx, chanstr); if (!chan) goto err_lctx_free; unsigned char bearer_psk[crypto_sign_PUBLICKEYBYTES] = {0}; key_combo_hex2psk(bearer_psk, sizeof bearer_psk, bearer_key); rc = key_cap_issue(state, signing_key, bearer_psk, chan, capbits, state->expires, flags); err_lctx_free: lc_ctx_free(lctx); return rc; } static int agent_whoami(state_t *state) { /* write key to stout when not a tty so it can be redirected */ FILE *f = (isatty(1)) ? stderr : stdout; fprintf(f, "%s\n", state->defaults.keyring.phex); return 0; } static int agent_load_channel_token(lc_token_t *token, state_t *state, char *chanstr) { unsigned char hash[HASHSIZE]; hash_generic(hash, sizeof hash, (unsigned char *)chanstr, strlen(chanstr)); return key_cap_load(token, state, hash, sizeof token->channel); } static int agent_set_channel_key(state_t *state, lc_channel_t *chan) { size_t len = 0; /* set encryption key, if we have one */ if (state->defaults.seed) { E(STATE_VERBOSE, "generating key from seed\n"); lc_key_t enc; fprintf(stderr, "generating channel key\n"); unsigned char salt[crypto_pwhash_SALTBYTES]; crypto_generichash(salt, sizeof salt, (unsigned char *)state->defaults.seed, len, NULL, 0); sodium_mlock(state->defaults.chan_key, sizeof state->defaults.chan_key); enc.key = state->defaults.chan_key; enc.keylen = sizeof state->defaults.chan_key; lc_channel_setkey(chan, &enc, LC_CODE_SYMM); } return 0; } static int agent_recv(state_t *state) { char *chanstr; if (!(chanstr = arg_pop(state))) { fprintf(stderr, "%s\n", AGENT_ERR_NOCHANNEL); return (errno = EINVAL), -1; } lc_ctx_t *lctx; lc_socket_t *sock; lc_channel_t *chan; lc_keyring_t keyring = {0}; lc_filter_t filter; ssize_t byt = 0; int rc; lctx = lc_ctx_new(); if (!lctx) return -1; sock = lc_socket_new(lctx); if (!sock) goto err_ctx_free; if (state->ifx) lc_socket_bind(sock, state->ifx); chan = lc_channel_new(lctx, chanstr); if (!chan) goto err_ctx_free; if (lc_channel_bind(sock, chan)) goto err_ctx_free; int encryption = 0; if (state->defaults.seed) { encryption = LC_CODE_SYMM; /* set encryption key, if we have one */ if (agent_set_channel_key(state, chan)) return -1; } lc_channel_coding_set(chan, LC_CODE_FEC_RQ | LC_CODE_FEC_OTI | encryption); /* set keyring + filter, join channel */ rc = keyring_load(state, &keyring); if (rc == -1) goto err_ctx_free; E(STATE_VERBOSE, "filter with %zu keys loaded\n", keyring.nkeys); filter.keyring = &keyring; filter.capbits = 0; lc_channel_filter_set(chan, &filter); if (lc_channel_join(chan)) goto err_ctx_free; agent_datahead_t datahead = {0}; char buf[32768 - sizeof datahead]; struct iovec iov[2] = {0}; struct msghdr msg = {0}; msg.msg_iov = iov; msg.msg_iovlen = sizeof iov / sizeof iov[0]; iov[0].iov_base = &datahead; iov[0].iov_len = sizeof datahead; iov[1].iov_base = buf; iov[1].iov_len = sizeof buf; recv_again: memset(&datahead, 0, sizeof datahead); while (!(datahead.flags & AGENT_EOF)) { byt = lc_channel_recvmsg(chan, &msg, 0); if (byt == -1) { perror("lc_channel_recvmsg"); rc = -1; break; } /* write data to stdout */ byt -= sizeof datahead; fwrite(buf, 1, byt, stdout); E(STATE_VERBOSE, "%zi bytes received\n", byt); } if (STATE(state, STATE_FOLLOW)) goto recv_again; keyring_freekeys(&keyring); lc_keyring_free(&keyring); err_ctx_free: lc_ctx_free(lctx); return (byt == -1) ? -1 : 0; } static int agent_send(state_t *state) { lc_ctx_t *lctx; lc_socket_t *sock; lc_channel_t *chan; lc_token_t token; lc_key_t ssk; char *chanstr; char *data; FILE *stream = NULL; size_t len = 0; ssize_t byt = -1; if (!(chanstr = arg_pop(state))) { fprintf(stderr, "%s\n", AGENT_ERR_NOCHANNEL); return (errno = EINVAL), -1; } data = arg_pop(state); if (STATE(state, STATE_STDIN)) { stream = stdin; } else if (data) { len = strlen(data); if (len) { stream = fmemopen(data, len, "r"); if (!stream) return -1; } } lctx = lc_ctx_new(); if (!lctx) goto close_stream; sock = lc_socket_new(lctx); if (!sock) goto err_ctx_free; if (state->ifx) lc_socket_bind(sock, state->ifx); if (STATE(state, STATE_LOOPBACK) && lc_socket_loop(sock, 1)) goto err_ctx_free; chan = lc_channel_new(lctx, chanstr); if (!chan) goto err_ctx_free; if (lc_channel_bind(sock, chan)) goto err_ctx_free; if (agent_load_channel_token(&token, state, chanstr) == -1) { /* no token for channel, self_sign */ DEBUG("no channel token, self-signing\n"); lc_keypair_t *signing_key = &state->defaults.keyring.s; char *bearer_key = state->defaults.keyring.phex; uint64_t valid_sec = KEY_CAP_DEFAULT_SECONDS; uint8_t capbits = 0; unsigned char bearer_psk[crypto_sign_PUBLICKEYBYTES]; key_combo_hex2psk(bearer_psk, sizeof bearer_psk, bearer_key); if (lc_token_new(&token, signing_key, bearer_psk, chan, capbits, valid_sec)) goto err_ctx_free; } else DEBUG("using channel token\n"); int encryption = 0; if (state->defaults.seed) { encryption = LC_CODE_SYMM; /* set encryption key, if we have one */ if (agent_set_channel_key(state, chan)) goto err_ctx_free; } lc_channel_coding_set(chan, LC_CODE_FEC_RQ | LC_CODE_FEC_OTI | encryption); lc_channel_rq_overhead(chan, state->overhead); lc_channel_ratelimit(chan, state->bpslimit, 0); /* set sending key + token */ ssk.key = state->defaults.keyring.s.sk; ssk.keylen = crypto_sign_SECRETKEYBYTES; lc_channel_setkey(chan, &ssk, LC_CODE_SIGN); lc_channel_token_set(chan, &token); /* send */ agent_datahead_t datahead = {0}; char buf[32768 - sizeof datahead]; struct iovec iov[2] = {0}; struct msghdr msg = {0}; struct timespec ts; size_t byt_out = 0; msg.msg_iov = iov; msg.msg_iovlen = sizeof iov / sizeof iov[0]; iov[0].iov_base = &datahead; iov[0].iov_len = sizeof datahead; iov[1].iov_base = buf; datahead.flags = 0; do { if (stream) { byt = fread(buf, 1, sizeof buf, stream); if (byt == -1) break; } else byt = 0; if (!stream || feof(stream)) datahead.flags |= AGENT_EOF; iov[1].iov_len = byt; if (clock_gettime(CLOCK_TAI, &ts) == 0) { datahead.ts = htobe64(ts.tv_sec * 1e9 + ts.tv_nsec); } byt = lc_channel_sendmsg(chan, &msg, 0); if (byt == -1) DEBUG("lc_channel_sendmsg: %s\n", strerror(errno)); if (byt > 0) byt_out += byt - sizeof datahead; } while (stream && !feof(stream)); if (STATE(state, STATE_VERBOSE)) { if (byt == -1) perror("lc_channel_send"); else fprintf(stderr, "%zu bytes sent\n", byt_out); } err_ctx_free: lc_ctx_free(lctx); close_stream: if (stream) fclose(stream); return (byt < 0) ? -1 : 0; } static int agent_pidfile_unlock(state_t *state) { if (flock(state->lockfd, LOCK_UN) == -1) return -1; close(state->lockfd); state->lockfd = 0; return 0; } static int agent_pidfile_write(state_t *state) { char pidbuf[21]; /* fits UINT64_MAX + NUL */ ssize_t len; if (ftruncate(state->lockfd, 0) == -1) goto unlock_pidfile; if (snprintf(pidbuf, sizeof pidbuf, "%ld", (long)state->pid) < 0) goto unlock_pidfile; len = strlen(pidbuf); if (write(state->lockfd, pidbuf, len) != len) goto unlock_pidfile; return 0; unlock_pidfile: agent_pidfile_unlock(state); return -1; } static int agent_pidfile_read(state_t *state) { char pidbuf[21] = {0}; /* fits UINT64_MAX + NUL */ char *pidfile; int rc = -1; pidfile = state_pidfile(state); if (!pidfile) return -1; state->lockfd = open(pidfile, O_RDONLY); if (state->lockfd == -1) goto free_pidfile; lseek(state->lockfd, 0, SEEK_SET); if (read(state->lockfd, pidbuf, sizeof pidbuf) > 0) { state->pid = atoi(pidbuf); rc = 0; } free_pidfile: free(pidfile); return rc; } /* open and lock pidfile. return -1 on error, fd of pidfile on success */ static int agent_pidfile_lock(state_t *state, int block) { char *pidfile; int fd, flags; pidfile = state_pidfile(state); if (!pidfile) return -1; create_pidfile: fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) goto free_pidfile; flags = fcntl(fd, F_GETFD); if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) goto close_pidfile; if (flock(fd, LOCK_EX | block) == -1) { if (agent_pidfile_read(state) == 0) { int rc = kill(state->pid, 0); if (rc == -1 && errno == ESRCH) { /* stale lockfile */ close(fd); if (unlink(pidfile) == 0) goto create_pidfile; goto free_pidfile; } } goto close_pidfile; } state->lockfd = fd; free_pidfile: free(pidfile); return fd; close_pidfile: close(fd); fd = -1; goto free_pidfile; } static int agent_reload(state_t *state) { if (agent_pidfile_read(state)) return -1; E(STATE_VERBOSE, "sending SIGHUP to pid %i\n", state->pid); return kill(state->pid, SIGHUP); } static int agent_stop(state_t *state) { if (agent_pidfile_read(state)) return -1; E(STATE_VERBOSE, "sending SIGINT to pid %i\n", state->pid); return kill(state->pid, SIGINT); } /* lc_channel_new() with a wrapper to support older librecast (< 0.9) hashes */ static lc_channel_t *agent_channel_new(state_t *state, char *channel_name, unsigned char *hash) { lc_ctx_t *lctx = lc_socket_ctx(state->sock); lc_channel_t *chan; #ifndef HAVE_LC_CHANNEL_GET_HASH /* support librecast < 0.9 */ struct in6_addr addr = {0}; hash_generic(hash, HASHSIZE, (unsigned char *)channel_name, strlen(channel_name)); addr.s6_addr[0] = 0xff; addr.s6_addr[1] = 0x1e; memcpy(&addr.s6_addr[2], hash, 14); chan = lc_channel_init_grp(lctx, &addr, LC_DEFAULT_PORT); #else (void)hash; chan = lc_channel_new(lctx, channel_name); #endif return chan; } int agent_load_config(state_t *state, int argc, char *argv[], char *home) { int rc; if (!state->overhead) state->overhead = RQ_OVERHEAD * 2; if (state_defaults_set(state)) return -1; if (state_parse_args(state, argc, argv)) return -1; if ((rc = state_dirs(state, home))) goto err_free_state; if (state->verb > VERB_VERSION) { /* every verb except HELP and VERSION require a key */ rc = key_load_default(state, &state->defaults.keyring); if (rc) goto err_free_state; } if ((rc = state_parse_configfile(state, state->rcfile))) { if (errno != ENOENT) goto err_free_state; } else E(STATE_VERBOSE, "loading rcfile %s\n", state->rcfile); return 0; err_free_state: free_state(state); return -1; } static int agent_join_channel(state_t *state, lc_filter_t *filter, state_chan_t *a) { unsigned char hash[HASHSIZE]; int onstart = 0; if ((a->flags & CHAN_ONSTART)) onstart++; if (!(a->flags & CHAN_JOIN)) return onstart; a->chan = agent_channel_new(state, a->chan_name, hash); if (!a->chan) return -1; if (lc_channel_bind(state->sock, a->chan)) return -1; lc_channel_coding_set(a->chan, LC_CODE_FEC_RQ | LC_CODE_FEC_OTI); lc_channel_filter_set(a->chan, filter); if (lc_channel_join(a->chan) == -1) return -1; DEBUG("join channel '%s'\n", a->chan_name); return onstart; } static lc_socket_t *agent_socket_new(void) { lc_ctx_t *lctx = lc_ctx_new(); if (!lctx) return NULL; return lc_socket_new(lctx); } static int agent_reload_rcfile(state_t *state) { state_t newstate = {0}; unsigned char hash[HASHSIZE]; int rc; g_reload = 0; rc = agent_load_config(&newstate, state->argc, state->argv, state->dir_home); if (rc == -1) return -1; newstate.sock = (state->sock) ? state->sock : agent_socket_new(); if (!newstate.sock) return -1; if ((newstate.logfile && !state->logfile)||(!newstate.logfile && state->logfile) ||((newstate.logfile && state->logfile) && !strcmp(newstate.logfile, state->logfile))) { /* logfile has changed */ log_open(&newstate, NULL); log_close(state); } else { newstate.log = state->log; state->log = NULL; } if (newstate.ifx != state->ifx && state->chan_head && newstate.chan_head) { /* network ifx has changed, rebind socket */ lc_socket_t *sock = lc_channel_socket(state->chan_head->chan); rc = lc_socket_bind(sock, newstate.ifx); } newstate.lockfd = state->lockfd; /* close channels we don't need */ for (state_chan_t *a = state->chan_head; a; ) { int found = 0; for (state_chan_t *b = newstate.chan_head; b; b = b->next) { if (!strcmp(a->chan_name, b->chan_name)) { b->chan = a->chan; a->chan = NULL; found++; break; } } if (!found) { /* close channel */ lc_channel_part(a->chan); } state_chan_t *tmp = a; a = a->next; if (tmp == state->chan_head) state->chan_head = a; state_channel_free(tmp); } /* open new channels */ for (state_chan_t *a = newstate.chan_head; a; a = a->next) { if (!a->chan && (a->flags & CHAN_JOIN)) { a->chan = agent_channel_new(&newstate, a->chan_name, hash); if (!a->chan) return -1; } } state->chan_head = NULL; free_state(state); *state = newstate; return rc; } static int agent_channel_exec(state_t *state, state_chan_t *schan, struct msghdr *msg, ssize_t len) { enum { READ, WRITE }; int rc = 0; char cwd[PATH_MAX]; if (!(schan->flags & CHAN_ENABLE)) { DEBUG("channel '%s' is not enabled.\n", schan->chan_name); return 0; } if (getcwd(cwd, sizeof cwd) == NULL) return -1; if (state->dir_home || schan->dir) { /* first change to home dir */ if (state->dir_home && chdir(state->dir_home)) return -1; /* command directory is relative to home dir (or absolute) */ if (schan->dir && chdir(schan->dir)) goto restore_cwd; } for (state_cmd_t *cmd = schan->cmd; cmd; cmd = cmd->next) { pid_t pid; int pipefd[2]; if (msg) { rc = pipe(pipefd); if (rc == -1) break; } DEBUG("channel '%s' executing cmd '%s'\n", schan->chan_name, cmd->cmd); pid = fork(); if (pid == -1) break; if (!pid) { if (msg) { close(pipefd[WRITE]); dup2(pipefd[READ], STDIN_FILENO); } execl("/bin/sh", "sh", "-c", cmd->cmd, (char *) NULL); /* only reached on error */ _exit(EXIT_FAILURE); } if (msg) { agent_datahead_t *datahead = msg->msg_iov[0].iov_base; ssize_t byt; write_again: close(pipefd[READ]); /* close read end */ byt = write(pipefd[WRITE], msg->msg_iov[1].iov_base, len); DEBUG("channel '%s' piped %zi bytes to cmd\n", schan->chan_name, byt); if (byt == len && !(datahead->flags & AGENT_EOF)) { byt = lc_channel_recvmsg(schan->chan, msg, 0); if (byt != -1) { len = byt - (ssize_t)sizeof *datahead; goto write_again; } } close(pipefd[WRITE]); DEBUG("channel '%s' closing pipe\n", schan->chan_name); } int wstatus; waitpid(pid, &wstatus, 0); /* command must succeed, unless CMD_TRY is set */ rc = (WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : -1; DEBUG("channel '%s' cmd exited %i\n", schan->chan_name, rc); if (cmd->flags & CMD_TRY) continue; if (rc) break; } if (schan->flags & CHAN_RELOAD) { INFO("reload %s\n", PACKAGE_NAME); agent_reload_rcfile(state); } else if (schan->flags & CHAN_RESTART) { INFO("restarting %s\n", PACKAGE_NAME); execvp(PACKAGE_NAME, state->argv); perror(PACKAGE_NAME); ERROR("failed to restart agent\n"); } restore_cwd: if (chdir(cwd)) return -1; return rc; } static int agent_server(state_t *state) { char buf[32768]; lc_ctx_t *lctx; lc_channel_t *dst; state_chan_t *state_chan; lc_keyring_t keyring; lc_filter_t filter; ssize_t byt; int onstart = 0; int rc; struct sigaction act = {0}; act.sa_sigaction = &handle_signal; if (sigaction(SIGINT, &act, NULL) == -1) { perror("sigaction"); _exit(EXIT_FAILURE); } if (sigaction(SIGHUP, &act, NULL) == -1) { perror("sigaction"); _exit(EXIT_FAILURE); } E(STATE_VERBOSE, "%s started (pid: %i)\n", PACKAGE_NAME, getpid()); INFO("%s started (pid: %i)\n", PACKAGE_NAME, getpid()); /* prepare context & socket */ lctx = lc_ctx_new(); if (!lctx) return -1; state->sock = lc_socket_new(lctx); if (!state->sock) goto err_ctx_free; if (state->ifx) lc_socket_bind(state->sock, state->ifx); /* wait for at least one IPv6 multicast interface to be ready */ for (int ifaces = 0; !ifaces; ) { struct ifaddrs *ifap; rc = getifaddrs(&ifap); if (rc == -1) return -1; for (struct ifaddrs *ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_addr->sa_family != AF_INET6) continue; /* IPv6 */ if (!(ifa->ifa_flags & IFF_MULTICAST)) continue; /* multicast */ /* test bind() on interface to ensure DAD is finished */ int sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1) continue; rc = bind(sock, ifa->ifa_addr, sizeof(struct sockaddr_in6)); close(sock); if (rc == -1) continue; ifaces++; } freeifaddrs(ifap); if (ifaces) { DEBUG("%i interfaces detected on startup\n", ifaces); break; } usleep(100); } /* prepare keyring + filter */ rc = keyring_load(state, &keyring); if (rc == -1) goto err_ctx_free; E(STATE_VERBOSE, "filter with %zu keys loaded\n", keyring.nkeys); filter.keyring = &keyring; filter.capbits = 0; /* iterate through channels, join, set filter */ for (state_chan_t *a = state->chan_head; a; a = a->next) { rc = agent_join_channel(state, &filter, a); if (rc == -1) goto err_free_keys; onstart += rc; } /* all channels now listening, run any onstart events */ if (onstart) { DEBUG("processing onstart events\n"); for (state_chan_t *a = state->chan_head; a; a = a->next) { if (a->flags & CHAN_ONSTART) agent_channel_exec(state, a, NULL, 0); } } /* server loop */ agent_datahead_t datahead; struct iovec iov[2]; struct msghdr msg = {0}; iov[0].iov_base = &datahead; iov[0].iov_len = sizeof datahead; iov[1].iov_base = buf; iov[1].iov_len = sizeof buf; msg.msg_iov = iov; msg.msg_iovlen = sizeof iov / sizeof iov[0]; for (;;) { byt = lc_socket_multi_recvmsg(state->sock, &msg, 0, &dst); if (byt == -1) { if (errno != EINTR) break; if (g_reload) agent_reload_rcfile(state); if (g_stop) break; continue; } struct in6_addr *addr = lc_channel_in6addr(dst); ssize_t len = byt - sizeof datahead; state_chan = state_chan_by_addr(state, addr); DEBUG("channel '%s' received %zi bytes\n", state_chan->chan_name, len); if (state_chan) rc = agent_channel_exec(state, state_chan, &msg, len); } INFO("%s stopping (pid: %i)\n", PACKAGE_NAME, getpid()); err_free_keys: keyring_freekeys(&keyring); lc_keyring_free(&keyring); err_ctx_free: lc_ctx_free(lctx); return agent_pidfile_unlock(state); } static int agent_start(state_t *state) { if (agent_pidfile_lock(state, LOCK_NB) == -1) { agent_pidfile_read(state); fprintf(stderr, AGENT_ERR_ISRUNNING, PACKAGE_NAME, state->pid); fputc('\n', stderr); return -1; } pid_t pid = fork(); if (pid == -1) { agent_pidfile_unlock(state); return -1; } if (pid) { struct sigaction act = {0}; act.sa_sigaction = &handle_signal; if (sigaction(SIGCONT, &act, NULL) == -1) return -1; state->pid = pid; pause(); /* wait until child has written pid */ } else { state->pid = getpid(); int rc = agent_pidfile_write(state); kill(getppid(), SIGCONT); /* tell parent we've written pid */ if (rc == -1) return -1; rc = agent_server(state); free_state(state); exit((rc == 0) ? EXIT_SUCCESS : EXIT_FAILURE); } return 0; } static int agent_exec(state_t *state) { char *chanstr = arg_pop(state); if (chanstr) { /* find channel */ state_chan_t *state_chan = state_chan_by_name(state, chanstr); if (!state_chan) { fprintf(stderr, AGENT_ERR_NOSUCHCHANNEL, chanstr); fputc('\n', stderr); return (errno = EINVAL), -1; } /* execute channel commands */ return agent_channel_exec(state, state_chan, NULL, 0); } return 0; } int agent_run(state_t *state) { int rc = -1; #define X(a,b,c,d) case c: return d(state); switch (state->verb) { STATE_VERBS } #undef X return rc; } int agent(state_t *state, int argc, char *argv[]) { int rc; if (agent_load_config(state, argc, argv, NULL)) return EXIT_FAILURE; rc = agent_run(state); free_state(state); return (rc == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } lcagent/src/agent.h000066400000000000000000000020131501702040600145200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett Sheffield */ #ifndef _AGENT_H #define _AGENT_H #include #include #include #include #include #include #define AGENT_ERR_ISRUNNING "%s already running (pid: %i)" #define AGENT_ERR_NOACTION "command requires an action" #define AGENT_ERR_NOCHANNEL "command requires a channel" #define AGENT_ERR_NOSUCHCHANNEL "channel '%s' is not defined in rcfile" #define AGENT_ERR_NOKEY "command requires key" /* fallback to CLOCK_REALTIME on non-Linux */ #ifndef CLOCK_TAI #define CLOCK_TAI CLOCK_REALTIME #endif enum { AGENT_EOF = 1, }; typedef struct agent_datahead_s { uint8_t flags; uint64_t ts; } __attribute__((__packed__)) agent_datahead_t; extern int lineno; int agent_run(state_t *state); int agent_load_config(state_t *state, int argc, char *argv[], char *home); int agent(state_t *state, int argc, char *argv[]); #endif /* _AGENT_H */ lcagent/src/config.h.in000066400000000000000000000143341501702040600153050ustar00rootroot00000000000000/* src/config.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the 'arc4random_uniform' function. */ #undef HAVE_ARC4RANDOM_UNIFORM /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the 'getcwd' function. */ #undef HAVE_GETCWD /* Define to 1 if you have the 'getpagesize' function. */ #undef HAVE_GETPAGESIZE /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the 'lc_channel_get_hash' function. */ #undef HAVE_LC_CHANNEL_GET_HASH /* Define to 1 if you have the header file. */ #undef HAVE_LIBINTL_H /* Define to 1 if you have the 'lcrq' library (-llcrq). */ #undef HAVE_LIBLCRQ /* Define to 1 if you have the 'librecast' library (-llibrecast). */ #undef HAVE_LIBLIBRECAST /* Define to 1 if you have the header file. */ #undef HAVE_LIBRECAST_CRYPTO_H /* Define to 1 if you have the header file. */ #undef HAVE_LIBRECAST_KEY_H /* Define to 1 if you have the header file. */ #undef HAVE_LIBRECAST_NET_H /* Define to 1 if you have the header file. */ #undef HAVE_LIBRECAST_TYPES_H /* Define to 1 if you have the 'sodium' library (-lsodium). */ #undef HAVE_LIBSODIUM /* Define to 1 if you have the 'memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if the system has the type 'ptrdiff_t'. */ #undef HAVE_PTRDIFF_T /* Define to 1 if you have the 'realpath' function. */ #undef HAVE_REALPATH /* Define to 1 if you have the 'setlocale' function. */ #undef HAVE_SETLOCALE /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the 'strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the 'strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILE_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_IOCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Define to 1 if 'lex' declares 'yytext' as a 'char *' by default, not a 'char[]'. */ #undef YYTEXT_POINTER /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define as 'int' if doesn't define. */ #undef gid_t /* Define to '__inline__' or '__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to the type of a signed integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef int16_t /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef int32_t /* Define to the type of a signed integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef int8_t /* Define to 'int' if does not define. */ #undef mode_t /* Define to 'long int' if does not define. */ #undef off_t /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported only directly. */ #undef restrict /* Work around a bug in older versions of Sun C++, which did not #define __restrict__ or support _Restrict or __restrict__ even though the corresponding Sun C compiler ended up with "#define restrict _Restrict" or "#define restrict __restrict__" in the previous line. This workaround can be removed once we assume Oracle Developer Studio 12.5 (2016) or later. */ #if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__ # define _Restrict # define __restrict__ #endif /* Define as 'unsigned int' if doesn't define. */ #undef size_t /* Define as 'int' if doesn't define. */ #undef ssize_t /* Define as 'int' if doesn't define. */ #undef uid_t /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef uint16_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t lcagent/src/cov.c000066400000000000000000000000471501702040600142110ustar00rootroot00000000000000/* empty modeling file for coverity */ lcagent/src/grammar.y000066400000000000000000000062371501702040600151050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett Sheffield */ %{ #include #include #include #include #include #include #include void yyerror(state_t *state, const char *str); int yylex(void); int yywrap(void); int reqs = 0; %} %parse-param { state_t *state } %union { int ival; char *sval; } %token AUTH_KEY %token CHANNEL %token CHROOT %token COMMAND %token COMMENT %token TOK_DEBUG %token DBLQUOTEDSTRING %token DIRECTORY %token ENABLE %token JOIN %token LOGFILE %token NOENABLE %token NOJOIN %token NUMBER %token ONSTART %token RELOAD %token RESTART %token SEED %token STATUS %token STATUS_COMMAND %token STDERR %token STDIN %token STDOUT %token STRING %token TRY %% globals: /* empty */ | globals global ; global: COMMENT { /* skip comment */ } | AUTH_KEY STRING { if (state_authkey_set(state, $2) == -1) YYABORT; } | CHANNEL STRING { if (state_push_channel(state, $2) == -1) YYABORT; } | CHROOT { if (state->chan_head) state->chan_head->flags |= CHAN_CHROOT; else state->defaults.flags |= CHAN_CHROOT; } | COMMAND STRING { if (state_push_command(state, $2, 0) == -1) YYABORT; } | TOK_DEBUG { state->flag |= STATE_DEBUG; } | TRY STRING { if (state_push_command(state, $2, CMD_TRY) == -1) YYABORT; } | DIRECTORY STRING { if (state_dir_set(state, $2) == -1) YYABORT; } | LOGFILE STRING { if (state_logfile_set(state, $2) == -1) YYABORT; } | ENABLE { if (state->chan_head) state->chan_head->flags |= CHAN_ENABLE; else state->defaults.flags |= CHAN_ENABLE; } | NOENABLE { if (state->chan_head) state->chan_head->flags &= ~CHAN_ENABLE; else state->defaults.flags &= ~CHAN_ENABLE; } | JOIN { if (state->chan_head) state->chan_head->flags |= CHAN_JOIN; else state->defaults.flags |= CHAN_JOIN; } | NOJOIN { if (state->chan_head) state->chan_head->flags &= ~CHAN_JOIN; else state->defaults.flags &= ~CHAN_JOIN; } | ONSTART { if (state->chan_head) state->chan_head->flags |= CHAN_ONSTART; else state->defaults.flags |= CHAN_ONSTART; } | RELOAD { if (state->chan_head) state->chan_head->flags |= CHAN_RELOAD; else state->defaults.flags |= CHAN_RELOAD; } | RESTART { if (state->chan_head) state->chan_head->flags |= CHAN_RESTART; else state->defaults.flags |= CHAN_RESTART; } | SEED STRING { if (state_seed_set(state, $2) == -1) YYABORT; } | STATUS STRING { if (state_status_set(state, $2) == -1) YYABORT; } | STATUS_COMMAND STRING { if (state_statuscmd_set(state, $2) == -1) YYABORT; } | STDOUT STRING { if (state_stdout_set(state, $2) == -1) YYABORT; } | STDIN STRING { if (state_stdin_set(state, $2) == -1) YYABORT; } | STDERR STRING { if (state_stderr_set(state, $2) == -1) YYABORT; } ; %% void yyerror(state_t *state, const char *str) { (void) state; fprintf(stderr,"%s in rcfile %s, error on line %i\n", str, state->rcfile, lineno); } int yywrap(void) { return 1; } lcagent/src/key.c000066400000000000000000000266201501702040600142170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2020-2025 Brett Sheffield */ #include "key.h" #include #include #include #include #include #include #include #include /* extract public encryption key from combo hex key */ int key_combo_hex2pek(unsigned char *pek, size_t len, char *phex_combo) { return sodium_hex2bin(pek, len, phex_combo, HEXKEY_PUBLIC_CRYPTBYTES, NULL, NULL, NULL); } /* extract public signing key from combo hex key */ int key_combo_hex2psk(unsigned char *psk, size_t len, char *phex_combo) { char *pskhex = phex_combo + HEXKEY_PUBLIC_CRYPTBYTES; return sodium_hex2bin(psk, len, pskhex, HEXKEY_PUBLIC_SIGNBYTES, NULL, NULL, NULL); } /* extract secret encryption key from combo hex key */ int key_combo_hex2sek(unsigned char *sek, size_t len, char *shex_combo) { return sodium_hex2bin(sek, len, shex_combo, HEXKEY_SECRET_CRYPTBYTES, NULL, NULL, NULL); } /* extract secret signing key from combo hex key */ int key_combo_hex2ssk(unsigned char *ssk, size_t len, char *shex_combo) { char *sskhex = shex_combo + HEXKEY_SECRET_CRYPTBYTES; return sodium_hex2bin(ssk, len, sskhex, HEXKEY_SECRET_SIGNBYTES, NULL, NULL, NULL); } /* generate a combo keypair * the resulting encryption and signing keys are concatenated */ int key_gen_keys(key_combo_t *keyring) { if (lc_keypair_new(&keyring->s, LC_KEY_ENC) == -1) return -1; return lc_keypair_new(&keyring->e, LC_KEY_SIG); } int key_gen_combopair_hex(key_combo_t *keyring) { char *phex_sign = keyring->phex + HEXKEY_PUBLIC_CRYPTBYTES; char *shex_sign = keyring->shex + HEXKEY_SECRET_CRYPTBYTES; if (key_gen_keys(keyring)) return -1; sodium_bin2hex(keyring->phex, HEXKEY_PUBLIC_CRYPTBYTES + 1, keyring->e.pk, crypto_box_PUBLICKEYBYTES); sodium_bin2hex(phex_sign, HEXKEY_PUBLIC_SIGNBYTES + 1, keyring->s.pk, crypto_sign_PUBLICKEYBYTES); sodium_bin2hex(keyring->shex, HEXKEY_SECRET_CRYPTBYTES + 1, keyring->e.sk, crypto_box_SECRETKEYBYTES); sodium_bin2hex(shex_sign, HEXKEY_SECRET_SIGNBYTES + 1, keyring->s.sk, crypto_sign_SECRETKEYBYTES); return 0; } char *key_config_dir(char *home, size_t *len) { char *str; int rc; rc = snprintf(NULL, 0, "%s/%s", home, KEY_PATH); if (rc < 0) return NULL; *len = (size_t)rc + 1; str = malloc(*len); if (!str) return NULL; rc = snprintf(str, *len, "%s/%s", home, KEY_PATH); if ((size_t)rc != *len - 1) { free(str); return NULL; } return str; } int key_gen_combopair_write(char *home, key_combo_t *keyring) { char *str; size_t len; int fd, err, rc; /* create config directory */ rc = snprintf(NULL, 0, "mkdir -p %s/%s", home, KEY_PATH); if (rc < 0) return -1; len = (size_t)rc + 1; str = malloc(len); if (str == NULL) return -1; rc = snprintf(str, len, "mkdir -p %s/%s", home, KEY_PATH); rc = system(str); if (rc == -1) goto err_free_str; /* generate keypair */ rc = key_gen_combopair_hex(keyring); if (rc == -1) goto err_free_str; free(str); str = NULL; /* create file for writing, named for public key */ rc = snprintf(NULL, 0, "%s/%s/%s", home, KEY_PATH, keyring->phex); len = rc + 1; str = malloc(len); if (str == NULL) return -1; rc = snprintf(str, len, "%s/%s/%s", home, KEY_PATH, keyring->phex); fd = open(str, O_CREAT|O_EXCL|O_WRONLY|O_SYNC, S_IRUSR|S_IWUSR); if (fd == -1) goto err_free_str; /* write secret key to file */ ssize_t byt = write(fd, keyring->shex, KEY_SECRET_HEXLEN); close(fd); if ((size_t)byt != KEY_SECRET_HEXLEN) goto err_free_str; /* create default symlink if not exist */ char *linkpath; rc = snprintf(NULL, 0, "%s/%s/default", home, KEY_PATH); if (rc == -1) goto err_free_str; len = (size_t)rc + 1; linkpath = malloc(len); if (linkpath == NULL) { rc = -1; goto err_free_str; } rc = snprintf(linkpath, len, "%s/%s/default", home, KEY_PATH); if (rc == -1) goto err_free_linkpath; rc = symlink(keyring->phex, linkpath); if (rc == -1 && errno == EEXIST) rc = 0; err_free_linkpath: free(linkpath); err_free_str: err = errno; free(str); errno = err; return rc; } int key_combo_hex_decode(key_combo_t *keyring) { if (key_combo_hex2pek(keyring->e.pk, sizeof keyring->e.pk, keyring->phex)) return -1; if (key_combo_hex2psk(keyring->s.pk, sizeof keyring->s.pk, keyring->phex)) return -1; if (key_combo_hex2psk(keyring->s.sk, sizeof keyring->s.pk, keyring->phex)) return -1; if (key_combo_hex2sek(keyring->e.sk, sizeof keyring->e.sk, keyring->shex)) return -1; return key_combo_hex2ssk(keyring->s.sk, sizeof keyring->s.sk, keyring->shex); } int key_load_default(state_t *state, key_combo_t *keyring) { char *configdir; char *keyfile; size_t len; int rc = -1; configdir = key_config_dir(state->dir_home, &len); if (configdir == NULL) return -1; len += strlen(KEY_DEFAULT) + 1; keyfile = malloc(len); if (keyfile == NULL) goto err_free_configdir; strcpy(keyfile, configdir); strcat(keyfile, "/"); strcat(keyfile, KEY_DEFAULT); int fd; fd = open(keyfile, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { /* no key, create it */ free(keyfile); free(configdir); E(STATE_VERBOSE, "generating keys\n"); return key_gen_combopair_write(state->dir_home, keyring); } else goto err_free_keyfile; } else { /* load keys */ E(STATE_VERBOSE, "loading keys from %s\n", keyfile); memset(keyring->phex, 0, KEY_PUBLIC_HEXLEN + 1); rc = readlink(keyfile, keyring->phex, KEY_PUBLIC_HEXLEN); if (rc != KEY_PUBLIC_HEXLEN) goto err_close_fd; rc = read(fd, keyring->shex, KEY_SECRET_HEXLEN); if (rc == KEY_SECRET_HEXLEN) rc = key_combo_hex_decode(keyring); } err_close_fd: close(fd); err_free_keyfile: free(keyfile); err_free_configdir: free(configdir); return rc; } char *key_cap_path(state_t *state, unsigned char *chanhash, size_t len) { char *pathname; char hexchan[len * 2 + 1]; int sz = sizeof hexchan; int rc; if (sodium_init() == -1) return NULL; sodium_bin2hex(hexchan, sizeof hexchan, chanhash, len); rc = snprintf(NULL, 0, "%s/%.*s.token", state->dir_state, sz, hexchan); if (rc < 0) return NULL; rc++; pathname = malloc(rc); if (!pathname) return NULL; rc = snprintf(pathname, rc, "%s/%.*s.token", state->dir_state, sz, hexchan); return pathname; } int key_cap_load(lc_token_t *token, state_t *state, unsigned char *chanhash, size_t len) { char *pathname; int fd; int rc = -1; pathname = key_cap_path(state, chanhash, len); if (!pathname) return -1; fd = open(pathname, O_RDONLY); if (fd >= 0) { ssize_t byt = read(fd, token, sizeof *token); rc = (byt == sizeof *token) ? 0 : -1; close(fd); } free(pathname); return rc; } /* save the token to the state directory, named CHANNELHEX.token, * where CHANNELHEX is the hex value of the token's channel hash */ static int key_cap_save(state_t *state, lc_token_t *token, int flags) { char *pathname; int fd; int rc = -1; pathname = key_cap_path(state, token->channel, sizeof token->channel); if (!pathname) return -1; fd = creat(pathname, S_IRUSR | S_IWUSR); if (fd >= 0) { ssize_t byt = write(fd, token, sizeof *token); rc = (byt == sizeof *token) ? 0 : -1; close(fd); } if (flags & KEY_CAP_PATH) { fprintf(stderr, "%s\n", pathname); } free(pathname); return rc; } int key_cap_issue(state_t *state, lc_keypair_t *signing_key, uint8_t *bearer_key, lc_channel_t *chan, uint8_t capbits, uint64_t valid_sec, int flags) { lc_token_t token = {0}; int rc; /* sign token */ rc = lc_token_new(&token, signing_key, bearer_key, chan, capbits, valid_sec); if (rc == -1) return -1; if (flags & KEY_CAP_SAVE) { rc = key_cap_save(state, &token, flags); if (rc) return rc; } if (flags & KEY_CAP_SEND) { /* TODO send token */ return (errno = ENOSYS), -1; } return rc; } static char *keyring_path(state_t *state) { char *pathname; int rc; if (!state->dir_state) return NULL; rc = snprintf(NULL, 0, "%s/%s", state->dir_state, KEY_AUTH_FILE); if (rc < 0) return NULL; rc++; pathname = malloc(rc); if (!pathname) return NULL; rc = snprintf(pathname, rc, "%s/%s", state->dir_state, KEY_AUTH_FILE); return pathname; } static FILE *keyring_open(state_t *state) { FILE *f; char *pathname; pathname = keyring_path(state); if (!pathname) return NULL; E(STATE_VERBOSE, "opening keyring at '%s'\n", pathname); f = fopen(pathname, "a+"); free(pathname); if (!f) return NULL; return f; } void keyring_freekeys(lc_keyring_t *keyring) { for (size_t i = 0; i < keyring->nkeys; i++) free(keyring->key[i]); } int keyring_add(state_t *state, char *hexkey) { char keybuf[KEY_PUBLIC_HEXLEN + 1]; FILE *f; size_t byt; int rc; f = keyring_open(state); if (!f) { perror("keyring_open"); return -1; } rewind(f); while ((byt = fread(keybuf, sizeof keybuf, 1, f))) { if (strncmp(keybuf, hexkey, KEY_PUBLIC_HEXLEN) != 0) continue; fclose(f); return (errno = EEXIST), -1; /* key already in file */ } rc = fseek(f, 0, SEEK_END); if (rc == 0) rc = fprintf(f, "%s\n", hexkey); fclose(f); return (rc < 0) ? -1 : 0; } int keyring_load(state_t *state, lc_keyring_t *keyring) { char keybuf[KEY_PUBLIC_HEXLEN + 1]; uint8_t *psk; FILE *f; size_t byt; int nkeys = 0; int rc = 0; f = keyring_open(state); if (!f) return -1; rewind(f); while ((byt = fread(keybuf, sizeof keybuf, 1, f))) nkeys++; memset(keyring, 0, sizeof *keyring); if (nkeys) { rc = lc_keyring_init(keyring, nkeys); rewind(f); while (!rc && (byt = fread(keybuf, sizeof keybuf, 1, f))) { keybuf[KEY_PUBLIC_HEXLEN] = 0; psk = malloc(crypto_sign_PUBLICKEYBYTES); if (!psk) {rc = -1; break; } rc = key_combo_hex2psk(psk, crypto_sign_PUBLICKEYBYTES, keybuf); if (rc == -1) break; rc = lc_keyring_add(keyring, psk); if (rc == -1) break; } } fclose(f); return rc; } int keyring_del(state_t *state, char *hexkey) { char keybuf[KEY_PUBLIC_HEXLEN + 1]; FILE *f; struct stat sb; char *pathname; char *fbuf = NULL; size_t fbufsz; size_t byt; int nkeys = 0; int rc; pathname = keyring_path(state); if (!pathname) return -1; f = fopen(pathname, "r+"); free(pathname); if (!f) return -1; rc = fstat(fileno(f), &sb); if (rc == -1) goto err_fclose; while ((byt = fread(keybuf, sizeof keybuf, 1, f))) { if (strncmp(keybuf, hexkey, KEY_PUBLIC_HEXLEN) == 0) { rc = -1; fbufsz = sb.st_size - (nkeys + 1) * sizeof keybuf; if (fbufsz) { /* load remaining keys into buffer */ fbuf = malloc(fbufsz); if (!fbuf) goto err_fclose; byt = fread(fbuf, fbufsz, 1, f); if (byt != 1) break; rc = fseek(f, nkeys * sizeof keybuf, SEEK_SET); if (rc == -1) break; } /* truncate file */ byt = sb.st_size - sizeof keybuf; rc = ftruncate(fileno(f), byt); if (rc == -1) break; if (fbuf) { /* write tail keys to new file position */ byt = fwrite(fbuf, fbufsz, 1, f); if (byt != 1) break; } rc = 0; break; } else nkeys++; } if (fbuf) free(fbuf); err_fclose: fclose(f); return rc; } int keyring_show(state_t *state) { char keybuf[KEY_PUBLIC_HEXLEN + 1] = {0}; FILE *f; struct stat sb; char *pathname; size_t byt; int nkeys = 0; int rc; pathname = keyring_path(state); if (!pathname) return -1; f = fopen(pathname, "r+"); free(pathname); if (!f) return -1; rc = fstat(fileno(f), &sb); if (rc == -1) goto err_fclose; size_t off = 0; while ((byt = fread(keybuf + off, 1, sizeof keybuf - off, f))) { if ((byt+off) < KEY_PUBLIC_HEXLEN) { off += byt; continue; } keybuf[byt + off] = '\0'; printf("%s", keybuf); nkeys++; off = 0; } fflush(stdout); fprintf(stderr, "%i authorized key(s) found\n", nkeys); err_fclose: fclose(f); return rc; } lcagent/src/key.h000066400000000000000000000043471501702040600142260ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett Sheffield */ #ifndef _KEY_H #define _KEY_H 1 #include #include #include #include #include #define KEY_AUTH_FILE "authorized_keys" #define KEY_CAP_DEFAULT_SECONDS 0 /* never expire */ /* decode hex keyring */ int key_combo_hex_decode(key_combo_t *keyring); /* extract public encryption key from combo hex key */ int key_combo_hex2pek(unsigned char *pek, size_t len, char *phex_combo); /* extract public signing key from combo hex key */ int key_combo_hex2psk(unsigned char *psk, size_t len, char *phex_combo); /* extract secret encryption key from combo hex key */ int key_combo_hex2sek(unsigned char *sek, size_t len, char *shex_combo); /* extract secret signing key from combo hex key */ int key_combo_hex2ssk(unsigned char *ssk, size_t len, char *shex_combo); /* generate encryption + signing keys */ int key_gen_keys(key_combo_t *keyring); /* generate combo keypair, convert to hex */ int key_gen_combopair_hex(key_combo_t *keyring); /* generate keypair, write into directory */ int key_gen_combopair_write(char *home, key_combo_t *keyring); /* load default key, or create one */ int key_load_default(state_t *state, key_combo_t *keyring); char *key_config_dir(char *home, size_t *len); #if 0 int key_cap_sign(unsigned char *cap, size_t len, unsigned char *s_auth, unsigned char *p_bear, uint8_t capbits, char *res); #endif /* flags to pass to key_cap_issue */ enum { KEY_CAP_SAVE = 0x1, KEY_CAP_SEND = 0x2, KEY_CAP_PATH = 0x4, /* print path to cap */ }; /* return pathname for channel hash. free() after use */ char *key_cap_path(state_t *state, unsigned char *chanhash, size_t len); int key_cap_load(lc_token_t *token, state_t *state, unsigned char *chanhash, size_t len); int key_cap_issue(state_t *state, lc_keypair_t *signing_key, uint8_t *bearer_key, lc_channel_t *chan, uint8_t capbits, uint64_t valid_sec, int flags); int keyring_add(state_t *state, char *hexkey); int keyring_del(state_t *state, char *hexkey); int keyring_show(state_t *state); int keyring_load(state_t *state, lc_keyring_t *keyring); void keyring_freekeys(lc_keyring_t *keyring); #endif /* _KEY_H */ lcagent/src/lcagent.c000066400000000000000000000003551501702040600150410ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024 Brett Sheffield */ #include int main(int argc, char *argv[]) { state_t state = {0}; return agent(&state, argc, argv); } lcagent/src/lexer.l000066400000000000000000000030351501702040600145520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett Sheffield */ %option noinput %option nounput %{ #include #include #include #include #include %} %% auth_key return AUTH_KEY; channel return CHANNEL; chroot return CHROOT; command return COMMAND; cmd return COMMAND; enable return ENABLE; debug return TOK_DEBUG; directory return DIRECTORY; disable return NOENABLE; noenable return NOENABLE; join return JOIN; logfile return LOGFILE; nojoin return NOJOIN; onstart return ONSTART; reload return RELOAD; restart return RESTART; seed return SEED; status return STATUS; status-command return STATUS_COMMAND; stderr return STDERR; stdin return STDIN; stdout return STDOUT; try return TRY; \"[^"]*["] { yylval.sval = yytext + 1; if (yylval.sval[yyleng - 2] != '"') fprintf(stderr, "double quoted string not closed\n"); else yylval.sval[yyleng-2] = 0; return STRING; } \'[^']*['] { yylval.sval = yytext + 1; if (yylval.sval[yyleng - 2] != '\'') fprintf(stderr, "single quoted string not closed\n"); else yylval.sval[yyleng-2] = 0; return STRING; } [a-zA-Z0-9\.\-/'"]+ { yylval.sval = yytext; return STRING; } \n { ++lineno; /* ignore newline */ } [ \t]+ /* ignore whitespace */; \#[^\n]* return COMMENT; %% lcagent/src/log.c000066400000000000000000000027151501702040600142070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include #include #include #include #include #include int log_close(state_t *state) { if (state->log && fclose(state->log)) return -1; return state->log = NULL, 0; } int log_open(state_t *state, const char *filename) { const char *logfile = (filename) ? filename : state->logfile; if (!logfile) return (state->log = stderr), 0; state->log = fopen(logfile, "a"); if (filename) state->logfile = (char *)filename; if (state->log == NULL) { perror("fopen"); return -1; } return 0; } int log_write(state_t *state, const char *fmt, ...) { va_list ap; if (!state->log && log_open(state, NULL)) return -1; /* if using a logfile or on a terminal */ if (state->logfile || isatty(fileno(state->log))) { /* output ISO8601 date */ char s[] = "1900-12-31T23:59:59.999+0000"; struct timeval tv; struct timezone tz; struct tm *local; size_t off = 0; if (gettimeofday(&tv, &tz) == -1) return -1; local = localtime(&tv.tv_sec); off = strftime(s, sizeof s - off, "%FT%T", local); off += snprintf(s + off, sizeof s - off, ".%03ld", (long int)tv.tv_usec/1000); off += strftime(s + off, sizeof s - off, "%z", local); fflush(state->log); fprintf(state->log, "%s ", s); } va_start(ap, fmt); vfprintf(state->log, fmt, ap); va_end(ap); fflush(state->log); return 0; } lcagent/src/log.h000066400000000000000000000011071501702040600142060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #ifndef _LOG_H #define _LOG_H #include #include #include #define INFO(...) log_write(state, __VA_ARGS__) #define DEBUG(...) if (state->flag & STATE_DEBUG) INFO(__VA_ARGS__) #define ERROR(...) { fprintf(stderr, "%s: ", PACKAGE_NAME); fprintf(stderr, __VA_ARGS__); } int log_open(state_t *state, const char *filename); int log_close(state_t *state); int log_write(state_t *state, const char *fmt, ...); #endif /* _LOG_H */ lcagent/src/state.c000066400000000000000000000301111501702040600145350ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett Sheffield */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int yyparse(state_t *state); /* default channel state */ state_chan_t state_channel_defaults = { .flags = CHAN_ENABLE | CHAN_JOIN, }; state_chan_t *state_channel_free(state_chan_t *a) { state_chan_t *tmp = a; free(a->auth_key); free(a->chan_name); free(a->dir); free(a->seed); for (state_cmd_t *cmd = a->cmd; cmd;) { state_cmd_t *tmp = cmd; free(cmd->cmd); cmd = cmd->next; free(tmp); } a = a->next; free(tmp); return a; } void free_state(state_t *state) { if (state->log) log_close(state); free(state->dir_config); free(state->dir_cache); free(state->dir_state); free(state->dir_data); free(state->defaults.auth_key); free(state->defaults.chan_name); free(state->defaults.dir); free(state->defaults.seed); free(state->rcfile); free(state->logfile); for (state_chan_t *a = state->chan_head; a; ) { a = state_channel_free(a); } state->chan_head = NULL; } static void freesv(void *ptr) { int err = errno; free(ptr); errno = err; } static int mkdirp(const char *pathname, size_t len) { char mkd[] = "mkdir -p "; char cmd[sizeof mkd + len]; snprintf(cmd, sizeof cmd, "%s%s", mkd, pathname); return system(cmd); } static char *state_dir_create(state_t *state, const char *xdg, const char *subdir) { char *dir = NULL; int len; if (xdg) { len = snprintf(NULL, 0, "%s/%s", xdg, PACKAGE_NAME); if (len == -1) return NULL; dir = malloc(len + 1); if (!dir) return NULL; len = snprintf(dir, len + 1, "%s/%s", xdg, PACKAGE_NAME); if (len == -1) goto err_free_dir; } else { len = snprintf(NULL, 0, "%s/%s/%s", state->dir_home, subdir, PACKAGE_NAME); if (len == -1) return NULL; dir = malloc(len + 1); if (!dir) return NULL; len = snprintf(dir, len + 1, "%s/%s/%s", state->dir_home, subdir, PACKAGE_NAME); if (len == -1) goto err_free_dir; } if (mkdirp(dir, len) == 0) return dir; err_free_dir: freesv(dir); return NULL; } static int state_rcfile(state_t *state) { int len; len = snprintf(NULL, 0, "%s/%s", state->dir_home, LCAGENTRC); if (len < 0) return -1; len++; state->rcfile = malloc(len); if (!state->rcfile) return -1; snprintf(state->rcfile, len, "%s/%s", state->dir_home, LCAGENTRC); return 0; } int state_dirs(state_t *state, char *home) { char *xdg_config_home = NULL; char *xdg_cache_home = NULL; char *xdg_state_home = NULL; char *xdg_data_home = NULL; state->dir_home = (home) ? home : getenv("HOME"); if (!state->dir_home) errx(EXIT_FAILURE, "HOME environment variable not set"); /* if home set, construct dirs else try env, then construct dirs * */ if (!home) { xdg_config_home = getenv("XDG_CONFIG_HOME"); xdg_cache_home = getenv("XDG_CACHE_HOME"); xdg_state_home = getenv("XDG_STATE_HOME"); xdg_data_home = getenv("XDG_DATA_HOME"); } if (!(state->dir_config = state_dir_create(state, xdg_config_home, ".config"))) return -1; if (!(state->dir_cache = state_dir_create(state, xdg_cache_home, ".cache"))) return -1; if (!(state->dir_state = state_dir_create(state, xdg_state_home, ".local/state"))) return -1; if (!(state->dir_data = state_dir_create(state, xdg_data_home, ".local/share"))) return -1; if (state_rcfile(state)) return -1; return 0; } state_chan_t *state_chan_by_addr(state_t *state, struct in6_addr *addr) { for (state_chan_t *a = state->chan_head; a; a = a->next) { if (a->chan && !memcmp(addr, lc_channel_in6addr(a->chan), sizeof(struct in6_addr))) return a; } return NULL; } state_chan_t *state_chan_by_name(state_t *state, char *name) { for (state_chan_t *a = state->chan_head; a; a = a->next) { if (!strcmp(a->chan_name, name)) return a; } return NULL; } int state_authkey_set(state_t *state, char *authkey) { state_chan_t *achan = (state->chan_head) ? state->chan_head : &state->defaults; char *tmp; size_t hexbyt = crypto_sign_PUBLICKEYBYTES * 2; size_t sz; achan->auth_keys++; sz = hexbyt * achan->auth_keys; tmp = realloc(achan->auth_key, sz); if (!tmp) return -1; achan->auth_key = tmp; tmp = achan->auth_key + hexbyt * (achan->auth_keys - 1); memcpy(tmp, authkey, hexbyt); return 0; } int state_push_channel(state_t *state, char *channel_name) { state_chan_t *a = malloc(sizeof (state_chan_t)); if (!a) return -1; memset(a, 0, sizeof(state_chan_t)); *a = state_channel_defaults; a->next = state->chan_head; a->chan_name = strdup(channel_name); if (!a->chan_name) return freesv(a), -1; a->chan_status_cmd = state->defaults.chan_status_cmd; a->flags = state->defaults.flags; a->dir = state->defaults.dir; hash_generic(a->hash, HASHSIZE, (unsigned char *)channel_name, strlen(channel_name)); hash_bin2hex(a->hex, HEXLEN, a->hash, HASHSIZE); state->chan_head = a; return 0; } int state_push_command(state_t *state, char *command, int flags) { if (!state->chan_head) return -1; state_cmd_t *cmd = malloc(sizeof (state_cmd_t)); if (!cmd) return -1; memset(cmd, 0, sizeof(state_cmd_t)); cmd->cmd = strdup(command); if (!cmd->cmd) return freesv(cmd), -1; cmd->flags |= flags; /* preserve cmd order => append to end of list */ if (state->chan_head->cmd) { state_cmd_t *ptr; for (ptr = state->chan_head->cmd; ptr->next; ptr = ptr->next); ptr->next = cmd; } else state->chan_head->cmd = cmd; return 0; } int state_dir_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->dir = dup; else state->defaults.dir = dup; return 0; } int state_seed_set(state_t *state, char *arg) { /* require strict permissions on rcfile */ struct stat sb; if (stat(state->rcfile, &sb) == -1) return -1; if ((sb.st_mode & S_IROTH)) { ERROR("rcfile is world readable\n"); return -1; } char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->seed = dup; else state->defaults.seed = dup; return 0; } int state_logfile_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; state->logfile = dup; return 0; } int state_status_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->chan_status = dup; else state->defaults.chan_status = dup; return 0; } int state_statuscmd_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->chan_status_cmd = dup; else state->defaults.chan_status_cmd = dup; return 0; } int state_stdout_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->chan_stdout = dup; else state->defaults.chan_stdout = dup; return 0; } int state_stderr_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->chan_stderr = dup; else state->defaults.chan_stderr = dup; return 0; } int state_stdin_set(state_t *state, char *arg) { char *dup = strdup(arg); if (!dup) return -1; if (state->chan_head) state->chan_head->chan_stdin = dup; else state->defaults.chan_stdin = dup; return 0; } static int state_err_option_requires_arg(char *opt) { fprintf(stderr, "option requires an argument -- '%s'\n", opt); return (errno = EINVAL), -1; } static int state_bpslimit_set(state_t *state, char *arg) { size_t len = strlen(arg); char *endptr; if (!len) return -1; errno = 0; state->bpslimit = strtoul(arg, &endptr, 10); if (errno) return -1; if (endptr) switch (endptr[0]) { case 'T': state->bpslimit *= 1000; /* fallthru */ case 'G': state->bpslimit *= 1000; /* fallthru */ case 'M': state->bpslimit *= 1000; /* fallthru */ case 'K': state->bpslimit *= 1000; } E(STATE_VERBOSE, "bpslimit = %" PRIu64 " bps\n", state->bpslimit); return 0; } static int state_u64_set(state_t *state, int argc, char *argv[], int *i, uint64_t *opt, char *name) { if (*i == argc - 1) return state_err_option_requires_arg(name); (*i)++; clrbit(state->optmask, *i); if (!strcmp(name, "bpslimit")) return state_bpslimit_set(state, argv[*i]); *opt = atoll(argv[*i]); return 0; } static int state_parse_long_option(state_t *state, int argc, char *argv[], int *i) { (void) argc; /* support commonly used options --help and --version */ if (!strcmp(argv[*i] + 2, "help")) { argv[*i] += 2; return 0; } if (!strcmp(argv[*i] + 2, "version")) { argv[*i] += 2; return 0; } #define X(a, b, c) \ if (!strcmp(argv[*i] + 2, a)) return (STATE_SET(state, c)), 0; STATE_OPTIONS_LONG #undef X #define X(a, b, c, d) \ if (!strcmp(argv[*i] + 2, a)) return state_u64_set(state, argc, argv, i, &state->d, a); STATE_OPTIONS_U64 #undef X return (errno = EINVAL), -1; } static int state_parse_short_options(state_t *state, int argc, char *argv[], int *i) { char *options = argv[*i] + 1; size_t optlen = strlen(options); if (!optlen) { /* "-" MUST be last argument */ if (argc - 1 != *i) return (errno = EINVAL), -1; optlen++; } for (size_t z = 0; z < optlen; z++) { switch (options[z]) { case 'i': if (*i == argc - 1) { return state_err_option_requires_arg(&options[z]); } (*i)++; clrbit(state->optmask, *i); state->ifx = if_nametoindex(argv[*i]); if (state->ifx > 0) continue; else { char ifname[IF_NAMESIZE]; state->ifx = atoi(argv[*i]); if (state->ifx) if_indextoname(state->ifx, ifname); } break; #define X(a, b, c) \ case a: \ STATE_SET(state, c); \ continue; STATE_OPTIONS_SHORT #undef X default: return (errno = EINVAL), -1; } } return 0; } static int state_parse_arg(state_t *state, char *arg) { #define X(a, b, c, d) \ if (!strcmp(arg, b)) return (state->verb = a), 0; STATE_VERBS #undef X ERROR("unknown command '%s'\n", arg); return (errno = EINVAL), -1; } char *arg_pop(state_t *state) { /* could be optimized with clz, but why complicate things? */ for (int i = 1; i < state->argc; i++) { if (!isset(state->optmask, i)) continue; clrbit(state->optmask, i); return state->argv[i]; } return NULL; } int state_parse_args(state_t *state, int argc, char *argv[]) { int rc = 0; int optlen = sizeof state->optmask; if (howmany(argc, CHAR_BIT) > optlen) { fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(E2BIG)); return (errno = E2BIG), -1; } memset(state->optmask, ~0, optlen); clrbit(state->optmask, 0); /* skip program name */ state->argc = argc; state->argv = argv; for (int i = 1; i < argc; i++) { /* option parsing ends if -- encountered */ if (!strcmp(argv[i], "--")) { clrbit(state->optmask, i); break; } /* process long options */ if (!strncmp(argv[i],"--", 2)) { clrbit(state->optmask, i); rc = state_parse_long_option(state, argc, argv, &i); if (rc) return rc; } /* process short options */ else if (*argv[i] == '-') { clrbit(state->optmask, i); rc = state_parse_short_options(state, argc, argv, &i); if (rc) return rc; } } /* options done, set verb from next arg */ char *verb = arg_pop(state); if (verb) rc = state_parse_arg(state, verb); return rc; } int state_parse_configfile(state_t *state, char *configfile) { int rc = -1; yyin = fopen(configfile, "r"); if (yyin != NULL) { rc = yyparse(state); fclose(yyin); } return (rc == 0) ? 0 : -1; } int state_parse_config(state_t *state, char *config, size_t len) { YY_BUFFER_STATE buf = yy_scan_bytes(config, len); int rc = yyparse(state); yy_delete_buffer(buf); return (rc == 0) ? 0 : -1; } /* return path to pidfile. free() after use */ char *state_pidfile(state_t *state) { char *pathname = NULL; int rc; if (!state->dir_state) return (errno = ENOENT), NULL; rc = snprintf(NULL, 0, "%s/%s.pid", state->dir_state, PACKAGE_NAME); if (rc < 0) return NULL; pathname = malloc(rc + 1); if (pathname) { if (snprintf(pathname, rc + 1, "%s/%s.pid", state->dir_state, PACKAGE_NAME) != rc) { free(pathname); pathname = NULL; } } return pathname; } int state_defaults_set(state_t *state) { state->defaults = state_channel_defaults; return 0; } lcagent/src/state.h000066400000000000000000000130001501702040600145400ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett Sheffield */ #ifndef _STATE_H #define _STATE_H 1 #include #include #include #include #define LCAGENTRC ".lcagentrc" #define STATE(a, b) (((a)->flag & b) == b) #define STATE_SET(a, b) ((a)->flag |= b) #define STATE_FLAGS \ X(0x01, STATE_VERBOSE) \ X(0x02, STATE_LOOPBACK) \ X(0x04, STATE_EXPIRES) \ X(0x08, STATE_BWLIMIT) \ X(0x10, STATE_OVERHEAD) \ X(0x20, STATE_DEBUG) \ X(0x40, STATE_STDIN) \ X(0x80, STATE_FOLLOW) #define STATE_VERBS \ X(0, "server", VERB_SERVER, agent_server) \ X(1, "help", VERB_HELP, agent_print_version) \ X(2, "version", VERB_VERSION, agent_print_version) \ X(3, "start", VERB_START, agent_start) \ X(4, "stop", VERB_STOP, agent_stop) \ X(5, "reload", VERB_RELOAD, agent_reload) \ X(6, "whoami", VERB_WHOAMI, agent_whoami) \ X(7, "recv", VERB_RECV, agent_recv) \ X(8, "send", VERB_SEND, agent_send) \ /* X(9, "talk", VERB_TALK, agent_talk) */ \ X(10, "sign", VERB_SIGN, agent_sign) \ /* X(11, "issue", VERB_ISSUE, agent_issue) */ \ X(12, "key", VERB_KEY, agent_key) \ X(13, "exec", VERB_EXEC, agent_exec) #define STATE_OPTIONS_SHORT \ X( 0 , OPT_TYPE_FLAG, STATE_STDIN) \ X('d', OPT_TYPE_FLAG, STATE_DEBUG) \ X('f', OPT_TYPE_FLAG, STATE_FOLLOW) \ X('l', OPT_TYPE_FLAG, STATE_LOOPBACK) \ X('v', OPT_TYPE_FLAG, STATE_VERBOSE) #define STATE_OPTIONS_U64 \ X("bpslimit", OPT_TYPE_U64, STATE_BWLIMIT, bpslimit) \ X("expires", OPT_TYPE_U64, STATE_EXPIRES, expires) \ X("overhead", OPT_TYPE_U64, STATE_OVERHEAD, overhead) #define STATE_OPTIONS_LONG \ X("debug", OPT_TYPE_FLAG, STATE_DEBUG) \ X("follow", OPT_TYPE_FLAG, STATE_FOLLOW) \ X("loopback", OPT_TYPE_FLAG, STATE_LOOPBACK) \ X("verbose", OPT_TYPE_FLAG, STATE_VERBOSE) #define STATE_DIRECTIVES \ X("directory", dir) enum { OPT_TYPE_FLAG, OPT_TYPE_U64, }; enum { #define X(a, b) b = a, STATE_FLAGS #undef X }; enum { #define X(a, b, c, d) c = a, STATE_VERBS #undef X }; enum { CMD_TRY = 1, /* continue if command fails */ }; enum { CHAN_RELOAD = 0x01, CHAN_RESTART = 0x02, CHAN_CHROOT = 0x04, CHAN_KEY = 0x08, /* public key channel */ CHAN_LOOP = 0x10, CHAN_JOIN = 0x20, CHAN_ENABLE = 0x40, CHAN_ONSTART = 0x80, }; #define KEY_PUBLIC_BYTES crypto_box_PUBLICKEYBYTES + crypto_sign_PUBLICKEYBYTES #define KEY_SECRET_BYTES crypto_box_SECRETKEYBYTES + crypto_sign_SECRETKEYBYTES #define HEXKEY_PUBLIC_CRYPTBYTES crypto_box_PUBLICKEYBYTES * 2 #define HEXKEY_SECRET_CRYPTBYTES crypto_box_SECRETKEYBYTES * 2 #define HEXKEY_PUBLIC_SIGNBYTES crypto_sign_PUBLICKEYBYTES * 2 #define HEXKEY_SECRET_SIGNBYTES crypto_sign_SECRETKEYBYTES * 2 #define KEY_PUBLIC_HEXLEN HEXKEY_PUBLIC_CRYPTBYTES + HEXKEY_PUBLIC_SIGNBYTES #define KEY_SECRET_HEXLEN HEXKEY_SECRET_CRYPTBYTES + HEXKEY_SECRET_SIGNBYTES #define KEY_PATH ".config/lcagent" #define KEY_DEFAULT "default" #define E(FLAG, ...) if (state->flag & FLAG) fprintf(stderr, __VA_ARGS__) typedef struct key_combo_s { char phex[KEY_PUBLIC_HEXLEN + 1]; char shex[KEY_SECRET_HEXLEN + 1]; lc_keypair_t s; /* signing */ lc_keypair_t e; /* encryption */ } key_combo_t; typedef struct state_cmd_s state_cmd_t; struct state_cmd_s { state_cmd_t *next; char *cmd; int flags; }; typedef struct state_chan_s state_chan_t; struct state_chan_s { state_chan_t *next; lc_channel_t *chan; state_cmd_t *cmd; key_combo_t keyring; lc_token_t *token; char *auth_key; char *chan_name; char *chan_status; char *chan_status_cmd; char *chan_stdout; char *chan_stdin; char *chan_stderr; char *dir; char *seed; unsigned char chan_key[crypto_secretbox_KEYBYTES]; unsigned char auth_pek[crypto_box_PUBLICKEYBYTES]; unsigned char auth_psk[crypto_sign_PUBLICKEYBYTES]; unsigned char hash[HASHSIZE]; /* hash of chan_name */ char hex[HEXLEN]; /* hex of hash */ int flags; unsigned int auth_keys; }; typedef struct state_s { lc_socket_t *sock; char **argv; char *dir_home; char *dir_cache; char *dir_config; char *dir_data; char *dir_state; char *rcfile; char *logfile; FILE *log; state_chan_t *chan_head; state_chan_t defaults; uint64_t bpslimit; uint64_t expires; uint64_t overhead; unsigned int flag; unsigned int ifx; unsigned char verb; int argc; char optmask[128]; int lockfd; /* file descriptor of lockfile */ pid_t pid; /* pid of running server */ } state_t; int state_defaults_set(state_t *state); int state_dirs(state_t *state, char *home); int state_parse_args(state_t *state, int argc, char *argv[]); int state_parse_configfile(state_t *state, char *configfile); int state_parse_config(state_t *state, char *config, size_t len); int state_parse_config_default(state_t *state); int state_authkey_set(state_t *state, char *authkey); int state_push_channel(state_t *state, char *channel_name); int state_push_command(state_t *state, char *command, int flags); int state_dir_set(state_t *state, char *arg); int state_logfile_set(state_t *state, char *arg); int state_seed_set(state_t *state, char *arg); int state_status_set(state_t *state, char *arg); int state_statuscmd_set(state_t *state, char *arg); int state_stderr_set(state_t *state, char *arg); int state_stdin_set(state_t *state, char *arg); int state_stdout_set(state_t *state, char *arg); char *state_pidfile(state_t *state); char *arg_pop(state_t *state); state_chan_t *state_chan_by_addr(state_t *state, struct in6_addr *addr); state_chan_t *state_chan_by_name(state_t *state, char *name); state_chan_t *state_channel_free(state_chan_t *schan); void free_state(state_t *state); #endif /* _STATE_H */ lcagent/test/000077500000000000000000000000001501702040600134455ustar00rootroot00000000000000lcagent/test/.gitignore000066400000000000000000000001471501702040600154370ustar00rootroot00000000000000*.log *.valgrind ????-????.tmp.?????? ????-????.???.tmp.?????? ????-????-?????? testlog-?????? lastlog lcagent/test/0000-0000.c000066400000000000000000000007001501702040600145420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include int main(void) { char name[] = "state flags"; state_t state = {0}; test_name(name); test_assert(!STATE(&state, STATE_VERBOSE), "STATE_VERBOSE not set"); STATE_SET(&state, STATE_VERBOSE); test_assert(STATE(&state, STATE_VERBOSE), "STATE_VERBOSE set"); return test_status; } lcagent/test/0000-0001.c000066400000000000000000000075251501702040600145570ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* process cmdline options and args, verify that state is set correctly */ #include "test.h" #include /* test state_parse_args() with bad option (-z) */ static int test_state_bad_option(char *opt) { char *argv[] = { "lcagent", opt, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; errno = 0; rc = state_parse_args(&state, argc, argv); test_assert(errno == EINVAL, "bad option (%s): EINVAL", opt); test_assert(rc == -1, "bad option (%s): return -1", opt); return test_status; } /* Everything after -- is treated as an argument, not an option */ static int test_state_endoptions(void) { char *argv[] = { "lcagent", "--", "whoami", "--verbose", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; rc = state_parse_args(&state, argc, argv); test_assert(rc == 0, "-- ends option parsing rc = %i", rc); test_assert(!STATE(&state, STATE_VERBOSE), "flag NOT set"); return test_status; } static int test_state_long(char *opt, unsigned int flag) { char flaarg[32] = "--"; char *argv[] = { "lcagent", flaarg, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; strcpy(flaarg + 2, opt); rc = state_parse_args(&state, argc, argv); test_assert(rc == 0, "%s, rc = %i", flaarg, rc); test_assert(STATE(&state, flag), "flag set"); return test_status; } static int test_state_short(char opt, unsigned int flag) { char flaarg[] = "-?"; char *argv[] = { "lcagent", flaarg, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; flaarg[1] = opt; rc = state_parse_args(&state, argc, argv); test_assert(rc == 0, "%s", flaarg); test_assert(STATE(&state, flag), "flag set"); return test_status; } /* too many arguments (> 256) */ static int test_state_args_E2BIG(void) { state_t state = {0}; char *argv[sizeof state.optmask * CHAR_BIT + 2] = {0}; int argc = sizeof argv / sizeof argv[0] - 1; int rc; argv[0] = PACKAGE_NAME; for (int i = 1; i < argc; i++) argv[i] = "arg"; errno = 0; rc = state_parse_args(&state, argc, argv); if (!test_assert(errno == E2BIG, "errno = E2BIG")) return test_status; if (!test_assert(rc == -1, "%s", __func__)) return test_status; return test_status; } /* test leftover args are appended to state with mask */ static int test_state_args(void) { char hexkey[] = "305c464b3c1839a0101a95c4a31c0022e93d861ee2d5084d5be3bb2a862b0c11e05c5a9902682b5f14ee39f68123e5cfc1dcb6a82175162377ebc24e3fb0a91a"; char *argv[] = { PACKAGE_NAME, "sign", "-v", hexkey, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; rc = state_parse_args(&state, argc, argv); test_assert(rc == 0, "%s", __func__); /* check argc */ test_assert(state.argc == argc, "state.argc == %i", state.argc); /* check argv */ for (int i = 0; i < argc; i++) { test_expect(argv[i], state.argv[i]); } /* check optmask */ test_assert(!isset(state.optmask, 0), "optmask: bit 0 not set"); test_assert(!isset(state.optmask, 1), "optmask: bit 1 not set"); test_assert(!isset(state.optmask, 2), "optmask: bit 2 not set"); test_assert( isset(state.optmask, 3), "optmask: bit 3 set"); return test_status; } int main(void) { char name[] = "state_parse_arg()"; test_name(name); #define X(a, b, c) if (b == OPT_TYPE_FLAG && test_state_short(a, c)) return test_status; STATE_OPTIONS_SHORT #undef X #define X(a, b, c) if (b == OPT_TYPE_FLAG && test_state_long(a, c)) return test_status; STATE_OPTIONS_LONG #undef X if (test_state_bad_option("-z")) return test_status; if (test_state_bad_option("--badoption")) return test_status; if (test_state_endoptions()) return test_status; if (test_state_args_E2BIG()) return test_status; if (test_state_args()) return test_status; return test_status; } lcagent/test/0000-0002.c000066400000000000000000000024461501702040600145550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* process cmdline options and args, verify that state is set correctly */ #include "test.h" #include "testnet.h" #include /* -i => interface */ static int test_state_interface_short(char *ifname, unsigned int ifx) { char *argv[] = { "lcagent", "-i", NULL, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; /* try -i with missing IFNAME */ errno = 0; rc = state_parse_args(&state, argc - 1, argv); test_assert(errno == EINVAL, "state_parse_args() errno set"); test_assert(rc == -1, "interface (-i)"); /* again with IFNAME set correctly */ argv[2] = ifname; rc = state_parse_args(&state, argc, argv); test_assert(rc == 0, "interface (-i)"); test_assert(state.ifx == ifx, "state: interface index set"); return test_status; } int main(void) { char name[] = "state_parse_arg() - options requiring network"; char ifname[IFNAMSIZ]; unsigned int ifx; test_name(name); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; if (test_state_interface_short(ifname, ifx)) return test_status; return test_status; } lcagent/test/0000-0003.c000066400000000000000000000031241501702040600145500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* process cmdline args, check verb set */ #include "test.h" #include static int test_state_verb(char *verb, char *options, char verbnum) { char *argv[] = { "lcagent", verb, options, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; test_log("%s: %s\n", __func__, verb); if (!options) argc--; rc = state_parse_args(&state, argc, argv); if (!test_assert(rc == 0, "state_parse_args() rc = %i", rc)) return test_status; if (!test_assert(state.verb == verbnum, "state_parse_args(): verb = %d", state.verb)) return test_status; return 0; } static int test_state_bad_verb(void) { char verb[] = "badverb"; char *argv[] = { "lcagent", verb, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; state_t state = {0}; int rc; test_log("%s: %s\n", __func__, verb); errno = 0; rc = state_parse_args(&state, argc, argv); if (!test_assert(errno == EINVAL, "EINVAL set")) return test_status; if (!test_assert(rc == -1, "state_parse_args() rc = %i", rc)) return test_status; return 0; } int main(void) { char name[] = "state_parse_arg() - verb"; test_name(name); /* test verbs with no preceeding options */ #define X(a, b, c, d) if (test_state_verb(b, NULL, c)) return test_status; STATE_VERBS #undef X /* test verbs again with preceeding options */ #define X(a, b, c, d) if (test_state_verb(b, "-v", c)) return test_status; STATE_VERBS #undef X if (test_state_bad_verb()) return test_status; return test_status; } lcagent/test/0000-0004-config.00.conf000066400000000000000000000011671501702040600167420ustar00rootroot00000000000000# skip comment logfile /path/to/logfile directory /path/to/dir auth_key ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff auth_key eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee chroot reload restart #status #status_command #stdout #stdin #stderr channel "I Can't Breathe" # comment runs to end of line directory /path/to/other/dir auth_key 0000000000000000000000000000000000000000000000000000000000000000 channel BlackLivesMatter directory 3 auth_key 1111111111111111111111111111111111111111111111111111111111111111 auth_key 2222222222222222222222222222222222222222222222222222222222222222 lcagent/test/0000-0004.c000066400000000000000000000060711501702040600145550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include static int test_state_config_00(void) { char configfile[] = "0000-0004-config.00.conf"; state_t state = {0}; const int hexbyt = crypto_sign_PUBLICKEYBYTES * 2; int rc; rc = state_defaults_set(&state); if (!test_assert(rc == 0, "state_defaults_set(), rc = %i", rc)) return test_status; if (!test_assert((state.defaults.flags & CHAN_ENABLE), "state.defaults.flags CHAN_ENABLE")) return test_status; if (!test_assert((state.defaults.flags & CHAN_JOIN), "state.defaults.flags CHAN_JOIN")) return test_status; rc = state_parse_configfile(&state, configfile); if (!test_assert(rc == 0, "state_parse_config(), rc = %i", rc)) return test_status; /* setting defaults */ if (!test_assert(state.defaults.dir != NULL, "state.defaults.dir is not NULL")) return test_status; if (!test_expect("/path/to/dir", state.defaults.dir)) return test_status; /* logfile */ if (!test_assert(state.logfile != NULL, "state.logfile is not NULL")) return test_status; if (!test_expect("/path/to/logfile", state.logfile)) return test_status; /* test first default key */ char auth_key[hexbyt]; memset(auth_key, 'f', sizeof auth_key); if (!test_assert((state.defaults.auth_key != NULL), "state.defaults.auth_key is not NULL")) return test_status; if (!test_expectn(auth_key, state.defaults.auth_key, hexbyt)) return test_status; /* test second default key */ char *tmp = state.defaults.auth_key + hexbyt; memset(auth_key, 'e', sizeof auth_key); if (!test_expectn(auth_key, tmp, hexbyt)) return test_status; if (!test_assert((state.defaults.flags & CHAN_CHROOT), "state.defaults.flags CHAN_CHROOT")) return test_status; if (!test_assert((state.defaults.flags & CHAN_RELOAD), "state.defaults.flags CHAN_RELOAD")) return test_status; if (!test_assert((state.defaults.flags & CHAN_RESTART), "state.defaults.flags CHAN_RESTART")) return test_status; /* channel-specific settings */ if (!test_assert(state.chan_head != NULL, "state.chan_head is not NULL")) return test_status; state_chan_t *achan; char *chan_name[] = { "I Can't Breathe", "BlackLivesMatter" }; char *chan_dir[] = { "/path/to/other/dir", "3" }; int channels = sizeof chan_name / sizeof chan_name[0]; for (int i = 0; i < channels; i++) { achan = state_chan_by_name(&state, chan_name[i]); if (!test_assert(achan != NULL, "found channel: %s", chan_name[i])) return test_status; if (!test_expect(chan_dir[i], achan->dir)) return test_status; if (!test_assert(achan->auth_key != NULL, "chan[%i] auth_key set", i)) return test_status; for (unsigned int n = 0; n < achan->auth_keys; n++) { memset(auth_key, i + n + 48, sizeof auth_key); tmp = achan->auth_key + hexbyt * n; if (!test_expectn(auth_key, tmp, hexbyt)) return test_status; } } free_state(&state); return test_status; } int main(void) { char name[] = "state_parse_config()"; test_name(name); if (test_state_config_00()) return test_status; return test_status; } lcagent/test/0000-0005.c000066400000000000000000000040101501702040600145450ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include #include static int check_rcfile(state_t *state, char *home) { char *rcfile; int rc; rc = snprintf(NULL, 0, "%s/%s", home, LCAGENTRC); if (!test_assert(rc > 0, "snprintf returned %i", rc)) return test_status; rcfile = malloc(rc + 1); if (!test_assert(rcfile != NULL, "malloc() rcfile")) return test_status; if (!test_assert(snprintf(rcfile, rc + 1, "%s/%s", home, LCAGENTRC) == rc, "snprintf()")) goto free_rcfile; test_expect(rcfile, state->rcfile); free_rcfile: free(rcfile); return test_status; } static int check_path(char *ptr, char *fakehome, char *stub, char *name) { char pathbuf[1024]; struct stat sb; int rc = snprintf(pathbuf, sizeof pathbuf, "%s/%s", fakehome, stub); assert(rc < (int)sizeof pathbuf); /* check state variable is set */ test_assert(!strcmp(ptr, pathbuf), "%s = '%s'", name, ptr); /* check directory path has been created */ rc = stat(pathbuf, &sb); test_assert(rc == 0, "stat '%s'", pathbuf); return test_status; } int main(void) { char name[] = "state_dirs()"; char fakehome[] = "0000-0005-XXXXXX"; state_t state = {0}; int rc; test_name(name); /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = state_dirs(&state, fakehome); if (!test_assert(rc == 0, "state_dirs() returned %i", rc)) goto err_free_statedirs; test_assert(!strcmp(state.dir_home, fakehome), "state.dir_home = '%s'", state.dir_home); check_path(state.dir_cache, fakehome, ".cache/lcagent", "dir_cache"); check_path(state.dir_config, fakehome, ".config/lcagent", "dir_config"); check_path(state.dir_data, fakehome, ".local/share/lcagent", "dir_data"); check_path(state.dir_state, fakehome, ".local/state/lcagent", "dir_state"); check_rcfile(&state, fakehome); err_free_statedirs: free_state(&state); return test_status; } lcagent/test/0000-0006.c000066400000000000000000000024071501702040600145560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett A C Sheffield */ #include "test.h" #include /* see https://doc.libsodium.org/public-key_cryptography/authenticated_encryption */ #define MESSAGE (const unsigned char *) "test" #define MESSAGE_LEN 4 #define CIPHERTEXT_LEN (crypto_box_MACBYTES + MESSAGE_LEN) int verify_crypto_key(unsigned char *public, unsigned char *secret) { unsigned char nonce[crypto_box_NONCEBYTES]; unsigned char ciphertext[CIPHERTEXT_LEN]; int rc; randombytes_buf(nonce, sizeof nonce); rc = crypto_box_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce, public, secret); if (!test_assert(rc == 0, "crypto_secretbox_easy()")) return test_status; unsigned char decrypted[MESSAGE_LEN]; rc = crypto_box_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, public, secret); test_assert(rc == 0, "decrypt message"); return test_status; } int main(void) { key_combo_t keyring; char name[] = "key_gen_keys() - verify encryption keypair"; int rc; test_name(name); rc = key_gen_keys(&keyring); test_assert(rc == 0, "key_gen_keys()"); /* verify the first part of the key is an encryption key */ if (verify_crypto_key(keyring.e.pk, keyring.e.sk)) return test_status; return 0; } lcagent/test/0000-0007.c000066400000000000000000000024231501702040600145550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett A C Sheffield */ #include "test.h" #include /* see https://doc.libsodium.org/public-key_cryptography/public-key_signatures */ #define MESSAGE (const unsigned char *) "test" #define MESSAGE_LEN 4 #define CIPHERTEXT_LEN (crypto_box_MACBYTES + MESSAGE_LEN) int verify_signing_key(unsigned char *public, unsigned char *secret) { unsigned char signed_message[crypto_sign_BYTES + MESSAGE_LEN]; unsigned long long signed_message_len; int rc; rc = crypto_sign(signed_message, &signed_message_len, MESSAGE, MESSAGE_LEN, secret); if (!test_assert(rc == 0, "crypto_secretbox_easy()")) return test_status; unsigned char unsigned_message[MESSAGE_LEN]; unsigned long long unsigned_message_len; rc = crypto_sign_open(unsigned_message, &unsigned_message_len, signed_message, signed_message_len, public); test_assert(rc == 0, "verify signature"); return test_status; } int main(void) { key_combo_t keyring; char name[] = "key_gen_keys() - verify signing key"; int rc; test_name(name); rc = key_gen_keys(&keyring); test_assert(rc == 0, "key_gen_keys()"); /* verify signing key */ if (verify_signing_key(keyring.s.pk, keyring.s.sk)) return test_status; return 0; } lcagent/test/0000-0008.c000066400000000000000000000061131501702040600145560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024 Brett A C Sheffield */ #include "test.h" #include "key.h" #define MESSAGE (const unsigned char *) "test" #define MESSAGE_LEN 4 #define CIPHERTEXT_LEN (crypto_box_MACBYTES + MESSAGE_LEN) int verify_crypto_key(unsigned char *public, unsigned char *secret) { unsigned char nonce[crypto_box_NONCEBYTES]; unsigned char ciphertext[CIPHERTEXT_LEN]; int rc; randombytes_buf(nonce, sizeof nonce); rc = crypto_box_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce, public, secret); if (!test_assert(rc == 0, "crypto_secretbox_easy()")) return test_status; unsigned char decrypted[MESSAGE_LEN]; rc = crypto_box_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, public, secret); test_assert(rc == 0, "decrypt message"); return test_status; } int verify_signing_key(unsigned char *public, unsigned char *secret) { unsigned char signed_message[crypto_sign_BYTES + MESSAGE_LEN]; unsigned long long signed_message_len; int rc; rc = crypto_sign(signed_message, &signed_message_len, MESSAGE, MESSAGE_LEN, secret); if (!test_assert(rc == 0, "crypto_secretbox_easy()")) return test_status; unsigned char unsigned_message[MESSAGE_LEN]; unsigned long long unsigned_message_len; rc = crypto_sign_open(unsigned_message, &unsigned_message_len, signed_message, signed_message_len, public); test_assert(rc == 0, "verify signature"); return test_status; } int main(void) { char name[] = "key_gen_combopair_hex() - generate hex keypair + verify"; key_combo_t keyring; char *psk_hex = keyring.phex + HEXKEY_PUBLIC_CRYPTBYTES; char *ssk_hex = keyring.shex + HEXKEY_SECRET_CRYPTBYTES; unsigned char public_bin[KEY_PUBLIC_BYTES]; unsigned char secret_bin[KEY_SECRET_BYTES]; unsigned char *psk_bin = public_bin + crypto_box_PUBLICKEYBYTES; unsigned char *ssk_bin = secret_bin + crypto_box_SECRETKEYBYTES; int rc; test_name(name); rc = key_gen_combopair_hex(&keyring); test_assert(rc == 0, "key_gen_combopair_hex()"); /* decode encryption keys */ rc = sodium_hex2bin( public_bin, crypto_box_PUBLICKEYBYTES, keyring.phex, HEXKEY_PUBLIC_CRYPTBYTES, NULL, NULL, NULL); test_assert(rc == 0, "sodium_hex2bin() - decode public encryption key"); rc = sodium_hex2bin( secret_bin, crypto_box_SECRETKEYBYTES, keyring.shex, HEXKEY_SECRET_CRYPTBYTES, NULL, NULL, NULL); test_assert(rc == 0, "sodium_hex2bin() - decode secret encryption key"); /* decode signing keys */ rc = sodium_hex2bin( psk_bin, crypto_sign_PUBLICKEYBYTES, psk_hex, HEXKEY_PUBLIC_SIGNBYTES, NULL, NULL, NULL); test_assert(rc == 0, "sodium_hex2bin() - decode public signing key"); rc = sodium_hex2bin( ssk_bin, crypto_sign_SECRETKEYBYTES, ssk_hex, HEXKEY_SECRET_SIGNBYTES, NULL, NULL, NULL); test_assert(rc == 0, "sodium_hex2bin() - decode secret signing key"); /* verify the first part of the key is an encryption key */ if (verify_crypto_key(public_bin, secret_bin)) return test_status; /* verify the second part of the key is a signing key */ if (verify_signing_key(psk_bin, ssk_bin)) return test_status; return 0; } lcagent/test/0000-0009.c000066400000000000000000000042701501702040600145610ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024 Brett A C Sheffield */ #include "test.h" #include "key.h" #include #include #include #include int main(void) { char name[] = "key_gen_combopair_write() - generate and write keys to file"; char fakehome[] = "0000-0009-XXXXXX"; int rc; test_name(name); /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } /* generate keypair and write to disk */ key_combo_t keyring; rc = key_gen_combopair_write(fakehome, &keyring); test_assert(rc == 0, "key_gen_combopair_write()"); /* check directory path has been created */ /* FAKEHOME/.config/lcagent/ */ char *configpath; struct stat sb; rc = snprintf(NULL, 0, "%s/%s", fakehome, KEY_PATH); if (!test_assert(rc > 0, "calculate length of configpath")) { perror("snprintf"); return test_status; } size_t len = (size_t)rc + 1; configpath = malloc(len); if (!test_assert(configpath != NULL, "allocate configpath")) return test_status; rc = snprintf(configpath, len, "%s/%s", fakehome, KEY_PATH); test_assert((size_t)rc == len - 1, "rc == len %zi/%zu", rc, len - 1); rc = stat(configpath, &sb); if (!test_assert(rc == 0, "stat '%s'", configpath)) goto err_free_configpath; if (!test_assert(chdir(configpath) == 0, "chdir %s", configpath)) goto err_free_configpath; /* check keyfile exists */ rc = stat(keyring.phex, &sb); if (!test_assert(rc == 0, "stat '%s'", keyring.phex)) goto err_free_configpath; /* test file permissions */ test_assert((sb.st_mode & ~S_IFMT) == (S_IRUSR|S_IWUSR), "file mode = %jo", sb.st_mode); /* verify secret key matches */ char buf[KEY_SECRET_HEXLEN + 1]; ssize_t byt; int fd = open(keyring.phex, O_RDONLY); test_assert(fd != -1, "open '%s'", keyring.phex); byt = read(fd, buf, sizeof buf); test_assert((size_t)byt == KEY_SECRET_HEXLEN, "%zi/%zu bytes read", byt, KEY_SECRET_HEXLEN); /* check default symlink */ rc = stat("default", &sb); if (!test_assert(rc == 0, "default symlink created")) goto err_free_configpath; err_free_configpath: free(configpath); return test_status; } lcagent/test/0000-0010.c000066400000000000000000000027201501702040600145470ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024 Brett A C Sheffield */ #include "test.h" #include "key.h" int main(void) { char name[] = "key_load_default()"; key_combo_t keyring[2]; char fakehome[] = "0000-0009-XXXXXX"; int rc; test_name(name); /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } /* randomize key buffers */ for (int i = 0; i < 2; i++) { arc4random_buf(keyring[i].phex, KEY_PUBLIC_HEXLEN + 1); arc4random_buf(keyring[i].shex, KEY_SECRET_HEXLEN + 1); } /* first, call key_load_default() with no key (create) */ state_t state = { .dir_home = fakehome }; rc = key_load_default(&state, &keyring[0]); test_assert(rc == 0, "key_load_default() - no key, create"); /* now, call key_load_default() again to load previous key (load) */ rc = key_load_default(&state, &keyring[1]); test_assert(rc == 0, "key_load_default() - load default key"); test_log("public[0]: %s %i bytes\n", keyring[0].phex, KEY_PUBLIC_HEXLEN); test_log("public[1]: %s\n", keyring[1].phex); test_log("KEY_PUBLIC_HEXLEN = %i\n", KEY_PUBLIC_HEXLEN); test_log("KEY_SECRET_HEXLEN = %i\n", KEY_SECRET_HEXLEN); test_assert(memcmp(keyring[0].phex, keyring[1].phex, KEY_PUBLIC_HEXLEN) == 0, "public keys match"); test_assert(memcmp(keyring[0].phex, keyring[1].phex, KEY_SECRET_HEXLEN) == 0, "secret keys match"); return test_status; } lcagent/test/0000-0011.c000066400000000000000000000025501501702040600145510ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024-2025 Brett A C Sheffield */ #include "test.h" #include #include #include #include int main(void) { char name[] = "sign capability token"; lc_ctx_t *lctx; lc_channel_t *chan; lc_token_t token; uint8_t capbits = 0; int rc; test_name(name); /* context & channel */ lctx = lc_ctx_new(); chan = lc_channel_new(lctx, "Trans Women are Women"); /* create authority keypair */ key_combo_t authring; rc = key_gen_keys(&authring); test_assert(rc == 0, "key_gen_keys() - AUTHORITY"); /* create bearer keypair */ key_combo_t bearring; rc = key_gen_keys(&bearring); test_assert(rc == 0, "key_gen_keys() - BEARER"); rc = lc_token_new(&token, &authring.s, bearring.s.pk, chan, capbits, 60); test_assert(rc == 0, "lc_token_new()"); /* check signature */ unsigned char *cap = (unsigned char *)&token; unsigned long long caplen = sizeof token - crypto_sign_BYTES; test_assert(crypto_sign_verify_detached(token.sig, cap, caplen, authring.s.pk) == 0, "crypto_sign_open() - verify signature"); /* check bearer key and capbits */ test_assert(memcmp(token.bearkey, bearring.s.pk, crypto_sign_PUBLICKEYBYTES) == 0, "bearerkey match"); test_assert(token.capbits == capbits, "capbits set"); return test_status; } lcagent/test/0000-0012.c000066400000000000000000000067601501702040600145610ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include #include static int verify_token(state_t *state, key_combo_t *authring, key_combo_t *bearring, lc_channel_t *chan, /* uint64_t valid_sec,*/ uint8_t capbits) { char *pathname; int rc; /* check token was written to disk */ pathname = key_cap_path(state, lc_channel_get_hash(chan), 14); if (!test_assert(pathname != NULL, "key_cap_path() returned '%s'", pathname)) return test_status; struct stat sb; rc = stat(pathname, &sb); test_assert(rc == 0, "stat() '%s' - file exists", pathname); free(pathname); if (test_status) return test_status; if (!test_assert((sb.st_mode & S_IFMT) == S_IFREG, "is a regular file")) return test_status; lc_token_t token = {0}; rc = key_cap_load(&token, state, lc_channel_get_hash(chan), 14); if (!test_assert(rc == 0, "key_cap_load()")) return test_status; /* verify token */ unsigned char *cap = (unsigned char *)&token; unsigned long long caplen = sizeof token - crypto_sign_BYTES; if (!test_assert(sodium_init() != -1, "sodium_init()")) return test_status; /* - check signature */ test_assert(crypto_sign_verify_detached(token.sig, cap, caplen, authring->s.pk) == 0, "crypto_sign_open() - verify signature"); /* check bearer key and capbits */ test_assert(memcmp(token.bearkey, bearring->s.pk, crypto_sign_PUBLICKEYBYTES) == 0, "bearerkey match"); test_assert(token.capbits == capbits, "capbits set"); test_assert(token.expires > 0, "expires set"); return test_status; } static int create_and_save_token(state_t *state, key_combo_t *authring, key_combo_t *bearring) { lc_ctx_t *lctx; lc_channel_t *chan; uint64_t valid_sec = 60; uint8_t capbits = 0; int flags = 0; int rc; /* create channel for token */ lctx = lc_ctx_new(); if (!test_assert(lctx != NULL, "lc_ctx_new()")) return -1; chan = lc_channel_new(lctx, "Black Lives (Still) Matter"); if (!test_assert(chan != NULL, "lc_channel_new()")) goto free_lctx; /* create and discard token */ rc = key_cap_issue(state, &authring->s, bearring->s.pk, chan, capbits, valid_sec, flags); if (!test_assert(rc == 0, "key_cap_issue() returned %i", rc)) return test_status; /* create and save token */ flags = KEY_CAP_SAVE; rc = key_cap_issue(state, &authring->s, bearring->s.pk, chan, capbits, valid_sec, flags); if (!test_assert(rc == 0, "key_cap_issue() returned %i", rc)) return test_status; verify_token(state, authring, bearring, chan, /*valid_sec,*/ capbits); free_lctx: lc_ctx_free(lctx); return test_status; } int main(void) { char name[] = "key_cap_issue() - save token to disk"; char fakehome[] = "0000-0012-XXXXXX"; key_combo_t authring, bearring; int rc; test_name(name); /* first, create authority keypair */ rc = key_gen_combopair_hex(&authring); if (!test_assert(rc == 0, "key_gen_combopair() - AUTHORITY")) return test_status; /* next, create bearer keypair */ rc = key_gen_combopair_hex(&bearring); if (!test_assert(rc == 0, "key_gen_combopair() - BEARER")) return test_status; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } /* create state directories */ state_t state = {0}; rc = state_dirs(&state, fakehome); if (!test_assert(rc == 0, "state_dirs() returned %i", rc)) return test_status; create_and_save_token(&state, &authring, &bearring); free_state(&state); return test_status; } lcagent/test/0000-0013.c000066400000000000000000000004121501702040600145460ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" int main(void) { char name[] = "key_cap_issue() - send token to bearer (NOT IMPLEMENTED)"; return test_skip(name); } lcagent/test/0000-0014.c000066400000000000000000000011661501702040600145560ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include int main(void) { char name[] = PACKAGE_NAME " help"; state_t state = { .verb = VERB_HELP, }; int rc; test_name(name); rc = agent_run(&state); test_assert(rc == 0, "agent_run() returned %i", rc); /* Testing the exact output of `lcagent help` is probably a waste of * time, so lets just assume it printed something helpful. The whole * thing needs localizing and keeping in sync with the available * commandline options. */ return test_status; } lcagent/test/0000-0015.c000066400000000000000000000013071501702040600145540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #define REDIRECT_BUF f = freopen("/dev/null", "a", stderr); assert(f); setbuf(stderr, buf); #define REDIRECT_OUT setbuf(stderr, NULL); int main(void) { char name[] = PACKAGE_NAME " version"; char buf[BUFSIZ]; state_t state = { .verb = VERB_VERSION, }; FILE *f; int rc; test_name(name); memset(buf, 0, sizeof buf); REDIRECT_BUF rc = agent_run(&state); REDIRECT_OUT test_assert(rc == 0, "agent_run() returned %i", rc); char expected[] = PACKAGE_NAME " " PACKAGE_VERSION "\n"; test_expect(expected, buf); return test_status; } lcagent/test/0000-0016.c000066400000000000000000000037171501702040600145640ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #define REDIRECT_BUF f = freopen("/dev/null", "a", stdout); assert(f); setbuf(stdout, buf); #define REDIRECT_OUT setbuf(stdout, NULL); int main(void) { char name[] = PACKAGE_NAME " whoami"; char fakehome[] = "0000-0016-XXXXXX"; char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; char buf[BUFSIZ]; FILE *f; state_t state = {0}; int rc; test_name(name); /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = setenv("HOME", fakehome, 1); if (!test_assert(rc == 0, "set HOME")) return test_status; /* run once, verify keys generated, whoami output */ memset(&state, 0, sizeof state); memset(buf, 0, sizeof buf); /* clear output buffer */ REDIRECT_BUF rc = agent(&state, argc, argv); REDIRECT_OUT test_assert(rc == EXIT_SUCCESS, "agent_run() returned %i", rc); /* verify keys generated */ char *phex = strdup(state.defaults.keyring.phex); test_assert(strlen(state.defaults.keyring.phex) > 0, "combo key generated"); /* verify cmd output */ test_log("buf: '%s'\n", buf); test_assert(strstr(buf, phex) != NULL, "combo key found in command output (1)"); /* run again, verify keys unchanged, whoami output */ memset(&state, 0, sizeof state); memset(buf, 0, sizeof buf); /* clear output buffer */ REDIRECT_BUF rc = agent(&state, argc, argv); REDIRECT_OUT test_assert(rc == 0, "agent_run() returned %i", rc); /* verify keys loaded into state */ test_assert(strlen(state.defaults.keyring.phex) > 0, "combo key loaded"); test_expect(phex, state.defaults.keyring.phex); /* key same as last */ /* verify cmd output */ test_log("buf: '%s'\n", buf); test_assert(strstr(buf, phex) != NULL, "combo key found in command output (2)"); free(phex); return test_status; } lcagent/test/0000-0017.c000066400000000000000000000071351501702040600145630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include #define REDIRECT_BUF f = freopen("/dev/null", "a", stderr); assert(f); setbuf(stderr, buf); #define REDIRECT_OUT setbuf(stderr, NULL); #define CHANNEL_NAME "0000-0017" int verify_disk_token(state_t *state, char *fakehome, char *buf, uint64_t expires) { unsigned char hash[HASHSIZE]; lc_token_t token; int rc = state_dirs(state, fakehome); if (!test_assert(rc == 0, "%s() state_dirs", __func__)) return test_status; hash_generic(hash, sizeof hash, (unsigned char *)CHANNEL_NAME, strlen(CHANNEL_NAME)); rc = key_cap_load(&token, state, hash, sizeof token.channel); perror("key_cap_load"); test_assert(rc == 0, "key_cap_load()"); /* while we're here, check cmd output contains token path */ char *tokpath = key_cap_path(state, hash, sizeof token.channel); if (!test_assert(tokpath != NULL, "key_cap_path()")) return test_status; test_assert(strstr(buf, tokpath) != NULL, "token path printed"); free(tokpath); test_assert(token.expires >= expires, "token expires"); free_state(state); return rc; } int main(void) { char name[] = PACKAGE_NAME " sign"; char fakehome[] = "0000-0017-XXXXXX"; char buf[BUFSIZ]; state_t state = {0}; FILE *f; int argc; int rc; test_name(name); /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); /* redirect agent output to buffer */ memset(buf, 0, sizeof buf); /* clear output buffer */ /* missing key argument => EINVAL */ char *argv_nokey[] = { PACKAGE_NAME, "sign", NULL }; argc = sizeof argv_nokey / sizeof argv_nokey[0] - 1; errno = 0; REDIRECT_BUF rc = agent(&state, argc, argv_nokey); REDIRECT_OUT test_assert(errno == EINVAL, "EINVAL"); test_assert(rc == EXIT_FAILURE, "agent() returned %i", rc); /* verify cmd output (error message) */ test_assert(strstr(buf, AGENT_ERR_NOKEY) != NULL, "error message found in output"); /* missing chan argument => EINVAL */ char *argv_nochan[] = { PACKAGE_NAME, "sign", "somekey", NULL }; argc = sizeof argv_nochan / sizeof argv_nochan[0] - 1; memset(buf, 0, sizeof buf); /* clear output buffer */ errno = 0; REDIRECT_BUF rc = agent(&state, argc, argv_nochan); REDIRECT_OUT test_assert(errno == EINVAL, "EINVAL"); test_assert(rc == EXIT_FAILURE, "agent() returned %i", rc); /* verify cmd output (error message) */ test_assert(strstr(buf, AGENT_ERR_NOCHANNEL) != NULL, "error message found in output"); /* run once, issue token to disk */ char bearerkey[] = "byteme"; char *argv_ok[] = { PACKAGE_NAME, "sign", bearerkey, CHANNEL_NAME, NULL }; argc = sizeof argv_ok / sizeof argv_ok[0] - 1; memset(buf, 0, sizeof buf); /* clear output buffer */ memset(&state, 0, sizeof state); errno = 0; REDIRECT_BUF rc = agent(&state, argc, argv_ok); REDIRECT_OUT test_assert(rc == 0, "agent_run() returned %i", rc); /* verify token on disk */ if (verify_disk_token(&state, fakehome, buf, 0)) return test_status; /* issue token with --expires option */ char *argv_expires[] = { PACKAGE_NAME, "sign", "--expires", "60", bearerkey, CHANNEL_NAME, NULL }; argc = sizeof argv_expires / sizeof argv_expires[0] - 1; memset(&state, 0, sizeof state); rc = agent(&state, argc, argv_expires); test_assert(rc == 0, "agent_run() returned %i", rc); test_assert(state.expires == 60, "state.expires set"); /* check token has non-zero expiry */ if (verify_disk_token(&state, fakehome, buf, 60)) return test_status; return test_status; } lcagent/test/0000-0018.c000066400000000000000000000111211501702040600145520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include "testnet.h" #include #include #include #define CHANNEL_NAME "transgender rights are human rights" #define WAITS 4 #define SPEEDLIMIT "1048576" #define REDIRECT_BUF f = freopen("/dev/null", "a", stderr); assert(f); setbuf(stderr, buf); #define REDIRECT_OUT setbuf(stderr, NULL); f = freopen("lastlog.log", "a", stderr); static sem_t sem_recv; static unsigned int ifx; static char ifname[IFNAMSIZ]; void *thread_recv(void *arg) { state_t state = {0}; char *argv_00[] = { PACKAGE_NAME, "-v", "-i", ifname, "recv", CHANNEL_NAME, NULL }; int argc = sizeof argv_00 / sizeof argv_00[0] - 1; int rc; rc = agent_load_config(&state, argc, argv_00, NULL); sem_post(&sem_recv); /* tell main thread we're ready to recv */ if (!test_assert(rc == 0, "agent_load_config()")) goto skip_test; rc = agent_run(&state); free_state(&state); test_assert(rc == EXIT_SUCCESS, "agent_run() returned %i", rc); skip_test: sem_post(&sem_recv); /* tell main thread we're done */ return arg; } static int create_keys_and_tokens(int make_token) { state_t state = {0}; int rc; { /* generate keys */ char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } if (make_token) { /* generate token */ char *bearer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "sign", bearer_key, CHANNEL_NAME, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } { /* add trusted key (self-signed) */ char *signer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } return test_status; } int test_token(int presign_token) { char fakehome[] = "0000-0018-XXXXXX"; char buf[BUFSIZ]; state_t state = {0}; FILE *f; int argc; int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); /* generate keys */ if (create_keys_and_tokens(presign_token)) return test_status; /* missing required arguments - missing channel */ test_log("%s() missing requred arguments - missing channel\n", __func__); char *argv_00[] = { PACKAGE_NAME, "send", NULL }; argc = sizeof argv_00 / sizeof argv_00[0] - 1; memset(buf, 0, sizeof buf); /* clear output buffer */ REDIRECT_BUF rc = agent(&state, argc, argv_00); REDIRECT_OUT test_assert(errno == EINVAL, "EINVAL"); test_assert(rc == EXIT_FAILURE, "agent() returned %i", rc); /* verify cmd output (error message) */ test_assert(strstr(buf, AGENT_ERR_NOCHANNEL) != NULL, "error message found in output"); /* start recv thread */ struct timespec timeout; pthread_t tid; rc = sem_init(&sem_recv, 0, 0); if (!test_assert(rc == 0, "sem_init()")) goto err_close_redirect; rc = pthread_create(&tid, NULL, &thread_recv, NULL); if (!test_assert(rc == 0, "pthread_create() recv thread")) goto err_sem_recv_destroy; sem_wait(&sem_recv); /* wait until recv thread ready */ test_log("%s() send with valid token and channel\n", __func__); char *argv_01[] = { PACKAGE_NAME, "send", "-v", "-i", ifname, "--loopback", "--bpslimit", SPEEDLIMIT, CHANNEL_NAME, NULL }; argc = sizeof argv_01 / sizeof argv_01[0] - 1; rc = agent(&state, argc, argv_01); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); rc = clock_gettime(CLOCK_REALTIME, &timeout); if (!test_assert(rc == 0, "clock_gettime()")) goto err_sem_recv_destroy; timeout.tv_sec += ((RUNNING_ON_VALGRIND) ? WAITS * 2 : WAITS); rc = sem_timedwait(&sem_recv, &timeout); test_assert(rc == 0, "timeout"); pthread_cancel(tid); pthread_join(tid, NULL); if (rc != 0) goto err_sem_recv_destroy; err_sem_recv_destroy: sem_destroy(&sem_recv); err_close_redirect: return test_status; } int main(void) { char name[] = PACKAGE_NAME " send + recv"; test_name(name); test_require_net(TEST_NET_BASIC); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; test_assert(test_token(1) == 0, "test with pre-signed token"); test_assert(test_token(0) == 0, "test with self-signed token"); return test_status; } lcagent/test/0000-0019.c000066400000000000000000000061151501702040600145620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include #define NKEYS 42 /* return -1 if found, 0 if not */ static int find_key(lc_keyring_t *keyring, key_combo_t *key) { for (size_t i = 0; i < keyring->nkeys; i++) { #if 0 hash_hex_debug(stderr, keyring->key[i], sizeof key->s.pk); hash_hex_debug(stderr, key->s.pk, sizeof key->s.pk); #endif if (memcmp(keyring->key[i], key->s.pk, sizeof key->s.pk) == 0) return -1; } return 0; } static int verify_keys(lc_keyring_t *keyring, key_combo_t keys[NKEYS], char *keymap) { for (int i = 0; i < NKEYS; i++) { if (!isset(keymap, i)) continue; test_assert(find_key(keyring, &keys[i]), "key %i on keyring", i); } return 0; } static void freakeys(lc_keyring_t *keyring) { keyring_freekeys(keyring); lc_keyring_free(keyring); memset(keyring, 0, sizeof *keyring); } static unsigned int hamm(char *map) { int len = howmany(NKEYS, CHAR_BIT); unsigned int c = 0; while (len--) for (char v = map[len]; v; c++) v &= v - 1; return c; } static int load_keys(state_t *state, lc_keyring_t *keyring, char *keymap) { int rc = keyring_load(state, keyring); unsigned int nkeys = hamm(keymap); if (!test_assert(rc == 0, "keyring_load() returned %i", rc)) return -1; if (!test_assert(keyring->nkeys == nkeys, "%zu keys loaded", keyring->nkeys)) return -1; return 0; } /* tests of basic keyring_* functions */ static int keyring_tests(void) { char fakehome[] = "0000-0019-XXXXXX"; char keymap[howmany(NKEYS, CHAR_BIT)]; key_combo_t keys[NKEYS]; lc_keyring_t keyring = {0}; state_t state = {0}; int rc; /* generate some keys */ for (int i = 0; i < NKEYS; i++) { rc = key_gen_combopair_hex(&keys[i]); test_assert(rc == 0, "generate key %i", i); } /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = state_dirs(&state, fakehome); if (!test_assert(rc == 0, "state_dirs() returned %i", rc)) return test_status; /* add keys to keyring file on disk */ memset(keymap, 0, sizeof keymap); for (int i = 0; i < NKEYS; i++) { rc = keyring_add(&state, keys[i].phex); if (!test_assert(rc == 0, "keyring_add() key %i", i)) goto err_free_state; setbit(keymap, i); } /* load keys from disk */ if (load_keys(&state, &keyring, keymap)) goto err_free_keyring; /* verify we have all keys */ verify_keys(&keyring, keys, keymap); /* delete some keys randomly */ for (int i = 0; i < NKEYS; i++) { if (arc4random_uniform(100) < 34) continue; rc = keyring_del(&state, keys[i].phex); clrbit(keymap, i); } /* (re)load keys from disk */ freakeys(&keyring); if (load_keys(&state, &keyring, keymap)) goto err_free_state; /* verify we have correct keys */ verify_keys(&keyring, keys, keymap); err_free_keyring: freakeys(&keyring); err_free_state: free_state(&state); return test_status; } int main(void) { char name[] = "lcagent key (keyring mgmt functions)"; test_name(name); if (keyring_tests()) return test_status; return test_status; } lcagent/test/0000-0020.c000066400000000000000000000117451501702040600145570ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #define NKEYS 42 #define REDIRECT_BUF f = freopen("/dev/null", "a", stderr); assert(f); setbuf(stderr, buf); #define REDIRECT_OUT setbuf(stderr, NULL); static char buf[BUFSIZ]; /* return -1 if found, 0 if not */ static int find_key(lc_keyring_t *keyring, key_combo_t *key) { for (size_t i = 0; i < keyring->nkeys; i++) { #if 0 hash_hex_debug(stderr, keyring->key[i], sizeof key->s.pk); hash_hex_debug(stderr, key->s.pk, sizeof key->s.pk); #endif if (memcmp(keyring->key[i], key->s.pk, sizeof key->s.pk) == 0) return -1; } return 0; } static int verify_keys(lc_keyring_t *keyring, key_combo_t keys[NKEYS], char *keymap) { for (int i = 0; i < NKEYS; i++) { if (!isset(keymap, i)) continue; test_assert(find_key(keyring, &keys[i]), "key %i on keyring", i); } return 0; } static void freakeys(lc_keyring_t *keyring) { keyring_freekeys(keyring); lc_keyring_free(keyring); memset(keyring, 0, sizeof *keyring); } static unsigned int hamm(char *map) { int len = howmany(NKEYS, CHAR_BIT); unsigned int c = 0; while (len--) for (char v = map[len]; v; c++) v &= v - 1; return c; } static int load_keys(state_t *state, lc_keyring_t *keyring, char *keymap) { int rc = keyring_load(state, keyring); unsigned int nkeys = hamm(keymap); if (!test_assert(rc == 0, "keyring_load() returned %i", rc)) return -1; if (!test_assert(keyring->nkeys == nkeys, "%zu keys loaded", keyring->nkeys)) return -1; return 0; } static int create_fakehome(char *fakehome) { int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = setenv("HOME", fakehome, 1); test_assert(rc == 0, "set HOME"); return test_status; } static int test_keyring_add_and_del(void) { char fakehome[] = "0000-0020-XXXXXX"; char keymap[howmany(NKEYS, CHAR_BIT)]; state_t state = {0}; key_combo_t keys[NKEYS]; lc_keyring_t keyring = {0}; char *argv_00[] = { PACKAGE_NAME, "key", "add", NULL }; FILE *f; int argc = sizeof argv_00 / sizeof argv_00[0] - 1; int rc; /* first, test with required argument (key) missing */ REDIRECT_BUF rc = agent(&state, argc, argv_00); REDIRECT_OUT test_assert(rc == EXIT_FAILURE, "%s: agent() returned %i", __func__, rc); test_assert(strstr(buf, AGENT_ERR_NOKEY) != NULL, "%s() error message found in output", __func__); memset(&state, 0, sizeof state); if (create_fakehome(fakehome)) return test_status; /* generate some keys */ for (int i = 0; i < NKEYS; i++) { rc = key_gen_combopair_hex(&keys[i]); test_assert(rc == 0, "generate key %i", i); } /* add keys to keyring file on disk */ memset(keymap, 0, sizeof keymap); for (int i = 0; i < NKEYS; i++) { char *argv_01[] = { PACKAGE_NAME, "key", "add", keys[i].phex, NULL }; argc = sizeof argv_01 / sizeof argv_01[0] - 1; rc = agent(&state, argc, argv_01); test_assert(rc == EXIT_SUCCESS, "[%i] %s: lcagent add returned %i", i, __func__, rc); setbit(keymap, i); } /* load keys from disk */ rc = state_dirs(&state, fakehome); if (!test_assert(rc == 0, "state_dirs() returned %i", rc)) goto err_free_state; if (load_keys(&state, &keyring, keymap)) goto err_free_keyring; /* verify we have all keys */ verify_keys(&keyring, keys, keymap); freakeys(&keyring); free_state(&state); /* delete some keys randomly */ for (int i = 0; i < NKEYS; i++) { if (arc4random_uniform(100) < 34) continue; char *argv_02[] = { PACKAGE_NAME, "key", "del", keys[i].phex, NULL }; argc = sizeof argv_02 / sizeof argv_02[0] - 1; rc = agent(&state, argc, argv_02); test_assert(rc == EXIT_SUCCESS, "[%i] %s: lcagent del returned %i", i, __func__, rc); clrbit(keymap, i); } /* (re)load keys from disk */ rc = state_dirs(&state, fakehome); if (!test_assert(rc == 0, "state_dirs() returned %i", rc)) goto err_free_state; if (load_keys(&state, &keyring, keymap)) goto err_free_state; /* verify we have correct keys */ verify_keys(&keyring, keys, keymap); err_free_keyring: freakeys(&keyring); err_free_state: free_state(&state); return test_status; } static int test_keyring_key_missing(char *action) { char fakehome[] = "0000-0020-XXXXXX"; state_t state = {0}; FILE *f; char *argv_00[] = { PACKAGE_NAME, "key", action, NULL }; int argc = sizeof argv_00 / sizeof argv_00[0] - 1; int rc; /* first, test with required argument (key) missing */ if (create_fakehome(fakehome)) return test_status; REDIRECT_BUF rc = agent(&state, argc, argv_00); REDIRECT_OUT test_assert(rc == EXIT_FAILURE, "%s: agent() returned %i", __func__, rc); test_assert(strstr(buf, AGENT_ERR_NOKEY) != NULL, "%s() error message found in output", __func__); return test_status; } int main(void) { char name[] = "lcagent key"; test_name(name); if (test_keyring_key_missing("add")) return test_status; if (test_keyring_key_missing("del")) return test_status; if (test_keyring_add_and_del()) return test_status; return test_status; } lcagent/test/0000-0021.c000066400000000000000000000125561501702040600145610ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include "testnet.h" #include #include #include #include #include #define CHANNEL_NAME "Cui bono?" #define CMDLINE_PAYLOAD "Alonso" #define WAITS 8 #define SPEEDLIMIT "1048576" #define REDIRECT_BUF f = freopen("/dev/null", "a", stdout); assert(f); setbuf(stdout, buf); #define REDIRECT_OUT f = freopen("/dev/tty", "a", stdout); assert(f); setbuf(stdout, NULL); static sem_t sem_recv; static unsigned int ifx; static char ifname[IFNAMSIZ]; static char payload[BUFSIZ]; enum { PAYLOAD_CMD, PAYLOAD_STDIN, #if 0 PAYLOAD_FILE, #endif PAYLOAD_TYPES, }; void *thread_recv(void *arg) { state_t state = {0}; char *argv_00[] = { PACKAGE_NAME, "-v", "-i", ifname, "recv", CHANNEL_NAME, NULL }; FILE *f; char buf[BUFSIZ]; int argc = sizeof argv_00 / sizeof argv_00[0] - 1; int rc; memset(buf, 0, sizeof buf); rc = agent_load_config(&state, argc, argv_00, NULL); sem_post(&sem_recv); /* tell main thread we're ready to recv */ if (!test_assert(rc == 0, "agent_load_config()")) goto skip_test; REDIRECT_BUF rc = agent_run(&state); free_state(&state); REDIRECT_OUT test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); /* check payload */ fprintf(stderr, "%s() got %zu bytes\n", __func__, strlen(buf)); test_assert(!memcmp(payload, buf, sizeof buf), "recv buffer matches"); skip_test: sem_post(&sem_recv); /* tell main thread we're done */ return arg; } static int create_keys_and_tokens(void) { state_t state = {0}; int rc; { /* generate keys */ char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } { /* add trusted key (self-signed) */ char *signer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } return test_status; } static int test_payload_stdin(state_t *state) { char *argv[] = { PACKAGE_NAME, "send", "-v", "-i", ifname, "--loopback", "--bpslimit", SPEEDLIMIT, CHANNEL_NAME, "-", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; int pipefd[2]; size_t paylen = arc4random_uniform(sizeof payload); test_log("generating random payload of %zu bytes\n", paylen); arc4random_buf(payload, paylen); if (!test_assert(pipe(pipefd) == 0, "pipe()")) return test_status; pid_t pid = fork(); if (!pid) { /* child writes back to parent stdin through pipe */ close(pipefd[0]); /* close read end */ ssize_t ret = write(pipefd[1], payload, paylen); close(pipefd[1]); exit((ret == (ssize_t)paylen) ? EXIT_SUCCESS : EXIT_FAILURE); } close(pipefd[1]); /* close write end */ dup2(pipefd[0], 0); /* hook up stdin to read end of pipe */ rc = agent(state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); waitpid(pid, &rc, 0); close(pipefd[0]); test_assert(rc == EXIT_SUCCESS, "child writer returned %i", rc); return test_status; } static int test_payload_cmdline(state_t *state) { char *argv[] = { PACKAGE_NAME, "send", "-v", "-i", ifname, "--loopback", "--bpslimit", SPEEDLIMIT, CHANNEL_NAME, CMDLINE_PAYLOAD, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; sprintf(payload, CMDLINE_PAYLOAD); rc = agent(state, argc, argv); return test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } static int test_payload(int paytype) { char fakehome[] = "0000-0021-XXXXXX"; state_t state = {0}; int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); /* generate keys */ if (create_keys_and_tokens()) return test_status; /* start recv thread */ struct timespec timeout; pthread_t tid; rc = sem_init(&sem_recv, 0, 0); if (!test_assert(rc == 0, "sem_init()")) return test_status; rc = pthread_create(&tid, NULL, &thread_recv, NULL); if (!test_assert(rc == 0, "pthread_create() recv thread")) goto err_sem_recv_destroy; sem_wait(&sem_recv); /* wait until recv thread ready */ switch (paytype) { case PAYLOAD_CMD: test_payload_cmdline(&state); break; case PAYLOAD_STDIN: test_payload_stdin(&state); break; #if 0 case PAYLOAD_FILE: test_payload_cmdline(&state); break; #endif } rc = clock_gettime(CLOCK_REALTIME, &timeout); if (!test_assert(rc == 0, "clock_gettime()")) goto err_sem_recv_destroy; timeout.tv_sec += WAITS; rc = sem_timedwait(&sem_recv, &timeout); test_assert(rc == 0, "timeout"); pthread_cancel(tid); pthread_join(tid, NULL); if (rc != 0) goto err_sem_recv_destroy; err_sem_recv_destroy: sem_destroy(&sem_recv); return test_status; } int main(void) { char name[] = PACKAGE_NAME " send + recv (payloads)"; test_name(name); test_require_net(TEST_NET_BASIC); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; int max = (RUNNING_ON_VALGRIND) ? 0 : PAYLOAD_TYPES; for (int i = 0; i < max; i++) { if (!test_assert(test_payload(i) == 0, "test with payload (%i)", i)) return test_status; } return test_status; } lcagent/test/0000-0022.c000066400000000000000000000034671501702040600145630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include static int test_agent_stop(state_t *state) { char *argv[] = { PACKAGE_NAME, "stop", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; rc = agent(state, argc, argv); test_assert(rc == EXIT_SUCCESS, "(stop) agent() returned %i", rc); return test_status; } static int test_agent_start(state_t *state) { char *argv[] = { PACKAGE_NAME, "start", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; rc = agent(state, argc, argv); test_assert(rc == EXIT_SUCCESS, "(start) agent() returned %i", rc); /* try to start agent again, fail with EWOULDBLOCK */ rc = agent(state, argc, argv); test_assert(errno == EWOULDBLOCK, "EWOULDBLOCK"); if (!test_assert(rc == EXIT_FAILURE, "(start again) agent() returned %i", rc)) return test_status; /* check pidfile */ if (!test_assert(state->lockfd > 0, "state->lockfd = %i", state->lockfd)) return test_status; char pidbuf[21] = {0}; /* fits UINT64_MAX + NUL */ lseek(state->lockfd, 0, SEEK_SET); if (!test_assert(read(state->lockfd, pidbuf, sizeof pidbuf) > 0, "read pidfile")) { perror("read"); return test_status; } if (!test_assert(atoi(pidbuf) == state->pid, "lockfile pid set")) return test_status; return test_status; } int main(void) { char name[] = PACKAGE_NAME " start/stop"; char fakehome[] = "0000-0022-XXXXXX"; state_t state = {0}; test_name(name); /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); if (test_agent_start(&state)) return test_status; if (test_agent_stop(&state)) return test_status; return test_status; } lcagent/test/0000-0023.c000066400000000000000000000102221501702040600145470ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* * test channel actions * * When lcagent receives an authenticated packet, it looks up the channel it was * received on and executes the configured commands with the options specified. * * `lcagent exec` simulates receiving such a packet on the commandline. * * Test this, by: * - create test state * - call agent_exec() with the test state * - check the results */ #include "test.h" #include #include #define TEST_DATA \ X(0, "non-binary") \ X(1, "female") \ X(2, "male") \ X(3, "none of your business") \ X(4, "why are you obsessed with toilets?") \ X(5, "didn't you used to be an author?") static char testfile[] = "0000-0023-XXXXXX"; static int fdtest; static void push_test_item(state_t *state, char *chanstr) { char cmd[128]; state_push_channel(state, chanstr); snprintf(cmd, sizeof cmd, "echo -n \"%s\" > %s", chanstr, testfile); state_push_command(state, cmd, 0); } static void load_test_data(state_t *state) { state_defaults_set(state); #define X(a, b) push_test_item(state, b); TEST_DATA #undef X } static int create_fakehome(char *fakehome) { int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = setenv("HOME", fakehome, 1); test_assert(rc == 0, "set HOME"); return test_status; } static int test_exec_cmd(state_t *state, char *chanstr) { char fakehome[] = "0000-0023-XXXXXX"; char buf[BUFSIZ] = ""; char *argv[] = { PACKAGE_NAME, "exec", chanstr, NULL }; ssize_t byt; int argc = sizeof argv / sizeof argv[0] - 1; int rc; if (create_fakehome(fakehome)) return test_status; state_defaults_set(state); state_parse_args(state, argc, argv); rc = agent_run(state); test_assert(rc == EXIT_SUCCESS, "`lcagent exec \"%s\"` returned %i", chanstr, rc); test_log("buffer: '%s'\n", buf); /* verify cmd output */ lseek(fdtest, 0, SEEK_SET); byt = read(fdtest, buf, sizeof buf); if (!test_assert(byt > 0, "read %zi bytes", byt)) return test_status; test_assert(strstr(buf, chanstr) != NULL, "buffer contains channel string '%s'", chanstr); return test_status; } static int test_exec_commands(void) { state_t state = {0}; fdtest = mkstemp(testfile); if (!test_assert(fdtest >= 0, "creating temp file '%s'", testfile)) return test_status; load_test_data(&state); #define X(a, b) if (test_exec_cmd(&state, b)) return test_status; TEST_DATA #undef X free_state(&state); close(fdtest); return test_status; } static int test_exec_good_channel(void) { char fakehome[] = "0000-0023-XXXXXX"; state_t state = {0}; char *argv[] = { PACKAGE_NAME, "exec", "none of your business", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; if (create_fakehome(fakehome)) return test_status; load_test_data(&state); rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent exec (good channel) returned %i", rc); return test_status; } static int test_exec_bad_channel(void) { char fakehome[] = "0000-0023-XXXXXX"; state_t state = {0}; char *argv[] = { PACKAGE_NAME, "exec", "no such channel", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; if (create_fakehome(fakehome)) return test_status; load_test_data(&state); rc = agent(&state, argc, argv); test_assert(rc == EXIT_FAILURE, "lcagent exec (bad channel) returned %i", rc); return test_status; } static int test_exec_noop(void) { char fakehome[] = "0000-0023-XXXXXX"; state_t state = {0}; char *argv[] = { PACKAGE_NAME, "exec", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; if (create_fakehome(fakehome)) return test_status; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent exec (NOOP) returned %i", rc); return test_status; } int main(void) { char name[] = "lcagent exec"; test_name(name); if (test_exec_noop()) return test_status; if (test_exec_bad_channel()) return test_status; if (test_exec_good_channel()) return test_status; if (test_exec_commands()) return test_status; // TODO if (test_exec_command_stdin()) return test_status; return test_status; } lcagent/test/0000-0024-config.00.conf000066400000000000000000000010601501702040600167340ustar00rootroot00000000000000channel "0000-0024 NOOP" channel "0000-0024 testfile" command "touch testfile" channel "0000-0024 testfile with dir" directory mysubdir command "touch testfilewithdir" channel "0000-0024 testfile cmdtry" command "touch one" try "/bin/false" # this command will fail command "touch two" # this command MUST still be executed channel "0000-0024 testfile cmdtryfail" command "touch eins" command "/bin/false" # this command will fail command "touch zwei" # this command MUST NOT be executed channel "0000-0024 stdin" command "cat > outfile" lcagent/test/0000-0024.c000066400000000000000000000312041501702040600145530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* * end to end testing of lcagent server + client * - create config * - start server * - send commands * - test effect */ #include "test.h" #include "testnet.h" #include #include #include #include #include #define SPEEDLIMIT "1048576" static char ifname[IFNAMSIZ]; static unsigned int ifx; static int test_net_writefile_cmd_stdin_large(void) { enum { READ, WRITE, PIPEFDS }; state_t state = {0}; char channel_name[] = "0000-0024 stdin"; char filename[] = "outfile"; size_t paylen = 32678 * 4; char payload[paylen + BUFSIZ]; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "--bpslimit", SPEEDLIMIT, "-i", ifname, channel_name, "-", NULL }; ssize_t byt; int argc = sizeof argv / sizeof argv[0] - 1; int rc; int pipefd[2]; int wstatus; pid_t pid; test_log("\n%s\n", __func__); paylen += arc4random_uniform(BUFSIZ); arc4random_buf(payload, paylen); test_log("generated payload of %zu bytes\n", paylen); rc = pipe(pipefd); if (!test_assert(rc == 0, "pipe()")) return test_status; pid = fork(); if (pid == -1) { test_status = TEST_FAIL; for (int i = 0; i < PIPEFDS; i++) close(pipefd[i]); return test_status; } if (!pid) { close(pipefd[WRITE]); dup2(pipefd[READ], STDIN_FILENO); char *home = getenv("HOME"); test_log("HOME: %s\n", home); rc = agent(&state, argc, argv); close(pipefd[READ]); _exit(rc); } close(pipefd[READ]); /* write payload via pipe to stdin of child process */ size_t byt_out = 0; for (char *ptr = payload; byt_out < paylen; ptr += byt) { byt = write(pipefd[WRITE], ptr, paylen - byt_out); if (byt == -1) { perror("write"); break; } if (byt > 0) byt_out += byt; } close(pipefd[WRITE]); test_assert(byt_out == paylen, "wrote %zi/%zu bytes to pipe", byt_out, paylen); waitpid(pid, &wstatus, 0); rc = (WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : -1; test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ char cwd[PATH_MAX]; struct stat sb; if (!test_assert(getcwd(cwd, sizeof cwd) != NULL, "getcwd() returned %s", cwd)) return test_status; rc = state_dirs(&state, NULL); test_assert(rc == 0, "state_dirs()"); rc = chdir(state.dir_home); test_assert(rc == 0, "chdir to %s", state.dir_home); rc = stat(filename, &sb); test_assert(rc == 0, "stat %s", filename); test_assert((size_t)sb.st_size == paylen, "file has size %li / %zu", sb.st_size, paylen); /* This test fails (file only partly written) on one of our FreeBSD test * VMs when run under CI conditions. Passes OK when run manually. Make * this a WARNing until the reason for this is known. */ if (test_status) return TEST_WARN; /* check payload written to file */ char buf[sizeof payload]; int fd; fd = open(filename, O_RDONLY); if (!test_assert(fd != -1, "open() %s", filename)) goto restore_cwd; test_log("reading %s\n", filename); size_t byt_read = 0; for (char *ptr = buf; byt_read < (size_t)sb.st_size; ptr += byt) { byt = read(fd, ptr, sb.st_size - byt_read); if (byt == -1) { perror("read"); break; } if (byt > 0) byt_read += byt; } if (!test_assert(byt_read == paylen, "read %zi/%zu bytes", byt, paylen)) { test_status = TEST_WARN; goto close_fd; } test_assert(!memcmp(payload, buf, paylen), "payload data matches"); close_fd: close(fd); free_state(&state); restore_cwd: rc = chdir(cwd); test_assert(rc == 0, "chdir back to %s", cwd); return test_status; } static int test_net_writefile_cmd_stdin(void) { state_t state = {0}; char channel_name[] = "0000-0024 stdin"; char filename[] = "outfile"; char payload[] = "Trousers? In this economy?"; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, payload, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ char cwd[PATH_MAX]; struct stat sb; if (!test_assert(getcwd(cwd, sizeof cwd) != NULL, "getcwd() returned %s", cwd)) return test_status; rc = chdir(state.dir_home); test_assert(rc == 0, "chdir to %s", state.dir_home); rc = stat(filename, &sb); test_assert(rc == 0, "stat %s", filename); /* check payload written to file */ char buf[BUFSIZ]; ssize_t byt; int fd; fd = open(filename, O_RDONLY); if (!test_assert(fd != -1, "open() %s", filename)) goto restore_cwd; test_log("reading %s\n", filename); byt = read(fd, buf, sb.st_size); ssize_t len = sizeof payload - 1; if (!test_assert(byt == len, "read %zi/%zu bytes", byt, len)) goto close_fd; buf[byt] = 0; test_expect(payload, buf); close_fd: close(fd); restore_cwd: rc = chdir(cwd); test_assert(rc == 0, "chdir back to %s", cwd); return test_status; } static int test_net_writefile_cmd_tryfail(void) { state_t state = {0}; char channel_name[] = "0000-0024 testfile cmdtryfail"; char payload[] = ""; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, payload, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ char cwd[PATH_MAX]; struct stat sb; if (!test_assert(getcwd(cwd, sizeof cwd) != NULL, "getcwd() returned %s", cwd)) return test_status; rc = chdir(state.dir_home); test_assert(rc == 0, "chdir to %s", state.dir_home); rc = stat("eins", &sb); test_assert(rc == 0, "stat eins"); rc = stat("zwei", &sb); test_assert(rc == -1, "stat zwei (MUST NOT exist)"); rc = chdir(cwd); test_assert(rc == 0, "chdir back to %s", cwd); return test_status; } static int test_net_writefile_cmd_try(void) { state_t state = {0}; char channel_name[] = "0000-0024 testfile cmdtry"; char payload[] = ""; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, payload, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ char cwd[PATH_MAX]; struct stat sb; if (!test_assert(getcwd(cwd, sizeof cwd) != NULL, "getcwd() returned %s", cwd)) return test_status; rc = chdir(state.dir_home); test_assert(rc == 0, "chdir to %s", state.dir_home); rc = stat("one", &sb); test_assert(rc == 0, "stat one"); rc = chdir(cwd); test_assert(rc == 0, "stat two"); rc = chdir(cwd); test_assert(rc == 0, "chdir back to %s", cwd); return test_status; } static int test_net_writefile_with_dir(void) { state_t state = {0}; struct stat sb; char cwd[PATH_MAX]; char channel_name[] = "0000-0024 testfile with dir"; char filename[] = "testfilewithdir"; char subdir[] = "mysubdir"; char payload[] = ""; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, payload, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); /* make subdirectory for this test */ state_dirs(&state, NULL); if (!test_assert(getcwd(cwd, sizeof cwd) != NULL, "getcwd() returned %s", cwd)) return test_status; rc = chdir(state.dir_home); if (!test_assert(rc == 0, "chdir to %s", state.dir_home)) return test_status; rc = mkdir(subdir, 0755); if (!test_assert(rc == 0, "mkdir %s", subdir)) return test_status; rc = chdir(cwd); if (!test_assert(rc == 0, "chdir back to %s", cwd)) return test_status; free_state(&state); /* run agent */ rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ rc = chdir(state.dir_home); test_assert(rc == 0, "chdir to %s", state.dir_home); rc = chdir(subdir); test_assert(rc == 0, "chdir to %s", subdir); rc = stat(filename, &sb); test_assert(rc == 0, "stat %s", filename); rc = chdir(cwd); test_assert(rc == 0, "chdir back to %s", cwd); return test_status; } static int test_net_writefile(void) { state_t state = {0}; char channel_name[] = "0000-0024 testfile"; char filename[] = "testfile"; char payload[] = ""; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, payload, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ char cwd[PATH_MAX]; struct stat sb; if (!test_assert(getcwd(cwd, sizeof cwd) != NULL, "getcwd() returned %s", cwd)) return test_status; rc = chdir(state.dir_home); test_assert(rc == 0, "chdir to %s", state.dir_home); rc = stat(filename, &sb); test_assert(rc == 0, "stat %s", filename); rc = chdir(cwd); test_assert(rc == 0, "chdir back to %s", cwd); return test_status; } static int test_net_noop(void) { state_t state = {0}; char channel_name[] = "0000-0024 NOOP"; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", channel_name, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); return test_status; } static int test_stop_server(void) { state_t state = {0}; char *argv[] = { PACKAGE_NAME, "stop", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent stop returned %i", rc); return test_status; } static int test_start_server(void) { state_t state = {0}; char configfile[] = "0000-0024-config.00.conf"; char *argv[] = { PACKAGE_NAME, "start", "-v", "-i", ifname, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; rc = state_defaults_set(&state); if (!test_assert(rc == 0, "state_defaults_set()")) return test_status; rc = state_parse_configfile(&state, configfile); if (!test_assert(rc == 0, "state_parse_configfile()")) return test_status; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent start returned %i", rc); return test_status; } static int create_keys_and_tokens(void) { state_t state = {0}; int rc; { /* generate keys */ char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } /* NB: skipping token generation — use self-signed */ { /* add trusted key (self-signed) */ char *signer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } return test_status; } static int create_fake_home(void) { char fakehome[] = "0000-0024-XXXXXX"; if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); return test_status; } int main(void) { char name[] = "network end to end testing of server + client"; test_name(name); test_require_net(TEST_NET_BASIC); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; /* initialize */ if (create_fake_home()) return test_status; if (create_keys_and_tokens()) return test_status; if (test_start_server()) return test_status; /* run tests */ if (test_net_noop()) goto stop_server; if (test_net_writefile()) goto stop_server; if (test_net_writefile_with_dir()) goto stop_server; if (test_net_writefile_cmd_try()) goto stop_server; if (test_net_writefile_cmd_tryfail()) goto stop_server; if (test_net_writefile_cmd_stdin()) goto stop_server; /* this sleep is needed to let the network settle on one of the test * FreeBSD VMs when running in CI mode */ sleep(1); if (test_net_writefile_cmd_stdin_large()) goto stop_server; test_log("\n"); stop_server: test_stop_server(); return test_status; } lcagent/test/0000-0025.c000066400000000000000000000034521501702040600145600ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include #define SI_UNITS \ X(' ', 1) \ X('K', 1000) \ X('M', 1000000) \ X('G', 1000000000) \ X('T', 1000000000000) static int test_bpslimit_option_set(char si) { uint64_t mult; #define X(a, b) \ if (si == a) mult = b; SI_UNITS #undef X state_t state = {0}; char bpslimit[] = "42 "; char *argv[] = { PACKAGE_NAME, "send", "--bpslimit", bpslimit, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; bpslimit[2] = si; test_log("%s %s\n", argv[2], argv[3]); rc = state_parse_args(&state, argc, argv); if (!test_assert(rc == 0, "state_parse_args() returned %i", rc)) return test_status; test_assert(state.bpslimit == 42 * mult, "state.bpslimit = %" PRIu64 "", state.bpslimit); return test_status; } /* combine --bpslimit with other options */ static int test_bpslimit_and_loopback(void) { state_t state = {0}; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "--bpslimit", "123", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; rc = state_parse_args(&state, argc, argv); if (!test_assert(rc == 0, "state_parse_args() returned %i", rc)) return test_status; test_assert(state.bpslimit == 123, "state.bpslimit = %" PRIu64 "", state.bpslimit); return test_status; } int main(void) { char name[] = "--bpslimit option"; test_name(name); if (test_bpslimit_option_set(' ')) return test_status; if (test_bpslimit_option_set('K')) return test_status; if (test_bpslimit_option_set('M')) return test_status; if (test_bpslimit_option_set('G')) return test_status; if (test_bpslimit_option_set('T')) return test_status; if (test_bpslimit_and_loopback()) return test_status; return test_status; } lcagent/test/0000-0026.c000066400000000000000000000127061501702040600145630ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include "testnet.h" #include #include #include #include #include #define CHANNEL_NAME "Cui bono?" #define CMDLINE_PAYLOAD "Alonso" #define WAITS 8 #define SPEEDLIMIT "1048576" #define PAYLOAD_SZ 32768 + BUFSIZ #define REDIRECT_BUF f = freopen("/dev/null", "a", stdout); assert(f); setbuffer(stdout, buf, sizeof buf); #define REDIRECT_OUT f = freopen("/dev/tty", "a", stdout); assert(f); setbuf(stdout, NULL); static sem_t sem_recv; static unsigned int ifx; static char ifname[IFNAMSIZ]; static char payload[PAYLOAD_SZ]; static size_t paylen; enum { PAYLOAD_CMD, PAYLOAD_STDIN, #if 0 PAYLOAD_FILE, #endif PAYLOAD_TYPES, }; static void *thread_recv(void *arg) { state_t state = {0}; char *argv_00[] = { PACKAGE_NAME, "-v", "-i", ifname, "recv", CHANNEL_NAME, NULL }; FILE *f; char buf[PAYLOAD_SZ]; int argc = sizeof argv_00 / sizeof argv_00[0] - 1; int rc; memset(buf, 0, sizeof buf); rc = agent_load_config(&state, argc, argv_00, NULL); sem_post(&sem_recv); /* tell main thread we're ready to recv */ if (!test_assert(rc == 0, "agent_load_config()")) goto skip_test; REDIRECT_BUF rc = agent_run(&state); free_state(&state); fflush(stdout); REDIRECT_OUT test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); /* check payload */ test_assert(!memcmp(payload, buf, paylen), "recv buffer matches (%zu bytes)", paylen); skip_test: sem_post(&sem_recv); /* tell main thread we're done */ return arg; } static int create_keys_and_tokens(void) { state_t state = {0}; int rc; { /* generate keys */ char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } { /* add trusted key (self-signed) */ char *signer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } return test_status; } static int test_payload_stdin(state_t *state) { char *argv[] = { PACKAGE_NAME, "send", "-v", "-i", ifname, "--loopback", "--bpslimit", SPEEDLIMIT, CHANNEL_NAME, "-", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; int pipefd[2]; paylen = 32768 + arc4random_uniform(BUFSIZ); test_log("generating random payload of %zu bytes\n", paylen); arc4random_buf(payload, paylen); if (!test_assert(pipe(pipefd) == 0, "pipe()")) return test_status; pid_t pid = fork(); if (!pid) { /* child writes back to parent stdin through pipe */ close(pipefd[0]); /* close read end */ ssize_t ret = write(pipefd[1], payload, paylen); close(pipefd[1]); exit((ret == (ssize_t)paylen) ? EXIT_SUCCESS : EXIT_FAILURE); } close(pipefd[1]); /* close write end */ dup2(pipefd[0], 0); /* hook up stdin to read end of pipe */ rc = agent(state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); waitpid(pid, &rc, 0); close(pipefd[0]); test_assert(rc == EXIT_SUCCESS, "child writer returned %i", rc); return test_status; } static int test_payload_cmdline(state_t *state) { char *argv[] = { PACKAGE_NAME, "send", "-v", "-i", ifname, "--loopback", "--bpslimit", SPEEDLIMIT, CHANNEL_NAME, CMDLINE_PAYLOAD, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; paylen = strlen(CMDLINE_PAYLOAD); sprintf(payload, CMDLINE_PAYLOAD); rc = agent(state, argc, argv); return test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } static int test_payload(int paytype) { char fakehome[] = "0000-0021-XXXXXX"; state_t state = {0}; int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); /* generate keys */ if (create_keys_and_tokens()) return test_status; /* start recv thread */ struct timespec timeout; pthread_t tid; rc = sem_init(&sem_recv, 0, 0); if (!test_assert(rc == 0, "sem_init()")) return test_status; rc = pthread_create(&tid, NULL, &thread_recv, NULL); if (!test_assert(rc == 0, "pthread_create() recv thread")) goto err_sem_recv_destroy; sem_wait(&sem_recv); /* wait until recv thread ready */ switch (paytype) { case PAYLOAD_CMD: test_payload_cmdline(&state); break; case PAYLOAD_STDIN: test_payload_stdin(&state); break; #if 0 case PAYLOAD_FILE: test_payload_cmdline(&state); break; #endif } rc = clock_gettime(CLOCK_REALTIME, &timeout); if (!test_assert(rc == 0, "clock_gettime()")) goto err_sem_recv_destroy; timeout.tv_sec += WAITS; rc = sem_timedwait(&sem_recv, &timeout); test_assert(rc == 0, "timeout"); pthread_cancel(tid); pthread_join(tid, NULL); if (rc != 0) goto err_sem_recv_destroy; err_sem_recv_destroy: sem_destroy(&sem_recv); return test_status; } int main(void) { char name[] = PACKAGE_NAME " send + recv (large payloads)"; test_name(name); test_require_net(TEST_NET_BASIC); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; int max = (RUNNING_ON_VALGRIND) ? 0 : PAYLOAD_TYPES; for (int i = 0; i < max; i++) { if (!test_assert(test_payload(i) == 0, "test with payload (%i)", i)) return test_status; } return test_status; } lcagent/test/0000-0027-config.00.conf000066400000000000000000000000241501702040600167360ustar00rootroot00000000000000logfile lcagent.log lcagent/test/0000-0027.c000066400000000000000000000066021501702040600145620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include #include #include #include #include #define TEST_FMT_00 "" #define TEST_FMT_01 "All human beings are born free and equal in dignity and rights\n" #define TEST_FMT_02 "Everyone has the right to %s, %s and %s\n" #define TEST_ARG_02 "life", "liberty", "security of person" #define TEST_OUT_02 "Everyone has the right to life, liberty and security of person" static int create_fakehome(char *fakehome) { int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = setenv("HOME", fakehome, 1); test_assert(rc == 0, "set HOME to %s", fakehome); return test_status; } static int test_logging_00(void) { char fakehome[] = "0000-0027-XXXXXX"; char configfile[] = "0000-0027-config.00.conf"; state_t state = {0}; int rc; if (create_fakehome(fakehome)) return test_status; rc = state_parse_configfile(&state, configfile); if (!test_assert(rc == 0, "state_parse_config(), rc = %i", rc)) return test_status; if (!test_assert(state.logfile != NULL, "state.logfile: %s", state.logfile)) goto err_free_state; /* chdir to fakehome to write logfile with relative path */ rc = chdir(fakehome); if (!test_assert(rc == 0, "chdir() to %s", fakehome)) return test_status; rc = log_open(&state, NULL); if (!test_assert(rc == 0, "log_open() returned %i", rc)) return test_status; if (!test_assert(state.log != NULL, "state.log is not NULL")) goto err_free_state; rc = log_write(&state, TEST_FMT_00); if (!test_assert(rc == 0, "log_write() returned %i", rc)) goto err_free_state; rc = log_write(&state, TEST_FMT_01); if (!test_assert(rc == 0, "log_write() returned %i", rc)) goto err_free_state; rc = log_write(&state, TEST_FMT_02, TEST_ARG_02); if (!test_assert(rc == 0, "log_write() returned %i", rc)) goto err_free_state; rc = log_close(&state); if (!test_assert(rc == 0, "log_close() returned %i", rc)) goto err_free_state; if (!test_assert(state.log == NULL, "state.log is NULL")) goto err_free_state; /* check logfile exists */ struct stat sb; rc = stat(state.logfile, &sb); test_assert(rc == 0, "stat %s", state.logfile); if (!test_assert(sb.st_size > 0, "%s size = %zu", state.logfile, sb.st_size)) goto err_free_state; /* verify log entries */ char *buf; ssize_t byt; ssize_t byt_read = 0; int fd; buf = malloc(sb.st_size); if (!test_assert(buf != NULL, "malloc'ed buffer of %zu bytes", sb.st_size)) goto err_free_state; memset(buf, 0, sb.st_size); fd = open(state.logfile, O_RDONLY); if (!test_assert(fd != -1, "opened %s", state.logfile)) goto free_buf; for (char *ptr = buf; byt_read < sb.st_size; ptr += byt) { byt = read(fd, ptr, sb.st_size - byt_read); if (byt == -1) { perror("read"); break; } if (byt > 0) byt_read += byt; } if (!test_assert(byt_read == sb.st_size, "read %zi bytes", byt_read)) goto close_fd; test_assert(strstr(buf, TEST_FMT_01) != NULL, "found '%s'", TEST_FMT_01); test_assert(strstr(buf, TEST_OUT_02) != NULL, "found '%s'", TEST_OUT_02); close_fd: close(fd); free_buf: free(buf); err_free_state: free_state(&state); return test_status; } int main(void) { char name[] = "logfile"; test_name(name); if (test_logging_00()) return test_status; return test_status; } lcagent/test/0000-0028.c000066400000000000000000000047261501702040600145700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ #include "test.h" #include "testnet.h" #include #include #include static int create_fakehome(char *fakehome) { int rc; /* create fake home directory */ if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } rc = setenv("HOME", fakehome, 1); test_assert(rc == 0, "set HOME"); return test_status; } static int test_agent_stop(void) { state_t state = {0}; char *argv[] = { PACKAGE_NAME, "stop", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "(stop) agent() returned %i", rc); return test_status; } static int test_agent_start(state_t *state) { char *argv[] = { PACKAGE_NAME, "start", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; rc = agent(state, argc, argv); test_assert(rc == EXIT_SUCCESS, "(start) agent() returned %i", rc); return test_status; } static int test_onstart(void) { state_t state = {0}; struct stat sb; char fakehome[] = "0000-0028-XXXXXX"; char cmd[128]; char *channame[] = { "one", "two" }; int channels = sizeof channame / sizeof channame[0]; int rc; if (create_fakehome(fakehome)) return test_status; state.logfile = strdup("lastlog.log"); state_defaults_set(&state); STATE_SET(&state, STATE_DEBUG); for (int i = 0; i < channels; i++) { state_push_channel(&state, channame[i]); if (i) state.chan_head->flags |= CHAN_ONSTART; snprintf(cmd, sizeof cmd, "touch \"%s\"", channame[i]); state_push_command(&state, cmd, 0); } /* start agent */ test_agent_start(&state); /* wait a moment for onstart events to run */ usleep(100000); /* stop agent */ test_agent_stop(); /* change to fake home directory to verify results */ rc = chdir(fakehome); if (!test_assert(rc == 0, "chdir() returned %i", rc)) return test_status; /* ensure file "one" was NOT created */ rc = stat(channame[0], &sb); test_assert(errno == ENOENT, "errno == ENOENT"); test_assert(rc == -1, "stat '%s' returned %i", channame[0], rc); /* ensure file "two" exists */ rc = stat(channame[1], &sb); test_assert(rc == 0, "stat '%s' returned %i", channame[1], rc); return test_status; } int main(void) { char name[] = "event: onstart"; test_name(name); test_require_net(TEST_NET_BASIC); if (test_onstart()) return test_status; return test_status; } lcagent/test/0000-0029.c000066400000000000000000000107721501702040600145670ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* * create fakehome * write config * start lcagent * lcagent send, verify command output * change config * lcagent reload * lcagent send, verify command output */ #include "test.h" #include "testnet.h" #include #include #include #include #include #define SPEEDLIMIT "1048576" #define CHANNEL "0000-0029" static char ifname[IFNAMSIZ]; static unsigned int ifx; static int test_config(int n) { state_t state = {0}; char testfile[] = "0000-0029-XXXXXX/success"; char channel_name[] = CHANNEL; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, NULL }; char *home = getenv("HOME"); int argc = sizeof argv / sizeof argv[0] - 1; int rc; test_log("\n%s\n", __func__); rc = snprintf(testfile, sizeof testfile, "%s/success", home); if (!test_assert(rc == sizeof testfile - 1, "build testfile string")) return test_status; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); /* wait for command to execute */ if (RUNNING_ON_VALGRIND) sleep(1); else usleep(10000); /* check file was written */ struct stat sb; rc = stat(testfile, &sb); test_assert(rc == n, "stat %s", testfile); return test_status; } static int test_write_config(int n) { char configfile[] = "0000-0029-XXXXXX/.lcagentrc"; char *cmd = (!n) ? "/bin/false" : "touch success"; char *home = getenv("HOME"); FILE *f; int rc; rc = snprintf(configfile, sizeof configfile, "%s/.lcagentrc", home); if (!test_assert(rc == sizeof configfile - 1, "build configfile string")) return test_status; f = fopen(configfile, "w+"); if (!test_assert(f != NULL, "creating config %s", configfile)) return test_status; fprintf(f, "logfile lastlog.log\n"); fprintf(f, "debug\n"); fprintf(f, "channel '%s'\n", CHANNEL); fprintf(f, " command '%s'\n", cmd); fclose(f); return test_status; } static int test_reload_server(void) { state_t state = {0}; char *argv[] = { PACKAGE_NAME, "reload", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent reload returned %i", rc); return test_status; } static int test_stop_server(void) { state_t state = {0}; char *argv[] = { PACKAGE_NAME, "stop", "-v", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent stop returned %i", rc); return test_status; } static int test_start_server(void) { state_t state = {0}; char *argv[] = { PACKAGE_NAME, "start", "-v", "-i", ifname, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "lcagent start returned %i", rc); return test_status; } static int create_keys_and_tokens(void) { state_t state = {0}; int rc; { /* generate keys */ char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } /* NB: skipping token generation — use self-signed */ { /* add trusted key (self-signed) */ char *signer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } return test_status; } static int create_fake_home(void) { char fakehome[] = "0000-0029-XXXXXX"; if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); return test_status; } int main(void) { char name[] = "lcagent reload"; test_name(name); test_require_net(TEST_NET_BASIC); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; /* initialize */ if (create_fake_home()) return test_status; if (create_keys_and_tokens()) return test_status; if (test_write_config(0)) return test_status; if (test_start_server()) return test_status; /* run tests */ if (test_config(-1)) goto stop_server; if (test_write_config(1)) goto stop_server; if (test_reload_server()) return test_status; if (test_config(0)) goto stop_server; test_log("\n"); stop_server: test_stop_server(); return test_status; } lcagent/test/0000-0030.c000066400000000000000000000131431501702040600145520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2025 Brett A C Sheffield */ /* * - fakehome * - write config with seed and wrong perms * - verify error * - correct perms * - verify config loaded successfully * - TODO send/recv test (FAIL, wrong key) * - send/recv test (PASS, right key) * - TODO send/recv test (stdin) */ #include "test.h" #include "testnet.h" #include #include #include #include #include #define SPEEDLIMIT "1048576" #define CHANNEL "0000-0030" #define CMDLINE_PAYLOAD "Alonso" #define WAITS 8 #define REDIRECT_BUF f = freopen("/dev/null", "a", stdout); assert(f); setbuf(stdout, buf); #define REDIRECT_OUT f = freopen("/dev/tty", "a", stdout); assert(f); setbuf(stdout, NULL); static char ifname[IFNAMSIZ]; static unsigned int ifx; static char configfile[] = "0000-0030-XXXXXX/.lcagentrc"; static char payload[BUFSIZ]; static sem_t sem_recv; void *thread_recv(void *arg) { state_t state = {0}; char *argv_00[] = { PACKAGE_NAME, "-v", "-i", ifname, "recv", CHANNEL, NULL }; FILE *f; char buf[BUFSIZ]; int argc = sizeof argv_00 / sizeof argv_00[0] - 1; int rc; memset(buf, 0, sizeof buf); rc = agent_load_config(&state, argc, argv_00, NULL); sem_post(&sem_recv); /* tell main thread we're ready to recv */ if (!test_assert(rc == 0, "agent_load_config()")) goto skip_test; REDIRECT_BUF rc = agent_run(&state); free_state(&state); REDIRECT_OUT test_assert(rc == EXIT_SUCCESS, "agent_run() returned %i", rc); /* check payload */ fprintf(stderr, "%s() got %zu bytes\n", __func__, strlen(buf)); test_assert(!memcmp(payload, buf, sizeof buf), "recv buffer matches"); skip_test: sem_post(&sem_recv); /* tell main thread we're done */ return arg; } static int test_payload_cmdline(state_t *state) { char *argv[] = { PACKAGE_NAME, "send", "-v", "-i", ifname, "--loopback", "--bpslimit", SPEEDLIMIT, CHANNEL, CMDLINE_PAYLOAD, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; sprintf(payload, CMDLINE_PAYLOAD); rc = agent(state, argc, argv); return test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } static int test_payload(void) { state_t state = {0}; int rc; /* start recv thread */ struct timespec timeout; pthread_t tid; rc = sem_init(&sem_recv, 0, 0); if (!test_assert(rc == 0, "sem_init()")) return test_status; rc = pthread_create(&tid, NULL, &thread_recv, NULL); if (!test_assert(rc == 0, "pthread_create() recv thread")) goto err_sem_recv_destroy; sem_wait(&sem_recv); /* wait until recv thread ready */ test_payload_cmdline(&state); rc = clock_gettime(CLOCK_REALTIME, &timeout); if (!test_assert(rc == 0, "clock_gettime()")) goto err_sem_recv_destroy; timeout.tv_sec += WAITS; rc = sem_timedwait(&sem_recv, &timeout); test_assert(rc == 0, "timeout"); pthread_cancel(tid); pthread_join(tid, NULL); if (rc != 0) goto err_sem_recv_destroy; err_sem_recv_destroy: sem_destroy(&sem_recv); return test_status; } static int test_config(void) { state_t state = {0}; char channel_name[] = CHANNEL; char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; int rc; /* require that rcfile is not world readable */ rc = agent(&state, argc, argv); test_assert(rc == EXIT_FAILURE, "%s() lcagent send returned %i", __func__, rc); /* fix permissions on rcfile */ rc = chmod(configfile, S_IRUSR|S_IWUSR); if (!test_assert(rc == 0, "fix permissions on rcfile")) return test_status; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc); return test_status; } static int test_write_config(void) { char *home = getenv("HOME"); FILE *f; int rc; rc = snprintf(configfile, sizeof configfile, "%s/.lcagentrc", home); if (!test_assert(rc == sizeof configfile - 1, "build configfile string")) return test_status; f = fopen(configfile, "w+"); if (!test_assert(f != NULL, "creating config %s", configfile)) return test_status; /* ensure file is world-readable (insecure) */ mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; fchmod(fileno(f), mode); fprintf(f, "logfile lastlog.log\n"); fprintf(f, "debug\n"); fprintf(f, "seed password\n"); fclose(f); return test_status; } static int create_keys_and_tokens(void) { state_t state = {0}; int rc; { /* generate keys */ char *argv[] = { PACKAGE_NAME, "whoami", NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } { /* add trusted key (self-signed) */ char *signer_key = state.defaults.keyring.phex; char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL }; int argc = sizeof argv / sizeof argv[0] - 1; rc = agent(&state, argc, argv); test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc); } return test_status; } static int create_fake_home(void) { char fakehome[] = "0000-0030-XXXXXX"; if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) { perror("mkdtemp"); return test_status; } setenv("HOME", fakehome, 1); return test_status; } int main(void) { char name[] = "symmetric encryption"; test_name(name); test_require_net(TEST_NET_BASIC); ifx = get_multicast_if(); if (!ifx) return (test_status = TEST_WARN); if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()")) return test_status; if (create_fake_home()) return test_status; if (test_write_config()) return test_status; if (test_config()) return test_status; if (create_keys_and_tokens()) return test_status; if (test_payload()) return test_status; return test_status; } lcagent/test/Makefile.in000066400000000000000000000113001501702040600155050ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only # Copyright (c) 2017-2024 Brett Sheffield TESTRUNNER := @TESTRUNNER@ CFLAGS := @CFLAGS@ -fno-builtin-malloc -fno-builtin-calloc -I../src CPPFLAGS := @CPPFLAGS@ FALSE := @FALSE@ TEST_SOURCES := $(wildcard ????-????.c) TEST_PROGS ?= $(TEST_SOURCES:.c=.test) TEST_MULTICAST_Linux := ip route get ff1e:: 2> /dev/null | wc -l TEST_MULTICAST_FreeBSD := case $$(route -6 get ff1e:: 2> /dev/null) in *REJECT*) echo 0;; *DONE*) echo 1;; *) echo 0 ;; esac TEST_MULTICAST_NetBSD := case $$(/sbin/route -n get -inet6 ff1e:: 2>/dev/null) in *REJECT*) echo 0;; *DONE*) echo 1;; *) echo 0 ;; esac TEST_MULTICAST_OpenBSD := case $$(route -n get -inet6 ff1e:: 2> /dev/null) in *REJECT*) echo 0;; *DONE*) echo 1;; *) echo 0 ;; esac TEST_MULTICAST_OUT = $(shell $(TEST_MULTICAST) || /bin/true) TEST_MULTICAST = $(TEST_MULTICAST_$(shell uname -s)) LDFLAGS = @LDFLAGS@ -L../src -Wl,-rpath,../src LDLIBS := @LIBS@ NETTOOLS := @IFCONFIG@ @IPROUTE@ NOTOBJS := ../src/lcagent.o OBJS := test.o testdata.o testnet.o $(filter-out $(NOTOBJS), $(wildcard ../src/*.o)) LASTLOG := lastlog.log LOGFILE := $(shell mktemp "testlog-XXXXXX") ANSI_RESET = "\033[0m" ANSI_BOLD := "\033[0m\033[2m" ANSI_GREEN = "\033[32m" ANSI_RED = "\033[31m" ANSI_YELLOW = "\033[33m" ANSI_WHITE = "\033[37m" PASS = $(ANSI_RESET)$(ANSI_GREEN)\ OK$(ANSI_RESET) WARN = $(ANSI_RESET)$(ANSI_YELLOW)WARN$(ANSI_RESET) FAIL = $(ANSI_RESET)$(ANSI_RED)FAIL$(ANSI_RESET) UNKN = $(ANSI_RESET)$(ANSI_WHITE)????$(ANSI_RESET) VALGRIND_LEAK_KINDS = definite,indirect,possible VALGRIND = valgrind --leak-check=full --show-leak-kinds=$(VALGRIND_LEAK_KINDS) --max-stackframe=20480160 --error-exitcode=2 --errors-for-leak-kinds=$(VALGRIND_LEAK_KINDS) --track-origins=yes define runtest = export HAVE_MULTICAST_ROUTES="$(TEST_MULTICAST_OUT)" @printf $(ANSI_BOLD) @echo -n " $(1:.test=) " @echo "================================================================================" >>$(LOGFILE) @echo "`date +"%Y-%m-%d %H:%M:%S"` test begins" >>$(LOGFILE) @echo "--------------------------------------------------------------------------------" >>$(LOGFILE) @echo -n "===> TEST $(1:.test=) " >>$(LOGFILE) @$(MEMCHECK) ./$(1) 2>>$(LOGFILE) ret=$$? @echo "--------------------------------------------------------------------------------" >>$(LOGFILE) @echo "`date +"%Y-%m-%d %H:%M:%S"` test ends" >>$(LOGFILE) @if [ $$ret -eq 0 ]; then \ printf $(PASS); echo; \ elif [ $$ret -eq 1 ]; then \ printf $(WARN); echo; \ elif [ $$ret -eq 2 ]; then \ printf $(FAIL); echo; \ fails=$$(( fails + 1 )) ; \ else \ printf $(UNKN); echo; \ fails=$$(( fails + 1 )) ; \ fi $(eval tests_run=$(shell echo $$(($(tests_run)+1)))) endef .PHONY: test clean logclean tmpclean netcheck test: $(TESTRUNNER) .PHONY: newtest newtest: $(EDITOR) `./newtest.sh` netcheck: $(NETTOOLS) ifconfig: ifconfig iproute: @echo "== LINKS ====================================================================" ip -c=never -d link show @echo "== ADDR =====================================================================" ip -c=never addr show @echo "== ROUTE ====================================================================" ip -c=never route show ip -6 -c=never route show @echo "== NEIGHBOURS ===============================================================" ip -6 -c=never neighbour @echo "== MROUTE ===================================================================" ip -6 -c=never mroute @echo "== NETNS ====================================================================" ip -6 -c=never netns @echo "=============================================================================" %.test: %.c $(OBJS) $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< $(OBJS) $(LDLIBS) %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< lctest: $(TEST_PROGS) lctest -b $^ memcheck: MEMCHECK="$(VALGRIND)" $(MAKE) test memcheck/%: MEMCHECK="$(VALGRIND)" $(MAKE) test/$(@F) # if $(@F) is a directory, run tests in that directory test/%: @if [ -d "$(@F)" ]; then $(eval TEST_PROGS=$(notdir $(wildcard $(@F)/*.test))) TEST_PROGS="$(TEST_PROGS)" $(MAKE) maketest else $(eval TEST_PROGS=0000-$(@F).test) TEST_PROGS="$(TEST_PROGS)" $(MAKE) maketest fi .ONESHELL: maketest maketest: $(TEST_PROGS) @fails=0 ln -sf $(LOGFILE) $(LASTLOG) $(foreach x,$^,$(call runtest,$(x))) @echo -e "\n$(TIMESTAMP) - tests done" >> $(LOGFILE) @echo -e "$(tests_run) tests run\nlogfile: $(LOGFILE)\n" @if [ $$fails -gt 0 ]; then \ echo ", $$fails tests failed"; \ $(FALSE); else echo fi logclean: $(RM) testlog-?????? $(LASTLOG) tmpclean: $(RM) -r ????-????-?????? $(RM) -r ????-????.src.tmp.?????? $(RM) -r ????-????.dst.tmp.?????? clean: logclean tmpclean $(RM) *.o *.test realclean: clean $(RM) Makefile lcagent/test/all/000077500000000000000000000000001501702040600142155ustar00rootroot00000000000000lcagent/test/all/0000-0000.test000077700000000000000000000000001501702040600201152../0000-0000.testustar00rootroot00000000000000lcagent/test/all/0000-0001.test000077700000000000000000000000001501702040600201172../0000-0001.testustar00rootroot00000000000000lcagent/test/all/0000-0002.test000077700000000000000000000000001501702040600201212../0000-0002.testustar00rootroot00000000000000lcagent/test/all/0000-0003.test000077700000000000000000000000001501702040600201232../0000-0003.testustar00rootroot00000000000000lcagent/test/all/0000-0004.test000077700000000000000000000000001501702040600201252../0000-0004.testustar00rootroot00000000000000lcagent/test/all/0000-0005.test000077700000000000000000000000001501702040600201272../0000-0005.testustar00rootroot00000000000000lcagent/test/all/0000-0006.test000077700000000000000000000000001501702040600201312../0000-0006.testustar00rootroot00000000000000lcagent/test/all/0000-0007.test000077700000000000000000000000001501702040600201332../0000-0007.testustar00rootroot00000000000000lcagent/test/all/0000-0008.test000077700000000000000000000000001501702040600201352../0000-0008.testustar00rootroot00000000000000lcagent/test/all/0000-0009.test000077700000000000000000000000001501702040600201372../0000-0009.testustar00rootroot00000000000000lcagent/test/all/0000-0010.test000077700000000000000000000000001501702040600201172../0000-0010.testustar00rootroot00000000000000lcagent/test/all/0000-0011.test000077700000000000000000000000001501702040600201212../0000-0011.testustar00rootroot00000000000000lcagent/test/all/0000-0012.test000077700000000000000000000000001501702040600201232../0000-0012.testustar00rootroot00000000000000lcagent/test/all/0000-0014.test000077700000000000000000000000001501702040600201272../0000-0014.testustar00rootroot00000000000000lcagent/test/all/0000-0015.test000077700000000000000000000000001501702040600201312../0000-0015.testustar00rootroot00000000000000lcagent/test/all/0000-0016.test000077700000000000000000000000001501702040600201332../0000-0016.testustar00rootroot00000000000000lcagent/test/all/0000-0017.test000077700000000000000000000000001501702040600201352../0000-0017.testustar00rootroot00000000000000lcagent/test/all/0000-0018.test000077700000000000000000000000001501702040600201372../0000-0018.testustar00rootroot00000000000000lcagent/test/all/0000-0019.test000077700000000000000000000000001501702040600201412../0000-0019.testustar00rootroot00000000000000lcagent/test/all/0000-0020.test000077700000000000000000000000001501702040600201212../0000-0020.testustar00rootroot00000000000000lcagent/test/all/0000-0021.test000077700000000000000000000000001501702040600201232../0000-0021.testustar00rootroot00000000000000lcagent/test/all/0000-0022.test000077700000000000000000000000001501702040600201252../0000-0022.testustar00rootroot00000000000000lcagent/test/all/0000-0023.test000077700000000000000000000000001501702040600201272../0000-0023.testustar00rootroot00000000000000lcagent/test/all/0000-0024.test000077700000000000000000000000001501702040600201312../0000-0024.testustar00rootroot00000000000000lcagent/test/all/0000-0025.test000077700000000000000000000000001501702040600201332../0000-0025.testustar00rootroot00000000000000lcagent/test/all/0000-0026.test000077700000000000000000000000001501702040600201352../0000-0026.testustar00rootroot00000000000000lcagent/test/all/0000-0027.test000077700000000000000000000000001501702040600201372../0000-0027.testustar00rootroot00000000000000lcagent/test/all/0000-0028.test000077700000000000000000000000001501702040600201412../0000-0028.testustar00rootroot00000000000000lcagent/test/all/0000-0029.test000077700000000000000000000000001501702040600201432../0000-0029.testustar00rootroot00000000000000lcagent/test/all/0000-0030.test000077700000000000000000000000001501702040600201232../0000-0030.testustar00rootroot00000000000000lcagent/test/falloc.c000066400000000000000000000027641501702040600150620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2021-2022 Brett Sheffield */ #define _GNU_SOURCE #include #include #include #include #include static volatile int allock; static size_t stackptr; static char stackbuf[1024]; static void *(*_malloc)(size_t); static void (*_free)(void *); static int falloc_fail = -1; /* *alloc fails when zero - decremented each allocation */ static void *falloc_enomem(void) { fprintf(stderr, "falloc forcing ENOMEM\n"); errno = ENOMEM; return NULL; } void *malloc(size_t size) { if (allock) { /* thanks to FatalFlaw for the idea * https://stackoverflow.com/questions/6083337/overriding-malloc-using-the-ld-preload-mechanism */ /* dlsym calls calloc() - hand it a block from our stack */ void *p; p = stackbuf + stackptr; stackptr += size; return p; } else if (!_malloc) { allock = 1; *(void **)&_malloc = dlsym(RTLD_NEXT, "malloc"); *(void **)&_free = dlsym(RTLD_NEXT, "free"); allock = 0; } if (falloc_fail > 0) falloc_fail--; if (!falloc_fail) return falloc_enomem(); return _malloc(size); } void *calloc(size_t nmemb, size_t size) { void *ptr; size_t sz = nmemb * size; ptr = malloc(sz); if (!ptr) return NULL; memset(ptr, 0, sz); return ptr; } void free(void *ptr) { if (!ptr) return; if ((char *)ptr < stackbuf || (char *)ptr > stackbuf + sizeof stackbuf) _free(ptr); } void falloc_setfail(int failafter) { falloc_fail = failafter; } lcagent/test/falloc.h000066400000000000000000000010241501702040600150530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2021,2023 Brett Sheffield */ #define FALLOC_NOFAIL -1 /* wrappers for *alloc */ __attribute__((malloc)) __attribute__((alloc_size(1,2))) void *calloc(size_t nmemb, size_t size); __attribute__((malloc)) __attribute__((alloc_size(1))) void *malloc(size_t size); /* set *alloc calls to force failure with ENOMEM after failafter allocations. * Set failafter to -1 to never force fail (default) */ void falloc_setfail(int failafter); lcagent/test/newtest.sh000077500000000000000000000006731501702040600155030ustar00rootroot00000000000000#!/bin/sh -ue SKEL="skel.c" # first, find filename for new test for i in `seq -w 0 9999` do TESTEXEC="0000-$i.test" TESTFILE="0000-$i.c" if [ ! -f ${TESTFILE} ]; then break fi done head -n1 ${SKEL} > ${TESTFILE} echo "/* Copyright (c) `date +"%Y"` `git config --get user.name` <`git config --get user.email`> */" >> ${TESTFILE} tail -n+3 ${SKEL} >> ${TESTFILE} if [ -d all ]; then cd all ln -s ../${TESTEXEC} fi echo ${TESTFILE} lcagent/test/skel.c000066400000000000000000000003431501702040600145470ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2024 Brett Sheffield */ #include "test.h" int main(void) { char name[] = "testname"; test_name(name); return test_status; } lcagent/test/test.c000066400000000000000000000125751501702040600146020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2020-2023 Brett Sheffield */ #include "test.h" #include #include #include #include #include #include #include #define DEFAULT_TERM_COLS 80 #define TESTID_WIDTH 16 #define SPINAL_TAP 11 int COLS = DEFAULT_TERM_COLS; int MSGW; int test_status = TEST_OK; /* test exit status (not a count of failures) */ int capreqd = 0; int capfail = 0; sem_t log_lock; static int _vscprintf (const char * format, va_list argp) { int r; va_list argc; va_copy(argc, argp); r = vsnprintf(NULL, 0, format, argc); va_end(argc); return r; } void vfail_msg(char *msg, va_list argp) { char *b; b = malloc(_vscprintf(msg, argp) + 1); pthread_cleanup_push(free, b); vsprintf(b, msg, argp); printf("\n%-*s%-*s", SPINAL_TAP, " ", MSGW, b); pthread_cleanup_pop(1); /* free(b); */ test_status = TEST_FAIL; } static int test_assert_va(int condition, int verbose, char *msg, va_list argp) { char *b; va_list copy; va_copy(copy, argp); b = malloc(_vscprintf(msg, argp) + 6); pthread_cleanup_push(free, b); if (b) vsprintf(b, msg, copy); if (!condition) { vfail_msg(msg, argp); if (b) test_log("[FAIL] %s\n", b); } else if (b && verbose) { test_log("[ OK ] %s\n", b); } pthread_cleanup_pop(1); /* free(b); */ va_end(argp); return condition; } int test_assert(int condition, char *msg, ...) { va_list argp; va_start(argp, msg); return test_assert_va(condition, 1, msg, argp); } /* assert quietly - only log on failure */ int test_assert_q(int condition, char *msg, ...) { va_list argp; va_start(argp, msg); return test_assert_va(condition, 0, msg, argp); } int test_strcmp(char *str1, char *str2, char *msg, ...) { if (str1 == NULL || str2 == NULL || strcmp(str1, str2)) { va_list argp; va_start(argp, msg); vfail_msg(msg, argp); va_end(argp); return 0; } return 1; } int test_strncmp(char *str1, char *str2, size_t len, char *msg, ...) { if (str1 == NULL || str2 == NULL || strncmp(str1, str2, len)) { va_list argp; va_start(argp, msg); vfail_msg(msg, argp); va_end(argp); return 0; } return 1; } int test_expect(char *expected, char *got) { test_log("expected: '%s', got: '%s'\n", expected, got); return test_strcmp(expected, got, "expected: '%s', got: '%s'", expected, got); } int test_expectn(char *expected, char *got, size_t len) { return test_strncmp(expected, got, len, "expected: '%s', got: '%s'", expected, got); } void test_log(char *msg, ...) { char *b; va_list argp; va_start(argp, msg); b = malloc(_vscprintf(msg, argp) + 1); pthread_cleanup_push(free, b); vsprintf(b, msg, argp); sem_wait(&log_lock); fprintf(stderr, "%s", b); fflush(stderr); sem_post(&log_lock); va_end(argp); pthread_cleanup_pop(1); /* free(b); */ } static void init_terminal(void) { #if HAVE_SYS_IOCTL_H if (isatty(fileno(stdout))) { /* get terminal size */ struct winsize w; COLS = (ioctl(fileno(stdout), TIOCGWINSZ, &w) != -1) ? w.ws_col : DEFAULT_TERM_COLS; } else COLS = DEFAULT_TERM_COLS; #endif MSGW = COLS - TESTID_WIDTH; } void test_init(void) { static int runonce; if (runonce++) return; init_terminal(); sem_init(&log_lock, 0, 1); } void test_name(char *str, ...) { char *b; va_list argp; int do_exit = 1; test_init(); va_start(argp, str); b = malloc(_vscprintf(str, argp) + 1); vsprintf(b, str, argp); test_log(" %s\n", b); test_log("\n"); if (capfail) { printf("%-*s", MSGW, "----- requires capabilities (skipping) -----"); } else if (!capreqd && geteuid() == 0) { printf("%-*s", MSGW, "----- does not require root (skipping) -----"); } else if (test_status != TEST_OK) { printf("%-*s", MSGW, "----- (skipping) -----"); } else { printf("%-*s", MSGW, b); do_exit ^= do_exit; } fflush(stdout); /* ensure the test name is displayed in case of SIGABORT */ va_end(argp); free(b); if (do_exit) exit(test_status); } int test_skip(char *str, ...) { char *b; va_list argp; init_terminal(); sem_init(&log_lock, 0, 1); va_start(argp, str); b = malloc(_vscprintf(str, argp) + 1); vsprintf(b, str, argp); printf("(skipped) %-*s", MSGW - 10, b); test_log(" %s\n", b); test_log("\n"); va_end(argp); free(b); return TEST_WARN; } void test_rusage(void) { struct rusage ru = {0}; if (getrusage(RUSAGE_SELF, &ru)) { perror("getrusage"); return; } test_log("user : %lis.%li\n", ru.ru_utime.tv_sec, ru.ru_utime.tv_usec); test_log("system: %lis.%li\n", ru.ru_stime.tv_sec, ru.ru_stime.tv_usec); test_log("maxrss: %li\n", ru.ru_maxrss); test_log("ixrss: %li\n", ru.ru_ixrss); test_log("idrss: %li\n", ru.ru_idrss); test_log("isrss: %li\n", ru.ru_isrss); test_log("minflt: %li\n", ru.ru_minflt); test_log("majflt: %li\n", ru.ru_majflt); test_log("nswap: %li\n", ru.ru_nswap); test_log("inblock: %li\n", ru.ru_inblock); test_log("oublock: %li\n", ru.ru_oublock); test_log("msgsnd: %li\n", ru.ru_msgsnd); test_log("msgrcv: %li\n", ru.ru_msgrcv); test_log("nsignals: %li\n", ru.ru_nsignals); test_log("nvcsw: %li\n", ru.ru_nvcsw); test_log("nivcsw: %li\n", ru.ru_nivcsw); } void test_cap_require(int cap) { (void) cap; // TODO check for capabilities on Linux if (geteuid()) capfail++; capreqd++; } void test_require_linux(void) { #ifndef __linux__ init_terminal(); printf("%-*s", MSGW, "----- linux only (skipping) -----"); exit(test_status); #endif } lcagent/test/test.h000066400000000000000000000022501501702040600145740ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2020-2022 Brett Sheffield */ #ifndef _TEST_H #define _TEST_H 1 #ifdef __linux__ # define _GNU_SOURCE #endif #include "../src/config.h" #include #include #include #include #include #include "valgrind.h" #if HAVE_SYS_ENDIAN_H # include #endif #ifdef __linux__ #include #else #define CAP_NET_ADMIN 12 #define CAP_NET_RAW 13 #endif enum { TEST_ERR = -1, TEST_OK = 0, TEST_WARN = 1, TEST_FAIL = 2, TEST_UNKN = 3 }; extern int test_status; void test_init(void); void fail_msg(char *msg, ...); int test_assert(int condition, char *msg, ...); int test_assert_q(int condition, char *msg, ...); int test_strcmp(char *str1, char *str2, char *msg, ...); int test_strncmp(char *str1, char *str2, size_t len, char *msg, ...); int test_expect(char *expected, char *got); int test_expectn(char *expected, char *got, size_t len); void test_log(char *msg, ...); void test_name(char *str, ...); int test_skip(char *str, ...); void test_cap_require(int cap); void test_require_linux(void); #endif /* _TEST_H */ lcagent/test/testdata.c000066400000000000000000000137231501702040600154300ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2023 Brett Sheffield */ #include "testdata.h" #include static size_t getbufsize(void) { int buflen = sysconf(_SC_PAGESIZE); return (size_t)(16 * buflen); } int test_random_bytes(void *ptr, size_t sz) { FILE *fr; size_t byt = 0, bi; fr = fopen("/dev/urandom", "r"); if (!fr) return -1; while (byt < sz) { bi = fread(ptr, 1, sz - byt, fr); if (bi > 0) { byt += bi; ptr = (char *)ptr + bi; } } fclose(fr); return 0; } int test_sparsify_fd(int fd, size_t sz) { FILE *f; int fdup; fdup = dup(fd); f = fdopen(fdup, "r+"); if (!f) return -1; fseek(f, sz - 1, SEEK_SET); fputc(0, f); fclose(f); return 0; } static int test_randomize_file(int fd, size_t sz) { FILE *f, *fr; char buf[BUFSIZ]; size_t byt = 0, bi; int fdup; fdup = dup(fd); f = fdopen(fdup, "w+"); if (!f) return -1; fr = fopen("/dev/urandom", "r"); if (!fr) goto exit_0; while (byt < sz) { bi = fread(buf, 1, MIN(sizeof buf, sz - byt), fr); if (bi > 0) { byt += fwrite(buf, 1, bi, f); } } fclose(fr); exit_0: fclose(f); return 0; } int test_data_file(char *filename, size_t sz, int flags) { int fd; if (flags & TEST_TMP) { if ((fd = mkstemp(filename)) == -1) { perror("mkstemp"); return -1; } } else if ((fd = open(filename, flags)) == -1) { perror("open"); return -1; } if (flags & TEST_RND) { if (test_randomize_file(fd, sz) == -1) { close(fd); return -1; } } else if (sz > 0) { /* size requested, but no data => sparse */ if (test_sparsify_fd(fd, sz) == -1) { close(fd); return -1; } } return fd; } #if HAVE_ARC4RANDOM_UNIFORM int test_random_meta(const char *path, int flags) { int rc = 0; if (flags & TEST_OWN) { uid_t uid; gid_t gid; uid = arc4random_uniform(1024); gid = arc4random_uniform(1024); rc = chown(path, uid, gid); if (rc) return rc; } if (flags & TEST_MOD) { mode_t mode; mode = arc4random_uniform(0777); rc = chmod(path, mode); } return rc; } int test_createtesttree(const char *path, off_t max_filesz, int max_files, int max_dirs, int depth, int flags) { char template[] = "test-XXXXXX"; char filename[] = "test-XXXXXX"; char cwd[PATH_MAX]; char owd[PATH_MAX]; size_t len; int fd, max, rc; if (!getcwd(owd, sizeof owd)) return -1; rc = chdir(path); if (!getcwd(cwd, sizeof cwd)) return -1; if (rc == -1) { perror(path); return -1; } len = strlen(template); max = arc4random_uniform(max_files); for (int i = 0; i < max; i++) { memcpy(filename, template, len); fd = mkstemp(filename); if (fd == -1) { perror("mkstemp"); break; } test_randomize_file(fd, arc4random_uniform(max_filesz)); test_random_meta(filename, flags); close(fd); } max = arc4random_uniform(max_dirs); if (!max) max = 1; for (int i = 0; i < max; i++) { memcpy(filename, template, len); if (mkdtemp(filename)) { test_random_meta(filename, flags); if (depth > 0) test_createtesttree(filename, max_filesz, max_files, max_dirs, depth - 1, flags); } } return chdir(owd); } #endif int test_createtestdir(char *testprog, char **dir, const char *srcdst) { char template[] = ".tmp.XXXXXX"; size_t buflen, sz; int rc; /* find length of required buffer and allocate */ sz = strlen(testprog) - 5; /* strip .test */ buflen = (size_t)snprintf(NULL, 0, "%.*s.%s%s", (int)sz, testprog, srcdst, template) + 1; *dir = malloc(buflen); if (!*dir) return -1; /* build template string for directory name */ rc = snprintf(*dir, buflen, "%.*s.%s%s", (int)sz, testprog, srcdst, template); if (rc != (int)(buflen - 1)) goto err_free_dir; /* create temp directory */ if (!mkdtemp(*dir)) goto err_free_dir; fprintf(stderr, "directory '%s' created\n", *dir); return 0; err_free_dir: free(*dir); return -1; } int test_createtestdirs(char *testprog, char **src, char **dst) { int rc; rc = test_createtestdir(testprog, src, "src"); if (rc == -1) return -1; rc = test_createtestdir(testprog, dst, "dst"); if (rc == 0) return 0; rc = errno; free(src); errno = rc; return -1; } int test_data_size(char *filename, size_t sz) { struct stat sb; if (stat(filename, &sb) == -1) return -1; if ((sb.st_mode & S_IFMT) != S_IFREG) return -1; if ((size_t)sb.st_size != sz) return -1; return 0; } int test_hash_file(const char *filename, unsigned char *hash, size_t hashlen, char *buf, size_t buflen) { hash_state state; FILE *f; size_t byt; int free_buf = (buf == NULL); /* was buffer provided ? */ if (!buf) { buflen = getbufsize(); buf = malloc(buflen); if (!buf) return -1; } f = fopen(filename, "r"); if (!f) goto exit_0; hash_init(&state, NULL, 0, hashlen); while ((byt = fread(buf, 1, buflen, f))) { hash_update(&state, (unsigned char *)buf, byt); } hash_final(&state, hash, hashlen); fclose(f); exit_0: if (free_buf) free(buf); return 0; } int test_file_match(const char *file1, const char *file2) { unsigned char hash[2][HASHSIZE]; char buf[BUFSIZ]; memset(hash, 0, sizeof hash); test_hash_file(file1, hash[0], HASHSIZE, buf, sizeof buf); test_hash_file(file2, hash[1], HASHSIZE, buf, sizeof buf); fprintf(stderr, "%s: ", file1); hash_hex_debug(stderr, hash[0], HASHSIZE); fprintf(stderr, "%s: ", file2); hash_hex_debug(stderr, hash[1], HASHSIZE); return memcmp(hash[0], hash[1], HASHSIZE); } #if HAVE_GETPAGESIZE int test_file_scratch(const char *filename, long offset, size_t len) { char *buf; FILE *f, *fr; size_t byt = 0, bi; int buflen = MIN(getpagesize(), (int)len); if (!len) return 0; f = fopen(filename, "r+"); if (!f) return -1; fr = fopen("/dev/urandom", "r"); if (!fr) goto exit_0; if (offset < 0) { if (fseek(f, offset, SEEK_END) == -1) goto exit_1; } else if (offset > 0) { if (fseek(f, offset, SEEK_SET) == -1) goto exit_1; } buf = malloc(buflen); if (!buf) goto exit_1; while (byt < len) { bi = fread(buf, 1, MIN((size_t)buflen, len - byt), fr); if (bi > 0) { byt += fwrite(buf, 1, bi, f); } } free(buf); exit_1: fclose(fr); exit_0: fclose(f); return 0; } #endif /* HAVE_GETPAGESIZE */ lcagent/test/testdata.h000066400000000000000000000057451501702040600154420ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2023 Brett Sheffield */ #ifndef _TESTDATA_H #define _TESTDATA_H 1 #include "test.h" #include #include #include #include #include #include #include #include #include #include enum { TEST_TMP = 1, TEST_RND = 2, TEST_OWN = 4, TEST_MOD = 8, }; /* write sz random bytes to ptr */ int test_random_bytes(void *ptr, size_t sz); /* create test file with of sz bytes at path filename * * flags: * TEST_TMP - if set, a temp file is created using filename as the template. * TEST_RND - fill the file with sz random bytes * * if sz > 0 but TEST_RND is not set, a sparse file is created. */ int test_data_file(char *filename, size_t sz, int flags); /* verify filename is a regular file and is correct size * return 0 if correct, -1 on error */ int test_data_size(char *filename, size_t sz); /* hash file * filename - file to hash * hash - buffer to return hash in * hashlen - length of hash buffer * buf - either NULL or a pointer to a preallocated buffer for file I/O * buflen - length of buf in bytes * returns 0 on success, -1 on error, setting errno */ int test_hash_file(const char *filename, unsigned char *hash, size_t hashlen, char *buf, size_t buflen); /* hash files and confirm data matches, return 0 on match, nonzero fail */ int test_file_match(const char *file1, const char *file2); /* write len random bytes to file, starting at offset off * returns 0 on success, -1 on error, setting errno */ int test_file_scratch(const char *filename, long offset, size_t len); /* extend file sparsely to sz bytes */ int test_sparsify_fd(int fd, size_t sz); #if HAVE_ARC4RANDOM_UNIFORM /* randomize file metadata * path must be an existing directory * flags can be: * - TEST_OWN - randomize uid and gid * - TEST_MOD - randomize permissions */ int test_random_meta(const char *path, int flags); /* create a directory tree of files and directories with directories nested * depth levels deep, up to max_files files and max_dirs directories in each * level. Fill files with up to max_filesz random bytes. * path must be an existing directory * flags can be: * - TEST_OWN - randomize uid and gid * - TEST_MOD - randomize permissions */ int test_createtesttree(const char *path, off_t max_filesz, int max_files, int max_dirs, int depth, int flags); #endif /* HAVE_ARC4RANDOM_UNIFORM */ /* create a pair of source and destination directories using the template * of the form 0000-0000.{src,dst}.tmp.XXXXXX based on the test name. * testprog should be passed as basename(argv[0]) * Both src and dst need to be free()ed after use. */ int test_createtestdirs(char *testprog, char **src, char **dst); /* create a single test directory as above. srcdst must be "src" or "dst" */ int test_createtestdir(char *testprog, char **dir, const char *srcdst); #endif /* _TESTDATA_H */ lcagent/test/testnet.c000066400000000000000000000070571501702040600153100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2022-2023 Brett Sheffield */ #include "test.h" #include #include #ifdef __linux__ # include # include #endif #include #include #include #ifndef AF_LINK # ifdef AF_PACKET # define AF_LINK AF_PACKET # endif #endif unsigned int get_invalid_ifx(void) { char ifname[IF_NAMESIZE]; for (unsigned int ifx = 1; ifx < UINT_MAX; ifx++) { if (!if_indextoname(ifx, ifname)) return ifx; } return 0; } /* find an interface that supports multicast */ unsigned get_multicast_if(void) { unsigned ifidx = 0; /* create tap interface and bring it up */ char ifname[IFNAMSIZ] = {0}; #ifdef __linux__ lc_tuntap_create(ifname, IFF_TAP | IFF_NO_PI); lc_ctx_t *lctx = lc_ctx_new(); lc_link_set(lctx, ifname, 1); lc_ctx_free(lctx); ifidx = if_nametoindex(ifname); #endif if (!ifidx) { test_log("unable to create tap device: %s\n", strerror(errno)); /* failed to create tap, find multicast capable interface */ struct ifaddrs *ifa = NULL, *ifap = NULL; test_assert(getifaddrs(&ifa) != -1, "getifaddrs(): %s", strerror(errno)); for (ifap = ifa; ifap; ifap = ifap->ifa_next) { if (!(ifap->ifa_flags & IFF_MULTICAST)) continue; /* we don't want the loopback interface */ if (ifap->ifa_flags & IFF_LOOPBACK) continue; if (ifap->ifa_addr == NULL) continue; if (ifap->ifa_addr->sa_family != AF_INET6) continue; ifidx = if_nametoindex(ifap->ifa_name); test_log("found multicast interface %s [%u]\n", ifap->ifa_name, ifidx); break; } freeifaddrs(ifa); } else { test_log("tap %s created\n", ifname); } return ifidx; } int test_net_level(void) { struct ifaddrs *ifaddr; int ifaces = 0; int ifup = 0; int mcast = 0; int addr6 = 0; int addr4 = 0; int level = 0; int mroutes = 0; char * have_multicast = getenv("HAVE_MULTICAST_ROUTES"); test_init(); test_log("Checking network connectivity...\n"); if (getifaddrs(&ifaddr) == -1) return -1; for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { switch (ifa->ifa_addr->sa_family) { case AF_LINK: ifaces++; if (ifa->ifa_flags & IFF_UP) { ifup++; /* test MULTICAST flag, skip loopback interfaces */ if (ifa->ifa_flags & IFF_LOOPBACK) break; if (ifa->ifa_flags & IFF_MULTICAST) mcast++; } break; case AF_INET6: if (ifa->ifa_flags & IFF_UP) addr6++; break; case AF_INET: if (ifa->ifa_flags & IFF_UP) addr4++; break; } } freeifaddrs(ifaddr); test_log("have_multicast: %s\n", have_multicast); if (have_multicast) mroutes = atoi(have_multicast); test_log(" %i network interfaces found\n", ifaces); test_log(" %i network interfaces up\n", ifup); test_log(" %i MULTICAST interfaces up\n", mcast); test_log(" %i IPv6 addresses\n", addr6); test_log(" %i IPv4 addresses\n", addr4); test_log(" %i multicast routes\n", mroutes); level |= (ifaces > 0); level |= (ifup > 0) << 1; level |= (addr4 > 0) << 2; level |= (addr6 > 0) << 3; level |= (mcast > 0) << 4; level |= (mroutes > 0) << 5; test_log("connectivity level = %i (%06b)\n", level, level); return level; } int test_require_net(int required) { int lvl = test_net_level(); if ((lvl & required) != required) { test_log("------------------------------------------------------\n"); test_log("Required network level not met. Skipping test. \n"); test_log("(%06b/%06b)\n", lvl, required); test_log("------------------------------------------------------\n"); exit(TEST_WARN); } return lvl; } lcagent/test/testnet.h000066400000000000000000000021501501702040600153020ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2022-2023 Brett Sheffield */ #include "test.h" #include #include #include #include /* levels of network availability. Each level requires the previous */ enum { TEST_NET_NONE = 0, /* no network interfaces detected */ TEST_NET_IFACE = 1,/* at least one interface detected */ TEST_NET_UP = 2, /* at least one interface is up */ TEST_NET_ADDR4 = 4, /* IPv4 network address found */ TEST_NET_ADDR6 = 8, /* IPv6 network address found */ TEST_NET_MCAST = 16,/* device with MULTICAST flag set found */ TEST_NET_MROUTE = 32 /* has multicast routes */ }; #define TEST_NET_BASIC TEST_NET_IFACE | TEST_NET_UP | TEST_NET_ADDR6 | TEST_NET_MCAST | TEST_NET_MROUTE /* find an invalid interface index */ unsigned int get_invalid_ifx(void); /* find an interface that supports multicast */ unsigned get_multicast_if(void); /* return level of network available */ int test_net_level(void); /* exit test with TEST_WARN unless network level is met */ int test_require_net(int required); lcagent/test/testsync.c000066400000000000000000000053461501702040600154750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2023-2024 Brett Sheffield */ #include "../src/config.h" #define _XOPEN_SOURCE 700 /* for nftw() */ #include #include "test.h" #include "testdata.h" #include #include #include static char *rsrc, *rdst, *owd; /* check mode, owner, group, times */ int statcmp_sb(const struct stat *ssb, const struct stat *dsb, const char *dst) { if ((ssb->st_mode & S_IFMT) == S_IFLNK) return 0; /* don't bother with symlinks */ test_assert(ssb->st_mode == dsb->st_mode, "st_mode: '%s' %o %o", dst, ssb->st_mode, dsb->st_mode); test_assert(ssb->st_uid == dsb->st_uid, "st_uid: '%s'", dst); test_assert(ssb->st_gid == dsb->st_gid, "st_gid: '%s'", dst); if ((ssb->st_mode & S_IFMT) != S_IFDIR) { /* check size, but not for directories */ test_assert(ssb->st_size == dsb->st_size, "st_size: '%s'", dst); } #ifndef HAVE_UTIMENSAT /* without utimensat(), we only have microsecond precision */ ssb->st_mtim.tv_nsec /= 1000; ssb->st_mtim.tv_nsec *= 1000; ssb->st_atim.tv_nsec /= 1000; ssb->st_atim.tv_nsec *= 1000; #endif /* No point testing atime unless the filesystem is mounted noatime */ /* test_assert(ssb.st_atim.tv_nsec == dsb.st_atim.tv_nsec, "st_atim: '%s'", dst); */ test_assert(ssb->st_mtim.tv_nsec == dsb->st_mtim.tv_nsec, "st_mtim: '%s'", dst); return 0; } /* check mode, owner, group, times */ int statcmp(const char *src, const char *dst) { struct stat ssb, dsb; int rc; rc = stat(src, &ssb); if (!test_assert(rc == 0, "stat '%s'", src)) return -1; rc = stat(dst, &dsb); if (!test_assert(rc == 0, "stat '%s'", dst)) return -1; return statcmp_sb(&ssb, &dsb, dst); } static int verify_dst(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { (void) typeflag, (void) ftwbuf; struct stat dsb; int rc; if (chdir(rdst) == -1) return -1; rc = stat(fpath, &dsb); if (!rc) rc = statcmp_sb(sb, &dsb, fpath); if (chdir(rsrc) == -1) return -1; return rc; } #if HAVE_GETCWD #if HAVE_REALPATH void test_verify_dirs(const char *src, const char *dst) { int n = 0, rc; statcmp(src, dst); owd = getcwd(NULL, 0); assert(owd); rsrc = realpath(src, NULL); rdst = realpath(dst, NULL); test_log("src: %s\n", src); test_log("dst: %s\n", dst); test_log("rsrc: %s\n", rsrc); test_log("rdst: %s\n", rdst); if (chdir(rsrc) == -1) goto err_free; rc = nftw(".", &verify_dst, 32, 0); if (!test_assert(rc == 0, "nftw() returned %i", rc)) goto err_free; test_log("%i entries verified\n", n); test_assert(test_status == TEST_OK, "source and destination trees match"); if (chdir(owd) == -1) perror("chdir"); err_free: free(rsrc); free(rdst); free(owd); } #endif /* HAVE_REALPATH */ #endif /* HAVE_GETCWD */ lcagent/test/testsync.h000066400000000000000000000006761501702040600155030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */ /* Copyright (c) 2023 Brett Sheffield */ #ifndef _TESTSYNC_H #define _TESTSYNC_H 1 /* check mode, owner, group, times */ int statcmp(const char *src, const char *dst); /* verify src and dst match (recursive directory compare) */ #if HAVE_GETCWD # if HAVE_REALPATH void test_verify_dirs(const char *src, const char *dst); # endif #endif #endif /* _TESTSYNC_H */ lcagent/test/valgrind.h000066400000000000000000014713751501702040600154460ustar00rootroot00000000000000/* -*- c -*- ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (valgrind.h) only. The rest of Valgrind is licensed under the terms of the GNU General Public License, version 2, unless otherwise indicated. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2017 Julian Seward. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------- Notice that the above BSD-style license applies to this one file (valgrind.h) only. The entire rest of Valgrind is licensed under the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- */ /* This file is for inclusion into client (your!) code. You can use these macros to manipulate and query Valgrind's execution inside your own programs. The resulting executables will still run without Valgrind, just a little bit more slowly than they otherwise would, but otherwise unchanged. When not running on valgrind, each client request consumes very few (eg. 7) instructions, so the resulting performance loss is negligible unless you plan to execute client requests millions of times per second. Nevertheless, if that is still a problem, you can compile with the NVALGRIND symbol defined (gcc -DNVALGRIND) so that client requests are not even compiled in. */ #ifndef __VALGRIND_H #define __VALGRIND_H /* ------------------------------------------------------------------ */ /* VERSION NUMBER OF VALGRIND */ /* ------------------------------------------------------------------ */ /* Specify Valgrind's version number, so that user code can conditionally compile based on our version number. Note that these were introduced at version 3.6 and so do not exist in version 3.5 or earlier. The recommended way to use them to check for "version X.Y or later" is (eg) #if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ && (__VALGRIND_MAJOR__ > 3 \ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) */ #define __VALGRIND_MAJOR__ 3 #define __VALGRIND_MINOR__ 16 #include /* Nb: this file might be included in a file compiled with -ansi. So we can't use C++ style "//" comments nor the "asm" keyword (instead use "__asm__"). */ /* Derive some tags indicating what the target platform is. Note that in this file we're using the compiler's CPP symbols for identifying architectures, which are different to the ones we use within the rest of Valgrind. Note, __powerpc__ is active for both 32 and 64-bit PPC, whereas __powerpc64__ is only active for the latter (on Linux, that is). Misc note: how to find out what's predefined in gcc by default: gcc -Wp,-dM somefile.c */ #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_win32 #undef PLAT_amd64_win64 #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64be_linux #undef PLAT_ppc64le_linux #undef PLAT_arm_linux #undef PLAT_arm64_linux #undef PLAT_s390x_linux #undef PLAT_mips32_linux #undef PLAT_mips64_linux #undef PLAT_nanomips_linux #undef PLAT_x86_solaris #undef PLAT_amd64_solaris #if defined(__APPLE__) && defined(__i386__) # define PLAT_x86_darwin 1 #elif defined(__APPLE__) && defined(__x86_64__) # define PLAT_amd64_darwin 1 #elif (defined(__MINGW32__) && defined(__i386__)) \ || defined(__CYGWIN32__) \ || (defined(_WIN32) && defined(_M_IX86)) # define PLAT_x86_win32 1 #elif (defined(__MINGW32__) && defined(__x86_64__)) \ || (defined(_WIN32) && defined(_M_X64)) /* __MINGW32__ and _WIN32 are defined in 64 bit mode as well. */ # define PLAT_amd64_win64 1 #elif defined(__linux__) && defined(__i386__) # define PLAT_x86_linux 1 #elif defined(__linux__) && defined(__x86_64__) && !defined(__ILP32__) # define PLAT_amd64_linux 1 #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) # define PLAT_ppc32_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 /* Big Endian uses ELF version 1 */ # define PLAT_ppc64be_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 /* Little Endian uses ELF version 2 */ # define PLAT_ppc64le_linux 1 #elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) # define PLAT_arm_linux 1 #elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) # define PLAT_arm64_linux 1 #elif defined(__linux__) && defined(__s390__) && defined(__s390x__) # define PLAT_s390x_linux 1 #elif defined(__linux__) && defined(__mips__) && (__mips==64) # define PLAT_mips64_linux 1 #elif defined(__linux__) && defined(__mips__) && (__mips==32) # define PLAT_mips32_linux 1 #elif defined(__linux__) && defined(__nanomips__) # define PLAT_nanomips_linux 1 #elif defined(__sun) && defined(__i386__) # define PLAT_x86_solaris 1 #elif defined(__sun) && defined(__x86_64__) # define PLAT_amd64_solaris 1 #else /* If we're not compiling for our target platform, don't generate any inline asms. */ # if !defined(NVALGRIND) # define NVALGRIND 1 # endif #endif /* ------------------------------------------------------------------ */ /* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ /* in here of use to end-users -- skip to the next section. */ /* ------------------------------------------------------------------ */ /* * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client * request. Accepts both pointers and integers as arguments. * * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind * client request that does not return a value. * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind * client request and whose value equals the client request result. Accepts * both pointers and integers as arguments. Note that such calls are not * necessarily pure functions -- they may have side effects. */ #define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ _zzq_request, _zzq_arg1, _zzq_arg2, \ _zzq_arg3, _zzq_arg4, _zzq_arg5) \ do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) #define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) #if defined(NVALGRIND) /* Define NVALGRIND to completely remove the Valgrind magic sequence from the compiled code (analogous to NDEBUG's effects on assert()) */ #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ (_zzq_default) #else /* ! NVALGRIND */ /* The following defines the magic code sequences which the JITter spots and handles magically. Don't look too closely at them as they will rot your brain. The assembly code sequences for all architectures is in this one file. This is because this file must be stand-alone, and we don't want to have multiple files. For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default value gets put in the return slot, so that everything works when this is executed not under Valgrind. Args are passed in a memory block, and so there's no intrinsic limit to the number that could be passed, but it's currently five. The macro args are: _zzq_rlval result lvalue _zzq_default default value (result returned when running on real CPU) _zzq_request request code _zzq_arg1..5 request params The other two macros are used to support function wrapping, and are a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the guest's NRADDR pseudo-register and whatever other information is needed to safely run the call original from the wrapper: on ppc64-linux, the R2 value at the divert point is also needed. This information is abstracted into a user-visible type, OrigFn. VALGRIND_CALL_NOREDIR_* behaves the same as the following on the guest, but guarantees that the branch instruction will not be redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a complete inline asm, since it needs to be combined with more magic inline asm stuff to be useful. */ /* ----------------- x86-{linux,darwin,solaris} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || (defined(PLAT_x86_win32) && defined(__GNUC__)) \ || defined(PLAT_x86_solaris) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "roll $3, %%edi ; roll $13, %%edi\n\t" \ "roll $29, %%edi ; roll $19, %%edi\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EDX = client_request ( %EAX ) */ \ "xchgl %%ebx,%%ebx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ "xchgl %%ecx,%%ecx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%EAX */ \ "xchgl %%edx,%%edx\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "xchgl %%edi,%%edi\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) || PLAT_x86_solaris */ /* ------------------------- x86-Win32 ------------------------- */ #if defined(PLAT_x86_win32) && !defined(__GNUC__) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #if defined(_MSC_VER) #define __SPECIAL_INSTRUCTION_PREAMBLE \ __asm rol edi, 3 __asm rol edi, 13 \ __asm rol edi, 29 __asm rol edi, 19 #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) static __inline uintptr_t valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, uintptr_t _zzq_arg5) { volatile uintptr_t _zzq_args[6]; volatile unsigned int _zzq_result; _zzq_args[0] = (uintptr_t)(_zzq_request); _zzq_args[1] = (uintptr_t)(_zzq_arg1); _zzq_args[2] = (uintptr_t)(_zzq_arg2); _zzq_args[3] = (uintptr_t)(_zzq_arg3); _zzq_args[4] = (uintptr_t)(_zzq_arg4); _zzq_args[5] = (uintptr_t)(_zzq_arg5); __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default __SPECIAL_INSTRUCTION_PREAMBLE /* %EDX = client_request ( %EAX ) */ __asm xchg ebx,ebx __asm mov _zzq_result, edx } return _zzq_result; } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ __asm xchg ecx,ecx \ __asm mov __addr, eax \ } \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX ERROR #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ __asm xchg edi,edi \ } \ } while (0) #else #error Unsupported compiler. #endif #endif /* PLAT_x86_win32 */ /* ----------------- amd64-{linux,darwin,solaris} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || defined(PLAT_amd64_solaris) \ || (defined(PLAT_amd64_win64) && defined(__GNUC__)) typedef struct { unsigned long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RDX = client_request ( %RAX ) */ \ "xchgq %%rbx,%%rbx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RAX = guest_NRADDR */ \ "xchgq %%rcx,%%rcx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_RAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%RAX */ \ "xchgq %%rdx,%%rdx\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "xchgq %%rdi,%%rdi\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ /* ------------------------- amd64-Win64 ------------------------- */ #if defined(PLAT_amd64_win64) && !defined(__GNUC__) #error Unsupported compiler. #endif /* PLAT_amd64_win64 */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned int _zzq_args[6]; \ unsigned int _zzq_result; \ unsigned int* _zzq_ptr; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64be_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ unsigned long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned long int _zzq_args[6]; \ unsigned long int _zzq_result; \ unsigned long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc64be_linux */ #if defined(PLAT_ppc64le_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ unsigned long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned long int _zzq_args[6]; \ unsigned long int _zzq_result; \ unsigned long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R12 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc64le_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("mov r3, %1\n\t" /*default*/ \ "mov r4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = client_request ( R4 ) */ \ "orr r10, r10, r10\n\t" \ "mov %0, r3" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "cc","memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = guest_NRADDR */ \ "orr r11, r11, r11\n\t" \ "mov %0, r3" \ : "=r" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R4 */ \ "orr r12, r12, r12\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "orr r9, r9, r9\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ arm64-linux ------------------------- */ #if defined(PLAT_arm64_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile("mov x3, %1\n\t" /*default*/ \ "mov x4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* X3 = client_request ( X4 ) */ \ "orr x10, x10, x10\n\t" \ "mov %0, x3" /*result*/ \ : "=r" (_zzq_result) \ : "r" ((unsigned long int)(_zzq_default)), \ "r" (&_zzq_args[0]) \ : "cc","memory", "x3", "x4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* X3 = guest_NRADDR */ \ "orr x11, x11, x11\n\t" \ "mov %0, x3" \ : "=r" (__addr) \ : \ : "cc", "memory", "x3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir X8 */ \ "orr x12, x12, x12\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "orr x9, x9, x9\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_arm64_linux */ /* ------------------------ s390x-linux ------------------------ */ #if defined(PLAT_s390x_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ } OrigFn; /* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific * code. This detection is implemented in platform specific toIR.c * (e.g. VEX/priv/guest_s390_decoder.c). */ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "lr 15,15\n\t" \ "lr 1,1\n\t" \ "lr 2,2\n\t" \ "lr 3,3\n\t" #define __CLIENT_REQUEST_CODE "lr 2,2\n\t" #define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" #define __CALL_NO_REDIR_CODE "lr 4,4\n\t" #define __VEX_INJECT_IR_CODE "lr 5,5\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile(/* r2 = args */ \ "lgr 2,%1\n\t" \ /* r3 = default */ \ "lgr 3,%2\n\t" \ __SPECIAL_INSTRUCTION_PREAMBLE \ __CLIENT_REQUEST_CODE \ /* results = r3 */ \ "lgr %0, 3\n\t" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "2", "3", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ __GET_NR_CONTEXT_CODE \ "lgr %0, 3\n\t" \ : "=a" (__addr) \ : \ : "cc", "3", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_R1 \ __SPECIAL_INSTRUCTION_PREAMBLE \ __CALL_NO_REDIR_CODE #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ __VEX_INJECT_IR_CODE); \ } while (0) #endif /* PLAT_s390x_linux */ /* ------------------------- mips32-linux ---------------- */ #if defined(PLAT_mips32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; /* .word 0x342 * .word 0x742 * .word 0xC2 * .word 0x4C2*/ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "srl $0, $0, 13\n\t" \ "srl $0, $0, 29\n\t" \ "srl $0, $0, 3\n\t" \ "srl $0, $0, 19\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("move $11, %1\n\t" /*default*/ \ "move $12, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* T3 = client_request ( T4 ) */ \ "or $13, $13, $13\n\t" \ "move %0, $11\n\t" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$11", "$12", "memory"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %t9 = guest_NRADDR */ \ "or $14, $14, $14\n\t" \ "move %0, $11" /*result*/ \ : "=r" (__addr) \ : \ : "$11" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%t9 */ \ "or $15, $15, $15\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or $11, $11, $11\n\t" \ ); \ } while (0) #endif /* PLAT_mips32_linux */ /* ------------------------- mips64-linux ---------------- */ #if defined(PLAT_mips64_linux) typedef struct { unsigned long nraddr; /* where's the code? */ } OrigFn; /* dsll $0,$0, 3 * dsll $0,$0, 13 * dsll $0,$0, 29 * dsll $0,$0, 19*/ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ "dsll $0,$0,29 ; dsll $0,$0,19\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile("move $11, %1\n\t" /*default*/ \ "move $12, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* $11 = client_request ( $12 ) */ \ "or $13, $13, $13\n\t" \ "move %0, $11\n\t" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$11", "$12", "memory"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* $11 = guest_NRADDR */ \ "or $14, $14, $14\n\t" \ "move %0, $11" /*result*/ \ : "=r" (__addr) \ : \ : "$11"); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir $25 */ \ "or $15, $15, $15\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or $11, $11, $11\n\t" \ ); \ } while (0) #endif /* PLAT_mips64_linux */ #if defined(PLAT_nanomips_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; /* 8000 c04d srl zero, zero, 13 8000 c05d srl zero, zero, 29 8000 c043 srl zero, zero, 3 8000 c053 srl zero, zero, 19 */ #define __SPECIAL_INSTRUCTION_PREAMBLE "srl[32] $zero, $zero, 13 \n\t" \ "srl[32] $zero, $zero, 29 \n\t" \ "srl[32] $zero, $zero, 3 \n\t" \ "srl[32] $zero, $zero, 19 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("move $a7, %1\n\t" /* default */ \ "move $t0, %2\n\t" /* ptr */ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* $a7 = client_request( $t0 ) */ \ "or[32] $t0, $t0, $t0\n\t" \ "move %0, $a7\n\t" /* result */ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$a7", "$t0", "memory"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* $a7 = guest_NRADDR */ \ "or[32] $t1, $t1, $t1\n\t" \ "move %0, $a7" /*result*/ \ : "=r" (__addr) \ : \ : "$a7"); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir $25 */ \ "or[32] $t2, $t2, $t2\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or[32] $t3, $t3, $t3\n\t" \ ); \ } while (0) #endif /* Insert assembly code for other platforms here... */ #endif /* NVALGRIND */ /* ------------------------------------------------------------------ */ /* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ /* ugly. It's the least-worst tradeoff I can think of. */ /* ------------------------------------------------------------------ */ /* This section defines magic (a.k.a appalling-hack) macros for doing guaranteed-no-redirection macros, so as to get from function wrappers to the functions they are wrapping. The whole point is to construct standard call sequences, but to do the call itself with a special no-redirect call pseudo-instruction that the JIT understands and handles specially. This section is long and repetitious, and I can't see a way to make it shorter. The naming scheme is as follows: CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} 'W' stands for "word" and 'v' for "void". Hence there are different macros for calling arity 0, 1, 2, 3, 4, etc, functions, and for each, the possibility of returning a word-typed result, or no result. */ /* Use these to write the name of your wrapper. NOTE: duplicates VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts the default behaviour equivalance class tag "0000" into the name. See pub_tool_redir.h for details -- normally you don't need to think about this, though. */ /* Use an extra level of macroisation so as to ensure the soname/fnname args are fully macro-expanded before pasting them together. */ #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd #define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) #define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) /* Use this macro from within a wrapper function to collect the context (address and possibly other info) of the original function. Once you have that you can then use it in one of the CALL_FN_ macros. The type of the argument _lval is OrigFn. */ #define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) /* Also provide end-user facilities for function replacement, rather than wrapping. A replacement function differs from a wrapper in that it has no way to get hold of the original function being called, and hence no way to call onwards to it. In a replacement function, VALGRIND_GET_ORIG_FN always returns zero. */ #define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) #define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) /* Derivatives of the main macros below, for calling functions returning void. */ #define CALL_FN_v_v(fnptr) \ do { volatile unsigned long _junk; \ CALL_FN_W_v(_junk,fnptr); } while (0) #define CALL_FN_v_W(fnptr, arg1) \ do { volatile unsigned long _junk; \ CALL_FN_W_W(_junk,fnptr,arg1); } while (0) #define CALL_FN_v_WW(fnptr, arg1,arg2) \ do { volatile unsigned long _junk; \ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) #define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) #define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) #define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ do { volatile unsigned long _junk; \ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) #define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ do { volatile unsigned long _junk; \ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) #define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ do { volatile unsigned long _junk; \ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) /* ----------------- x86-{linux,darwin,solaris} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || defined(PLAT_x86_solaris) /* These regs are trashed by the hidden call. No need to mention eax as gcc can already see that, plus causes gcc to bomb. */ #define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "movl %%esp,%%edi\n\t" \ "andl $0xfffffff0,%%esp\n\t" #define VALGRIND_RESTORE_STACK \ "movl %%edi,%%esp\n\t" /* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 48(%%eax)\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin || PLAT_x86_solaris */ /* ---------------- amd64-{linux,darwin,solaris} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || defined(PLAT_amd64_solaris) /* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ "rdi", "r8", "r9", "r10", "r11" /* This is all pretty complex. It's so as to make stack unwinding work reliably. See bug 243270. The basic problem is the sub and add of 128 of %rsp in all of the following macros. If gcc believes the CFA is in %rsp, then unwinding may fail, because what's at the CFA is not what gcc "expected" when it constructs the CFIs for the places where the macros are instantiated. But we can't just add a CFI annotation to increase the CFA offset by 128, to match the sub of 128 from %rsp, because we don't know whether gcc has chosen %rsp as the CFA at that point, or whether it has chosen some other register (eg, %rbp). In the latter case, adding a CFI annotation to change the CFA offset is simply wrong. So the solution is to get hold of the CFA using __builtin_dwarf_cfa(), put it in a known register, and add a CFI annotation to say what the register is. We choose %rbp for this (perhaps perversely), because: (1) %rbp is already subject to unwinding. If a new register was chosen then the unwinder would have to unwind it in all stack traces, which is expensive, and (2) %rbp is already subject to precise exception updates in the JIT. If a new register was chosen, we'd have to have precise exceptions for it too, which reduces performance of the generated code. However .. one extra complication. We can't just whack the result of __builtin_dwarf_cfa() into %rbp and then add %rbp to the list of trashed registers at the end of the inline assembly fragments; gcc won't allow %rbp to appear in that list. Hence instead we need to stash %rbp in %r15 for the duration of the asm, and say that %r15 is trashed instead. gcc seems happy to go with that. Oh .. and this all needs to be conditionalised so that it is unchanged from before this commit, when compiled with older gccs that don't support __builtin_dwarf_cfa. Furthermore, since this header file is freestanding, it has to be independent of config.h, and so the following conditionalisation cannot depend on configure time checks. Although it's not clear from 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', this expression excludes Darwin. .cfi directives in Darwin assembly appear to be completely different and I haven't investigated how they work. For even more entertainment value, note we have to use the completely undocumented __builtin_dwarf_cfa(), which appears to really compute the CFA, whereas __builtin_frame_address(0) claims to but actually doesn't. See https://bugs.kde.org/show_bug.cgi?id=243270#c47 */ #if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) # define __FRAME_POINTER \ ,"r"(__builtin_dwarf_cfa()) # define VALGRIND_CFI_PROLOGUE \ "movq %%rbp, %%r15\n\t" \ "movq %2, %%rbp\n\t" \ ".cfi_remember_state\n\t" \ ".cfi_def_cfa rbp, 0\n\t" # define VALGRIND_CFI_EPILOGUE \ "movq %%r15, %%rbp\n\t" \ ".cfi_restore_state\n\t" #else # define __FRAME_POINTER # define VALGRIND_CFI_PROLOGUE # define VALGRIND_CFI_EPILOGUE #endif /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "movq %%rsp,%%r14\n\t" \ "andq $0xfffffffffffffff0,%%rsp\n\t" #define VALGRIND_RESTORE_STACK \ "movq %%r14,%%rsp\n\t" /* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned long) == 8. */ /* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ macros. In order not to trash the stack redzone, we need to drop %rsp by 128 before the hidden call, and restore afterwards. The nastyness is that it is only by luck that the stack still appears to be unwindable during the hidden call - since then the behaviour of any routine using this macro does not match what the CFI data says. Sigh. Why is this important? Imagine that a wrapper has a stack allocated local, and passes to the hidden call, a pointer to it. Because gcc does not know about the hidden call, it may allocate that local in the redzone. Unfortunately the hidden call may then trash it before it comes to use it. So we must step clear of the redzone, for the duration of the hidden call, to make it safe. Probably the same problem afflicts the other redzone-style ABIs too (ppc64-linux); but for those, the stack is self describing (none of this CFI nonsense) so at least messing with the stack pointer doesn't give a danger of non-unwindable stack. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 96(%%rax)\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) /* This is useful for finding out about the on-stack stuff: extern int f9 ( int,int,int,int,int,int,int,int,int ); extern int f10 ( int,int,int,int,int,int,int,int,int,int ); extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); int g9 ( void ) { return f9(11,22,33,44,55,66,77,88,99); } int g10 ( void ) { return f10(11,22,33,44,55,66,77,88,99,110); } int g11 ( void ) { return f11(11,22,33,44,55,66,77,88,99,110,121); } int g12 ( void ) { return f12(11,22,33,44,55,66,77,88,99,110,121,132); } */ /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rlwinm 1,1,0,0,27\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc32-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg12 */ \ "lwz 3,48(11)\n\t" \ "stw 3,20(1)\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64be_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rldicr 1,1,0,59\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(11)\n\t" \ "std 3,136(1)\n\t" \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64be_linux */ /* ------------------------- ppc64le-linux ----------------------- */ #if defined(PLAT_ppc64le_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rldicr 1,1,0,59\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(12)\n\t" \ "std 3,112(1)\n\t" \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(12)\n\t" \ "std 3,120(1)\n\t" \ /* arg11 */ \ "ld 3,88(12)\n\t" \ "std 3,112(1)\n\t" \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64le_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4", "r12", "r14" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ /* This is a bit tricky. We store the original stack pointer in r10 as it is callee-saves. gcc doesn't allow the use of r11 for some reason. Also, we can't directly "bic" the stack pointer in thumb mode since r13 isn't an allowed register number in that context. So use r4 as a temporary, since that is about to get trashed anyway, just after each use of this macro. Side effect is we need to be very careful about any future changes, since VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ #define VALGRIND_ALIGN_STACK \ "mov r10, sp\n\t" \ "mov r4, sp\n\t" \ "bic r4, r4, #7\n\t" \ "mov sp, r4\n\t" #define VALGRIND_RESTORE_STACK \ "mov sp, r10\n\t" /* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "push {r0, r1, r2, r3} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #40] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "ldr r2, [%1, #48] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ arm64-linux ------------------------ */ #if defined(PLAT_arm64_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ "x18", "x19", "x20", "x30", \ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ "v26", "v27", "v28", "v29", "v30", "v31" /* x21 is callee-saved, so we can use it to save and restore SP around the hidden call. */ #define VALGRIND_ALIGN_STACK \ "mov x21, sp\n\t" \ "bic sp, x21, #15\n\t" #define VALGRIND_RESTORE_STACK \ "mov sp, x21\n\t" /* These CALL_FN_ macros assume that on arm64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x20 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x20 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x30 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1, #88] \n\t" \ "str x8, [sp, #16] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11, \ arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x30 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1, #88] \n\t" \ "str x8, [sp, #16] \n\t" \ "ldr x8, [%1, #96] \n\t" \ "str x8, [sp, #24] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm64_linux */ /* ------------------------- s390x-linux ------------------------- */ #if defined(PLAT_s390x_linux) /* Similar workaround as amd64 (see above), but we use r11 as frame pointer and save the old r11 in r7. r11 might be used for argvec, therefore we copy argvec in r1 since r1 is clobbered after the call anyway. */ #if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) # define __FRAME_POINTER \ ,"d"(__builtin_dwarf_cfa()) # define VALGRIND_CFI_PROLOGUE \ ".cfi_remember_state\n\t" \ "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ "lgr 7,11\n\t" \ "lgr 11,%2\n\t" \ ".cfi_def_cfa r11, 0\n\t" # define VALGRIND_CFI_EPILOGUE \ "lgr 11, 7\n\t" \ ".cfi_restore_state\n\t" #else # define __FRAME_POINTER # define VALGRIND_CFI_PROLOGUE \ "lgr 1,%1\n\t" # define VALGRIND_CFI_EPILOGUE #endif /* Nb: On s390 the stack pointer is properly aligned *at all times* according to the s390 GCC maintainer. (The ABI specification is not precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and VALGRIND_RESTORE_STACK are not defined here. */ /* These regs are trashed by the hidden call. Note that we overwrite r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the function a proper return address. All others are ABI defined call clobbers. */ #if defined(__VX__) || defined(__S390_VX__) #define __CALLER_SAVED_REGS "0", "1", "2", "3", "4", "5", "14", \ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", \ "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", \ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", \ "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" #else #define __CALLER_SAVED_REGS "0", "1", "2", "3", "4", "5", "14", \ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" #endif /* Nb: Although r11 is modified in the asm snippets below (inside VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for two reasons: (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not modified (2) GCC will complain that r11 cannot appear inside a clobber section, when compiled with -O -fno-omit-frame-pointer */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 1, 0(1)\n\t" /* target->r1 */ \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) /* The call abi has the arguments in r2-r6 and stack */ #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1, arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-168\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,168\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-176\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,176\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-184\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,184\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-192\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,192\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-200\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,200\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10, arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-208\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "mvc 200(8,15), 88(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,208\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-216\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "mvc 200(8,15), 88(1)\n\t" \ "mvc 208(8,15), 96(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "aghi 15,216\n\t" \ VALGRIND_CFI_EPILOGUE \ "lgr %0, 2\n\t" \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_s390x_linux */ /* ------------------------- mips32-linux ----------------------- */ #if defined(PLAT_mips32_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" /* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16\n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" /* arg1*/ \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 24\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 24 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 32\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "nop\n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 32 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 32\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 32 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 40\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 40 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 40\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 40 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 48\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 48 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 48\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 44(%1) \n\t" \ "sw $4, 40($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 48 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 56\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 44(%1) \n\t" \ "sw $4, 40($29) \n\t" \ "lw $4, 48(%1) \n\t" \ "sw $4, 44($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 56 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_mips32_linux */ /* ------------------------- nanomips-linux -------------------- */ #if defined(PLAT_nanomips_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$t4", "$t5", "$a0", "$a1", "$a2", \ "$a3", "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3", \ "$t8","$t9", "$at" /* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ "lw $a2,12(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ "lw $a2,12(%1)\n\t" \ "lw $a3,16(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ "lw $a2,12(%1)\n\t" \ "lw $a3,16(%1)\n\t" \ "lw $a4,20(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ "lw $a2,12(%1)\n\t" \ "lw $a3,16(%1)\n\t" \ "lw $a4,20(%1)\n\t" \ "lw $a5,24(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ "lw $a2,12(%1)\n\t" \ "lw $a3,16(%1)\n\t" \ "lw $a4,20(%1)\n\t" \ "lw $a5,24(%1)\n\t" \ "lw $a6,28(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "lw $t9, 0(%1)\n\t" \ "lw $a0, 4(%1)\n\t" \ "lw $a1, 8(%1)\n\t" \ "lw $a2,12(%1)\n\t" \ "lw $a3,16(%1)\n\t" \ "lw $a4,20(%1)\n\t" \ "lw $a5,24(%1)\n\t" \ "lw $a6,28(%1)\n\t" \ "lw $a7,32(%1)\n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "addiu $sp, $sp, -16 \n\t" \ "lw $t9,36(%1) \n\t" \ "sw $t9, 0($sp) \n\t" \ "lw $t9, 0(%1) \n\t" \ "lw $a0, 4(%1) \n\t" \ "lw $a1, 8(%1) \n\t" \ "lw $a2,12(%1) \n\t" \ "lw $a3,16(%1) \n\t" \ "lw $a4,20(%1) \n\t" \ "lw $a5,24(%1) \n\t" \ "lw $a6,28(%1) \n\t" \ "lw $a7,32(%1) \n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0 \n\t" \ "addiu $sp, $sp, 16 \n\t" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "addiu $sp, $sp, -16 \n\t" \ "lw $t9,36(%1) \n\t" \ "sw $t9, 0($sp) \n\t" \ "lw $t9,40(%1) \n\t" \ "sw $t9, 4($sp) \n\t" \ "lw $t9, 0(%1) \n\t" \ "lw $a0, 4(%1) \n\t" \ "lw $a1, 8(%1) \n\t" \ "lw $a2,12(%1) \n\t" \ "lw $a3,16(%1) \n\t" \ "lw $a4,20(%1) \n\t" \ "lw $a5,24(%1) \n\t" \ "lw $a6,28(%1) \n\t" \ "lw $a7,32(%1) \n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0 \n\t" \ "addiu $sp, $sp, 16 \n\t" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "addiu $sp, $sp, -16 \n\t" \ "lw $t9,36(%1) \n\t" \ "sw $t9, 0($sp) \n\t" \ "lw $t9,40(%1) \n\t" \ "sw $t9, 4($sp) \n\t" \ "lw $t9,44(%1) \n\t" \ "sw $t9, 8($sp) \n\t" \ "lw $t9, 0(%1) \n\t" \ "lw $a0, 4(%1) \n\t" \ "lw $a1, 8(%1) \n\t" \ "lw $a2,12(%1) \n\t" \ "lw $a3,16(%1) \n\t" \ "lw $a4,20(%1) \n\t" \ "lw $a5,24(%1) \n\t" \ "lw $a6,28(%1) \n\t" \ "lw $a7,32(%1) \n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0 \n\t" \ "addiu $sp, $sp, 16 \n\t" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "addiu $sp, $sp, -16 \n\t" \ "lw $t9,36(%1) \n\t" \ "sw $t9, 0($sp) \n\t" \ "lw $t9,40(%1) \n\t" \ "sw $t9, 4($sp) \n\t" \ "lw $t9,44(%1) \n\t" \ "sw $t9, 8($sp) \n\t" \ "lw $t9,48(%1) \n\t" \ "sw $t9,12($sp) \n\t" \ "lw $t9, 0(%1) \n\t" \ "lw $a0, 4(%1) \n\t" \ "lw $a1, 8(%1) \n\t" \ "lw $a2,12(%1) \n\t" \ "lw $a3,16(%1) \n\t" \ "lw $a4,20(%1) \n\t" \ "lw $a5,24(%1) \n\t" \ "lw $a6,28(%1) \n\t" \ "lw $a7,32(%1) \n\t" \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $a0 \n\t" \ "addiu $sp, $sp, 16 \n\t" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_nanomips_linux */ /* ------------------------- mips64-linux ------------------------- */ #if defined(PLAT_mips64_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" /* These CALL_FN_ macros assume that on mips64-linux, sizeof(long long) == 8. */ #define MIPS64_LONG2REG_CAST(x) ((long long)(long)x) #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[1]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ __asm__ volatile( \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[2]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" /* arg1*/ \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[3]; \ volatile unsigned long long _res; \ _argvec[0] = _orig.nraddr; \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[4]; \ volatile unsigned long long _res; \ _argvec[0] = _orig.nraddr; \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[5]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[6]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[7]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[8]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[9]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[10]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ __asm__ volatile( \ "dsubu $29, $29, 8\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 8\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[11]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ __asm__ volatile( \ "dsubu $29, $29, 16\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 16\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[12]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ __asm__ volatile( \ "dsubu $29, $29, 24\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 88(%1)\n\t" \ "sd $4, 16($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 24\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long long _argvec[13]; \ volatile unsigned long long _res; \ _argvec[0] = MIPS64_LONG2REG_CAST(_orig.nraddr); \ _argvec[1] = MIPS64_LONG2REG_CAST(arg1); \ _argvec[2] = MIPS64_LONG2REG_CAST(arg2); \ _argvec[3] = MIPS64_LONG2REG_CAST(arg3); \ _argvec[4] = MIPS64_LONG2REG_CAST(arg4); \ _argvec[5] = MIPS64_LONG2REG_CAST(arg5); \ _argvec[6] = MIPS64_LONG2REG_CAST(arg6); \ _argvec[7] = MIPS64_LONG2REG_CAST(arg7); \ _argvec[8] = MIPS64_LONG2REG_CAST(arg8); \ _argvec[9] = MIPS64_LONG2REG_CAST(arg9); \ _argvec[10] = MIPS64_LONG2REG_CAST(arg10); \ _argvec[11] = MIPS64_LONG2REG_CAST(arg11); \ _argvec[12] = MIPS64_LONG2REG_CAST(arg12); \ __asm__ volatile( \ "dsubu $29, $29, 32\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 88(%1)\n\t" \ "sd $4, 16($29)\n\t" \ "ld $4, 96(%1)\n\t" \ "sd $4, 24($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 32\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) (long)_res; \ } while (0) #endif /* PLAT_mips64_linux */ /* ------------------------------------------------------------------ */ /* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ /* */ /* ------------------------------------------------------------------ */ /* Some request codes. There are many more of these, but most are not exposed to end-user view. These are the public ones, all of the form 0x1000 + small_number. Core ones are in the range 0x00000000--0x0000ffff. The non-public ones start at 0x2000. */ /* These macros are used by tools -- they must be public, but don't embed them into other programs. */ #define VG_USERREQ_TOOL_BASE(a,b) \ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) #define VG_IS_TOOL_USERREQ(a, b, v) \ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) /* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE NUMERIC VALUES OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end of the most relevant group. */ typedef enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, /* These allow any function to be called from the simulated CPU but run on the real CPU. Nb: the first arg passed to the function is always the ThreadId of the running thread! So CLIENT_CALL0 actually requires a 1 arg function, etc. */ VG_USERREQ__CLIENT_CALL0 = 0x1101, VG_USERREQ__CLIENT_CALL1 = 0x1102, VG_USERREQ__CLIENT_CALL2 = 0x1103, VG_USERREQ__CLIENT_CALL3 = 0x1104, /* Can be useful in regression testing suites -- eg. can send Valgrind's output to /dev/null and still count errors. */ VG_USERREQ__COUNT_ERRORS = 0x1201, /* Allows the client program and/or gdbserver to execute a monitor command. */ VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, /* Allows the client program to change a dynamic command line option. */ VG_USERREQ__CLO_CHANGE = 0x1203, /* These are useful and can be interpreted by any tool that tracks malloc() et al, by using vg_replace_malloc.c. */ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, VG_USERREQ__FREELIKE_BLOCK = 0x1302, /* Memory pool support. */ VG_USERREQ__CREATE_MEMPOOL = 0x1303, VG_USERREQ__DESTROY_MEMPOOL = 0x1304, VG_USERREQ__MEMPOOL_ALLOC = 0x1305, VG_USERREQ__MEMPOOL_FREE = 0x1306, VG_USERREQ__MEMPOOL_TRIM = 0x1307, VG_USERREQ__MOVE_MEMPOOL = 0x1308, VG_USERREQ__MEMPOOL_CHANGE = 0x1309, VG_USERREQ__MEMPOOL_EXISTS = 0x130a, /* Allow printfs to valgrind log. */ /* The first two pass the va_list argument by value, which assumes it is the same size as or smaller than a UWord, which generally isn't the case. Hence are deprecated. The second two pass the vargs by reference and so are immune to this problem. */ /* both :: char* fmt, va_list vargs (DEPRECATED) */ VG_USERREQ__PRINTF = 0x1401, VG_USERREQ__PRINTF_BACKTRACE = 0x1402, /* both :: char* fmt, va_list* vargs */ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, /* Stack support. */ VG_USERREQ__STACK_REGISTER = 0x1501, VG_USERREQ__STACK_DEREGISTER = 0x1502, VG_USERREQ__STACK_CHANGE = 0x1503, /* Wine support */ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, /* Querying of debug info. */ VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, /* Disable/enable error reporting level. Takes a single Word arg which is the delta to this thread's error disablement indicator. Hence 1 disables or further disables errors, and -1 moves back towards enablement. Other values are not allowed. */ VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, /* Some requests used for Valgrind internal, such as self-test or self-hosting. */ /* Initialise IR injection */ VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901, /* Used by Inner Valgrind to inform Outer Valgrind where to find the list of inner guest threads */ VG_USERREQ__INNER_THREADS = 0x1902 } Vg_ClientRequest; #if !defined(__GNUC__) # define __extension__ /* */ #endif /* Returns the number of Valgrinds this code is running under. That is, 0 if running natively, 1 if running under Valgrind, 2 if running under Valgrind which is running under another Valgrind, etc. */ #define RUNNING_ON_VALGRIND \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ VG_USERREQ__RUNNING_ON_VALGRIND, \ 0, 0, 0, 0, 0) \ /* Discard translation of code in the range [_qzz_addr .. _qzz_addr + _qzz_len - 1]. Useful if you are debugging a JITter or some such, since it provides a way to make sure valgrind will retranslate the invalidated area. Returns no value. */ #define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ _qzz_addr, _qzz_len, 0, 0, 0) #define VALGRIND_INNER_THREADS(_qzz_addr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__INNER_THREADS, \ _qzz_addr, 0, 0, 0, 0) /* These requests are for getting Valgrind itself to print something. Possibly with a backtrace. This is a really ugly hack. The return value is the number of characters printed, excluding the "**** " part at the start and the backtrace (if present). */ #if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) /* Modern GCC will optimize the static routine out if unused, and unused attribute will shut down warnings about it. */ static int VALGRIND_PRINTF(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); #endif static int #if defined(_MSC_VER) __inline #endif VALGRIND_PRINTF(const char *format, ...) { #if defined(NVALGRIND) (void)format; return 0; #else /* NVALGRIND */ #if defined(_MSC_VER) || defined(__MINGW64__) uintptr_t _qzz_res; #else unsigned long _qzz_res; #endif va_list vargs; va_start(vargs, format); #if defined(_MSC_VER) || defined(__MINGW64__) _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_VALIST_BY_REF, (uintptr_t)format, (uintptr_t)&vargs, 0, 0, 0); #else _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); return (int)_qzz_res; #endif /* NVALGRIND */ } #if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); #endif static int #if defined(_MSC_VER) __inline #endif VALGRIND_PRINTF_BACKTRACE(const char *format, ...) { #if defined(NVALGRIND) (void)format; return 0; #else /* NVALGRIND */ #if defined(_MSC_VER) || defined(__MINGW64__) uintptr_t _qzz_res; #else unsigned long _qzz_res; #endif va_list vargs; va_start(vargs, format); #if defined(_MSC_VER) || defined(__MINGW64__) _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (uintptr_t)format, (uintptr_t)&vargs, 0, 0, 0); #else _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); return (int)_qzz_res; #endif /* NVALGRIND */ } /* These requests allow control to move from the simulated CPU to the real CPU, calling an arbitrary function. Note that the current ThreadId is inserted as the first argument. So this call: VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) requires f to have this signature: Word f(Word tid, Word arg1, Word arg2) where "Word" is a word-sized type. Note that these client requests are not entirely reliable. For example, if you call a function with them that subsequently calls printf(), there's a high chance Valgrind will crash. Generally, your prospects of these working are made higher if the called function does not refer to any global variables, and does not refer to any libc or other functions (printf et al). Any kind of entanglement with libc or dynamic linking is likely to have a bad outcome, for tricky reasons which we've grappled with a lot in the past. */ #define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL0, \ _qyy_fn, \ 0, 0, 0, 0) #define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL1, \ _qyy_fn, \ _qyy_arg1, 0, 0, 0) #define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL2, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, 0, 0) #define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL3, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, \ _qyy_arg3, 0) /* Counts the number of errors that have been recorded by a tool. Nb: the tool must record the errors with VG_(maybe_record_error)() or VG_(unique_error)() for them to be counted. */ #define VALGRIND_COUNT_ERRORS \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ 0 /* default return */, \ VG_USERREQ__COUNT_ERRORS, \ 0, 0, 0, 0, 0) /* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing when heap blocks are allocated in order to give accurate results. This happens automatically for the standard allocator functions such as malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, delete[], etc. But if your program uses a custom allocator, this doesn't automatically happen, and Valgrind will not do as well. For example, if you allocate superblocks with mmap() and then allocates chunks of the superblocks, all Valgrind's observations will be at the mmap() level and it won't know that the chunks should be considered separate entities. In Memcheck's case, that means you probably won't get heap block overrun detection (because there won't be redzones marked as unaddressable) and you definitely won't get any leak detection. The following client requests allow a custom allocator to be annotated so that it can be handled accurately by Valgrind. VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated by a malloc()-like function. For Memcheck (an illustrative case), this does two things: - It records that the block has been allocated. This means any addresses within the block mentioned in error messages will be identified as belonging to the block. It also means that if the block isn't freed it will be detected by the leak checker. - It marks the block as being addressable and undefined (if 'is_zeroed' is not set), or addressable and defined (if 'is_zeroed' is set). This controls how accesses to the block by the program are handled. 'addr' is the start of the usable block (ie. after any redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator can apply redzones -- these are blocks of padding at the start and end of each block. Adding redzones is recommended as it makes it much more likely Valgrind will spot block overruns. `is_zeroed' indicates if the memory is zeroed (or filled with another predictable value), as is the case for calloc(). VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a heap block -- that will be used by the client program -- is allocated. It's best to put it at the outermost level of the allocator if possible; for example, if you have a function my_alloc() which calls internal_alloc(), and the client request is put inside internal_alloc(), stack traces relating to the heap block will contain entries for both my_alloc() and internal_alloc(), which is probably not what you want. For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out custom blocks from within a heap block, B, that has been allocated with malloc/calloc/new/etc, then block B will be *ignored* during leak-checking -- the custom blocks will take precedence. VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For Memcheck, it does two things: - It records that the block has been deallocated. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - It marks the block as being unaddressable. VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a heap block is deallocated. VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For Memcheck, it does four things: - It records that the size of a block has been changed. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - If the block shrunk, it marks the freed memory as being unaddressable. - If the block grew, it marks the new area as undefined and defines a red zone past the end of the new block. - The V-bits of the overlap between the old and the new block are preserved. VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block and before deallocation of the old block. In many cases, these three client requests will not be enough to get your allocator working well with Memcheck. More specifically, if your allocator writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call will be necessary to mark the memory as addressable just before the zeroing occurs, otherwise you'll get a lot of invalid write errors. For example, you'll need to do this if your allocator recycles freed blocks, but it zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). Alternatively, if your allocator reuses freed blocks for allocator-internal data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. Really, what's happening is a blurring of the lines between the client program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the memory should be considered unaddressable to the client program, but the allocator knows more than the rest of the client program and so may be able to safely access it. Extra client requests are necessary for Valgrind to understand the distinction between the allocator and the rest of the program. Ignored if addr == 0. */ #define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ addr, sizeB, rzB, is_zeroed, 0) /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ addr, oldSizeB, newSizeB, rzB, 0) /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ addr, rzB, 0, 0, 0) /* Create a memory pool. */ #define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, 0, 0) /* Create a memory pool with some flags specifying extended behaviour. When flags is zero, the behaviour is identical to VALGRIND_CREATE_MEMPOOL. The flag VALGRIND_MEMPOOL_METAPOOL specifies that the pieces of memory associated with the pool using VALGRIND_MEMPOOL_ALLOC will be used by the application as superblocks to dole out MALLOC_LIKE blocks using VALGRIND_MALLOCLIKE_BLOCK. In other words, a meta pool is a "2 levels" pool : first level is the blocks described by VALGRIND_MEMPOOL_ALLOC. The second level blocks are described using VALGRIND_MALLOCLIKE_BLOCK. Note that the association between the pool and the second level blocks is implicit : second level blocks will be located inside first level blocks. It is necessary to use the VALGRIND_MEMPOOL_METAPOOL flag for such 2 levels pools, as otherwise valgrind will detect overlapping memory blocks, and will abort execution (e.g. during leak search). Such a meta pool can also be marked as an 'auto free' pool using the flag VALGRIND_MEMPOOL_AUTO_FREE, which must be OR-ed together with the VALGRIND_MEMPOOL_METAPOOL. For an 'auto free' pool, VALGRIND_MEMPOOL_FREE will automatically free the second level blocks that are contained inside the first level block freed with VALGRIND_MEMPOOL_FREE. In other words, calling VALGRIND_MEMPOOL_FREE will cause implicit calls to VALGRIND_FREELIKE_BLOCK for all the second level blocks included in the first level block. Note: it is an error to use the VALGRIND_MEMPOOL_AUTO_FREE flag without the VALGRIND_MEMPOOL_METAPOOL flag. */ #define VALGRIND_MEMPOOL_AUTO_FREE 1 #define VALGRIND_MEMPOOL_METAPOOL 2 #define VALGRIND_CREATE_MEMPOOL_EXT(pool, rzB, is_zeroed, flags) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, flags, 0) /* Destroy a memory pool. */ #define VALGRIND_DESTROY_MEMPOOL(pool) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ pool, 0, 0, 0, 0) /* Associate a piece of memory with a memory pool. */ #define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ pool, addr, size, 0, 0) /* Disassociate a piece of memory from a memory pool. */ #define VALGRIND_MEMPOOL_FREE(pool, addr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ pool, addr, 0, 0, 0) /* Disassociate any pieces outside a particular range. */ #define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ pool, addr, size, 0, 0) /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ poolA, poolB, 0, 0, 0) /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ pool, addrA, addrB, size, 0) /* Return 1 if a mempool exists, else 0. */ #define VALGRIND_MEMPOOL_EXISTS(pool) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__MEMPOOL_EXISTS, \ pool, 0, 0, 0, 0) /* Mark a piece of memory as being a stack. Returns a stack id. start is the lowest addressable stack byte, end is the highest addressable stack byte. */ #define VALGRIND_STACK_REGISTER(start, end) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__STACK_REGISTER, \ start, end, 0, 0, 0) /* Unmark the piece of memory associated with a stack id as being a stack. */ #define VALGRIND_STACK_DEREGISTER(id) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ id, 0, 0, 0, 0) /* Change the start and end address of the stack id. start is the new lowest addressable stack byte, end is the new highest addressable stack byte. */ #define VALGRIND_STACK_CHANGE(id, start, end) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ id, start, end, 0, 0) /* Load PDB debug info for Wine PE image_map. */ #define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ fd, ptr, total_size, delta, 0) /* Map a code address to a source file name and line number. buf64 must point to a 64-byte buffer in the caller's address space. The result will be dumped in there and is guaranteed to be zero terminated. If no info is found, the first byte is set to zero. */ #define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__MAP_IP_TO_SRCLOC, \ addr, buf64, 0, 0, 0) /* Disable error reporting for this thread. Behaves in a stack like way, so you can safely call this multiple times provided that VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times to re-enable reporting. The first call of this macro disables reporting. Subsequent calls have no effect except to increase the number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable reporting. Child threads do not inherit this setting from their parents -- they are always created with reporting enabled. */ #define VALGRIND_DISABLE_ERROR_REPORTING \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ 1, 0, 0, 0, 0) /* Re-enable error reporting, as per comments on VALGRIND_DISABLE_ERROR_REPORTING. */ #define VALGRIND_ENABLE_ERROR_REPORTING \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ -1, 0, 0, 0, 0) /* Execute a monitor command from the client program. If a connection is opened with GDB, the output will be sent according to the output mode set for vgdb. If no connection is opened, output will go to the log output. Returns 1 if command not recognised, 0 otherwise. */ #define VALGRIND_MONITOR_COMMAND(command) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ command, 0, 0, 0, 0) /* Change the value of a dynamic command line option. Note that unknown or not dynamically changeable options will cause a warning message to be output. */ #define VALGRIND_CLO_CHANGE(option) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CLO_CHANGE, \ option, 0, 0, 0, 0) #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_win32 #undef PLAT_amd64_win64 #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64be_linux #undef PLAT_ppc64le_linux #undef PLAT_arm_linux #undef PLAT_s390x_linux #undef PLAT_mips32_linux #undef PLAT_mips64_linux #undef PLAT_nanomips_linux #undef PLAT_x86_solaris #undef PLAT_amd64_solaris #endif /* __VALGRIND_H */