pseudo-1.8.1+git20161012/000077500000000000000000000000001300506512600144125ustar00rootroot00000000000000pseudo-1.8.1+git20161012/.gitignore000066400000000000000000000003271300506512600164040ustar00rootroot00000000000000*.o Makefile libpseudo.so pseudo_wrapfuncs.* pseudo_wrapper_table.c pseudo pseudodb pseudolog pseudo_profile pseudo_tables.c pseudo_tables.h port_wrappers.c pseudo_ports.h templatefile.pyc func_deps.mk port_deps.mk pseudo-1.8.1+git20161012/COPYING000066400000000000000000000635021300506512600154530ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! pseudo-1.8.1+git20161012/ChangeLog.txt000066400000000000000000000606521300506512600170130ustar00rootroot000000000000002016-10-13: * (seebs) handle commas in CFLAGS 2016-09-30: * (seebs) Fix rename at, matching fix from ee00f63d for rename. Bug and fix provided by Anton Gerasimov . 2016-09-28: * (seebs) Send errors to log when daemonizing, but do that a lot sooner to prevent startup messages which can show up spuriously with multiple clients. * (seebs) return file descriptor from pseudo_logfile, so we set pseudo_util_debug_fd to the right value instead of to stdin. Nice bug. 2016-09-22: * (seebs) don't send SIGUSR1 to init. 2016-07-29: * (seebs) Don't clear bindings for a statement with no bindings. 2016-07-28: * (seebs) Fix performance issue on deletion with xattr changes. 2016-07-08: * (RP) release 1.8.1 * (joshuagl) Fix log table creation issue * (joshuagl) Fix sqlite usage pseudo segfault * (joshuagl) Fix directory permissions for build 2016-05-18: * (seebs) move syncfs to a subport so it can be enabled only on systems which have it. * (seebs) make pseudo_foo() wrappers for mknod/mknodat, similar to the ones for stat/fstat, because Linux doesn't actually have mknod (or stat) in glibc. * Post diagnostic for missing real_foo() functions even when dlerror doesn't give a specific error. * All of this because ld.so started giving dlerror results for missing functions with RTLD_NEXT. It turns out we were failing to notice some missing functions. None of them actually mattered, but it's worth fixing. 2016-05-12: * (seebs) merge kergoth's fix to respect $(LDFLAGS) from environment. * (seebs) call this 1.8.0 2016-03-25: * (seebs) handle deletion of xattrs, renames, and so on. Eww. 2016-03-24: * (seebs) make xattr db use {dev, ino}. this includes an in-place migration of the tables for existing databases. 2016-03-22: * (seebs) extract variable name from message for remove_xattr so it actually works. 2016-03-11: * (seebs) set sane umask in server, just in case. 2016-03-10: * (seebs) make bind work (so userspace NFS server can handle pseudo) 2016-03-09: * (seebs) workaround for bash redefining getenv/unsetenv/etc. 2016-03-02: * (seebs) more server launch rework and updates * (seebs) make dup/dup2 less verbose in client * (seebs) cleanup client spawning code a bit 2016-03-01: * (seebs) server launch reworking 2016-02-24: * (seebs) event logger cleanup, improved diagnostics for client spawning, server exit cleanup. 2016-02-23: * (seebs) event logger initial implementation 2016-02-16: * (seebs) don't try to force pseudo's debug fd to fd 2 in clients. Also, don't allow server to end up with a client on fd 2, whether or not fd 2 is already in use. 2016-02-09: * (seebs) 1.7.5 release 2016-02-08: * (seebs) require -S to shutdown server when running a command. * (seebs) improve logic for server shutdowns, increase short timeout * (seebs) rework client server-spawning logic significantly * (seebs) make abort messages make it out, display message on abort 2016-02-05: * (seebs) don't abort search for server on first try. * (seebs) new clients cancel a shutdown request. * (seebs) sort xattrs when returning list. * (seebs) add retry interval, rather than instant retries. 2016-01-22: * (seebs) Mask in S_IFREG when mknod called with no S_IFMT bits. 2015-09-22: * (seebs) Fix modes after fopen/fopen64. 2015-09-04: * (seebs) add return value printing to wrapper debug! * (seebs) make mkdirat() restore errno, also don't pass AT_SYMLINK_NOFOLLOW to fchmodat() since it is ignored, and we know that if the thing exists at all, it is a directory and thus AT_SYMLINK_NOFOLLOW would be irrelevant anyway. * (seebs) and 1.7.3, of course. 2015-09-03: * (seebs) Use original mode, not 0600, for the chmods used to ensure that an 0700 umask didn't prevent writing. * (seebs) remove the comment saying that 0700 umasks were expected to break things. * (seebs) and call this 1.7.2. 2015-09-02: * (seebs) call this 1.7.1, since that seems to be all the bugs I can immediately find. 2015-09-01: * (seebs) use PSEUDO_STATBUF and base_lstat in path resolution, because plain lstat can fail on XFS if an inode number is out of the 32-bit range. * (seebs) if no pointer was provided to realpath, provide allocated dup. 2015-08-24: * (seebs) drop unused/unimplemented "picky_fchmodat", since I currently have no systems which implement it, and the implementation for NO_REAL_AT_FUNCTIONS was difficult to define. * (seebs) drop unused "doing_link" in fchownat, which I think predates the current database entirely. * (seebs) drop unused "old_trailing_slash". * (seebs) tag this as 1.7.0. 2015-08-22: * (seebs) Prevent files from getting created with real filesystem mode 0, even with umask. * (seebs) xattrdb logic fixes. * (seebs) Debugging messages. 2015-08-21: * (seebs) don't put incorrect host UIDs in dummy entries in db. * (seebs) merge existing values for chown/chmod with xattrdb. 2015-08-20: * (seebs) don't send open/exec messages unless server is logging. 2015-08-19: * (seebs) Reduce alloc/free cycles in path computations. 2015-08-17: * (seebs) profiling improvements 2015-08-14: * (seebs) profiling cleanup, add profiler to gitignore * Start using 1.7.0 version for internal work. 2015-08-13: * (seebs) client profiling stuff (first pass) 2015-07-17: * (seebs) allow actually making fifos without randomly closing fd 0 * 1.6.7 2015-07-16: * (seebs) don't truncate xattr attributes that end with a slash. * (seebs) allow actually making fifos * 1.6.6 2015-05-04: * (seebs) don't give spurious trailing slash diagnostics * 1.6.5 2015-01-22: * (seebs) work when --prefix doesn't exist yet * 1.6.4 2015-01-15: * (seebs) Rework PSEUDO_PASSWD path allocation for clarity, I hope. * 1.6.3 2015-01-14: * (seebs) Merge pabigot's fix for a missing antimagic() in etc_file code. * (seebs) Make configure reject a "prefix" of the current directory. It fails in strange ways and is easy to detect. * (seebs) handle colon-separated lists in PSEUDO_PASSWD. Derived in part from contribution from pabigot. * (seebs) change allocation order in password file locking. 2015-01-05: * (seebs) First try at handling trailing slashes in path names correctly. (Which is to say, a trailing slash on a file name which is not a directory name should yield ENOTDIR from a lot of things.) 2014-10-03: * (seebs) in fact, suppress a lot of sanity checks entirely for did_unlink. * (seebs) merge get_file_path functionality into find_file_dev, since you never call find_file_dev unless you want to do that. * (seebs) If a file is in the database by inode but not path, don't try to create a new link for it. * (seebs) when renaming, the link of the "old" file name should be contingent on whether *it* was in the database, not whether the *new* name was in the database. Whoops. * (seebs) 1.6.2 2014-10-02: * (seebs) use sqlite3_bind_int64 for inodes. * (seebs) suppress path mismatch warnings for did_unlink. 2014-07-18: * (seebs) Make "server already offline" message into a debugging message (PDBGF_INVOKE). * (seebs) 1.6.1 2014-07-17: * (seebs) Restrict symbol version restrictions to x86-64 and i386 rather than x86-64 and any old thing. Fixes build problems on ARM. 2014-07-11: * (seebs) merge in symbol version restrictions (slightly tweaked) for Linux. 2014-07-10: * (seebs) Don't pass -L/usr/lib* for sqlite, because that's probably the default. * (seebs) include sys/xattr.h explicitly for Darwin, also fix things up so we aren't relying on seeing a declaration for our dummy wrapper for fgetgrent_r which doesn't exist here. * (seebs) fix typo in xattr message * (seebs) make xattr work on Darwin, too. 2014-06-13: * (seebs) don't follow symlinks for lutimes. * (seebs) Use sizeof instead of strlen to initialize static value. 2014-05-27: * (seebs) start noticing umask, mask it out from open or mkdir calls rather than relying on underlying open/mkdir to do it. * (seebs) add a test for umask and filesystem-vs-db modes. 2014-05-16: * (seebs) fchmodat: don't drop flags, report failures, to improve compatibility/consistency. Cache the knowledge that AT_SYMLINK_NOFOLLOW gets ENOTSUP. * (seebs) mask out group/other write bits in real filesystem to reduce risks when assembling a rootfs including world-writeable directories. * (seebs) send a round-trip message to the server before exiting if any messages have been sent, so the program won't exit before the server processed those messages. * (pabigot) Don't memory-leak on checks for PSEUDO_UNLOAD, also check the correct environment when spawning new things. (Contributed by Peter A. Bigot). * (seebs) mask 022 back in too, when restoring a database mode. 2014-05-15: * (seebs) drop flags when calling fchmodat() to appease GNU tar. 2014-04-24: * (seebs) extended attribute support 2014-01-23: * (seebs) mknod wasn't calling mknodat. * (seebs) mkdirat wasn't recording the logical DB mode for directories 2014-01-22: * (seebs) notes on some Futures planning. * (seebs) Typo/formatting issue in man page. * (seebs) message formatting/clarity cleanup and typo fixes. * (seebs) First draft of passwd_fallback implementation. * (seebs) bump this all to 1.6.0. 2014-01-15: * (seebs) performance test/test case updates. 2013-06-20: * (seebs) refactor debugging code * (seebs) start using 1.5.2 numbering 2013-06-18: * (seebs) Fix bug in oldclone (reported by rich@noir.com). 2013-02-27: * (seebs) Oh, hey, what if I took out my debug messages? * (seebs) update docs a bit to reduce bitrot 2013-02-26: * (seebs) When built -DNDEBUG, completely drop pseudo_debug calls. * (seebs) Add PSEUDO_ALLOW_FSYNC to allow temporary re-enabling of fsync to work around filesystem bugs. * (seebs) call that 1.5.1. 2013-02-17: * (seebs) Fix uninitialized variable in unlink, fix force-fsync for Darwin (off64_t is not a distinct type there). 2013-02-16: * (seebs) Add wrapper setting which automatically returns a fixed value from some wrappers. Add fixed-value wrappers for all the *sync() functions. These are all contingent on --enable-force-async. 2013-02-15: * (seebs) Add support for in-memory DB. This, plus upcoming fsync-related changes, are expected to be big enough to justify calling this 1.5. 2013-02-13: * (seebs) calling link while chrooted could in some cases result in the root path not being prepended at all. One more try! * (seebs) 1.4.5. 2013-02-12: * (seebs) calling link while chrooted could in some cases result in the root path being double-appended. * (seebs) and tag 1.4.4 so that can get out as a clean update. 2013-01-31: * (seebs) tag 1.4.3 (to avoid any problems with the changes since 1.4.2 and old tarballs) 2013-01-30: * (seebs) Subtle tweaks to avoid GLIBC_2.7 dependencies which can cause trouble trying to copy libpseudo.so to older hosts. * (seebs) add "with-sqlite-lib" to simplify cases where sqlite's libdir computation differs from what we otherwise want; for instance, with bitbake, we often end up wanting $lib = lib64, but $sqlite_lib = lib. 2012-12-13: * (seebs) tag 1.4.2. 2012-12-12: * (seebs) Remove extra tab from the path alloc code in makewrappers. (Which has no effect since I changed my mind about the implementation which would have made it show up.) * (seebs) linkat() implementation. as a side effect, clear up the documentation and behavior of link(2) to reflect host OS semantics: Linux will hardlink symlinks, Darwin will only hardlink their targets. * (seebs) make linkat() implementation compile/run on Darwin, fix header bitrot for Mountain Lion. 2012-08-09: * (seebs) base_stat should be real_stat64, not stat64 * (seebs) add stat64/lstat64/fstat64 wrappers to Linux (not previously needed because the libc versions call stuff we already wrap). 2012-08-02: * (seebs) fix some Darwin-specific bitrot for clang/llvm. * (seebs) Drop the _plain thing, convert unix/guts/* to use PSEUDO_STATBUF. * (seebs) Tag 1.4.1. 2012-07-27: * (seebs) Convert from .tgz to tar.bz2 since that's handier for Yocto. 2012-07-24: * (seebs) Fix a couple of bitrots from the update. 2012-07-20: * (seebs) Add --cflags, deprecate --arch. * (seebs) tag 1.4 (since this should now work on arbitrary targets) 2012-06-28: * (seebs) Tag 1.3.1. 2012-06-27: * (seebs) Fix chroot coredump with long root path. 2012-04-30: * (seebs) Update README about new upstream. 2012-04-09: * (seebs) Improvements to rpath logic for sqlite, etc. * (seebs) Improvements to the logic for picking options like -m32, -m64 (which is to say, on ARM: Don't.) 2012-03-28: * (seebs) Cleanup unused variables, stray semicolons, add comments to some unused functions which exist because the wrapper generator makes them anyway. * (seebs) Make system() drop environment if PSEUDO_UNLOAD is set. 2012-03-27: * (seebs) Merge in: * (mhatle) Improve configuration compatibility with OE-Core. * (seebs) Provide option to statically link libsqlite. * (seebs) 1.3 branch 2012-03-26: * (seebs) Add popen() call to set up environment. 2012-02-06: * (seebs) Merge O_LARGEFILE into flags, not mode (thanks to Lei Liu at Wind River for the fix). 2012-02-02: * (seebs) stash dir name for DIR * from opendir using dirfd. * (seebs) add closedir. * (seebs) add initial pass at renameat() * (seebs) update makewrappers with smarter *dirfd handling. * (seebs) in base_path, don't try to strlen the result if fd_path() returns NULL. 2011-11-02: * (seebs) Call this 1.2 because the UNLOAD change is moderately significant, and so's the clone change. 2011-11-01: * (mhatle) Stop valgrind from reporting use of uninitialized memory from pseudo_client:client_ping() 2011-10-26: * (mhatle) update clone wrapper to add an intermediate function to avoid setting environment variables in the parent. 2011-10-20: * (mhatle) change from internal PSEUDO_RELOADED to external PSEUDO_UNLOAD environment variable. Enable external programs to have a safe and reliable way to unload pseudo on the next exec*. PSEUDO_UNLOAD also will disable pseudo if we're in a fork/clone situation in the same way PSEUDO_DISABLED=1 would. 2011-07-19: * (seebs) initialize a variable in that "realpath" code. 2011-06-08: * (seebs) Get the modern realpath from glibc instead of the old one inexplicably proferred by RTLD_NEXT. Fixes realpath(path, NULL) when PSEUDO_DISABLED=1. 2011-06-06: * (seebs) revise system() handler substantially. It now pollutes the environment but works. * (seebs) Call it "1.1.1" so the nice folks doing Yocto can have an official branch and not need to use git. * (seebs) add "tarball" make target. 2011-06-02: * (seebs) intercept system() so the pseudo environment is properly set for it. * (seebs) call this "1.1" since the cumulative result of all of these changes is pretty big. 2011-05-31: * (seebs) Don't mask in 0100 to filesystem modes for things which are not actually directories, because this breaks the special logic in euidaccess for X_OK. 2011-05-25: * (seebs) fix for ulckpwdf() 2011-04-21: * (seebs) don't use strerror in wrappers, because it can lead to malloc deadlocks if part of setting up a malloc operation falls into strerror which uses locale... Curse you, Fedora 13. You and your perfectly reasonable and standards-conforming behavior which happened to inconvenience me. 2011-04-16: * (seebs) remove duplicate definition of real_clone() from the oldclone port. 2011-04-13: * (seebs) base_path of an empty string should be an empty string, not $pwd. 2011-04-04: * (seebs) whitespace cleanup for Python code 2011-04-01: * (seebs) update README 2011-03-25: * (seebs) don't try to search path when you don't have one * (seebs) merge in ports branch * (seebs) fix permissions on subports/preports * (seebs) try to force debug fd to 2 2011-03-24: * (seebs) more work on OS X port. * (seebs) include errno in the verbose debug output * (seebs) fix darwin fcntl. * (seebs) fix *xattr for darwin (they take more arguments) 2011-02-18: * (seebs) moving things to Unix port, cleanup for Darwin 2011-02-14: * (seebs) first pass on splitting out ports * (seebs) various cleanup 2011-02-10: * (seebs) pseudo_client_shutdown(), and the pseudo server, have to be smart enough to make the local state directory in case the pseudo binary is invoked directly by a user before being spawned by the client. 2011-02-09: * (seebs) the long-awaited cleanup of the database initialization code. it's not really beautiful but it's quite a bit better. 2011-02-08: * (seebs) Get full paths for exec*() 2011-01-24: * (mhatle) Revert last result cache and related commits. caching proved to be unreliable. 2011-01-14: * (seebs) Automatically create prefix/state directories. * (mhatle) Avoid caching OP_EXEC calls 2011-01-13: * (seebs) Subtle cache fixup. 2010-12-17: * (mhatle) Disabled additional early setup if pseudo is disabled 2010-12-16: * (mhatle) change the journal from PERSIST to OFF * (seebs) update docs now that fakeroot and password support are in, this being long overdue * (seebs) fix parallel build issue introduced with maketables 2010-12-15: * (mhatle) add sqlite call profiling, enable with NPROFILE * (mhatle) as a result of profiling, optimize inode search * (mhatle) rearrange the pseudo_op file data operations to reduce the number of selects. * (mhatle) add the ability to cache the last select result * (mhatle) change the indexing in pseudo_db.c 2010-12-14: * (mhatle) restructure wrapfuncs.c 2010-12-09: * (mhatle) Add doc/program_flow to attempt to explain startup/running * (mhatle) guts/* minor cleanup * (mhatle) Reorganize into a new constructor for libpseudo ONLY pseudo main() now manually calls the util init new / revised init for client, wrappers and utils * (mhatle) Add central "reinit" function * (mhatle) Add manul execv* functions * (mhatle) rename pseudo_populate_wrappers to pseudo_check_wrappers 2010-12-08: * (mhatle) Add guts/clone.c to cleanup the clone support * (mhatle) guts/clone.c only run setupenv and reinit when NOT PSEUDO_RELOADED * (mhatle) guts/execve.c whitespace fixe * (mhatle) guts/fork.c similar to guts/clone.c change * (mhatle) pseudo_client.c add reinit function * (mhatle) pseudo_client.c revise client reset, include code from pseudo_wrappers.c * (mhatle) pseudo_server.c move the pid writing to the parent * (mhatle) pseudo_wrappers.c clone cleanup and populate cleanup 2010-12-07: * (seebs) whitespace fixes * (seebs) improve fork and PSEUDO_DISABLED * (seebs) add support for clone(2) * (mhatle) rework/improve clone(2) support * (mhatle) add test code for PSEUDO_DISABLED 2010-12-02: * (seebs) rework of fork/exec, add PSEUDO_DISABLED 2010-11-30: * (seebs) move *_t types to a separate file. * (seebs) remove unused tables from pseudo_db.c * (seebs) cleanup .gitignore 2010-11-17: * (seebs) add "Futures.txt" notes about future development plans * (seebs) split some of the templating code out of makewrappers 2010-11-16: * (seebs) database move functionality (first pass) 2010-10-25: * (seebs) various makewrappers cleanups (pylint, mostly) 2010-10-12: * (seebs) add missing copyright to Python makewrappers. * (seebs) detab makewrappers 2010-10-11: * (seebs) do the other *xattr() wrappers. * (seebs) Replace makewrappers with Python implementation and some template files. 2010-10-06: * (mhatle) Add the fsetxattr wrapper to return ENOTSUP (note: workaround gnu coreutils 'cp') 2010-09-16: * (seebs) change exec*() to use file, rather than path or filename, also add OP_EXEC where it was missing. 2010-09-15: * (seebs) allow setting default RPATH entry distinct from sqlite3 directory. 2010-09-08: * (seebs) handle mkfifo without guaranteeing an EINVAL response. 2010-09-02: * (seebs) fix errno for getcwd() with insufficient size * (seebs) Add an RPATH entry to the pseudo binary to find the sqlite3 library. 2010-09-01: * (seebs) add missing casts to even more printf arguments 2010-08-31: * (seebs) add missing casts to printf arguments, after being warned about them a mere twenty or thirty thousand times. 2010-08-27: * (seebs) fix a bug caused by memcmp with wrong length * (seebs) stop hand-coding lengths of memcmp (torek was right...) 2010-08-26: * (seebs) make offsets.c slightly less useless * (seebs) don't overwrite LD_LIBRARY_PATH values that include us 2010-08-25: * (seebs) fix the signal mask restore 2010-08-24: * (seebs) try to restore signal mask before calling exec() * (seebs) move errno restoration after code which could set errno 2010-08-19: * (seebs) handle insane edge case involving regcomp/regexec 2010-08-17: * (seebs) create speculative-deletion logic * (seebs) remove crackpot theories about cross-device renames 2010-08-16: * (rp) Fix ld_preload/ld_library_path mixup. * (seebs) Handle failed allocations. * (seebs) Fix logic for dropping empty LD_PRELOAD. 2010-08-12: * (seebs) Fix install of libpseudo so the plain library is created when using $(SUFFIX), this is needed so pseudo daemons don't need to know $(SUFFIX) so you can use prebuilts. * (seebs) Remove spurious "const" from modifiable table. 2010-08-11: * (seebs) document the new variables. 2010-08-10: * (mhatle) add execl, execle, execlp, execv, and execvp wrappers * (seebs) handle ... for execl, etc. * (mhatle) add a local cache of variables, instead of using environ * (mhatle) rewrite pseudo_setupenv, pseudo_dropenv routines we now support running "/usr/bin/env -i env" in pseudo! 2010-08-06: * (mhatle) Fix an exec program with an empty environment 2010-08-03: * (mhatle) Fix parallel build problem * (mhatle) allow both environment CFLAGS and internals CFLAGS * (mhatle) add PSEUDO_BINDIR, PSEUDO_LIBDIR, PSEUDO_LOCALSTATEDIR to allow specific overrides above and beyond PSEUDO_PREFIX 2010-07-30: * (kscherer) added .gitignore file * (kscherer) added sqlite version check to configure script * (kscherer) added basic test harness * (kscherer) fixed bug that when moving a directory the contents of the dir were removed from the db 2010-06-29: * (seebs) handle the other half of the suffix case -- when libpseudo is in LD_PRELOAD under another name. * (seebs) remove a couple of debugging messages. 2010-06-28: 0.3 * (seebs) back out PSEUDO_SUFFIX -- it causes problem when rebuilding the library but not the server, but this is a permissible use case. 2010-06-21: * (seebs) add mkstemp64 2010-06-02: * (seebs) add PSEUDO_NOSYMLINKEXP feature and documentation. 2010-04-30: * (seebs) rework pdb_history * (seebs) small cleanups and bulletproofing. * (seebs) fix up PSEUDO_DEBUG_FILE, use it for server as well. 2010-04-27: * (seebs) fix -P in pseudolog * (seebs) document PSEUDO_DEBUG_FILE 2010-04-26: * (seebs) many bug fixes and updates * (seebs) allow deleting entries in pseudolog * (seebs) correct race conditions and related bugs 2010-04-20: * (seebs) add quick sanity-check option for pseudo * (seebs) report on rows deleted * (seebs) unlink after removing db entry to reduce race conditions 2010-04-19: * (seebs) fix crash if xstat() or similar routine called with null path * (seebs) fix list of client processes still running * (seebs) fix pathname mismatches introduced by chroot() support 2010-04-16: * (seebs) add tracking of program names * (seebs) track message types * (seebs) small bug fixes and improvements galore 2010-04-06: * (seebs) implement various passwd-related utilities, various bugfixes. 2010-03-25: * (seebs) fix return values. 2010-03-24: * (seebs) add chroot syscall * (seebs) add chroot handling to path canonicalization * (seebs) add many calls just to get path fixups * (seebs) handle virtualizing rootness of absolute symlinks 2010-03-24: * (seebs) regenerate wrappers when makewrappers is changed. * (seebs) begin prep for chroot * (seebs) standardize path expansion * (seebs) extend makewrappers to handle function pointer args 2010-03-17: * (seebs) fixup help options * (seebs) use strerror() in a couple more places * (seebs) mention ^ specification in pseudolog -h output. 2010-03-16: * (seebs) Fix missing error checking noted by comp.lang.c reader. 2010-03-16: 0.2 * first public release (There's no changelog before that, sorry.) pseudo-1.8.1+git20161012/Futures.txt000066400000000000000000000042371300506512600166160ustar00rootroot00000000000000Some notes on features under discussion or consideration, and some vague implementation thoughts: * Add some kind of filter for "the directory we care about" - pseudo operations would only go to the server for items in this directory - optionally, logging could be generated for accesses *outside* this directory - intent is to reduce server load dramatically, and improve and/or streamline the logging path when, e.g., doing host contamination checking - probably implies a new message type, MSG_LOG. * Database recovery/reconstruction - capability for failing harder in the event of apparent corruption * Log database performance improvements - some way to do "live" queries against the log database while the server is running - some way to flush unwanted data - possibly a way to separate logging into multiple files or otherwise restrict the amount of stuff produced - logging filters of some sort * Canonicalization speedups - possibly implement some kind of cache of directory names and known canonicalizations * Possibly limited handling for extended attributes - specifically, the case where they're used to manpulate or query the plain old modes ala chmod/stat. * Test cases - boy, do we need test cases! * Clean up *at() functions - The *at() function implementations are gratuitously complicated, I believe because they predate the auto-canonicalization that came with the rewritten pseudo wrappers. * Memory allocation/deallocation issues. - there's a number of places where results from pseudo_get_value() are strdup'd, then the originals freed, which makes no sense. - there's at least one unchecked realloc() to do with the fd table. * Benchmarking/performance work. - It'd be nice to get some kind of measurement of how much time is going to which parts of pseudo (database access, filesystem access, IPC overhead, logic, client processing, and so on). - Maybe some work on finding ways to make profiling work, since I was having issues getting good profiling data. - Some moderately-canonical benchmarks would be nice to have for evaluating the costs of various common use cases. pseudo-1.8.1+git20161012/Makefile.in000066400000000000000000000151131300506512600164600ustar00rootroot00000000000000# # Makefile.in/Makefile, build rules for pseudo # # Copyright (c) 2008-2015 Wind River Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the Lesser GNU General Public License version 2.1 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the Lesser GNU General Public License for more details. # # You should have received a copy of the Lesser GNU General Public License # version 2.1 along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # configuration flags PREFIX=@PREFIX@ LIBDIR=@LIBDIR@ SUFFIX=@SUFFIX@ SQLITE=@SQLITE@ SQLITE_LIB=@SQLITE_LIB@ SQLITE_MEMORY=@SQLITE_MEMORY@ FORCE_ASYNC=@FORCE_ASYNC@ XATTR=@XATTR@ XATTRDB=@XATTRDB@ PROFILING=@PROFILING@ ifeq (true,$(PROFILING)) PSEUDO_PROFILE=$(BIN)/pseudo_profile else PSEUDO_PROFILE= endif define cond $(if $(filter true,$($(1))),-D$(2)) endef OPTDEFS=$(call cond,XATTR,PSEUDO_XATTR_SUPPORT) \ $(call cond,XATTRDB,PSEUDO_XATTRDB) \ $(call cond,PROFILING,PSEUDO_PROFILING) PASSWD_FALLBACK=@PASSWD_FALLBACK@ BITS=@BITS@ ARCH_FLAGS=@ARCH_FLAGS@ MARK64=@MARK64@ RPATH=@RPATH@ VERSION=1.8.1 LIB=@LIB@ BIN=bin LOCALSTATE=var/pseudo BINDIR=$(PREFIX)/$(BIN) LOCALSTATEDIR=$(PREFIX)/$(LOCALSTATE) CFLAGS_BASE=-pipe -std=gnu99 -Wall -W -Wextra CFLAGS_CODE=-fPIC -D_LARGEFILE64_SOURCE -D_ATFILE_SOURCE $(ARCH_FLAGS) CFLAGS_DEFS=-DPSEUDO_PREFIX='"$(PREFIX)"' -DPSEUDO_SUFFIX='"$(SUFFIX)"' -DPSEUDO_BINDIR='"$(BIN)"' -DPSEUDO_LIBDIR='"$(LIB)"' -DPSEUDO_LOCALSTATEDIR='"$(LOCALSTATE)"' -DPSEUDO_VERSION='"$(VERSION)"' $(SQLITE_MEMORY) $(FORCE_ASYNC) -DPSEUDO_PASSWD_FALLBACK='$(PASSWD_FALLBACK)' $(OPTDEFS) CFLAGS_DEBUG=-O2 -g @DEFAULT_SQLITE@CFLAGS_SQL=-L$(SQLITE)/$(SQLITE_LIB) -I$(SQLITE)/include $(RPATH) CFLAGS_PSEUDO=$(CFLAGS_BASE) $(CFLAGS_CODE) $(CFLAGS_DEFS) \ $(CFLAGS_DEBUG) $(CFLAGS_SQL) GLOB_PATTERN=guts/*.c GUTS=$(filter-out "$(GLOB_PATTERN)",$(wildcard $(GLOB_PATTERN))) SOURCES=$(wildcard *.c) OBJS=$(subst .c,.o,$(SOURCES)) SHOBJS=pseudo_tables.o pseudo_util.o DBOBJS=pseudo_db.o WRAPOBJS=pseudo_wrappers.o # needed for anything that links with pseduo_client.o, pretty much CLIENT_LDFLAGS=-ldl -lpthread DB_LDFLAGS=@SQLITE_LDARG@ -lpthread PSEUDO=$(BIN)/pseudo PSEUDODB=$(BIN)/pseudodb PSEUDOLOG=$(BIN)/pseudolog LIBPSEUDO=$(LIB)/libpseudo.so TEMPLATES=templates/guts templates/wrapfuncs.c templates/wrapfuncs.h templates/wrapper_table TABLES=table_templates/pseudo_tables.c table_templates/pseudo_tables.h all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) $(PSEUDO_PROFILE) test: all $(BIN) $(LIB) $(LOCALSTATE) @./run_tests.sh -v install-lib: $(LIBPSEUDO) mkdir -p $(DESTDIR)$(LIBDIR) cp $(LIBPSEUDO) $(DESTDIR)$(LIBDIR) $(if $(SUFFIX),cp $(LIBPSEUDO) $(DESTDIR)$(LIBDIR)/libpseudo$(SUFFIX).so,:) install-bin: $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) $(PSEUDO_PROFILE) mkdir -p $(DESTDIR)$(BINDIR) cp $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) $(PSEUDO_PROFILE) $(DESTDIR)$(BINDIR) install-data: mkdir -p $(DESTDIR)$(LOCALSTATEDIR) install: all install-lib install-bin install-data $(BIN) $(LIB) $(LOCALSTATE): mkdir -p $@ pseudo: $(PSEUDO) $(PSEUDO): $(BIN) pseudo.o $(SHOBJS) $(DBOBJS) pseudo_client.o pseudo_server.o pseudo_ipc.o $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o $(PSEUDO) \ pseudo.o pseudo_server.o pseudo_client.o pseudo_ipc.o \ $(DBOBJS) $(SHOBJS) $(LDFLAGS) $(DB_LDFLAGS) $(CLIENT_LDFLAGS) pseudolog: $(PSEUDOLOG) $(PSEUDOLOG): $(BIN) pseudolog.o $(SHOBJS) $(DBOBJS) pseudo_client.o pseudo_ipc.o $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o $(PSEUDOLOG) pseudolog.o pseudo_client.o pseudo_ipc.o \ $(DBOBJS) $(SHOBJS) $(LDFLAGS) $(DB_LDFLAGS) $(CLIENT_LDFLAGS) pseudodb: $(PSEUDODB) $(PSEUDODB): $(BIN) pseudodb.o $(SHOBJS) $(DBOBJS) pseudo_ipc.o $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o $(PSEUDODB) pseudodb.o \ $(DBOBJS) $(SHOBJS) pseudo_ipc.o $(LDFLAGS) $(DB_LDFLAGS) $(CLIENT_LDFLAGS) libpseudo: $(LIBPSEUDO) $(LIBPSEUDO): $(LIB) $(WRAPOBJS) pseudo_client.o pseudo_ipc.o $(SHOBJS) $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -shared -o $(LIBPSEUDO) \ pseudo_client.o pseudo_ipc.o \ $(WRAPOBJS) $(SHOBJS) $(LDFLAGS) $(CLIENT_LDFLAGS) # *everything* now relies on stuff that's generated in the # wrapper process. %.o: %.c pseudo_wrapfuncs.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(CFLAGS_PSEUDO) $< $(OBJS): pseudo_tables.h pseudo_client.o pseudo_server.o pseudo_ipc.o: pseudo_ipc.h pseudo_client.o: pseudo_client.h pseudo_server.o: pseudo_server.h tables: enums/*.in maketables templatefile.py $(TABLES) ./maketables enums/*.in wrappers: makewrappers templatefile.py $(TEMPLATES) ports/*/wrapfuncs.in CC="$(CC) $(CFLAGS) $(CFLAGS_PSEUDO)" ./makewrappers "xattr=$(XATTR)" .SECONDARY: tables wrappers pseudo_wrapfuncs.c pseudo_wrapfuncs.h: wrappers pseudo_tables.c pseudo_tables.h: tables pseudo_tables.o: pseudo_tables.c $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -c -o pseudo_tables.o pseudo_tables.c # no-strict-aliasing is needed for the function pointer trickery. pseudo_wrappers.o: $(GUTS) pseudo_wrappers.c pseudo_wrapfuncs.c pseudo_wrapfuncs.h pseudo_tables.h $(CC) -fno-strict-aliasing $(CFLAGS) $(CFLAGS_PSEUDO) -D_GNU_SOURCE -c -o pseudo_wrappers.o pseudo_wrappers.c offsets32: $(CC) -m32 -o offsets32 offsets.c offsets64: $(CC) -m64 -o offsets64 offsets.c $(PSEUDO_PROFILE): $(BIN) pseudo_profile cp pseudo_profile $(BIN) pseudo_profile: Makefile pseudo_profile.c tables wrappers $(CC) $(CFLAGS) $(CFLAGS_PSEUDO) -o pseudo_profile pseudo_profile.c clean: rm -f *.o *.so $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) \ pseudo_wrapfuncs.h pseudo_wrapfuncs.c \ pseudo_wrapper_table.c \ pseudo_tables.c pseudo_tables.h \ pseudo_ports.h port_wrappers.c \ offsets32 offsets64 \ pseudo_profile \ port_deps.mk func_deps.mk touch port_deps.mk func_deps.mk distclean: clean rm -f Makefile rm -rf ./$(BIN) ./$(LIB) ./$(LOCALSTATE) @echo "WARNING: Makefile has been removed. You must reconfigure to do anything else." nuke: distclean case "$(PREFIX)" in "`pwd`"/*) rm -rf "$(PREFIX)";; esac @echo "WARNING: Removed $(PREFIX)." tarball: @test -d .git || ( echo >&2 "Tarball can only be made from git tree."; exit 1) rm -rf pseudo-$(VERSION) mkdir -p pseudo-$(VERSION) ( parent=$(PWD); \ cd pseudo-$(VERSION) && \ git clone $$parent && \ mv pseudo/* . && \ rm -rf pseudo/.git* && \ rmdir pseudo \ ) tar cjf pseudo-$(VERSION).tar.bz2 pseudo-$(VERSION) # Note when we need to rebuild pseudo_wrappers.o include port_deps.mk include func_deps.mk pseudo-1.8.1+git20161012/README000066400000000000000000000057711300506512600153040ustar00rootroot00000000000000pseudo -- an analogue to sudo IMPORTANT NOTE: As of this writing, the official home for pseudo's git repository has changed to: git://git.yoctoproject.org/pseudo The old site at: https://github.com/wrpseudo/pseudo is no longer the "real" master, although I'll probably try to keep it in sync. OVERVIEW: The pseudo utility offers a way to run commands in a virtualized "root" environment, allowing ordinary users to run commands which give the illusion of creating device nodes, changing file ownership, and otherwise doing things necessary for creating distribution packages or filesystems. To configure, run the provided configure script. Note that this is NOT an autoconf script. Configure options: --prefix=/path/to/install/dir --with-sqlite=/path/to/sqlite/dir --bits={32,64} --suffix= --enable-static-sqlite --with-sqlite-lib --libdir=... --enable-memory-db --enable-force-async --with-sqlite-lib=... --enable-static-sqlite --with-static-sqlite=... --with-rpath=...|--without-rpath --cflags='' There is no default prefix. The default for sqlite is /usr, and for bits is 32. You need a reasonably modern sqlite3 -- it has to have sqlite3_clear_bindings(), which at least one default sqlite3 install did not. (But that was dated 2006, so I'm not sure this will affect most readers.) The suffix value can be used to append a particular text string to file names (such as libpseudo.so). This was used in the WR environment to create libpseudo-.so, to ensure that libpseudo was rebuilt if the host libc changed. This code is not particularly portable, but works on Linux and also on 64-bit Intel Darwin systems. Limited user documentation is provided in the given man page files (these are not currently installed, merely provided in the source directory), and some documentation on internals is provided in the doc/ directory. The memory-db option uses an in-memory SQLite database, which is flushed to disk on failures. It's disabled by default with SQLite 3.6 and earlier due to performance weirdness. The --enable-force-async option causes wrappers for fsync() and friends to all return "success" instantly unless PSEUDO_ALLOW_FSYNC was set in the environment when the client initialized (usually after a fork or exec). FUTURE DIRECTIONS: * The chroot() functionality is incomplete, though now sufficient for the real-world use cases we've tried. * I have no intention of converting to autoconf. It is the wrong tool for the job. Please feel free to send bug feedback, change requests, or general commentary. ACKNOWLEDGEMENTS: My various coworkers, both engineering and management, made this possible. While I did most of the actual typing, this code has benefitted greatly from detailed code reviews, excellent reproducers for bugs, and the consistent support of the whole group for the project. It's been a great deal of fun, and I'm pretty happy that we're finally ready to make it available for other people to look at. pseudo-1.8.1+git20161012/configure000077500000000000000000000215171300506512600163270ustar00rootroot00000000000000#!/bin/sh # # configure, simulation of autoconf script, much simplified # # Copyright (c) 2008-2014 Wind River Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the Lesser GNU General Public License version 2.1 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the Lesser GNU General Public License for more details. # # You should have received a copy of the Lesser GNU General Public License # version 2.1 along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # not a real configure script... opt_prefix= opt_libdir= opt_suffix= opt_arch=x86 opt_bits= opt_sqlite=/usr opt_rpath= opt_memory= opt_async= opt_xattr= opt_xattrdb=false opt_profile=false opt_passwd_fallback='""' compile_x86_32=-m32 compile_x86_64=-m64 usage() { echo >&2 "usage:" echo >&2 " configure --prefix=..." echo >&2 " [--libdir=...]" echo >&2 " [--suffix=...]" echo >&2 " [--enable-memory-db]" echo >&2 " [--enable-xattr]" echo >&2 " [--enable-xattrdb]" echo >&2 " [--enable-profiling]" echo >&2 " [--enable-force-async]" echo >&2 " [--with-sqlite=...]" echo >&2 " [--with-sqlite-lib=...]" echo >&2 " [--enable-static-sqlite]" echo >&2 " [--with-static-sqlite=...]" echo >&2 " [--with-rpath=...|--without-rpath]" echo >&2 " [--with-passwd-fallback=...|--without-passwd-fallback]" echo >&2 " [--cflags='']" echo >&2 " [--bits=32|64]" exit 1 } sqlite_ldarg=-lsqlite3 maybe_rpath= use_maybe_rpath=true for arg do case $arg in --disable-*) arg="--enable-${arg%--disable-}=no" ;; esac case $arg in --) shift; break ;; --prefix=*) opt_prefix=${arg#--prefix=} if [ -d "$opt_prefix" ]; then maybe_prefix=$(cd "$opt_prefix"; pwd) else maybe_prefix=$opt_prefix fi if [ "$maybe_prefix" = "$(pwd)" ]; then echo >&2 "ERROR: Prefix is current directory. That doesn't work." exit 1 fi ;; --libdir=*) opt_libdir=${arg#--libdir=} ;; --with-static-sqlite=*) opt_sqlite_ldarg=${arg#--with-static-sqlite=} sqlite_ldarg=$opt_sqlite_ldarg use_maybe_rpath=false ;; --without-passwd-fallback) opt_passwd_fallback='NULL' ;; --with-passwd-fallback=*) opt_passwd_fallback="${arg#--with-passwd-fallback=}" # Trim a trailing /, remove quotes. So / => "". opt_passwd_fallback="\"${opt_passwd_fallback%/}\"" ;; --enable-static-sqlite) sqlite_ldarg='$(SQLITE)/$(SQLITE_LIB)/libsqlite3.a' use_maybe_rpath=false ;; --enable-force-async=no | --disable-force-async) opt_async=false ;; --enable-force-async=yes | --enable-force-async) opt_async=true ;; --enable-profiling=no) opt_profiling=false ;; --enable-profiling=yes | --enable-profiling) opt_profiling=true ;; --enable-xattr=no) opt_xattr=false ;; --enable-xattr=yes | --enable-xattr) opt_xattr=true ;; --enable-xattrdb=no) opt_xattrdb=false ;; --enable-xattrdb=yes | --enable-xattrdb) opt_xattrdb=true ;; --enable-memory-db=no) opt_memory=false ;; --enable-memory-db=yes | --enable-memory-db) opt_memory=true ;; --with-sqlite=*) opt_sqlite=${arg#--with-sqlite=} # assign new value if unset maybe_rpath='-Wl,-R$(SQLITE)/$(SQLITE_LIB)' ;; --with-sqlite-lib=*) opt_sqlite_lib=${arg#--with-sqlite-lib=} # assign new value if unset maybe_rpath='-Wl,-R$(SQLITE)/$(SQLITE_LIB)' ;; --without-rpath) opt_rpath='' use_maybe_rpath=false ;; --with-rpath=*) rpath=${arg#--with-rpath=} opt_rpath=${rpath:+-Wl,-R$rpath} use_maybe_rpath=false ;; --suffix=*) opt_suffix=${arg#--suffix=} ;; --arch=*) echo >&2 "WARNING: The --arch option is now deprecated. Use --cflags." opt_arch=${arg#--arch=} ;; --cflags=*) opt_cflags=${arg#--cflags=} ;; --bits=*) opt_bits=${arg#--bits=} ;; *) echo >&2 "warning: Unrecognized option '$arg'" ;; esac done case $opt_arch in '' | x86 | arm ) ;; *) echo >&2 "Untested arch $opt_arch." ;; esac if [ -z "$opt_bits" ]; then printf >&2 "Bit width unspecified;" case $(file -L /bin/sh 2>/dev/null) in *64-bit*) opt_bits=64;; *32-bit*) opt_bits=32;; esac if [ -n "$opt_bits" ]; then echo >&2 " guessing bit width is $opt_bits, based on /bin/sh." else echo >&2 " can't tell, assuming 32." opt_bits=32 fi fi case $opt_bits in 64) opt_mark64=64;; 32) opt_mark64=;; *) echo >&2 "Unknown bit size $opt_bits (only 32 and 64 known)." ;; esac if [ "${opt_cflags-UNSET}" = "UNSET" ]; then # Some targets want something like -m64. eval arch_flags=\$compile_${opt_arch}_${opt_bits} echo >&2 "WARNING: Guessing architecture CFLAGS '${arch_flags-}'." echo >&2 "If you need specific flags, use --cflags." else arch_flags=$opt_cflags fi arch_flags=$(echo "$arch_flags" | sed -e 's/,/\\,/g') if $use_maybe_rpath && [ -n "$maybe_rpath" ]; then echo >&2 "Adding default RPATH for sqlite." opt_rpath="${opt_rpath+${opt_rpath} }${maybe_rpath}" fi if [ -z "$opt_prefix" ]; then usage fi if [ -z "$opt_libdir" ]; then opt_libdir=$opt_prefix/lib$opt_mark64 fi # We need to find the libdir relative to the prefix, this is required # by the code in pseudo-utils.c that handles relocation. opt_lib=${opt_libdir#$opt_prefix/} if [ "$opt_lib" = "$opt_libdir" ]; then echo >&2 "libdir must be relative to prefix." exit 1 fi if [ -z "$opt_sqlite_lib" ]; then opt_sqlite_lib=$opt_lib fi if [ ! -f "${opt_sqlite}/include/sqlite3.h" ]; then echo >&2 "SQLite3 headers not found in at ${opt_sqlite}/include/sqlite3.h. Please check that SQLite3 and SQLite3 headers are installed." exit 1 fi read t1 t2 SQLITE3_VERSION << EOF `grep "#define SQLITE_VERSION_NUMBER " ${opt_sqlite}/include/sqlite3.h` EOF echo "SQLite header for version ${SQLITE3_VERSION} found in ${opt_sqlite}." if [ "${SQLITE3_VERSION}" -lt "03006000" ]; then echo >&2 "Pseudo requires SQLite version 3, 3.6.x or later." exit 1 fi if [ -z "$opt_async" ]; then opt_async=false fi if $opt_async; then FORCE_ASYNC="-DPSEUDO_FORCE_ASYNC" else FORCE_ASYNC="" fi if getfattr --help >/dev/null 2>&1; then xattr_runs=true else xattr_runs=false fi if [ -z "$opt_xattr" ]; then if $opt_xattrdb; then opt_xattr=true echo "xattr DB support implies extended attribute support" else if $xattr_runs; then opt_xattr=true echo "getfattr runs, enabling extended attribute support" else opt_xattr=false echo "getfattr fails, disabling extended attribute support" fi fi fi if $opt_xattr || $opt_xattrdb; then if ! $xattr_runs; then echo >&2 "WARNING: getfattr doesn't work, but xattr-related features requestd." fi fi if [ -z "$opt_memory" ]; then if [ "${SQLITE3_VERSION}" -lt "03007000" ]; then echo "Disabling in-memory database by default (sqlite too old)." opt_memory=false else echo "Enabling in-memory database by default." opt_memory=true fi fi if $opt_memory; then if [ "${SQLITE3_VERSION}" -lt "03007000" ]; then cat >&2 < Makefile pseudo-1.8.1+git20161012/doc/000077500000000000000000000000001300506512600151575ustar00rootroot00000000000000pseudo-1.8.1+git20161012/doc/chroot000066400000000000000000000031321300506512600163770ustar00rootroot00000000000000Pseudo incorporates functionality similar to that of fakechroot. The implementation is basically as follows: When incoming arguments are known to be paths, they are automatically converted to absolute, fully canonicalized, paths. When the chroot(2) call is used, pseudo intercepts it and stashes the path to the new root directory internally in the client. This path is then silently prepended to absolute paths. The internal tracking of file paths uses absolute paths including the path to the chroot directory. So, all underlying calls are invoked with absolute, canonical, paths. A few calls are then modified. For instance, when readlink() returns a link, it strips a leading prefix looking like the chroot directory; when symlink() creates a link, if the link path is absolute, the chroot directory is prepended to it. This behavior, which may seem surprising, is an attempt to produce path resolution equivalent to what would normally happen when canonicalizing a path using real chroot; if you create a link to /tmp while in a chroot environment, following that link should yield /tmp. Arguably, now that we are intercepting nearly every incoming path, we should just prepend the chroot path when following absolute symlinks -- this may get revisited. Similarly, when glob() is called, the path to be globbed has the chroot directory prepended, and then the results have any leading chroot directory stripped from them. This implementation is far from complete or perfect; the intent is that it provide enough functionality to allow RPM and similar tools to run file system installs using chroot(). pseudo-1.8.1+git20161012/doc/database000066400000000000000000000130141300506512600166450ustar00rootroot00000000000000There are two databases. The log database contains a record of operations and events. (Operation logging is optional.) The file database contains a record of known files. In general, the file database is configured with sqlite options favoring stability, while the log database is configured for speed, as operation logging tends to outnumber file operations by a large margin. FILES: id (unique key) path (varchar, if known) dev (integer) ino (integer) uid (integer) gid (integer) mode (integer) rdev (integer) deleting (integer) There are two indexes on the file database, one by path and one by device and inode. Earlier versions of pseudo ignored symlinks, but this turned out to create problems; specifically, if you had a symlink to a directory, and accessed a file through that, it could create unexpected results. Names are fully canonicalized by the client, except for functions which would operate directly on a symlink, in which case the last path component is not replaced. It is not an error to have multiple entries with the same device and inode. Updates to uid, gid, mode, or rdev are applied to every file with the same device and inode. Operations by name are handled by looking up the name to obtain the device and inode, then modifying all matching records. If a file shows up with no name (this should VERY rarely happen), it is stored in the database with the special name 'NAMELESS FILE'. This name can never be sent by the client (all names are sent as absolute paths). If a laterThe request comes in with a valid name, the 'NAMELESS FILE' is renamed to it so it can be unlinked later. The "deleting" field is used to track files which are in the midst of being possibly-deleted. The issue is that if you issue an unlink database operation only after removing a file, there is a window during which another program can genuinely create a new file with the same name or inode, and that request can reach the daemon before the unlink operation does. To address this, three operations are addded: may-unlink, did-unlink, and cancel-unlink. The may-unlink operation tags a file for possible deletion, setting "deleting" to 1. A clash involving a file marked for deletion is resolved by deleting the existing database entry without complaint. A did-unlink operation deletes a file ONLY if it is marked for deletion. A cancel-unlink operation unmarks a file for deletion. You can still have a race condition here, but it seems a lot less likely. The failure would be: Process A marks file X for deletion. Process A unlinks file X. Process B creates file X. Process B requests a link for X. The server unlinks the previously marked X. The server creates a new link for X, not marked for deletion. Process B marks file X for deletion. Process A's delete-marked-file request finally shows up. The server unlinks the newly-marked X. Process B fails to delete the file. Process B attempts to cancel the mark-for-deletion. This shouldn't be a common occurrence. Rename operations use a pair of paths, separated by a null byte; the client sends the total length of both names (plus the null byte), and the server knows to split them around the null byte. The impact of a rename on things contained within a directory is handled in SQL: UPDATE files SET path = replace(path, oldpath, newpath) WHERE path = oldpath; UPDATE files SET path = replace(path, oldpath, newpath) WHERE (path > oldpath || '/') && (path < oldpath || '0); That is to say, anything which either starts with "oldpath/" or is exactly equal to oldpath gets renamed, with oldpath replaced by newpath... The unusual constructions are to address two key issues. One is that an "OR" would prevent proper use of the index. The other is that a pattern, such as "LIKE oldpath || '/%'", would prevent use of the index (at least in sqlite). The gimmick is that the only things greater than 'a/' and less than 'a0' are strings which begin with 'a/' and have additional characters after it. LOGS id (unique key) stamp (integer, seconds since epoch) operation (id from operations, can be null) client (integer identifier) dev (integer) ino (integer) mode (integer) path (varchar) result (result id) severity (severity id) text (anything else you wanted to say) tag (identifier for operations) access (integer) The log database contains a primary table (logs). As of this writing it is not indexed, because indexing is expensive during writes (common, for the log database) and very few queries are usually run. The log database also contains, when created, tables of operations, result types, and severities. These exist so that queries can be run against a log database even if these values might have changed in a newer build of pseudo. The tables of operations and severities are just id->name pairs. No enforcement of the relation is currently provided. The log database "tag" field, added since the initial release of pseudo, is available for tagging operations. When a client connects to the pseudo server, it passes the value of the environment variable PSEUDO_TAG; this tag is then recorded for all log entries pertaining to that client. The log database "access" field, added since the initial release of pseudo, stores values which are bitwise masks of the values 1 (execute), 2 (write), 4 (read), and 8 (append), used to report the modes with which a file was opened. These values are not completely reliable. A value of 0 is typical for non-open operations, and a value outside the 0-15 range (usually -1) indicates that something went wrong trying to identify the mode of a given open. pseudo-1.8.1+git20161012/doc/overview000066400000000000000000000130011300506512600167430ustar00rootroot00000000000000Overview: The pseudo program and library combine to provide an environment which provides the illusion of root permissions with respect to file creation, ownership, and related functions. The underlying mechanism of pseudo is a library inserted using LD_PRELOAD, which provides replacement symbols for core C library functions. At this time, the implementation is specific to modern glibc. Support for other systems is certainly possible, but not currently implemented or immediately planned. The symbols wrapped are generally those that are documented in section 2 of the manual -- the ones which are essentially system calls. The library works by replacing each real function with a wrapper function which obtains the addresses of "real" functions (those in the next library down in the chain, typically glibc) and then calls custom-written wrappers which alter the behavior of these functions and return results corresponding to the virtual environment. Underlying this is access to a server process, which is automatically spawned by the library if one is not available. The server process maintains a UNIX domain socket while it is active, and maintains a database (using sqlite) of files known to the system. Files are recorded in the database only if they are created within the virtualized environment or have been altered by it; files merely read are not added. There are four layers of logic for performing or wrapping any function, although not all functions involve all four layers: 1. The generic wrapper, which handles details such as thread-synchronization. This function handles the mutex used to keep multiple threads from trying to write to the same socket at once, and also disables wrappers when a value called "antimagic" is set. The antimagic value is set internally by the pseudo client code, and the check for whether or not to use it is controlled by the mutex (actually by the mutex owner variable, which is protected by the mutex.) Without that, read operations in another thread during the "antimagic" part of an operation would bypass pseudo, yielding erratically wrong results! Wrappers are where pathnames get canonicalized. 2. The wrapper function itself. This function may translate a single operation into two or more logical operations. This function has no awareness of the database, but can send queries to the general client code. 3. The general client code. This code maintains additional data, such as a mapping of file descriptors to paths. In most cases, this code also forwards requests to the server code. (If the server is unavailable, the client can restart it.) 4. The server code. This code is fairly simple; all it does is maintain the database of file information. Operations consist either of a request for information (e.g., a stat(2) call) or notification of a change. The server sends back failure or success notices. As a fairly typical example, the progress of a stat(2) call is: * The __xstat() wrapper is called. This wrapper checks the version argument against the _STAT_VER constant in case we some day run into a system where programs call stat with different versions of struct stat. (Hasn't happened yet.) * The __xstat() wrapper calls the __fxstatat() wrapper, which in turn calls the __fxstatat64() wrapper (this allows us to have only one copy of the logic shared among all the path-based stat syscalls). * The __fxstatat64() wrapper calls the underlying __fxstatat64() function, which has been mapped to the name real___fxstatat64(). (If this fails, the wrapper function returns immediately.) * The __fxstatat64() wrapper passes the resulting stat buffer and path to the client code and asks for a response. * The client code converts the stat buffer into a pseudo_msg_t message object, and canonicalizes the path (resolving symlinks and eliminating extra slashes, as well as references to . and ..). * The client code now sends the pseudo_msg_t object and converted path to the server as a message. * The server receives the message. Since this is a stat() operation (using a path, not a dev/inode pair, for identification), the server searches its database for existing entries with the corresponding name. * If the server finds an object, it updates the contents of the pseudo_msg_t with the recorded values for uid, gid, mode, and raw device number, and sends the message back with status SUCCEED. * The server also performs sanity checks to see whether there may be other suspiciously-similar entries in the database, in which case it emits diagnostics. (Usually to pseudo.log.) * If the server finds no object, it sends the message back with status FAIL. * The client code returns the message to the wrapper function. * If the status was SUCCEED, the wrapper function copies the modified fields back into its stat buffer; otherwise, it does not. * The wrapper function returns the original exit status from stat. Most of the functions wrapped are syscalls. There are a few exceptions, such as mkstemp, fopen, and freopen. These are wrapped because, in glibc, they call internal functions which make inline assembly syscalls, rather than calling the syscall entry points. In each case, the wrapper makes the real call without intervention, then snoops the results for a file descriptor to path mapping. (This would be done to opendir/fdopendir/closedir as well, but the DIR * is opaque and can't be snooped practically. This is why some versions of 'rm -r' can, at higher diagnostic levels, generate a slew of warnings about file descriptors being reopened when no close was observed.) pseudo-1.8.1+git20161012/doc/passwd000066400000000000000000000022711300506512600164050ustar00rootroot00000000000000Pseudo provides limited support for faking up password file access. Routines such as setpwent(), getpwent(), and so on, are modified to possibly pick a password file other than /etc/passwd, and likewise for setgrent() and /etc/group. The logic is as follows: * If a chroot directory is set, /etc is tried first. * If PSEUDO_PASSWD is set, PSEUDO_PASSWD/etc is tried next. * Otherwise, fall back on /etc. In each case, failure to find a passwd or group file results in going on to trying the next case. The behavior of lckpwdf()/ulckpwdf() is a special case. In this case, the same order of directories is tried, but pseudo attempts to create the files, rather than attempting to open existing files. The underlying implementation directs nearly everything to fgetpwent_r() and fgetgrent_r(), which are extensions available in glibc. This allows pseudo to avoid having to actually implement yet another horrible passwd file parser which would inevitably have bugs that have already been fixed dozens of times in other implementations. Note that both the chroot directory and PSEUDO_PASSWD are assumed to be the parent directory of etc, not the directory containing the passwd and group files. pseudo-1.8.1+git20161012/doc/perftest000066400000000000000000000005021300506512600167330ustar00rootroot00000000000000There's a new "perftest" program. I recommend you run it with a ramdisk. So, if you've mounted a ramdisk on /tmp/ram: ./perftest /tmp/ram This is a pretty unscientific test, but is designed to exercise some reasonably common use cases and give ballpark figures as to the effect of changes to the database indexes, etc. pseudo-1.8.1+git20161012/doc/ports000066400000000000000000000023621300506512600162540ustar00rootroot00000000000000The "ports" system provides functionality for porting pseudo to new targets. The original motivation was a requirement to support older and newer Linux systems on which the signature for clone() differed. The Darwin port is totally nonfunctional at this point, but it compiles, and sufficiently careful hand-tuning of DYLD_INSERT_LIBRARIES and a manually-started server can actually pass a simple test. It will get worked on in my copious free time. The basic design of a port is that it provides a wrapfuncs.in list of function signatures, guts implementations, and optionally some port-specific defines or a block of wrapper code for pseudo_wrappers.c. This is used for cases where the default wrapper would not be appropriate, and may be combined with the new hand_wrapped=1 flag in wrapfuncs.in. A port may specify preports or subports. Preports are ports that are included and processed *before* the current port -- meaning that functions in the current port can override them. Subports are processed *after* the current port -- meaning that they can override functions in the current port. The preports and subports are specified by scripts, which echo a list of port names to standard output. (Be sure any diagnostic messages go to standard error.) pseudo-1.8.1+git20161012/doc/program_flow000066400000000000000000000037641300506512600176120ustar00rootroot00000000000000This is a quick attempt at documenting the basic program flow for both the main pseudo executable and the pseudo wrapper library. The key thing to note is that there are key init functions. These functions are designed to be re-invoked if it becomes necessary to reset the system environment. libpseudo execution flow: # on startup # pseudo_wrappers.c: (constructor) _libpseudo_init() pseudo_util.c: pseudo_init_util() copy environment setup PSEUDO_DEBUG levels pseudo_wrappers.c: pseudo_init_wrappers() setup pseudo_functions setup pseudo_logfile pseudo_client.c: pseudo_init_client() setup PSEUDO_DISABLED setup pseudo_prefix_dir_fd setup pseudo_localstate_dir_fd setup PSEUDO_NOSYMLINKEXP setup PSEUDO_UIDS setup PSEUDO_GIDS setup PSEUDO_CHROOT setup PSEUDO_PASSWD # regular program execution # exec*() pseudo_check_wrappers(): pseudo_reinit_libpseudo if necessary call wrap_exec*() pseudo_setupenv() if PSEUDO_UNLOAD pseudo_dropenv() real_exec*() fork() pseudo_check_wrappers(): pseudo_reinit_libpseudo if necessary call wrap_fork() real_fork() if (child) pseudo_setupenv() if !PSEUDO_UNLOAD pseudo_reinit_libpseudo() _libpseudo_init() else pseudo_dropenv() clone() pseudo_check_wrappers(): pseudo_reinit_libpseudo if necessary call wrap_clone() pseudo_setupenv() if !PSEUDO_UNLOAD pseudo_reinit_libpseudo() _libpseudo_init() else pseudo_dropenv() real_clone() ... normal function wrappers ... (templates/wrapfuncs.c): pseudo_check_wrappers() || !real_* return enosys variadic setup (if necessary) if pseudo_disabled return real_*() pseudo_sigblock pseudo_getlock if antimagic rc = real_*() else rc = wrap_*() variadic end (if necessary) pseudo_droplock unmask signals return rc pseudo execution flow: pseudo.c: main() pseudo_util.c: pseudo_init_util() check LD_PRELOAD process arguments setup PSEUDO_OPTS ... startup server pseudo-1.8.1+git20161012/doc/pseudo_ipc000066400000000000000000000065001300506512600172350ustar00rootroot00000000000000MESSAGE PASSING typedef struct { pseudo_msg_type_t type; op_id_t op; res_id_t result; int access; int client; dev_t dev; unsigned long long ino; uid_t uid; gid_t gid; unsigned long long mode; dev_t rdev; unsigned int pathlen; int nlink; char path[]; } pseudo_msg_t; This structure is used for every communication between the client and the server. The last field is optional (it's a C99ism called a flexible array member, allowing a single allocation to hold both the structure and the variable-length character data at the end). All messages contain items up through 'pathlen'. If pathlen is not zero, an additional pathlen bytes containing path are provided; path is null-terminated. Every message from client should get a response from server. The server never really sends a path, currently, but maybe it will someday. Note that all server responses will in general share a single message object, and future operations may cause that object to be reallocated; the same goes for messages received by the server. Basically, pseudo_msg_receive is not thread-safe; this is part of (but not all of) the reason that there's mutex stuff in the wrappers. (The other part is the "antimagic" being able to blow things up.) type is one of PING, OP, FASTOP, SHUTDOWN, ACK, or NAK. The client only sends PING, OP, or FASTOP. FASTOP takes no response, otherwise the server should always send ACK. When run with '-S', the pseudo program runs as a client, sending a SHUTDOWN message to a server -- but only if it can find one, it does not start a new one. In this case, the server could respond with a NAK, in which case it sends a message in which "path" is a list of space-separated PIDs of currently-living clients, for the program to print out in an error message. The server will not shut down while there are living clients. (The request, though, causes it to shut down immediately when there are no more clients, rather than waiting for the timeout period.) result is the result of a particular operation. It applies only in replies to OP messages. client should be the client's PID on send, and the server's client number for that client on response. (The response isn't checked, and this is just a debugging feature.) dev/ino/uid/gid/mode/rdev/path are information about the file. They should all be provided on send if possible, but the server only generally changes uid/gid/mode/rdev on response, and never sends a path back. Dev and inode are currently changed by stat-by-path operations, but this may turn out to be wrong. access holds information about the open mode of a file (read, write, append, etc.), but is not fully implemented. A field "xerrno" used to exist; it was never actually implemented. nlink is used to forward the number of links. The server DOES NOT modify this. Rather, nlink is used to provide better diagnostics when checking paths against inodes. 32/64 bit: This structure should have the same offsets for every element, including path, on both 64-bit and 32-bit machines. (Check with 'offsets.c'.) It is *not* an error if sizeof(pseudo_msg_t) is different; the padding happens after the path element. (Note: This is contrary to C99, TC1, but is correct according to the current standard. Anyway, gcc's always done it this way.) The data written are always pathlen + offsetof(pseudo_msg_t, path), and that's correct. pseudo-1.8.1+git20161012/doc/utils000066400000000000000000000026671300506512600162550ustar00rootroot00000000000000pseudolog Displays or creates log entries. This offers a quick first approximation of the sorts of queries one is likely to need to run. pseudo The pseudo server. Run on the command line, the default behavior is to set up a pseudo environment (LD_PRELOAD, etc) then run either the specified command or a shell by default. The -d option specifies a background daemon, and -f specifies a foreground daemon (which may display output directly). The launcher function isn't really 32-bit/64-bit aware, but if you have both types of libraries in suitably-named directories, it'll do the right thing anyway. Path may be in environment as PSEUDO_PREFIX, specified on command line with -P path, or inferred from the path to $0. (The last generates a diagnostic.) To stop the pseudo server, either wait a while (the default timeout is 30 seconds, or whatever you specified with "-t" or PSEUDO_OPTS when starting it) or run "pseudo -S". The server will not exit while clients are active, but requesting a shutdown sets the timeout to one second, so it will exit quickly after the last client disconnects. libpseudo.so The library providing the wrapper functionality, which spawns the pseudo server automatically if needed. If the environment variable PSEUDO_ENOSYS_ABORT is set, attempts to call missing system calls will abort() rather than merely emitting a diagnostic. pseudodb allows browsing and modification of db (not implemented) pseudo-1.8.1+git20161012/enums/000077500000000000000000000000001300506512600155415ustar00rootroot00000000000000pseudo-1.8.1+git20161012/enums/debug_type.in000066400000000000000000000024321300506512600202210ustar00rootroot00000000000000debug_type: PDBG; INDEXED unsigned char symbolic = '\0', const char * description = NULL, FLAGS # Note: For convenience/consistency with the old numerc debug levels # stuff, these are sorted in a very rough approximation of "likelihood # that the user will care." So, PSEUDO_DEBUG=1 will pick up the # consistency checks. In general, most numeric debug levels will be # significantly less chatty than they used to be; there was one # level 5 message, I think, and nothing else above a 4. Which was a # problem. # Note: Descriptions should be under 32 characters to match the formatting # in pseudo's help message. consistency, 'n', "consistency checks" file, 'f', "file creation/deletion" op, 'o', "operations" pid, 'P', "show process IDs" client, 'c', "client side startup/shutdown" server, 'v', "server side startup/shutdown" db, 'd', "database interactions" xattrdb, 'D', "xattr database" profile, 'R', "profiling" syscall, 'y', "system calls" env, 'e', "environment manipulation" chroot, 'r', "chroot functionality" path, 'p', "path computations" sql, 's', "SQL query information" wrapper, 'w', "wrapper functionality" ipc, 'i', "client/server interactions" invoke, 'k', "invocation and launching" benchmark, 'b', "performance statistics" verbose, 'V', "extra detail" xattr, 'x', "extended attributes" pseudo-1.8.1+git20161012/enums/exit_status.in000066400000000000000000000017631300506512600204540ustar00rootroot00000000000000exit_status: PSEUDO_EXIT; char * message = "exit status unknown" # 0 indicates success. The others indicate where in the startup process # something went wrong, for any point at which we'd exit. general, "unspecified error" fork_failed, "fork failed" lock_path, "path allocation failure for lock file" lock_held, "lock already held by another process" lock_failed, "could not create/lock lockfile" timeout, "child process timed out" waitpid, "waitpid() for child process failed unexpectedly" socket_create, "couldn't create socket" socket_fd, "couldn't move socket to safe file descriptor" socket_path, "path allocation failure for server socket" socket_unlink, "couldn't unlink existing server socket" socket_bind, "couldn't bind server socket" socket_listen, "couldn't listen on server socket" listen_fd, "server loop had no valid listen fd" pseudo_loaded, "server couldn't get out of pseudo environment" pseudo_prefix, "couldn't get valid pseudo prefix" pseudo_invocation, "invalid server command arguments" pseudo-1.8.1+git20161012/enums/msg_type.in000066400000000000000000000000651300506512600177210ustar00rootroot00000000000000msg_type: PSEUDO_MSG ping shutdown op ack nak fastop pseudo-1.8.1+git20161012/enums/op.in000066400000000000000000000005071300506512600165110ustar00rootroot00000000000000op: OP; int wait = 0 chdir, 0 chmod, 0 chown, 0 chroot, 0 close, 0 creat, 0 dup, 0 fchmod, 0 fchown, 0 fstat, 1 link, 0 mkdir, 0 mknod, 1 open, 0 rename, 0 stat, 1 unlink, 0 symlink, 0 exec, 0 may-unlink, 1 did-unlink, 0 cancel-unlink, 0 get-xattr, 1 list-xattr, 1 remove-xattr, 1 set-xattr, 0 create-xattr, 1 replace-xattr, 1 pseudo-1.8.1+git20161012/enums/query_field.in000066400000000000000000000007131300506512600204020ustar00rootroot00000000000000query_field: PSQF # Note: These are later used as bitwise masks into a value, # currently an unsigned long; if the number of these gets up # near 32, that may take rethinking. The first thing to # go would probably be something special to do for FTYPE and # PERM because they aren't "real" database fields -- both # of them actually imply MODE. access client dev fd ftype gid id inode mode op order path perm program result severity stamp tag text type uid pseudo-1.8.1+git20161012/enums/query_type.in000066400000000000000000000002501300506512600202740ustar00rootroot00000000000000query_type: PSQT; const char * sql = "LITTLE BOBBY TABLES" exact, "=" less, "<" greater, ">" bitand, "&" notequal, "!=" like, "LIKE" notlike, "NOT LIKE" sqlpat, "LIKE" pseudo-1.8.1+git20161012/enums/res.in000066400000000000000000000000371300506512600166620ustar00rootroot00000000000000res: RESULT succeed fail error pseudo-1.8.1+git20161012/enums/sev.in000066400000000000000000000000551300506512600166660ustar00rootroot00000000000000sev: SEVERITY debug info warn error critical pseudo-1.8.1+git20161012/guts/000077500000000000000000000000001300506512600153745ustar00rootroot00000000000000pseudo-1.8.1+git20161012/guts/COPYRIGHT000066400000000000000000000013521300506512600166700ustar00rootroot00000000000000/* * Copyright (c) 2008-2014 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ pseudo-1.8.1+git20161012/guts/README000066400000000000000000000126021300506512600162550ustar00rootroot00000000000000The files in this directory are partially machine-generated, and are all covered by the COPYRIGHT file in this directory. The set of functions covered here may seem surprising. For instance, obviously, fopen(3) simply calls the underlying open(2) syscall. But... There is a problem. In a few places in glibc, the syscalls are inlined such that there is no actual call to the C function open(2), just a raw call. So there are a couple of functions (fopen, freopen) which are wrapped with intent only to detect the possible creation of files. Many of these functions are closely related. Some programs may have calls to openat(), while others have calls to __openat_2(). To reduce code duplication, a number of functions are implemented purely as calls to other functions. When a *at() function exists, the regular function is implemented as *at() with AT_FDCWD as the directory fd (see the dummy #define of this in pseudo_client.h, used for systems which lack these.) On systems where AT_NOFOLLOW_SYMLINKS is not defined, the underlying *at() functions don't exist, so we provide a bare implementation which works only when the fd is AT_FDCWD... The creat64 and open64 families are equivalent to the plain versions with O_LARGEFILE in mode bits. (Again, there's a suitable dummy #define in pseudo_client.h.) By contrast, the stat64 functions actually do have some difference -- the structure they manipulate is not the same. The following table shows which functions are merely wrappers around other functions: canonicalize_file_name: realpath chmod: fchmodat chown: fchownat creat64: openat creat: openat __fxstatat: __fxstatat64 __fxstat: __fxstat64 get_current_dir_name: getcwd getwd: getcwd __lxstat64: __fxstatat64 __lxstat: __fxstatat mkdir: mkdirat mkfifoat: __xmknodat mkfifo: mkfifoat open64: openat __openat_2: openat __openat64_2: openat openat64: openat open: openat remove: unlink or rmdir rename: renameat symlink: symlinkat unlink: unlinkat __xmknod: __xmknodat __xstat64: __fxstatat64 __xstat: __fxstatat The following functions are full implementations: chdir fchdir fchmod fchmodat fchown fchownat __fxstat64 __fxstatat64 getcwd lchown mkdirat openat rmdir symlinkat unlinkat __xmknodat The following functions provide only partial implementations, to trap special cases, to track internal data structures (for instance, close() is tracked so that the path to a file descriptor can be dropped when the file descriptor is closed), or to handle functions which may not use the underlying syscall wrappers: close dup dup2 execl* (no guts implementations; see pseudo_wrappers.c) execv execve execvp fclose fopen fopen64 freopen freopen64 mkstemp mkstemp64 fcntl fork link The following functions don't have any direct database interactions, but are used to simulate the permissions system: getegid getuid setgid setreuid geteuid setegid setgroups setuid getgid seteuid setregid getresgid setfsgid setresgid getresuid setfsuid setresuid The following functions are present only to allow filename mangling for chroot(2) implementation. Most of them have no logic beyond calling the underlying routine. access acct chroot eaccess euidaccess fts_open ftw64 ftw glob64 glob lutimes mkdtemp mktemp nftw64 nftw opendir pathconf readlinkat readlink realpath scandir64 scandir truncate64 truncate utime utimes The following functions are unimplemented. renameat could be done now (it would have been hard previously due to file name mangling issues), but since it's never come up, we haven't done it. The tempnam() functions are fairly hard to get right, and perhaps more imporantly, extremely dangerous. Since there's no evidence that they're in use anymore, I've dummied them out: renameat tempnam tmpnam The following functions are partially emulated in order to provide for emulation of various getpw*() and getgr*() functions. No handling is provided for putpw*() or putgr*(). Nearly everything is ultimately implemented in terms of fgetpwent_r() and fgetgrent_r(), which are GNU extensions corresponding to fgetpwent() and fgetgrent(), allowing pseudo to read password information from an arbitrary stream; the setpwent() and setgrent() functions are modified to pick /etc/* from the pseudo_chroot path, if one is set, or from PSEUDO_PASSWD, if that is set, or else the system /etc/* files. endgrent endpwent getgrent getgrent_r getgrgid getgrgid_r getgrnam getgrnam_r getgrouplist getgroups getpw getpwent getpwent_r getpwnam getpwnam_r getpwuid getpwuid_r lckpwdf setgrent setgroups setpwent ulckpwdf The implementation of getgroups() is inauthentic in that it always checks the group file when called, rather than checking the group file once "at login" (whatever that means in our context) and returning that saved status. We don't think this matters. setgroups() just fails; this will be corrected if we come up with a compelling reason to do so. The following functions set errnot to ENOTSUP and return -1; this is needed because we don't actually track or manage extended attributes, but a few programs attempt to use *setxattr() to set regular permissions, and only use a regular chmod if the *setxattr() call returns -1 and sets errno to ENOTSUP. fgetxattr flistxattr fremovexattr fsetxattr getxattr lgetxattr listxattr llistxattr lremovexattr lsetxattr removexattr setxattr pseudo-1.8.1+git20161012/makedata.c000066400000000000000000000012731300506512600163300ustar00rootroot00000000000000#include #include #include int main(void) { int i, j, k, l; /* templateish form used so it's obvious that it's long enough */ char name[] = "dir_%d/dir_%d/%d%d.txt"; for (i = 0; i < 10; ++i) { snprintf(name, sizeof(name), "dir_%d", i); mkdir(name, 0755); for (j = 0; j < 40; ++j) { snprintf(name, sizeof(name), "dir_%d/dir_%d", i, j); mkdir(name, 0755); for (k = 0; k < 10; ++k) { for (l = 0; l < 10; ++l) { FILE *fp; snprintf(name, sizeof(name), "dir_%d/dir_%d/%d%d.txt", i, j, k, l); fp = fopen(name, "w"); if (fp) { fprintf(fp, "dummy file.\n"); fclose(fp); } } } } } return 0; } pseudo-1.8.1+git20161012/maketables000077500000000000000000000245441300506512600164610ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (c) 2008-2010, 2013 Wind River Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the Lesser GNU General Public License version 2.1 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the Lesser GNU General Public License for more details. # # You should have received a copy of the Lesser GNU General Public License # version 2.1 along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """convert tables.in files to enums, tables, and support code. Inputs are a type name, prefix, and a list of columns, followed by a list of names with optional "= value" suffixes, plus optional additional columns. The names are used to create enums and a table of strings, as well as to/from lookups between the ids and names. If additional columns are defined, each column (separated by ", ") is used to create an additional table of the given name, and a lookup function from ids. Example: foo: FFF; const char *bar = "GOZINTA" hello, "yah" world, "nope" produces: typedef enum { FFF_UNKNOWN = -1, FFF_MIN = 0, FFF_NONE = 0, FFF_HELLO, FFF_WORLD, FFF_MAX } foo_id_t; extern const char *foo_name(foo_id_t id); extern foo_id_t foo_id(const char *name); extern const char *foo_bar(foo_id_t id); such that foo_name(1) => "hello" and foo_bar(1) => "yah". If there is an assigned value for a column description, missing column values yield that value, otherwise they yield "unknown". Values out of range yield "unknown", and unrecognized names yield the value -1. Note that the "MAX" value is one more than the highest defined value. (This is for consistency with C array bounds.) """ import glob import sys import string from templatefile import TemplateFile class DataType: """a set of related DataItem objects""" def __init__(self, path): """read the first line of path, then make tuples of the rest""" source = file(path) definition = source.readline().rstrip() self.name, qualifiers = string.split(definition, ': ', 2) if '; ' in qualifiers: self.prefix, columns = string.split(qualifiers, '; ') else: self.prefix = qualifiers columns = [] self.flags = False if len(columns): self.columns = [] columns = string.split(columns, ', ') for col in columns: indexed = False if col.startswith("FLAGS"): print "Flags: set for %s" % self.name self.flags = True continue if col.startswith("INDEXED "): col = col[8:] indexed = True if "=" in col: name, default = string.split(col, ' = ') else: name, default = col, "" if " " in name: words = string.split(name, ' ') name = words[-1] del words[-1] type = ' '.join(words) else: type = "char *" self.columns.append({"indexed":indexed, "type":type, "name":name, "value":default}) else: self.columns = [] self.data = [] self.comments = [] index = 1 for line in source.readlines(): item = {} if line.startswith('#'): self.comments.append(line.rstrip().replace('#', '')) continue # first entry on the line is the "real" name/id, following hunks # are additional columns cols = string.split(line.rstrip(), ', ') item["name"] = cols.pop(0) item["upper"] = item["name"].replace('-', '_').upper() column_list = [] for col in self.columns: if len(cols) > 0: value = cols.pop(0) if col["indexed"]: if not "max" in col: col["max"] = value if value > col["max"]: col["max"] = value if not "min" in col: col["min"] = value if value < col["min"]: col["min"] = value column_list.append({"name":col["name"], "value":value}) else: column_list.append({"name":col["name"], "value":col["value"]}) item["cols"] = column_list item["index"] = index index = index + 1 self.data.append(item) def __getitem__(self, key): """Make this object look like a dict for Templates to use""" attr = getattr(self, key) if callable(attr): return attr() else: return attr def __repr__(self): column = 0 out = "" out += "type: %s_t" % self.name out += " (prefix '%s_ENUM')\n" % self.prefix for col in self.columns: out += " extra column: %s %s (default %s)\n" % (col["type"], col["name"], col["value"]) out += " " for item in self.data: column = column + 1 if column > 4 and column % 4 == 1: out += "\n " out += "%-19s" % item["name"] # for col in item["cols"]: # out += "\t%s(%s)\n" % (col["name"], col["value"]) return out def comment(self): if len(self.comments): return '/*' + '\n *'.join(self.comments) + ' */\n' else: return '' def names(self): return ',\n\t'.join('"%s"' % x["name"] for x in self.data) def enums(self): return ',\n\t'.join('%s_%s' % (self.prefix, x["upper"]) for x in self.data) def flag_enums(self): if not self.flags: return "" enum_lines = [] enum_lines.append('typedef enum {') prefix = self.prefix + 'F' for x in self.data: enum_lines.append('\t%s_%s = (1 << %s_%s),' % (prefix, x["upper"], self.prefix, x["upper"])) enum_lines.append('} pseudo_%s_f;' % self.name) return '\n'.join(enum_lines) def column_names(self): decl_lines = [] column = 0 for col in self.columns: decl_lines.append("static %s %s_id_to_%s[] = {" % (col["type"], self.name, col["name"])) decl_lines.append('\t%s,' % col["value"]) for item in self.data: decl_lines.append('\t%s,' % item["cols"][column]["value"]) decl_lines.append('\t0') decl_lines.append("};") if col["indexed"]: decl_lines.append("static int %s_%s_to_id[] = {" % (self.name, col["name"])) for item in self.data: decl_lines.append('\t[%s] = %d,' % (item["cols"][column]["value"], item["index"])) decl_lines.append("};") column = column + 1 return '\n'.join(decl_lines) def column_funcs(self): decl_lines = [] for col in self.columns: decl_lines.append('extern %s' % col["type"]) decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' % (self.name, col["name"], self.name)) decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix)) decl_lines.append('\t\treturn %s;' % col["value"]) decl_lines.append('\treturn %s_id_to_%s[id];' % (self.name, col["name"])) decl_lines.append('}') if col["indexed"]: table_name = '%s_%s_to_id' % (self.name, col["name"]) decl_lines.append('extern int') decl_lines.append('pseudo_%s_%s_id(%s val) {' % (self.name, col["name"], col["type"])) decl_lines.append('\tif ((val < %s) || (val > %s)) {' % (col["min"], col["max"])) decl_lines.append('\t\treturn -1;') decl_lines.append('\t}') decl_lines.append('\tif (%s[val] != 0) {' % table_name) decl_lines.append('\t\treturn %s[val];' % table_name) decl_lines.append('\t}') decl_lines.append('\treturn -1;') decl_lines.append('}') return '\n'.join(decl_lines) def column_protos(self): decl_lines = [] for col in self.columns: decl_lines.append('extern %s pseudo_%s_%s(pseudo_%s_t id);' % (col["type"], self.name, col["name"], self.name)) if col["indexed"]: decl_lines.append('extern int pseudo_%s_%s_id(%s val);' % (self.name, col["name"], col["type"])) return '\n'.join(decl_lines) def main(): """Read in function defintions, write out files based on templates.""" datatypes = [] templates = [] # error checking helpfully provided by the exception handler copyright_file = open('guts/COPYRIGHT') TemplateFile.copyright = copyright_file.read() copyright_file.close() for path in glob.glob('table_templates/*'): try: template_file = TemplateFile(path) template_file.emit('copyright') template_file.emit('header') templates.append(template_file) except IOError: print "Invalid or malformed template %s. Aborting." % path exit(1) for filename in sys.argv[1:]: # read in the datatype sys.stdout.write("%s: " % filename) datatype = DataType(filename) datatypes.append(datatype) print datatype.__repr__() print "" print "Writing datatypes...", for datatype in datatypes: # populate various tables and files with each datatype for template_file in templates: template_file.emit('body', datatype) print "done. Cleaning up." for template_file in templates: # clean up files template_file.emit('footer') template_file.close() if __name__ == '__main__': main() pseudo-1.8.1+git20161012/makewrappers000077500000000000000000000525751300506512600170570ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (c) 2008-2011,2013 Wind River Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the Lesser GNU General Public License version 2.1 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the Lesser GNU General Public License for more details. # # You should have received a copy of the Lesser GNU General Public License # version 2.1 along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """convert wrapfuncs.in to wrapper function stubs and tables""" import datetime import glob import sys import re import os.path import string import subprocess from templatefile import TemplateFile class ArgumentList: """A (possibly empty) list of arguments""" def __init__(self, text): "parse a comma-separated argument list (including function prototypes)" self.args = [] self.variadic = False self.variadic_decl = "" self.variadic_start = "" self.variadic_end = "" # (void) is an empty list, not a list of a single argument which is void if text == "void": return depth = 0 accum = '' comma_sep = text.split(', ') # now, what if there was a comma embedded in an argument? for arg in comma_sep: lcount = arg.count('(') rcount = arg.count(')') depth = depth + lcount - rcount if (depth > 0): accum += arg + ', ' else: self.args.append(Argument(accum + arg)) accum = '' if depth != 0: raise Exception("mismatched ()s while parsing '%s'" % text) if self.args[-1].vararg: self.variadic = True self.variadic_arg = self.args[-1] self.last_fixed_arg = self.args[-2].name self.variadic_decl = "va_list ap;\n" self.variadic_start = "va_start(ap, %s);\n" % self.last_fixed_arg if self.variadic_arg.vararg_wraps: self.variadic_decl += "\t%s;\n" % \ self.variadic_arg.vararg_wraps.decl() self.variadic_start += ("\t%s = va_arg(ap, %s);" "\n\tva_end(ap);\n") % \ (self.variadic_arg.name, self.variadic_arg.type) else: # lie blatantly; we don't handle this case self.variadic = False # for a wrap function, the outer foo() wrapper will convert to a va_list, # but the inner wrap_foo() just passes the va_list through. def maybe_variadic_start(self): """Use va_arg() to grab an optional argument if needed.""" if self.variadic and self.variadic_arg.vararg_wraps: return self.variadic_start else: return "" def maybe_variadic_decl(self): """Declare va_list ap and optional argument, if needed.""" if self.variadic and self.variadic_arg.vararg_wraps: return self.variadic_decl else: return "" def decl(self, comment=False, wrap=False): """Produce the declaration form of this argument list.""" if not self.args: return "void" return ', '.join(x.decl(comment=comment, wrap=wrap) for x in self.args) def call(self): """Produce the calling form of this argument list.""" if not self.args: return "" return ', '.join([x.call() for x in self.args]) def __repr__(self): if not self.args: return "no arguments" else: return '::'.join([x.decl() for x in self.args]) class Argument: """A function argument such as 'char *path' or 'char (*foo)(void)'""" def __init__(self, text): """get the type and name of a trivial C declaration""" self.vararg = False self.function_pointer = False self.spacer = '' if text == 'void': raise Exception("Tried to declare a nameless object of type void.") if text.startswith('...'): self.vararg = True if len(text) > 3: # we're a wrapper for something else, declared as # ...{real_decl}, as in the third argument to open(2) text = text[4:-1] # stash a copy of these values without the vararg flag, so # we can declare them prettily later self.vararg_wraps = Argument(text) else: # nothing to do. self.vararg_wraps = None self.type, self.name = None, None return else: self.vararg = False # try for a function pointer match = re.match('(.*)\(\*([a-zA-Z0-9$_]*)\)\((.*)\)', text) if match: self.function_pointer = True self.args = match.group(3) self.type = match.group(1) self.name = match.group(2).rstrip() else: # plain declaration match = re.match('(.*[ *])\(?\*?([a-zA-Z0-9$_]*)\)?', text) # there may not be a match, say in the special case # where an arg is '...' if match: self.type, self.name = match.group(1).rstrip(), match.group(2) else: self.type, self.name = None, None # spacing between type and name, needed if type ends with a character # which could be part of an identifier if re.match('[_a-zA-Z0-9]', self.type[-1]): self.spacer = ' ' def decl(self, comment=False, wrap=False): """Produce the declaration form of this argument.""" if self.function_pointer: decl = "%s%s(*%s)(%s)" % \ (self.type, self.spacer, self.name, self.args) else: decl = "%s%s%s" % (self.type, self.spacer, self.name) if self.vararg: if self.vararg_wraps: if comment: decl = "... { %s }" % decl else: decl = "... /* %s */" % decl else: if wrap: decl = "va_list ap" else: decl = "..." return decl def call(self): """Produce the call form of this argument (usually its name).""" if self.type == 'void': return '' if self.vararg and not self.vararg_wraps: return "ap" return self.name def __str__(self): return self.decl() def __repr__(self): return self.decl() typedata = { 'char *': { 'format': '%s', 'value': 'rc ? rc : ""' }, 'const char *': { 'format': '%s', 'value': 'rc ? rc : ""' }, 'DIR *': { 'format': '%p', 'value': '(void *) rc' }, 'FILE *': { 'format': '%p', 'value': '(void *) rc' }, 'FTS *': { 'format': '%p', 'value': '(void *) rc' }, 'gid_t': { 'format': '%ld', 'value': ' (long) rc' }, 'int': { 'format': '%d', 'value': 'rc' }, 'long': { 'format': '%ld', 'value': 'rc' }, 'mode_t': { 'format': '0%lo', 'value': '(long) rc' }, 'off_t': { 'format': '%lld', 'value': '(long long) rc' }, 'size_t': { 'format': '%lu', 'value': '(unsigned long) rc' }, 'ssize_t': { 'format': '%ld', 'value': '(long) rc' }, 'struct group *': { 'format': '%p', 'value': '(void *) rc' }, 'struct passwd *': { 'format': '%p', 'value': '(void *) rc' }, 'uid_t': { 'format': '%ld', 'value': ' (long) rc' }, 'void *': { 'format': '%p', 'value': 'rc' }, 'void': { 'format': 'void%s', 'value': '""' }, } class Function: """A function signature and additional data about how the function works""" def __init__(self, port, line): # table of known default values: default_values = { 'gid_t': '0', 'uid_t': '0', 'int': '-1', 'long': '-1', 'mode_t': '0', 'ssize_t': '-1' } self.dirfd = 'AT_FDCWD' self.flags = '0' self.port = port self.directory = '' self.version = 'NULL' # On Darwin, some functions are SECRETLY converted to foo$INODE64 # when called. So we have to look those up for real_* self.inode64 = None self.real_func = None self.paths_to_munge = [] self.specific_dirfds = {} self.hand_wrapped = None self.async_skip = None # used for the copyright date when creating stub functions self.date = datetime.date.today().year function, comments = line.split(';') comment = re.search('/\* *(.*) *\*/', comments) if comment: self.comments = comment.group(1) else: self.comments = None bits = re.match('([^(]*)\((.*)\)', function) type_and_name = Argument(bits.group(1)) self.type, self.name = type_and_name.type, type_and_name.name # convenient to have this declared here so we can use its .decl later if self.type != 'void': self.return_code = Argument("%s rc" % self.type) # Some args get special treatment: # * If the arg has a name ending in 'path', we will canonicalize it. # * If the arg is named 'dirfd' or 'flags', it becomes the default # values for the dirfd and flags arguments when canonicalizing. # * If the name ends in dirfd, we do the same fancy stuff. # * Note that the "comments" field (/* ... */ after the decl) can # override the dirfd/flags values. self.args = ArgumentList(bits.group(2)) for arg in self.args.args: # ignore varargs, they never get these special treatments if arg.vararg: pass elif arg.name.endswith('dirfd'): if len(arg.name) > 5: self.specific_dirfds[arg.name[:-5]] = True self.dirfd = 'dirfd' elif arg.name == 'flags': self.flags = 'flags' elif arg.name.endswith('path'): self.paths_to_munge.append(arg.name) # pick default values if self.type == 'void': self.default_value = '' elif self.type[-1:] == '*': self.default_value = 'NULL' else: try: self.default_value = default_values[self.type] except KeyError: raise KeyError("Function %s has return type %s," "for which there is no default value." % (self.name, self.type)) # handle special comments, such as flags=AT_SYMLINK_NOFOLLOW if self.comments: modifiers = self.comments.split(', ') for mod in modifiers: key, value = mod.split('=') value = value.rstrip() setattr(self, key, value) def maybe_inode64(self): if self.inode64 and os.uname()[0] == 'Darwin': return "$INODE64" else: return "" def end_maybe_skip(self): if self.hand_wrapped: return """/* Hand-written wrapper for this function. */ #endif """ else: return "" def maybe_skip(self): if self.hand_wrapped: return """/* Hand-written wrapper for this function. */ #if 0 """ else: return "" def maybe_async_skip(self): if self.async_skip: return """/* This function is not called if pseudo is configured --enable-force-async */ #ifdef PSEUDO_FORCE_ASYNC if (!pseudo_allow_fsync) { PROFILE_DONE; return %s; } #endif """ % self.async_skip else: return "" def comment(self): """declare self (in a comment)""" return self.decl(comment = True) def decl(self, comment=False, wrap=True): """declare self""" if self.type[-1:] == '*': spacer = '' else: spacer = ' ' return "%s%s%s(%s)" % \ (self.type, spacer, self.name, self.args.decl(comment, wrap)) def decl_args(self): """declare argument list""" return self.args.decl() def wrap_args(self): """declare argument list for wrap_foo() variant""" return self.args.decl(wrap = True) def call_args(self): """present argument list for a function call""" return self.args.call() def fix_paths(self): """create/allocate canonical paths""" fix_paths = [] for path in self.paths_to_munge: prefix = path[:-4] if prefix not in self.specific_dirfds: prefix = '' fix_paths.append( "%s = pseudo_root_path(__func__, __LINE__, %s%s, %s, %s);" % (path, prefix, self.dirfd, path, self.flags)) return "\n\t\t".join(fix_paths) def real_predecl(self): if self.real_func: return self.decl().replace(self.name, self.real_func, 1) + ";" else: return "" def real_init(self): if self.real_func: return self.real_func else: return "NULL" def rc_return(self): """return rc (or just return)""" if self.type == 'void': return "return;" else: return "return rc;" def rc_format(self): """the format string to use for the return value""" return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['format'] def rc_value(self): """the value to pass for the format string for the return value""" return typedata.get(self.type, { 'format': '[%s]', 'value': '"' + self.type + '"' })['value'] def rc_decl(self): """declare rc (if needed)""" if self.type == 'void': return "" else: return "%s = %s;" % (self.return_code.decl(), self.default_value) def rc_assign(self): """assign something to rc (or discard it)""" if self.type == 'void': return "(void)" else: return "rc =" def def_return(self): """return default value (or just return)""" if self.type == 'void': return "return;" else: return "return %s;" % self.default_value def __getitem__(self, key): """Make this object look like a dict for Templates to use""" try: attr = getattr(self, key) except AttributeError: # There's a few attributes that are handled inside the args # object, so check there too... attr = getattr(self.args, key) if callable(attr): return attr() else: return attr def __repr__(self): pretty = "%(name)s returns %(type)s and takes " % self pretty += repr(self.args) if self.comments: pretty += ' (%s)' % self.comments return pretty def funcdeps(self): return 'pseudo_wrappers.o: ports/%s/guts/%s.c' % ( self.port, self.name ) class Port: """ A Port is a set of function declarations and code providing details specific to a specific host environment, such as Linux. Ports can override each other, and each port can indicate additional ports to include. """ def __init__(self, port, sources): self.name = port self.subports = [] self.preports = [] print port if os.path.exists(self.portfile("pseudo_wrappers.c")): self.wrappers = self.portfile("pseudo_wrappers.c") else: self.wrappers = None if os.path.exists(self.portfile("portdefs.h")): self.portdef_file = self.portfile("portdefs.h") else: self.portdef_file = None if os.path.exists(self.portfile("wrapfuncs.in")): self.funcs = process_wrapfuncs(port) else: self.funcs = {} for source in sources: source.emit('port', self) if os.path.exists(self.portfile("preports")): subport_proc = subprocess.Popen([self.portfile("preports"), self.name], stdout=subprocess.PIPE) portlist = subport_proc.communicate()[0] retcode = subport_proc.poll() if retcode: raise Exception("preports script failed for port %s" % self.name) for preport in string.split(portlist): next = Port(preport, sources) self.preports.append(next) if os.path.exists(self.portfile("subports")): subport_proc = subprocess.Popen([self.portfile("subports"), self.name], stdout=subprocess.PIPE) portlist = subport_proc.communicate()[0] retcode = subport_proc.poll() if retcode: raise Exception("subports script failed for port %s" % self.name) for subport in string.split(portlist): next = Port(subport, sources) self.subports.append(next) def functions(self): mergedfuncs = {} for pre in self.preports: prefuncs = pre.functions() for name in prefuncs.keys(): if name in mergedfuncs: print "Warning: %s from %s overriding %s" % (name, pre.name, mergedfuncs[name].port) mergedfuncs[name] = prefuncs[name] for name in self.funcs.keys(): if name in mergedfuncs: print "Warning: %s from %s overriding %s" % (name, self.name, mergedfuncs[name].port) mergedfuncs[name] = self.funcs[name] for sub in self.subports: subfuncs = sub.functions() for name in subfuncs.keys(): if name in mergedfuncs: print "Warning: %s from %s overriding %s" % (name, sub.name, mergedfuncs[name].port) mergedfuncs[name] = subfuncs[name] return mergedfuncs def define(self): return '#define PSEUDO_PORT_%s 1' % string.upper(self.name).replace('/', '_') def portdeps(self): deps = [] if self.wrappers: deps.append(self.wrappers) if self.portdef_file: deps.append(self.portdef_file) if deps: return 'pseudo_wrappers.o: %s' % ' '.join(deps) else: return '# no extra dependencies for %s.' % self.name def portdefs(self): if self.portdef_file: return '#include "%s"' % self.portdef_file else: return '/* no portdefs for %s */' % self.name def include(self): if self.wrappers: return '#include "%s"' % self.wrappers else: return '/* no #include for %s */' % self.name def portfile(self, name): return "ports/%s/%s" % (self.name, name) def __getitem__(self, key): """Make this object look like a dict for Templates to use""" try: attr = getattr(self, key) except AttributeError: return None if callable(attr): return attr() else: return attr def process_wrapfuncs(port): """Process a wrapfuncs.in file, generating a list of prototypes.""" filename = "ports/%s/wrapfuncs.in" % port funcs = {} directory = os.path.dirname(filename) sys.stdout.write("%s: " % filename) funclist = open(filename) for line in funclist: line = line.rstrip() if line.startswith('#') or not line: continue try: func = Function(port, line) func.directory = directory funcs[func.name] = func sys.stdout.write(".") except Exception, e: print "Parsing failed:", e exit(1) funclist.close() print "" return funcs def main(argv): """Read in function definitions, write out files based on templates.""" funcs = [] sources = [] for arg in argv: name, value = arg.split('=') os.environ["port_" + name] = value # error checking helpfully provided by the exception handler copyright_file = open('guts/COPYRIGHT') TemplateFile.copyright = copyright_file.read() copyright_file.close() for path in glob.glob('templates/*'): try: print "Considering template: " + path source = TemplateFile(path) if source.name.endswith('.c') or source.name.endswith('.h'): source.emit('copyright') source.emit('header') sources.append(source) except IOError: print "Invalid or malformed template %s. Aborting." % path exit(1) try: port = Port('common', sources) except KeyError: print "Unknown uname -s result: '%s'." % uname_s print "Known system types are:" print "%-20s %-10s %s" % ("uname -s", "port name", "description") for key in host_ports: print "%-20s %-10s %s" % (key, host_ports[key], host_descrs[host_ports[key]]) # the per-function stuff print "Writing functions...", all_funcs = port.functions() for name in sorted(all_funcs.keys()): # populate various tables and files with each function for source in sources: source.emit('body', all_funcs[name]) print "done. Cleaning up." for source in sources: # clean up files source.emit('footer') source.close() if __name__ == '__main__': main(sys.argv[1:]) pseudo-1.8.1+git20161012/offsets.c000066400000000000000000000040311300506512600162250ustar00rootroot00000000000000/* * offsets.c, print offsets in pseudo_ipc structure * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #include #include #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" int main(void) { printf("type: %d\n", (int) offsetof(pseudo_msg_t, type)); printf("op: %d\n", (int) offsetof(pseudo_msg_t, op)); printf("result: %d\n", (int) offsetof(pseudo_msg_t, result)); printf("access: %d\n", (int) offsetof(pseudo_msg_t, access)); printf("client: %d\n", (int) offsetof(pseudo_msg_t, client)); printf("dev: %d\n", (int) offsetof(pseudo_msg_t, dev)); printf("ino: %d\n", (int) offsetof(pseudo_msg_t, ino)); printf("uid: %d\n", (int) offsetof(pseudo_msg_t, uid)); printf("gid: %d\n", (int) offsetof(pseudo_msg_t, gid)); printf("mode: %d\n", (int) offsetof(pseudo_msg_t, mode)); printf("rdev: %d\n", (int) offsetof(pseudo_msg_t, rdev)); printf("pathlen: %d\n", (int) offsetof(pseudo_msg_t, pathlen)); printf("nlink: %d\n", (int) offsetof(pseudo_msg_t, nlink)); printf("deleting: %d\n", (int) offsetof(pseudo_msg_t, deleting)); printf("path: %d\n", (int) offsetof(pseudo_msg_t, path)); printf("size: %d\n", (int) sizeof(pseudo_msg_t)); return 0; } pseudo-1.8.1+git20161012/perftest000077500000000000000000000023271300506512600162000ustar00rootroot00000000000000#!/bin/sh # do a quick performance test of pseudo opt_f=false flag_f= while getopts "f" o do case $o in f) opt_f=true flag_f=-f ;; \?) die "Usage: perftest [-f] [directory]";; esac; done shift `expr $OPTIND - 1` die() { printf "%s\n" "$*" >&2 exit 1 } doit() ( cd $dir printf "%s\n" "Making test data..." time ./makedata printf "%s\n" "Timing tar command." time sh -c 'tar cf - dir_[0-9] | tar -C new -xf -' printf "%s\n" "Timing find command." time find new -perm 0100 -exec true {} + printf "%s\n" "Timing rm." time rm -rf dir_[0-9] new ) [ -x bin/pseudo ] || die "You need a bin/pseudo to test." case $# in 0) dir="perftest.d";; 1) [ -d "$1" ] || die "Specify an existing directory to test in. '%s' is not a directory." "$1" dir="$1/perftest.d" ;; *) die "Usage: perftest [directory]" ;; esac if $opt_f || [ `id -u` = x0 ]; then printf "Running test in %s.\n" "$dir" doit printf "Done.\n" else [ -d $dir ] && die "Directory '$dir' already exists, delete it if you're done." mkdir $dir mkdir -p $dir/new cc -o $dir/makedata makedata.c printf "%s\n" "Running performance test (total time at end)" time bin/pseudo ./perftest -f ${dir%perftest.d} rm -rf $dir fi pseudo-1.8.1+git20161012/ports/000077500000000000000000000000001300506512600155615ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/common/000077500000000000000000000000001300506512600170515ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/common/guts/000077500000000000000000000000001300506512600200335ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/common/guts/execl.c000066400000000000000000000005301300506512600212750ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int execl(const char *file, const char *arg, va_list ap) * int rc = -1; */ /* NOTE THAT THIS IS NEVER USED! * We implement all execl() in terms of execv() * so this call is not used. */ rc = real_execl(file, arg, ap); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/guts/execle.c000066400000000000000000000005321300506512600214440ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int execle(const char *file, const char *arg, va_list ap) * int rc = -1; */ /* NOTE THAT THIS IS NEVER USED! * We implement all execl() in terms of execv() * so this call is not used. */ rc = real_execle(file, arg, ap); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/guts/execlp.c000066400000000000000000000005321300506512600214570ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int execlp(const char *file, const char *arg, va_list ap) * int rc = -1; */ /* NOTE THAT THIS IS NEVER USED! * We implement all execl() in terms of execv() * so this call is not used. */ rc = real_execlp(file, arg, ap); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/guts/execv.c000066400000000000000000000016631300506512600213170ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_execv(const char *file, char *const *argv) { * int rc = -1; */ /* note: we don't canonicalize this, because we are intentionally * NOT redirecting execs into the chroot environment. If you try * to execute /bin/sh, you get the actual /bin/sh, not * /bin/sh. This allows use of basic utilities. This * design will likely be revisited. */ if (antimagic == 0) { const char *path_guess = pseudo_exec_path(file, 0); pseudo_client_op(OP_EXEC, PSA_EXEC, -1, -1, path_guess, 0); } pseudo_setupenv(); if (pseudo_has_unload(NULL)) { /* and here we attach */ pseudo_dropenv(); } /* if exec() fails, we may end up taking signals unexpectedly... * not much we can do about that. */ sigprocmask(SIG_SETMASK, &pseudo_saved_sigmask, NULL); rc = real_execv(file, argv); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/guts/execve.c000066400000000000000000000020311300506512600214520ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_execve(const char *file, char *const *argv, char *const *envp) { * int rc = -1; */ char * const *new_environ; /* note: we don't canonicalize this, because we are intentionally * NOT redirecting execs into the chroot environment. If you try * to execute /bin/sh, you get the actual /bin/sh, not * /bin/sh. This allows use of basic utilities. This * design will likely be revisited. */ if (antimagic == 0) { const char *path_guess = pseudo_exec_path(file, 0); pseudo_client_op(OP_EXEC, PSA_EXEC, -1, -1, path_guess, 0); } new_environ = pseudo_setupenvp(envp); if (pseudo_has_unload(new_environ)) new_environ = pseudo_dropenvp(new_environ); /* if exec() fails, we may end up taking signals unexpectedly... * not much we can do about that. */ sigprocmask(SIG_SETMASK, &pseudo_saved_sigmask, NULL); rc = real_execve(file, argv, new_environ); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/guts/execvp.c000066400000000000000000000016521300506512600214750ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_execvp(const char *file, char *const *argv) { * int rc = -1; */ /* note: we don't canonicalize this, because we are intentionally * NOT redirecting execs into the chroot environment. If you try * to execute /bin/sh, you get the actual /bin/sh, not * /bin/sh. This allows use of basic utilities. This * design will likely be revisited. */ if (antimagic == 0) { const char *path_guess = pseudo_exec_path(file, 1); pseudo_client_op(OP_EXEC, PSA_EXEC, -1, -1, path_guess, 0); } pseudo_setupenv(); if (pseudo_has_unload(NULL)) pseudo_dropenv(); /* if exec() fails, we may end up taking signals unexpectedly... * not much we can do about that. */ sigprocmask(SIG_SETMASK, &pseudo_saved_sigmask, NULL); rc = real_execvp(file, argv); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/guts/fork.c000066400000000000000000000006421300506512600211420ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fork(void) { * int rc = -1; */ rc = real_fork(); /* special case: we may want to enable or disable * pseudo in the child process */ if (rc == 0) { pseudo_setupenv(); if (!pseudo_has_unload(NULL)) { pseudo_reinit_libpseudo(); } else { pseudo_dropenv(); } } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/common/pseudo_wrappers.c000066400000000000000000000167361300506512600224540ustar00rootroot00000000000000/* these aren't used, but the wrapper table isn't happy unless they * exist */ static int wrap_execl(const char *file, const char *arg, va_list ap) { (void) file; (void) arg; (void) ap; return 0; } static int wrap_execle(const char *file, const char *arg, va_list ap) { (void) file; (void) arg; (void) ap; return 0; } static int wrap_execlp(const char *file, const char *arg, va_list ap) { (void) file; (void) arg; (void) ap; return 0; } static char ** execl_to_v(va_list ap, const char *argv0, char *const **envp) { size_t i = 0; size_t alloc_size = 256; char **argv = malloc((sizeof *argv) * alloc_size); if (!argv) { pseudo_debug(PDBGF_CLIENT, "execl failed: couldn't allocate memory for %lu arguments\n", (unsigned long) alloc_size); return NULL; } argv[i++] = (char *) argv0; while (argv[i-1]) { argv[i++] = va_arg(ap, char *const); if (i > alloc_size - 1) { alloc_size = alloc_size + 256; argv = realloc(argv, (sizeof *argv) * alloc_size); if (!argv) { pseudo_debug(PDBGF_CLIENT, "execl failed: couldn't allocate memory for %lu arguments\n", (unsigned long) alloc_size); return NULL; } } } if (envp) { *envp = va_arg(ap, char **); } return argv; } /* The following wrappers require Special Handling */ int execl(const char *file, const char *arg, ...) { sigset_t saved; va_list ap; char **argv; int rc = -1; PROFILE_START; if (!pseudo_check_wrappers()) { /* rc was initialized to the "failure" value */ pseudo_enosys("execl"); PROFILE_DONE; return rc; } va_start(ap, arg); argv = execl_to_v(ap, arg, 0); va_end(ap); if (!argv) { errno = ENOMEM; PROFILE_DONE; return -1; } pseudo_debug(PDBGF_WRAPPER, "called: execl\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); PROFILE_DONE; return -1; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_execv(file, argv); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: execl\n"); errno = save_errno; free(argv); PROFILE_DONE; return rc; } int execlp(const char *file, const char *arg, ...) { sigset_t saved; va_list ap; char **argv; int rc = -1; PROFILE_START; if (!pseudo_check_wrappers()) { /* rc was initialized to the "failure" value */ pseudo_enosys("execlp"); PROFILE_DONE; return rc; } va_start(ap, arg); argv = execl_to_v(ap, arg, 0); va_end(ap); if (!argv) { errno = ENOMEM; PROFILE_DONE; return -1; } pseudo_debug(PDBGF_WRAPPER, "called: execlp\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); PROFILE_DONE; return -1; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_execvp(file, argv); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: execlp\n"); errno = save_errno; free(argv); PROFILE_DONE; return rc; } int execle(const char *file, const char *arg, ...) { sigset_t saved; va_list ap; char **argv; char **envp; int rc = -1; PROFILE_START; if (!pseudo_check_wrappers()) { /* rc was initialized to the "failure" value */ pseudo_enosys("execle"); PROFILE_DONE; return rc; } va_start(ap, arg); argv = execl_to_v(ap, arg, (char *const **)&envp); va_end(ap); if (!argv) { errno = ENOMEM; PROFILE_DONE; return -1; } pseudo_debug(PDBGF_WRAPPER, "called: execle\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); PROFILE_DONE; return -1; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_execve(file, argv, envp); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: execle\n"); errno = save_errno; free(argv); PROFILE_DONE; return rc; } int execv(const char *file, char *const *argv) { sigset_t saved; int rc = -1; PROFILE_START; if (!pseudo_check_wrappers() || !real_execv) { /* rc was initialized to the "failure" value */ pseudo_enosys("execv"); PROFILE_DONE; return rc; } pseudo_debug(PDBGF_WRAPPER, "called: execv\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); PROFILE_DONE; return -1; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_execv(file, argv); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: execv\n"); errno = save_errno; PROFILE_DONE; return rc; } int execve(const char *file, char *const *argv, char *const *envp) { sigset_t saved; int rc = -1; PROFILE_START; if (!pseudo_check_wrappers() || !real_execve) { /* rc was initialized to the "failure" value */ pseudo_enosys("execve"); PROFILE_DONE; return rc; } pseudo_debug(PDBGF_WRAPPER, "called: execve\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); PROFILE_DONE; return -1; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_execve(file, argv, envp); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: execve\n"); errno = save_errno; PROFILE_DONE; return rc; } int execvp(const char *file, char *const *argv) { sigset_t saved; int rc = -1; PROFILE_START; if (!pseudo_check_wrappers() || !real_execvp) { /* rc was initialized to the "failure" value */ pseudo_enosys("execvp"); PROFILE_DONE; return rc; } pseudo_debug(PDBGF_WRAPPER, "called: execvp\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); PROFILE_DONE; return -1; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_execvp(file, argv); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: execvp\n"); errno = save_errno; PROFILE_DONE; return rc; } /* no profiling in fork because it wouldn't work anyway * half the time */ int fork(void) { sigset_t saved; int rc = -1; if (!pseudo_check_wrappers() || !real_fork) { /* rc was initialized to the "failure" value */ pseudo_enosys("fork"); return rc; } pseudo_debug(PDBGF_WRAPPER, "called: fork\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); return -1; } int save_errno; rc = wrap_fork(); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: fork\n"); errno = save_errno; return rc; } int vfork(void) { /* we don't provide support for the distinct semantics * of vfork() */ return fork(); } static int wrap_execv(const char *file, char *const *argv) { int rc = -1; #include "guts/execv.c" return rc; } static int wrap_execve(const char *file, char *const *argv, char *const *envp) { int rc = -1; #include "guts/execve.c" return rc; } static int wrap_execvp(const char *file, char *const *argv) { int rc = -1; #include "guts/execvp.c" return rc; } static int wrap_fork(void) { int rc = -1; #include "guts/fork.c" return rc; } pseudo-1.8.1+git20161012/ports/common/subports000077500000000000000000000002401300506512600206540ustar00rootroot00000000000000#!/bin/sh case $(uname -s) in Linux) echo "linux";; Darwin) echo "darwin";; *) echo >&2 "Unknown result from uname -s: %(uname -s). Aborting." exit 1 ;; esac pseudo-1.8.1+git20161012/ports/common/wrapfuncs.in000066400000000000000000000007431300506512600214150ustar00rootroot00000000000000int execlp(const char *file, const char *arg, ...); /* hand_wrapped=1 */ int execl(const char *file, const char *arg, ...); /* hand_wrapped=1 */ int execle(const char *file, const char *arg, ...); /* hand_wrapped=1 */ int execv(const char *file, char *const *argv); /* hand_wrapped=1 */ int execve(const char *file, char *const *argv, char *const *envp); /* hand_wrapped=1 */ int execvp(const char *file, char *const *argv); /* hand_wrapped=1 */ int fork(void); /* hand_wrapped=1 */ pseudo-1.8.1+git20161012/ports/darwin/000077500000000000000000000000001300506512600170455ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/darwin/guts/000077500000000000000000000000001300506512600200275ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/darwin/guts/COPYRIGHT000066400000000000000000000013521300506512600213230ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ pseudo-1.8.1+git20161012/ports/darwin/guts/fcntl.c000066400000000000000000000016321300506512600213030ustar00rootroot00000000000000/* * Copyright (c) 2011, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * int fcntl(int fd, int cmd, ... { struct flock *lock }) * int rc = -1; */ int save_errno; long long flag = 0; va_start(ap, cmd); flag = va_arg(ap, long long); va_end(ap); rc = real_fcntl(fd, cmd, flag); switch (cmd) { case F_DUPFD: #ifdef F_DUPFD_CLOEXEC /* it doesn't exist now, but if I take this out they'll add it * just to mess with me. */ case F_DUPFD_CLOEXEC: #endif /* actually do something */ save_errno = errno; if (rc != -1) { pseudo_debug(PDBGF_OP, "fcntl_dup: %d->%d\n", fd, rc); pseudo_client_op(OP_DUP, 0, fd, rc, 0, 0); } errno = save_errno; break; default: /* nothing to do, we hope */ break; } save_errno = errno; pseudo_debug(PDBGF_OP, "fcntl(fd %d, cmd %d, %llx) => %d (%s)\n", fd, cmd, flag, rc, strerror(errno)); errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/fgetgrent_r.c000066400000000000000000000004321300506512600225000ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int fgetgrent_r(FILE *fp, struct group*gbuf, char *buf, size_t buflen, struct group **gbufp) * int rc = -1; */ rc = real_fgetgrent_r(fp, gbuf, buf, buflen, gbufp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/fgetpwent_r.c000066400000000000000000000004351300506512600225210ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int fgetpwent_r(FILE *fp, struct passwd *pbuf, char *buf, size_t buflen, struct passwd **pbufp) * int rc = -1; */ rc = real_fgetpwent_r(fp, pbuf, buf, buflen, pbufp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/fgetxattr.c000066400000000000000000000005031300506512600222010ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size, u_int32_t position, int options) * ssize_t rc = -1; */ rc = shared_getxattr(NULL, filedes, name, value, size, position, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/flistxattr.c000066400000000000000000000004151300506512600223770ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t flistxattr(int filedes, char *list, size_t size, int options) * ssize_t rc = -1; */ rc = shared_listxattr(NULL, filedes, list, size, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/fremovexattr.c000066400000000000000000000003741300506512600227250ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int fremovexattr(int filedes, const char *name, int options) * int rc = -1; */ rc = shared_removexattr(NULL, filedes, name, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/fsetxattr.c000066400000000000000000000005011300506512600222130ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int fsetxattr(int filedes, const char *name, const void *value, size_t size, u_int32_t position, int options) * int rc = -1; */ rc = shared_setxattr(NULL, filedes, name, value, size, position, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/fstat.c000066400000000000000000000010071300506512600213120ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int fstat(int fd, struct stat *buf) * int rc = -1; */ pseudo_msg_t *msg; rc = real_fstat(fd, buf); if (rc == -1) { return rc; } /* query database * note that symlink canonicalizing is now automatic, so we * don't need to check for a symlink on this end */ msg = pseudo_client_op(OP_FSTAT, 0, fd, -1, 0, buf); if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(buf, msg); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/getgrent_r.c000066400000000000000000000004131300506512600223310ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) * int rc = -1; */ rc = real_getgrent_r(gbuf, buf, buflen, gbufp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/getgrouplist.c000066400000000000000000000014721300506512600227270ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgrouplist(const char *name, int basegid, int *groups, int *ngroups) { * int rc = -1; */ int found = 0; int found_group = 0; char buf[PSEUDO_PWD_MAX]; struct group grp, *gbuf = &grp; setgrent(); while ((rc = wrap_getgrent_r(gbuf, buf, PSEUDO_PWD_MAX, &gbuf)) == 0) { int i = 0; for (i = 0; gbuf->gr_mem[i]; ++i) { if (!strcmp(gbuf->gr_mem[i], name)) { if (found < *ngroups) groups[found] = gbuf->gr_gid; ++found; if ((int) gbuf->gr_gid == basegid) found_group = 1; } } } endgrent(); if (!found_group) { if (found < *ngroups) groups[found] = basegid; ++found; } if (found >= *ngroups) rc = -1; else rc = found; *ngroups = found; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/getgroups.c000066400000000000000000000006551300506512600222200ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgroups(int size, gid_t *list) { * int rc = -1; */ struct passwd *p = wrap_getpwuid(wrap_getuid()); int oldsize = size; if (p) { rc = wrap_getgrouplist(p->pw_name, wrap_getgid(), (int *) list, &size); if (oldsize == 0 || size <= oldsize) rc = size; } else { errno = ENOENT; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/getpwent_r.c000066400000000000000000000004211300506512600223460ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) * int rc = -1; */ rc = real_getpwent_r(pwbuf, buf, buflen, pwbufp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/getxattr.c000066400000000000000000000005021300506512600220320ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t getxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options) * ssize_t rc = -1; */ rc = shared_getxattr(path, -1, name, value, size, position, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/listxattr.c000066400000000000000000000004141300506512600222300ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t listxattr(const char *path, char *list, size_t size, int options) * ssize_t rc = -1; */ rc = shared_listxattr(path, -1, list, size, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/lstat.c000066400000000000000000000010251300506512600213200ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int lstat(const char *path, struct stat *buf) * int rc = -1; */ pseudo_msg_t *msg; rc = real_lstat(path, buf); if (rc == -1) { return rc; } /* query database * note that symlink canonicalizing is now automatic, so we * don't need to check for a symlink on this end */ msg = pseudo_client_op(OP_STAT, 0, -1, -1, path, buf); if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(buf, msg); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/open.c000066400000000000000000000030251300506512600211340ustar00rootroot00000000000000/* * Copyright (c) 2011-2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int open(const char *path, int flags, ... { int mode }) * int rc = -1; */ struct stat buf = { }; int existed = 1; int save_errno; /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; #ifdef PSEUDO_FORCE_ASYNCH flags &= ~O_SYNC; #endif /* if a creation has been requested, check whether file exists */ if (flags & O_CREAT) { save_errno = errno; rc = real_stat(path, &buf); existed = (rc != -1); if (!existed) pseudo_debug(PDBGF_FILE, "open_creat: %s -> 0%o\n", path, mode); errno = save_errno; } /* because we are not actually root, secretly mask in 0600 to the * underlying mode. The ", 0" is because the only time mode matters * is if a file is going to be created, in which case it's * not a directory. */ rc = real_open(path, flags, PSEUDO_FS_MODE(mode, 0)); save_errno = errno; if (rc != -1) { int stat_rc; stat_rc = real_stat(path, &buf); if (stat_rc != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); if (!existed) { real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); } pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(flags), rc, -1, path, &buf); } else { pseudo_debug(PDBGF_CONSISTENCY, "open (fd %d, path %s, flags %d) succeeded, but stat failed (%s).\n", rc, path, flags, strerror(errno)); pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(flags), rc, -1, path, 0); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/removexattr.c000066400000000000000000000003731300506512600225560ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int removexattr(const char *path, const char *name, int options) * int rc = -1; */ rc = shared_removexattr(path, -1, name, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/scandir.c000066400000000000000000000005211300506512600216140ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_scandir(const char *path, struct dirent ***namelist, int (*filter)(struct dirent *), int (*compar)(const void *, const void *)) { * int rc = -1; */ rc = real_scandir(path, namelist, filter, compar); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/setxattr.c000066400000000000000000000005001300506512600220440ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int setxattr(const char *path, const char *name, const void *value, size_t size, u_int32_t position, int options) * int rc = -1; */ rc = shared_setxattr(path, -1, name, value, size, position, options); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/stat.c000066400000000000000000000011251300506512600211450ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int stat(const char *path, struct stat *buf) * int rc = -1; */ pseudo_msg_t *msg; int save_errno; rc = real_stat(path, buf); if (rc == -1) { return rc; } save_errno = errno; /* query database * note that symlink canonicalizing is now automatic, so we * don't need to check for a symlink on this end */ msg = pseudo_client_op(OP_STAT, 0, -1, AT_FDCWD, path, buf); if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(buf, msg); } errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/guts/sync_file_range.c000066400000000000000000000004121300506512600233170ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int sync_file_range(int fd, off_t offset, off_t nbytes, unsigned int flags) * int rc = -1; */ rc = real_sync_file_range(fd, offset, nbytes, flags); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/darwin/portdefs.h000066400000000000000000000010521300506512600210420ustar00rootroot00000000000000#define PRELINK_LIBRARIES "DYLD_INSERT_LIBRARIES" #define PRELINK_PATH "DYLD_LIBRARY_PATH" #define PSEUDO_STATBUF_64 0 #define PSEUDO_STATBUF struct stat #define PSEUDO_LINKPATH_SEPARATOR ":" /* hackery to allow sneaky things to be done with getgrent() */ extern int pseudo_host_etc_passwd_fd; extern int pseudo_host_etc_group_fd; extern FILE *pseudo_host_etc_passwd_file; extern FILE *pseudo_host_etc_group_file; /* Darwin ALWAYS follows symlinks for link(2) */ #undef PSEUDO_LINK_SYMLINK_BEHAVIOR #define PSEUDO_LINK_SYMLINK_BEHAVIOR AT_SYMLINK_FOLLOW pseudo-1.8.1+git20161012/ports/darwin/preports000077500000000000000000000000451300506512600206500ustar00rootroot00000000000000#!/bin/sh echo "unix" "uids_generic" pseudo-1.8.1+git20161012/ports/darwin/pseudo_wrappers.c000066400000000000000000000342571300506512600224460ustar00rootroot00000000000000/* * pseudo_wrappers.c, darwin pseudo wrappers * * Copyright (c) 2008-2011 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* we need XATTR_NOFOLLOW in scope */ #include /* shared functionality for the xattr code */ /* Each of these functions is expecting to get an optional name, and * a populated statbuf to use for sending messages to the server. */ /* to avoid namespace pollution and such, we now duplicate the * basic functionality of a POSIX ACL list, as used by libacl or * the kernel. Documentation was obtained from the headers of libacl * and from a page or two of _The Linux Programming Interface_, by * Michael Kerrisk. */ typedef struct { uint16_t tag; uint16_t perm; uint32_t id; } acl_entry; typedef struct { uint32_t version; acl_entry entries[]; } acl_header; enum acl_tags { ACL_UNDEFINED = 0x0, ACL_USER_OBJ = 0x1, ACL_USER = 0x2, ACL_GROUP_OBJ = 0x4, ACL_GROUP = 0x8, ACL_MASK = 0x10, ACL_OTHER = 0x20, }; static const int endian_test = 1; static const char *endian_tester = (char *) &endian_test; static inline int le16(int x16) { if (*endian_tester) { return x16; } else { return ((x16 & 0xff) << 8) | ((x16 & 0xff00) >> 8); } } static inline int le32(int x32) { if (*endian_tester) { return x32; } else { return ((x32 & 0xff) << 24) | ((x32 & 0xff00) << 8) | ((x32 & 0xff0000) >> 8) | ((x32 & 0xff000000) >> 24); } } /* set mode to match the contents of header. Return non-zero on error. * On a zero return, mode is a valid posix mode, and *extra is set to * 1 if any of the entries are not reflected by that mode. On a non-zero * return, no promises are made about *extra or *mode. */ static int posix_permissions(const acl_header *header, int entries, int *extra, int *mode) { int acl_seen = 0; if (le32(header->version) != 2) { pseudo_diag("Fatal: ACL support no available for header version %d.\n", le32(header->version)); return 1; } *mode = 0; *extra = 0; for (int i = 0; i < entries; ++i) { const acl_entry *e = &header->entries[i]; int tag = le16(e->tag); int perm = le16(e->perm); acl_seen |= tag; switch (tag) { case ACL_USER_OBJ: *mode = *mode | (perm << 6); break; case ACL_GROUP_OBJ: *mode = *mode | (perm << 3); break; case ACL_OTHER: *mode = *mode | perm; break; case ACL_USER: case ACL_GROUP: case ACL_MASK: *extra = *extra + 1; break; default: pseudo_debug(PDBGF_XATTR, "Unknown tag in ACL: 0x%x.\n", tag); return 1; } } return 0; } #define RC_AND_BUF \ int rc; \ PSEUDO_STATBUF buf; \ if (path) { \ rc = base_lstat(path, &buf); \ } else { \ rc = base_fstat(fd, &buf); \ } \ if (rc == -1) { \ return rc; \ } /* Note: no profiling implementation yet. */ static ssize_t shared_getxattr(const char *path, int fd, const char *name, void *value, size_t size, u_int32_t position, int options) { RC_AND_BUF if (!strncmp(name, "com.apple.", 10)) { if (fd != -1) { return real_fgetxattr(fd, name, value, size, position, options); } else { return real_getxattr(path, name, value, size, position, options); } } pseudo_debug(PDBGF_XATTR, "getxattr(%s [fd %d], %s)\n", path ? path : "", fd, name); pseudo_msg_t *result = pseudo_client_op(OP_GET_XATTR, 0, fd, -1, path, &buf, name); if (result->result != RESULT_SUCCEED) { errno = ENOATTR; return -1; } if (value) { pseudo_debug(PDBGF_XATTR, "returned attributes: '%s' (%d bytes)\n", result->path, result->pathlen); if (size >= result->pathlen) { memcpy(value, result->path, result->pathlen); } else { memcpy(value, result->path, size); errno = ERANGE; } } return result->pathlen; } static int shared_setxattr(const char *path, int fd, const char *name, const void *value, size_t size, u_int32_t position, int options) { RC_AND_BUF pseudo_op_t op; pseudo_debug(PDBGF_XATTR, "setxattr(%s [fd %d], %s => '%.*s')\n", path ? path : "", fd, name, (int) size, (char *) value); if (!strncmp(name, "com.apple.", 10)) { if (fd != -1) { return real_fsetxattr(fd, name, value, size, position, options); } else { return real_setxattr(path, name, value, size, position, options); } } /* this may be a plain chmod */ if (!strcmp(name, "system.posix_acl_access")) { int extra; int mode; int entries = (size - sizeof(acl_header)) / sizeof(acl_entry); if (!posix_permissions(value, entries, &extra, &mode)) { pseudo_debug(PDBGF_XATTR, "posix_acl_access translated to mode %04o. Remaining attribute(s): %d.\n", mode, extra); buf.st_mode = mode; /* we want to actually issue a corresponding chmod, * as well, or else the file ends up 0600 on the * host. Using the slightly-less-efficient wrap_chmod * avoids possible misalignment. */ if (path) { wrap_chmod(path, mode); } else { wrap_fchmod(fd, mode); } /* we are sneaky, and do not actually record this using * extended attributes. */ if (!extra) { return 0; } } } if (options & XATTR_CREATE) { op = OP_CREATE_XATTR; } else if (options & XATTR_REPLACE) { op = OP_REPLACE_XATTR; } else { op = OP_SET_XATTR; } pseudo_msg_t *result = pseudo_client_op(op, 0, fd, -1, path, &buf, name, value, size); /* we automatically assume success */ if (op == OP_SET_XATTR) { return 0; } /* CREATE/REPLACE operations can report failure */ if (!result || result->result == RESULT_FAIL) { return -1; } return 0; } static ssize_t shared_listxattr(const char *path, int fd, char *list, size_t size, int options) { RC_AND_BUF char extra_name_buf[4096]; ssize_t real_attr_len; ssize_t used = 0; if (fd != -1) { real_attr_len = real_flistxattr(fd, extra_name_buf, sizeof(extra_name_buf), options); } else { real_attr_len = real_listxattr(path, extra_name_buf, sizeof(extra_name_buf), options); } pseudo_debug(PDBGF_XATTR, "listxattr: %d bytes of FS xattr names, starting '%.*s'\n", (int) real_attr_len, (int) real_attr_len, extra_name_buf); /* we don't care why there aren't any */ if (real_attr_len < 1) { real_attr_len = 0; } pseudo_msg_t *result = pseudo_client_op(OP_LIST_XATTR, 0, fd, -1, path, &buf); if (result->result != RESULT_SUCCEED && real_attr_len < 1) { pseudo_debug(PDBGF_XATTR, "listxattr: no success.\n"); errno = ENOATTR; return -1; } if (list) { pseudo_debug(PDBGF_XATTR, "listxattr: %d bytes of names, starting '%.*s'\n", (int) result->pathlen, (int) result->pathlen, result->path); if (size >= result->pathlen) { memcpy(list, result->path, result->pathlen); used = result->pathlen; } else { memcpy(list, result->path, size); used = size; errno = ERANGE; } if (real_attr_len > 0) { if ((ssize_t) size >= used + real_attr_len) { memcpy(list + used, extra_name_buf, real_attr_len); used += real_attr_len; } else { memcpy(list + used, extra_name_buf, size - used); used = size; errno = ERANGE; } } } else { used = real_attr_len + result->pathlen; } return used; } static int shared_removexattr(const char *path, int fd, const char *name, int options) { RC_AND_BUF if (!strncmp(name, "com.apple.", 10)) { if (fd != -1) { return real_fremovexattr(fd, name, options); } else { return real_removexattr(path, name, options); } } pseudo_msg_t *result = pseudo_client_op(OP_REMOVE_XATTR, 0, fd, -1, path, &buf, name); if (result->result != RESULT_SUCCEED) { /* docs say ENOATTR, but I don't have one */ errno = ENOENT; return -1; } return 0; } /* there's no fgetgrent_r or fgetpwent_r in Darwin */ #define PLENTY_LONG 2048 /* the original uid/gid code for Linux was written in terms of the * fget*ent_r() functions... which Darwin doesn't have. But wait! They're * actually pretty easy to implement. */ int pseudo_fgetgrent_r(FILE *fp, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) { char linebuf[PLENTY_LONG] = { 0 }; char *s, *t, *u; size_t max_members; char **members; size_t member = 0; long started_at = -1; gid_t gid; int error = ENOENT; size_t len; /* any early exit should set *gbufp to NULL */ if (gbufp) *gbufp = NULL; if (!gbuf || !fp || !buf) goto error_out; if (fp == pseudo_host_etc_group_file) { struct group *g; pseudo_antimagic(); g = getgrent(); pseudo_magic(); if (g) { char *s = linebuf; s += snprintf(linebuf, PLENTY_LONG, "%s:%s:%ld:", g->gr_name, g->gr_passwd, (long) g->gr_gid); if (g->gr_mem) { int i; for (i = 0; g->gr_mem[i]; ++i) { s += snprintf(s, PLENTY_LONG - (s - linebuf), "%s,", g->gr_mem[i]); } if (s[-1] == ',') --s; } strcpy(s, "\n"); } else { goto error_out; } } else { started_at = ftell(fp); if (started_at == -1) { goto error_out; } s = fgets(linebuf, PLENTY_LONG, fp); if (!s) { goto error_out; } } /* fgets will have stored a '\0' if there was no error; if there * was an error, though, linebuf was initialized to all zeroes so * the string is null-terminated anyway... */ len = strlen(linebuf); if (len > buflen) { error = ERANGE; goto error_out; } memcpy(buf, linebuf, len); /* round up to 8, hope for the best? */ len = len + 8 + (((unsigned long long) (buf + len)) % 8); members = (char **) (buf + len); if (len >= buflen) { error = ERANGE; goto error_out; } /* this is how many pointers we have room for... */ max_members = (buflen - len) / sizeof(*members); t = buf; /* yes, I can assume that Darwin has strsep() */ s = strsep(&t, ":"); if (!s) { goto error_out; } gbuf->gr_name = s; s = strsep(&t, ":"); if (!s) { goto error_out; } gbuf->gr_passwd = s; s = strsep(&t, ":"); if (!s) { goto error_out; } gid = (gid_t) strtol(s, &u, 10); /* should be a null byte, otherwise we didn't get a valid number */ if (*u) goto error_out; gbuf->gr_gid = gid; /* now, s points to a comma-separated list of members, which we * want to stash pointers to in 'members'. */ s = strsep(&t, ":"); t = s; while ((s = strsep(&t, ",")) != NULL) { if (*s) { if (member + 1 > max_members) { errno = ERANGE; goto error_out; } members[member++] = s; } } if (member + 1 > max_members) { errno = ERANGE; goto error_out; } members[member++] = NULL; *gbufp = gbuf; return 0; error_out: if (started_at != -1) fseek(fp, started_at, SEEK_SET); return error; return -1; } int pseudo_fgetpwent_r(FILE *fp, struct passwd *pbuf, char *buf, size_t buflen, struct passwd **pbufp) { char linebuf[PLENTY_LONG] = { 0 }; char *s, *t, *u; long started_at = -1; __darwin_time_t timestamp; uid_t uid; gid_t gid; int error = ENOENT; size_t len; /* any early exit should set *gbufp to NULL */ if (pbufp) *pbufp = NULL; if (!pbuf || !fp || !buf) goto error_out; if (fp == pseudo_host_etc_passwd_file) { struct passwd *p; pseudo_antimagic(); p = getpwent(); pseudo_magic(); if (p) { snprintf(linebuf, PLENTY_LONG, "%s:%s:%ld:%ld:%s:%ld:%ld:%s:%s:%s\n", p->pw_name, p->pw_passwd, (long) p->pw_uid, (long) p->pw_gid, p->pw_class, (long) p->pw_change, (long) p->pw_expire, p->pw_gecos, p->pw_dir, p->pw_shell); } else { goto error_out; } } else { started_at = ftell(fp); if (started_at == -1) { goto error_out; } s = fgets(linebuf, PLENTY_LONG, fp); if (!s) { goto error_out; } } /* fgets will have stored a '\0' if there was no error; if there * was an error, though, linebuf was initialized to all zeroes so * the string is null-terminated anyway... */ len = strlen(linebuf); if (len > buflen) { error = ERANGE; goto error_out; } if (linebuf[len - 1] == '\n') { linebuf[len - 1] = '\0'; --len; } memcpy(buf, linebuf, len); t = buf; /* yes, I can assume that Darwin has strsep() */ s = strsep(&t, ":"); if (!s) { goto error_out; } pbuf->pw_name = s; s = strsep(&t, ":"); if (!s) goto error_out; pbuf->pw_passwd = s; s = strsep(&t, ":"); if (!s) goto error_out; uid = (uid_t) strtol(s, &u, 10); /* should be a null byte, otherwise we didn't get a valid number */ if (*u) goto error_out; pbuf->pw_uid = uid; s = strsep(&t, ":"); if (!s) goto error_out; gid = (gid_t) strtol(s, &u, 10); /* should be a null byte, otherwise we didn't get a valid number */ if (*u) goto error_out; pbuf->pw_gid = gid; s = strsep(&t, ":"); if (!s) goto error_out; pbuf->pw_class = s; s = strsep(&t, ":"); if (!s) goto error_out; timestamp = (__darwin_time_t) strtol(s, &u, 10); /* should be a null byte, otherwise we didn't get a valid number */ if (*u) goto error_out; pbuf->pw_change = timestamp; timestamp = (__darwin_time_t) strtol(s, &u, 10); /* should be a null byte, otherwise we didn't get a valid number */ if (*u) goto error_out; pbuf->pw_expire = timestamp; s = strsep(&t, ":"); if (!s) goto error_out; pbuf->pw_gecos = s; s = strsep(&t, ":"); if (!s) goto error_out; pbuf->pw_dir = s; s = strsep(&t, ":"); if (!s) goto error_out; pbuf->pw_shell = s; *pbufp = pbuf; return 0; error_out: if (started_at != -1) fseek(fp, started_at, SEEK_SET); return error; return -1; } int pseudo_getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) { /* note that we don't wrap fgetpwent_r, since there's no path * references in it. */ if (!pseudo_pwd) { errno = ENOENT; return -1; } return pseudo_fgetpwent_r(pseudo_pwd, pwbuf, buf, buflen, pwbufp); } int pseudo_getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) { /* note that we don't wrap fgetgrent_r, since there's no path * references in it. */ if (!pseudo_grp) { errno = ENOENT; return -1; } return pseudo_fgetgrent_r(pseudo_grp, gbuf, buf, buflen, gbufp); } pseudo-1.8.1+git20161012/ports/darwin/subports000077500000000000000000000000541300506512600206530ustar00rootroot00000000000000#!/bin/sh # no subports at this time exit 0 pseudo-1.8.1+git20161012/ports/darwin/wrapfuncs.in000066400000000000000000000042531300506512600214110ustar00rootroot00000000000000# On Darwin, mode_t promotes to int, so you have to use int for va_arg int open(const char *path, int flags, ...{int mode}); /* flags=0 */ int stat(const char *path, struct stat *buf); /* inode64=1 */ int lstat(const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW, inode64=1 */ int fstat(int fd, struct stat *buf); /* inode64=1 */ int fcntl(int fd, int cmd, ...{struct flock *lock}); # just so we know the inums of symlinks # for emulation of passwd utilities int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); # local color UIDs int getgrouplist(const char *name, int basegid, int *groups, int *ngroups); int scandir(const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)()); int getgroups(int size, gid_t *list); int fgetgrent_r(FILE *fp, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); /* real_func=pseudo_fgetgrent_r */ int fgetpwent_r(FILE *fp, struct passwd *pbuf, char *buf, size_t buflen, struct passwd **pbufp); /* real_func=pseudo_fgetpwent_r */ int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); /* real_func=pseudo_getpwent_r */ int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); /* real_func=pseudo_getgrent_r */ int sync_file_range(int fd, off_t offset, off_t nbytes, unsigned int flags); /* async_skip=0 */ ssize_t getxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options); /* flags=(options&XATTR_NOFOLLOW?AT_SYMLINK_NOFOLLOW:0) */ ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size, u_int32_t position, int options); int setxattr(const char *path, const char *name, const void *value, size_t size, u_int32_t position, int options); /* flags=0 */ int fsetxattr(int filedes, const char *name, const void *value, size_t size, u_int32_t position, int options); ssize_t listxattr(const char *path, char *list, size_t size, int options); /* flags=0 */ ssize_t flistxattr(int filedes, char *list, size_t size, int options); int removexattr(const char *path, const char *name, int options); /* flags=0 */ int fremovexattr(int filedes, const char *name, int options); pseudo-1.8.1+git20161012/ports/linux/000077500000000000000000000000001300506512600167205ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/guts/000077500000000000000000000000001300506512600177025ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/guts/COPYRIGHT000066400000000000000000000013521300506512600211760ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ pseudo-1.8.1+git20161012/ports/linux/guts/__fxstat.c000066400000000000000000000006321300506512600216560ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int * wrap___fxstat(int ver, int fd, struct stat *buf) { * int rc = -1; */ struct stat64 buf64; /* populate buffer with complete data */ real___fxstat(ver, fd, buf); /* obtain fake data */ rc = wrap___fxstat64(ver, fd, &buf64); /* overwrite */ pseudo_stat32_from64(buf, &buf64); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__fxstat64.c000066400000000000000000000012171300506512600220300ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int * wrap___fxstat64(int ver, int fd, struct stat64 *buf) { * int rc = -1; */ pseudo_msg_t *msg; int save_errno; rc = real___fxstat64(ver, fd, buf); save_errno = errno; if (rc == -1) { return rc; } if (ver != _STAT_VER) { pseudo_debug(PDBGF_CLIENT, "version mismatch: got stat version %d, only supporting %d\n", ver, _STAT_VER); errno = save_errno; return rc; } msg = pseudo_client_op(OP_FSTAT, 0, fd, -1, 0, buf); if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(buf, msg); } errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__fxstatat.c000066400000000000000000000013171300506512600222040ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags) { * int rc = -1; */ struct stat64 buf64; /* populate buffer with complete data */ #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } if (flags & AT_SYMLINK_NOFOLLOW) { rc = real___lxstat(ver, path, buf); } else { rc = real___xstat(ver, path, buf); } #else real___fxstatat(ver, dirfd, path, buf, flags); #endif /* obtain fake data */ rc = wrap___fxstatat64(ver, dirfd, path, &buf64, flags); /* overwrite */ pseudo_stat32_from64(buf, &buf64); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__fxstatat64.c000066400000000000000000000023771300506512600223650ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags) { * int rc = -1; */ pseudo_msg_t *msg; int save_errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } #endif if (flags & AT_SYMLINK_NOFOLLOW) { #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real___lxstat64(ver, path, buf); #else rc = real___fxstatat64(ver, dirfd, path, buf, flags); #endif if (rc == -1) { return rc; } } else { #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real___xstat64(ver, path, buf); #else rc = real___fxstatat64(ver, dirfd, path, buf, flags); #endif if (rc == -1) { return rc; } } save_errno = errno; if (ver != _STAT_VER) { pseudo_debug(PDBGF_CLIENT, "version mismatch: got stat version %d, only supporting %d\n", ver, _STAT_VER); errno = save_errno; return rc; } /* query database * note that symlink canonicalizing is now automatic, so we * don't need to check for a symlink on this end */ msg = pseudo_client_op(OP_STAT, 0, -1, dirfd, path, buf); if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(buf, msg); } errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__lxstat.c000066400000000000000000000004231300506512600216620ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___lxstat(int ver, const char *path, struct stat *buf) { * int rc = -1; */ rc = wrap___fxstatat(ver, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); /* * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__lxstat64.c000066400000000000000000000004311300506512600220330ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___lxstat64(int ver, const char *path, struct stat64 *buf) { * int rc = -1; */ rc = wrap___fxstatat64(ver, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); /* * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__openat64_2.c000066400000000000000000000004241300506512600222250ustar00rootroot00000000000000/* * Copyright (c) 2008-2010,2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___openat64_2(int dirfd, const char *path, int flags) { * int rc = -1; */ rc = wrap_openat(dirfd, path, flags | O_LARGEFILE, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__openat_2.c000066400000000000000000000003771300506512600220620ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___openat_2(int dirfd, const char *path, int flags) { * int rc = -1; */ rc = wrap_openat(dirfd, path, flags, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__xmknod.c000066400000000000000000000004261300506512600216460ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___xmknod(int ver, const char *path, mode_t mode, dev_t *dev) { * int rc = -1; */ rc = wrap___xmknodat(ver, AT_FDCWD, path, mode, dev); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__xmknodat.c000066400000000000000000000040551300506512600221750ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___xmknodat(int ver, int dirfd, const char *path, mode_t mode, dev_t *dev) { * int rc = -1; */ pseudo_msg_t *msg; struct stat64 buf; /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; /* if you don't specify a type, assume regular file */ if (!(mode & S_IFMT)) { mode |= S_IFREG; } pseudo_debug(PDBGF_FILE, "xmknodat creating '%s', mode 0%o\n", path ? path : "", (int) mode); /* we don't use underlying call, so _ver is irrelevant to us */ (void) ver; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = real___xstat64(_STAT_VER, path, &buf); #else rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); #endif if (rc != -1) { /* if we can stat the file, you can't mknod it */ errno = EEXIST; return -1; } if (!dev) { errno = EINVAL; return -1; } #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real_open(path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode, 0)); #else rc = real_openat(dirfd, path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode, 0)); #endif if (rc == -1) { return -1; } real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); real___fxstat64(_STAT_VER, rc, &buf); /* mknod does not really open the file. We don't have * to use wrap_close because we've never exposed this file * descriptor to the client code. */ real_close(rc); /* mask in the mode type bits again */ buf.st_mode = (PSEUDO_DB_MODE(buf.st_mode, mode) & 07777) | (mode & ~07777); buf.st_rdev = *dev; msg = pseudo_client_op(OP_MKNOD, 0, -1, dirfd, path, &buf); if (msg && msg->result != RESULT_SUCCEED) { errno = EPERM; rc = -1; } else { /* just pretend we worked */ rc = 0; } if (rc == -1) { int save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS real_unlink(path); #else real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); #endif errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__xstat.c000066400000000000000000000004131300506512600215050ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___xstat(int ver, const char *path, struct stat *buf) { * int rc = -1; */ rc = wrap___fxstatat(ver, AT_FDCWD, path, buf, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/__xstat64.c000066400000000000000000000004201300506512600216550ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap___xstat64(int ver, const char *path, struct stat64 *buf) { * int rc = -1; */ rc = wrap___fxstatat64(ver, AT_FDCWD, path, buf, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/canonicalize_file_name.c000066400000000000000000000003741300506512600245100ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_canonicalize_file_name(const char *filename) { * char * rc = NULL; */ rc = wrap_realpath(filename, NULL); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/creat64.c000066400000000000000000000004331300506512600213160ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_creat64(const char *path, ...mode_t mode) { * int rc = -1; */ rc = wrap_openat(AT_FDCWD, path, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/eaccess.c000066400000000000000000000003401300506512600214510ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_eaccess(const char *path, int mode) { * int rc = -1; */ rc = wrap_access(path, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/euidaccess.c000066400000000000000000000003431300506512600221560ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_euidaccess(const char *path, int mode) { * int rc = -1; */ rc = wrap_access(path, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/fcntl.c000066400000000000000000000027561300506512600211660ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fcntl(int fd, int cmd, ...struct flock *lock) { * int rc = -1; */ long arg; int save_errno; /* we don't know whether we need lock or arg; grab both, which * should be safe enough on Linuxy systems. */ va_start(ap, cmd); arg = va_arg(ap, long); va_end(ap); switch (cmd) { case F_DUPFD: #ifdef F_DUPFD_CLOEXEC case F_DUPFD_CLOEXEC: #endif /* actually do something */ rc = real_fcntl(fd, cmd, arg); save_errno = errno; if (rc != -1) { pseudo_debug(PDBGF_OP, "fcntl_dup: %d->%d\n", fd, rc); pseudo_client_op(OP_DUP, 0, fd, rc, 0, 0); } errno = save_errno; break; /* no argument: */ case F_GETFD: case F_GETFL: case F_GETOWN: case F_GETSIG: case F_GETLEASE: rc = real_fcntl(fd, cmd); break; /* long argument */ case F_SETFD: case F_SETFL: case F_SETOWN: case F_SETSIG: case F_SETLEASE: case F_NOTIFY: rc = real_fcntl(fd, cmd, arg); break; /* struct flock * argument */ case F_GETLK: case F_SETLK: case F_SETLKW: rc = real_fcntl(fd, cmd, lock); break; #if defined(F_GETLK64) && (F_GETLK64 != F_GETLK) /* the cast is safe, all struct pointers must smell the same */ case F_GETLK64: case F_SETLK64: case F_SETLKW64: rc = real_fcntl(fd, cmd, (struct flock64 *) lock); break; #endif default: pseudo_diag("unknown fcntl argument %d, assuming long argument.\n", cmd); rc = real_fcntl(fd, cmd, arg); break; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/fopen64.c000066400000000000000000000017331300506512600213330ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static FILE * * wrap_fopen64(const char *path, const char *mode) { * FILE * rc = 0; */ struct stat64 buf; int save_errno; int existed = (real___xstat64(_STAT_VER, path, &buf) != -1); rc = real_fopen64(path, mode); save_errno = errno; if (rc) { int fd = fileno(rc); pseudo_debug(PDBGF_FILE, "fopen64 '%s': fd %d \n", path, fd, (void *) rc); if (real___fxstat64(_STAT_VER, fd, &buf) != -1) { if (!existed) { real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { pseudo_debug(PDBGF_CONSISTENCY, "fopen64 (fd %d) succeeded, but fstat failed (%s).\n", fd, strerror(errno)); pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, 0); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/freopen64.c000066400000000000000000000017301300506512600216570ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static FILE * * wrap_freopen64(const char *path, const char *mode, FILE *stream) { * FILE * rc = NULL; */ struct stat64 buf; int save_errno; int existed = (real___xstat64(_STAT_VER, path, &buf) != -1); rc = real_freopen64(path, mode, stream); save_errno = errno; if (rc) { int fd = fileno(rc); pseudo_debug(PDBGF_FILE, "freopen64 '%s': fd %d\n", path, fd); if (real___fxstat64(_STAT_VER, fd, &buf) != -1) { if (!existed) { real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { pseudo_debug(PDBGF_FILE, "fopen (fd %d) succeeded, but stat failed (%s).\n", fd, strerror(errno)); pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, 0); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/fstat.c000066400000000000000000000003241300506512600211660ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int fstat(int fd, struct stat *buf) * int rc = -1; */ rc = wrap___fxstat(_STAT_VER, fd, buf); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/fstat64.c000066400000000000000000000003301300506512600213350ustar00rootroot00000000000000/* * Copyright (c) 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * int fstat64(int fd, struct stat *buf) * int rc = -1; */ rc = wrap___fxstat64(_STAT_VER, fd, buf); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/ftw64.c000066400000000000000000000004341300506512600210210ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd) { * int rc = -1; */ rc = real_ftw64(path, fn, nopenfd); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/get_current_dir_name.c000066400000000000000000000004731300506512600242310ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_get_current_dir_name(void) { * char * rc = NULL; */ /* this relies on a Linux extension, but we dutifully * emulated that extension. */ rc = wrap_getcwd(NULL, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getgrent_r.c000066400000000000000000000006721300506512600222130ustar00rootroot00000000000000/* * Copyright (c) 2010-2011 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) { * int rc = -1; */ /* note that we don't wrap fgetgrent_r, since there's no path * references in it. */ if (!pseudo_grp) { errno = ENOENT; return -1; } rc = fgetgrent_r(pseudo_grp, gbuf, buf, buflen, gbufp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getgrouplist.c000066400000000000000000000014621300506512600226010ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) { * int rc = -1; */ int found = 0; int found_group = 0; char buf[PSEUDO_PWD_MAX]; struct group grp, *gbuf = &grp; setgrent(); while ((rc = wrap_getgrent_r(gbuf, buf, PSEUDO_PWD_MAX, &gbuf)) == 0) { int i = 0; for (i = 0; gbuf->gr_mem[i]; ++i) { if (!strcmp(gbuf->gr_mem[i], user)) { if (found < *ngroups) groups[found] = gbuf->gr_gid; ++found; if (gbuf->gr_gid == group) found_group = 1; } } } endgrent(); if (!found_group) { if (found < *ngroups) groups[found] = group; ++found; } if (found >= *ngroups) rc = -1; else rc = found; *ngroups = found; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getgroups.c000066400000000000000000000006451300506512600220720ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgroups(int size, gid_t *list) { * int rc = -1; */ struct passwd *p = wrap_getpwuid(wrap_getuid()); int oldsize = size; if (p) { rc = wrap_getgrouplist(p->pw_name, wrap_getgid(), list, &size); if (oldsize == 0 || size <= oldsize) rc = size; } else { errno = ENOENT; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getpw.c000066400000000000000000000012761300506512600212020ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getpw(uid_t uid, char *buf) { * int rc = -1; */ static struct passwd pwd; static char pwbuf[PSEUDO_PWD_MAX]; struct passwd *pwp; pseudo_diag("warning: unsafe getpw() called. hoping buf has at least %d chars.\n", PSEUDO_PWD_MAX); rc = wrap_getpwuid_r(uid, &pwd, pwbuf, PSEUDO_PWD_MAX, &pwp); /* different error return conventions */ if (rc != 0) { errno = rc; rc = -1; } else { snprintf(buf, PSEUDO_PWD_MAX, "%s:%s:%d:%d:%s:%s:%s", pwd.pw_name, pwd.pw_passwd, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getpwent_r.c000066400000000000000000000007001300506512600222210ustar00rootroot00000000000000/* * Copyright (c) 2010-2011 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) { * int rc = -1; */ /* note that we don't wrap fgetpwent_r, since there's no path * references in it. */ if (!pseudo_pwd) { errno = ENOENT; return -1; } rc = fgetpwent_r(pseudo_pwd, pwbuf, buf, buflen, pwbufp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getresgid.c000066400000000000000000000006101300506512600220200ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) { * int rc = -1; */ if (rgid) *rgid = pseudo_rgid; if (egid) *egid = pseudo_egid; if (sgid) *sgid = pseudo_sgid; if (rgid && egid && sgid) { rc = 0; } else { rc = -1; errno = EFAULT; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/getresuid.c000066400000000000000000000006101300506512600220360ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { * int rc = -1; */ if (ruid) *ruid = pseudo_ruid; if (euid) *euid = pseudo_euid; if (suid) *suid = pseudo_suid; if (ruid && euid && suid) { rc = 0; } else { rc = -1; errno = EFAULT; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/glob64.c000066400000000000000000000015031300506512600211420ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_glob64(const char *pattern, int flags, int (*errfunc)(const char *, int), glob64_t *pglob) { * int rc = -1; */ char *rpattern = NULL; int alloced = 0; /* note: no canonicalization */ if (pattern && (*pattern == '/') && pseudo_chroot_len) { size_t len = strlen(pattern) + pseudo_chroot_len + 2; rpattern = malloc(len); if (!rpattern) { errno = ENOMEM; return GLOB_NOSPACE; } snprintf(rpattern, len, "%s/%s", pseudo_chroot, pattern); alloced = 1; } rc = real_glob64(alloced ? rpattern : pattern, flags, errfunc, pglob); free(rpattern); if (rc == 0) { unsigned int i; for (i = 0; i < pglob->gl_pathc; ++i) { pseudo_dechroot(pglob->gl_pathv[i], (size_t) -1); } } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/lchown.c000066400000000000000000000004151300506512600213400ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_lchown(const char *path, uid_t owner, gid_t group) { */ rc = wrap_fchownat(AT_FDCWD, path, owner, group, AT_SYMLINK_NOFOLLOW); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/lckpwdf.c000066400000000000000000000011531300506512600215000ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_lckpwdf(void) { * int rc = -1; */ rc = pseudo_pwd_lck_open(); if (rc != -1) { struct flock lck = { .l_type = F_RDLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 1 }; /* I don't really care whether this works. */ fcntl(rc, F_SETFD, FD_CLOEXEC); /* Now lock it. */ alarm(15); /* magic number from man page */ rc = fcntl(rc, F_SETLKW, &lck); alarm(0); if (rc == -1) { int save_errno = errno; pseudo_pwd_lck_close(); errno = save_errno; } } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/lstat.c000066400000000000000000000004011300506512600211700ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int lstat(const char *path, struct stat *buf) * int rc = -1; */ rc = wrap___fxstatat(_STAT_VER, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/lstat64.c000066400000000000000000000004051300506512600213460ustar00rootroot00000000000000/* * Copyright (c) 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * int lstat64(const char *path, struct stat *buf) * int rc = -1; */ rc = wrap___fxstatat64(_STAT_VER, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/mknod.c000066400000000000000000000003561300506512600211620ustar00rootroot00000000000000/* * Copyright (c) 2016 Wind River Systems; see * guts/COPYRIGHT for information. * * int mknod(const char *path, mode_t mode, dev_t dev) * int rc = -1; */ rc = wrap___xmknod(_MKNOD_VER, path, mode, &dev); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/mknodat.c000066400000000000000000000004041300506512600215010ustar00rootroot00000000000000/* * Copyright (c) 2016 Wind River Systems; see * guts/COPYRIGHT for information. * * int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev) * int rc = -1; */ rc = wrap___xmknodat(_MKNOD_VER, dirfd, path, mode, &dev); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/mkstemp64.c000066400000000000000000000021611300506512600217000ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_mkstemp64(char *template) { * int rc = -1; */ struct stat64 buf; int save_errno; size_t len; char *tmp_template; if (!template) { errno = EFAULT; return 0; } len = strlen(template); tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW); if (!tmp_template) { errno = ENOENT; return -1; } rc = real_mkstemp64(tmp_template); if (rc != -1) { save_errno = errno; if (real___fxstat64(_STAT_VER, rc, &buf) != -1) { real_fchmod(rc, PSEUDO_FS_MODE(0600, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, tmp_template, &buf); pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, &buf); } else { pseudo_debug(PDBGF_CONSISTENCY, "mkstemp (fd %d) succeeded, but fstat failed (%s).\n", rc, strerror(errno)); pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, 0); } errno = save_errno; } /* mkstemp only changes the XXXXXX at the end. */ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/nftw64.c000066400000000000000000000004741300506512600212030ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag) { * int rc = -1; */ rc = real_nftw64(path, fn, nopenfd, flag); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/open.c000066400000000000000000000004061300506512600210070ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_open(const char *path, int flags, ...mode_t mode) { * int rc = -1; */ return wrap_openat(AT_FDCWD, path, flags, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/open64.c000066400000000000000000000004311300506512600211570ustar00rootroot00000000000000/* * Copyright (c) 2008-2010,2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_open64(const char *path, int flags, ...mode_t mode) { * int rc = -1; */ rc = wrap_openat(AT_FDCWD, path, flags | O_LARGEFILE, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/openat.c000066400000000000000000000044711300506512600213420ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_openat(int dirfd, const char *path, int flags, ...mode_t mode) { * int rc = -1; */ struct stat64 buf; int existed = 1; int save_errno; /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } #endif #ifdef PSEUDO_FORCE_ASYNCH /* Yes, I'm aware that every Linux system I've seen has * DSYNC and RSYNC being the same value as SYNC. */ flags &= ~(O_SYNC #ifdef O_DIRECT | O_DIRECT #endif #ifdef O_DSYNC | O_DSYNC #endif #ifdef O_RSYNC | O_RSYNC #endif ); #endif /* if a creation has been requested, check whether file exists */ if (flags & O_CREAT) { save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real___xstat64(_STAT_VER, path, &buf); #else rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, 0); #endif existed = (rc != -1); if (!existed) pseudo_debug(PDBGF_FILE, "openat_creat: %s -> 0%o\n", path, mode); errno = save_errno; } /* because we are not actually root, secretly mask in 0600 to the * underlying mode. The ", 0" is because the only time mode matters * is if a file is going to be created, in which case it's * not a directory. */ #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real_open(path, flags, PSEUDO_FS_MODE(mode, 0)); #else rc = real_openat(dirfd, path, flags, PSEUDO_FS_MODE(mode, 0)); #endif save_errno = errno; if (rc != -1) { int stat_rc; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS stat_rc = real___xstat64(_STAT_VER, path, &buf); #else stat_rc = real___fxstatat64(_STAT_VER, dirfd, path, &buf, 0); #endif if (stat_rc != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); if (!existed) { real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); pseudo_client_op(OP_CREAT, 0, -1, dirfd, path, &buf); } pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(flags), rc, dirfd, path, &buf); } else { pseudo_debug(PDBGF_FILE, "openat (fd %d, path %d/%s, flags %d) succeeded, but stat failed (%s).\n", rc, dirfd, path, flags, strerror(errno)); pseudo_client_op(OP_OPEN, PSEUDO_ACCESS(flags), rc, dirfd, path, 0); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/openat64.c000066400000000000000000000004431300506512600215070ustar00rootroot00000000000000/* * Copyright (c) 2008-2010,2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_openat64(int dirfd, const char *path, int flags, ...mode_t mode) { * int rc = -1; */ rc = wrap_openat(dirfd, path, flags | O_LARGEFILE, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/scandir.c000066400000000000000000000005271300506512600214750ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_scandir(const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const void *, const void *)) { * int rc = -1; */ rc = real_scandir(path, namelist, filter, compar); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/scandir64.c000066400000000000000000000005371300506512600216500ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_scandir64(const char *path, struct dirent64 ***namelist, int (*filter)(const struct dirent64 *), int (*compar)(const void *, const void *)) { * int rc = -1; */ rc = real_scandir64(path, namelist, filter, compar); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/setfsgid.c000066400000000000000000000005461300506512600216630ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setfsgid(gid_t fsgid) { * int rc = -1; */ if (pseudo_euid == 0 || pseudo_egid == fsgid || pseudo_rgid == fsgid || pseudo_sgid == fsgid) { pseudo_fgid = fsgid; rc = 0; } else { rc = -1; errno = EPERM; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/setfsuid.c000066400000000000000000000005461300506512600217010ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setfsuid(uid_t fsuid) { * int rc = -1; */ if (pseudo_euid == 0 || pseudo_euid == fsuid || pseudo_ruid == fsuid || pseudo_suid == fsuid) { pseudo_fuid = fsuid; rc = 0; } else { rc = -1; errno = EPERM; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/setgroups.c000066400000000000000000000005251300506512600221030ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setgroups(size_t size, const gid_t *list) { * int rc = -1; */ /* let gcc know we're ignoring these */ (void) size; (void) list; /* you always have all group privileges. we're like magic! */ rc = 0; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/setresgid.c000066400000000000000000000016241300506512600220420ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setresgid(gid_t rgid, gid_t egid, gid_t sgid) { * int rc = -1; */ rc = 0; if (pseudo_euid != 0 && rgid != (gid_t) -1 && rgid != pseudo_egid && rgid != pseudo_rgid && rgid != pseudo_sgid) { rc = -1; errno = EPERM; } if (pseudo_euid != 0 && egid != (gid_t) -1 && egid != pseudo_egid && egid != pseudo_rgid && egid != pseudo_sgid) { rc = -1; errno = EPERM; } if (pseudo_euid != 0 && sgid != (gid_t) -1 && sgid != pseudo_egid && sgid != pseudo_rgid && sgid != pseudo_sgid) { rc = -1; errno = EPERM; } if (rc != -1) { if (rgid != (gid_t) -1) pseudo_rgid = rgid; if (egid != (gid_t) -1) pseudo_egid = egid; if (sgid != (gid_t) -1) pseudo_sgid = sgid; pseudo_fgid = pseudo_egid; pseudo_client_touchuid(); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/setresuid.c000066400000000000000000000016241300506512600220600ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setresuid(uid_t ruid, uid_t euid, uid_t suid) { * int rc = -1; */ rc = 0; if (pseudo_euid != 0 && ruid != (uid_t) -1 && ruid != pseudo_euid && ruid != pseudo_ruid && ruid != pseudo_suid) { rc = -1; errno = EPERM; } if (pseudo_euid != 0 && euid != (uid_t) -1 && euid != pseudo_euid && euid != pseudo_ruid && euid != pseudo_suid) { rc = -1; errno = EPERM; } if (pseudo_euid != 0 && suid != (uid_t) -1 && suid != pseudo_euid && suid != pseudo_ruid && suid != pseudo_suid) { rc = -1; errno = EPERM; } if (rc != -1) { if (ruid != (uid_t) -1) pseudo_ruid = ruid; if (euid != (uid_t) -1) pseudo_euid = euid; if (suid != (uid_t) -1) pseudo_suid = suid; pseudo_fuid = pseudo_euid; pseudo_client_touchuid(); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/stat.c000066400000000000000000000003561300506512600210250ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int stat(const char *path, struct stat *buf) * int rc = -1; */ rc = wrap___fxstatat(_STAT_VER, AT_FDCWD, path, buf, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/stat64.c000066400000000000000000000003621300506512600211740ustar00rootroot00000000000000/* * Copyright (c) 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * int stat64(const char *path, struct stat *buf) * int rc = -1; */ rc = wrap___fxstatat64(_STAT_VER, AT_FDCWD, path, buf, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/truncate64.c000066400000000000000000000003571300506512600220520ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_truncate64(const char *path, off64_t length) { * int rc = -1; */ rc = real_truncate64(path, length); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/guts/ulckpwdf.c000066400000000000000000000003701300506512600216650ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_ulckpwdf(void) { * int rc = -1; */ /* lock is cleared automatically on close */ rc = pseudo_pwd_lck_close(); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/newclone/000077500000000000000000000000001300506512600205325ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/newclone/guts/000077500000000000000000000000001300506512600215145ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/newclone/guts/clone.c000066400000000000000000000014151300506512600227610ustar00rootroot00000000000000/* * Copyright (c) 2008-2011 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * clone(...) { * .... */ /* because clone() doesn't actually continue in this function, we * can't check the return and fix up environment variables in the * child. Instead, we have to create an intermediate function, * wrap_clone_child, which does this fix up. */ struct clone_args * myargs = malloc(sizeof(struct clone_args)); myargs->fn = fn; myargs->flags = flags; myargs->arg = arg; /* call the real syscall */ rc = (*real_clone)(wrap_clone_child, child_stack, flags, myargs, pid, tls, ctid); /* If we're not sharing memory, we need to free myargs in the parent */ if (!(flags & CLONE_VM)) free(myargs); /* ... * return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/newclone/pseudo_wrappers.c000066400000000000000000000032411300506512600241200ustar00rootroot00000000000000static int wrap_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, va_list ap) { /* unused */ (void) fn; (void) child_stack; (void) flags; (void) arg; (void) ap; return 0; } struct clone_args { int (*fn)(void *); int flags; void *arg; }; int wrap_clone_child(void *args) { struct clone_args *clargs = args; int (*fn)(void *) = clargs->fn; int flags = clargs->flags; void *arg = clargs->arg; /* We always free in the client */ free(clargs); if (!(flags & CLONE_VM)) { pseudo_setupenv(); if (!pseudo_has_unload(NULL)) { pseudo_reinit_libpseudo(); } else { pseudo_dropenv(); } } return fn(arg); } int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) { sigset_t saved; va_list ap; pid_t *pid; struct user_desc *tls; pid_t *ctid; int rc = -1; if (!pseudo_check_wrappers() || !real_clone) { /* rc was initialized to the "failure" value */ pseudo_enosys("clone"); return rc; } va_start(ap, arg); pid = va_arg(ap, pid_t *); tls = va_arg(ap, struct user_desc *); ctid = va_arg(ap, pid_t *); va_end(ap); pseudo_debug(PDBGF_WRAPPER, "called: clone\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); return -1; } int save_errno; int save_disabled = pseudo_disabled; #include "guts/clone.c" if (save_disabled != pseudo_disabled) { if (pseudo_disabled) { pseudo_disabled = 0; pseudo_magic(); } else { pseudo_disabled = 1; pseudo_antimagic(); } } save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: clone\n"); errno = save_errno; return rc; } pseudo-1.8.1+git20161012/ports/linux/newclone/wrapfuncs.in000066400000000000000000000001411300506512600230660ustar00rootroot00000000000000int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...); /* hand_wrapped=1 */ pseudo-1.8.1+git20161012/ports/linux/noxattr/000077500000000000000000000000001300506512600204175ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/000077500000000000000000000000001300506512600214015ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/fgetxattr.c000066400000000000000000000004751300506512600235630ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size) * ssize_t rc = -1; */ /* suppress warnings */ (void) filedes; (void) name; (void) value; (void) size; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/flistxattr.c000066400000000000000000000004341300506512600237520ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t flistxattr(int filedes, char *list, size_t size) * ssize_t rc = -1; */ /* suppress warnings */ (void) filedes; (void) list; (void) size; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/fremovexattr.c000066400000000000000000000004011300506512600242660ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int fremovexattr(int filedes, const char *name) * int rc = -1; */ /* suppress warnings */ (void) filedes; (void) name; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/fsetxattr.c000066400000000000000000000005251300506512600235730ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) * int rc = -1; */ /* suppress warnings */ (void) filedes; (void) name; (void) value; (void) size; (void) flags; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/getxattr.c000066400000000000000000000005061300506512600234100ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size) * ssize_t rc = -1; */ /* suppress warnings */ (void) pathname; (void) name; (void) value; (void) size; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/lgetxattr.c000066400000000000000000000005071300506512600235650ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size) * ssize_t rc = -1; */ /* suppress warnings */ (void) pathname; (void) name; (void) value; (void) size; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/listxattr.c000066400000000000000000000004451300506512600236060ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t listxattr(const char *pathname, char *list, size_t size) * ssize_t rc = -1; */ /* suppress warnings */ (void) pathname; (void) list; (void) size; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/llistxattr.c000066400000000000000000000004461300506512600237630ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t llistxattr(const char *pathname, char *list, size_t size) * ssize_t rc = -1; */ /* suppress warnings */ (void) pathname; (void) list; (void) size; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/lremovexattr.c000066400000000000000000000004131300506512600242770ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int lremovexattr(const char *pathname, const char *name) * int rc = -1; */ /* suppress warnings */ (void) pathname; (void) name; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/lsetxattr.c000066400000000000000000000005371300506512600236040ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags) * int rc = -1; */ /* suppress warnings */ (void) pathname; (void) name; (void) value; (void) size; (void) flags; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/removexattr.c000066400000000000000000000004121300506512600241220ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int removexattr(const char *pathname, const char *name) * int rc = -1; */ /* suppress warnings */ (void) pathname; (void) name; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/guts/setxattr.c000066400000000000000000000005361300506512600234270ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags) * int rc = -1; */ /* suppress warnings */ (void) pathname; (void) name; (void) value; (void) size; (void) flags; errno = ENOTSUP; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/noxattr/wrapfuncs.in000066400000000000000000000017231300506512600227620ustar00rootroot00000000000000# we use "pathname" to avoid canonicalizing paths, because these functions are # unimplemented ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size); ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size); ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); ssize_t listxattr(const char *pathname, char *list, size_t size); ssize_t llistxattr(const char *pathname, char *list, size_t size); ssize_t flistxattr(int filedes, char *list, size_t size); int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags); int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags); int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags); int removexattr(const char *pathname, const char *name); int lremovexattr(const char *pathname, const char *name); int fremovexattr(int filedes, const char *name); pseudo-1.8.1+git20161012/ports/linux/oldclone/000077500000000000000000000000001300506512600205175ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/oldclone/guts/000077500000000000000000000000001300506512600215015ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/oldclone/guts/clone.c000066400000000000000000000013751300506512600227530ustar00rootroot00000000000000/* * Copyright (c) 2008-2011 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * clone(...) { * .... */ /* because clone() doesn't actually continue in this function, we * can't check the return and fix up environment variables in the * child. Instead, we have to create an intermediate function, * wrap_clone_child, which does this fix up. */ struct clone_args * myargs = malloc(sizeof(struct clone_args)); myargs->fn = fn; myargs->flags = flags; myargs->arg = arg; /* call the real syscall */ rc = (*real_clone)(wrap_clone_child, child_stack, flags, myargs); /* If we're not sharing memory, we need to free myargs in the parent */ if (!(flags & CLONE_VM)) free(myargs); /* ... * return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/oldclone/pseudo_wrappers.c000066400000000000000000000026051300506512600241100ustar00rootroot00000000000000static int wrap_clone(int (*fn)(void *), void *child_stack, int flags, void *arg) { /* unused */ return 0; } struct clone_args { int (*fn)(void *); int flags; void *arg; }; int wrap_clone_child(void *args) { struct clone_args *clargs = args; int (*fn)(void *) = clargs->fn; int flags = clargs->flags; void *arg = clargs->arg; /* We always free in the client */ free(clargs); if (!(flags & CLONE_VM)) { pseudo_setupenv(); if (!pseudo_has_unload(NULL)) { pseudo_reinit_libpseudo(); } else { pseudo_dropenv(); } } return fn(arg); } int clone(int (*fn)(void *), void *child_stack, int flags, void *arg) { sigset_t saved; int rc = -1; if (!pseudo_check_wrappers() || !real_clone) { /* rc was initialized to the "failure" value */ pseudo_enosys("clone"); return rc; } pseudo_debug(PDBGF_WRAPPER, "called: clone\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); return -1; } int save_errno; int save_disabled = pseudo_disabled; #include "guts/clone.c" if (save_disabled != pseudo_disabled) { if (pseudo_disabled) { pseudo_disabled = 0; pseudo_magic(); } else { pseudo_disabled = 1; pseudo_antimagic(); } } save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "completed: clone\n"); errno = save_errno; return rc; } pseudo-1.8.1+git20161012/ports/linux/oldclone/wrapfuncs.in000066400000000000000000000001341300506512600230550ustar00rootroot00000000000000int clone(int (*fn)(void *), void *child_stack, int flags, void *arg); /* hand_wrapped=1 */ pseudo-1.8.1+git20161012/ports/linux/portdefs.h000066400000000000000000000017771300506512600207330ustar00rootroot00000000000000#define PRELINK_LIBRARIES "LD_PRELOAD" #define PRELINK_PATH "LD_LIBRARY_PATH" #define PSEUDO_STATBUF_64 1 #define PSEUDO_STATBUF struct stat64 #define PSEUDO_LINKPATH_SEPARATOR " " /* Linux NEVER follows symlinks for link(2)... except on old kernels * I don't care about. */ #undef PSEUDO_LINK_SYMLINK_BEHAVIOR /* Note: 0, here, really means AT_SYMLINK_NOFOLLOW, but specifying that * causes errors; you have to leave it empty or specify AT_SYMLINK_FOLLOW. */ #define PSEUDO_LINK_SYMLINK_BEHAVIOR 0 /* There were symbol changes that can cause the linker to request * newer versions of glibc, which causes problems occasionally on * older hosts if pseudo is built against a newer glibc and then * run with an older one. Sometimes we can just avoid the symbols, * but memcpy's pretty hard to get away from. */ #define GLIBC_COMPAT_SYMBOL(sym, ver) __asm(".symver " #sym "," #sym "@GLIBC_" #ver) #ifdef __amd64__ GLIBC_COMPAT_SYMBOL(memcpy,2.2.5); #elif defined(__i386__) GLIBC_COMPAT_SYMBOL(memcpy,2.0); #endif pseudo-1.8.1+git20161012/ports/linux/preports000077500000000000000000000000451300506512600205230ustar00rootroot00000000000000#!/bin/sh echo "unix" "uids_generic" pseudo-1.8.1+git20161012/ports/linux/pseudo_wrappers.c000066400000000000000000000020331300506512600223040ustar00rootroot00000000000000/* the unix port wants to know that real_stat() and * friends exist. So they do. And because the Linux * port really uses stat64 for those... */ int pseudo_stat(const char *path, struct stat *buf) { return real___xstat(_STAT_VER, path, buf); } int pseudo_lstat(const char *path, struct stat *buf) { return real___lxstat(_STAT_VER, path, buf); } int pseudo_fstat(int fd, struct stat *buf) { return real___fxstat(_STAT_VER, fd, buf); } int pseudo_stat64(const char *path, struct stat64 *buf) { return real___xstat64(_STAT_VER, path, buf); } int pseudo_lstat64(const char *path, struct stat64 *buf) { return real___lxstat64(_STAT_VER, path, buf); } int pseudo_fstat64(int fd, struct stat64 *buf) { return real___fxstat64(_STAT_VER, fd, buf); } /* similar thing happens with mknod */ int pseudo_mknod(const char *path, mode_t mode, dev_t dev) { return real___xmknod(_MKNOD_VER, path, mode, &dev); } int pseudo_mknodat(int dirfd, const char *path, mode_t mode, dev_t dev) { return real___xmknodat(_MKNOD_VER, dirfd, path, mode, &dev); } pseudo-1.8.1+git20161012/ports/linux/subports000077500000000000000000000020741300506512600205320ustar00rootroot00000000000000#!/bin/sh found=false printf >&2 "Checking for old/new clone mechanics... " cat > dummy.c < int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) { return 0; } EOF if ${CC} -c -o dummy.o dummy.c >/dev/null 2>&1; then rm -f dummy.c dummy.o echo >&2 "New clone." echo "linux/newclone" found=true fi cat > dummy.c < int clone(int (*fn)(void *), void *child_stack, int flags, void *arg) { return 0; } EOF if ! $found && ${CC} -c -o dummy.o dummy.c >/dev/null 2>&1; then rm -f dummy.c dummy.o echo >&2 "Old clone." echo "linux/oldclone" found=true fi rm -f dummy.c dummy.o if ! $found; then echo >&2 "Can't tell, omitting clone(2) support." fi if $port_xattr; then cat > dummy.c < #include int i; EOF if ! ${CC} -c -o dummy.o dummy.c >/dev/null 2>&1; then echo >&2 "Warning: Can't compile trivial program using ". echo >&2 " xattr support will require that header." fi echo "linux/xattr" else echo "linux/noxattr" fi rm -f dummy.c dummy.o pseudo-1.8.1+git20161012/ports/linux/wrapfuncs.in000066400000000000000000000072331300506512600212650ustar00rootroot00000000000000int open(const char *path, int flags, ...{mode_t mode}); /* flags=0 */ char *get_current_dir_name(void); int __xstat(int ver, const char *path, struct stat *buf); int __lxstat(int ver, const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW */ int __fxstat(int ver, int fd, struct stat *buf); int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */ int __fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags); int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); int __openat_2(int dirfd, const char *path, int flags); int mknod(const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknod */ int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknodat */ int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */ int __xmknodat(int ver, int dirfd, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */ int fcntl(int fd, int cmd, ...{struct flock *lock}); # just so we know the inums of symlinks char *canonicalize_file_name(const char *filename); int eaccess(const char *path, int mode); int open64(const char *path, int flags, ...{mode_t mode}); /* flags=0 */ int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=0 */ int __openat64_2(int dirfd, const char *path, int flags); /* flags=0 */ int creat64(const char *path, mode_t mode); int stat(const char *path, struct stat *buf); /* real_func=pseudo_stat */ int lstat(const char *path, struct stat *buf); /* real_func=pseudo_lstat, flags=AT_SYMLINK_NOFOLLOW */ int fstat(int fd, struct stat *buf); /* real_func=pseudo_fstat */ int stat64(const char *path, struct stat64 *buf); /* real_func=pseudo_stat64 */ int lstat64(const char *path, struct stat64 *buf); /* real_func=pseudo_lstat64, flags=AT_SYMLINK_NOFOLLOW */ int fstat64(int fd, struct stat64 *buf); /* real_func=pseudo_fstat64 */ int __xstat64(int ver, const char *path, struct stat64 *buf); int __lxstat64(int ver, const char *path, struct stat64 *buf); /* flags=AT_SYMLINK_NOFOLLOW */ int __fxstat64(int ver, int fd, struct stat64 *buf); int __fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags); FILE *fopen64(const char *path, const char *mode); int nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag); FILE *freopen64(const char *path, const char *mode, FILE *stream); int ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd); int glob64(const char *pattern, int flags, int (*errfunc)(const char *, int), glob64_t *pglob); int scandir64(const char *path, struct dirent64 ***namelist, int (*filter)(const struct dirent64 *), int (*compar)()); int truncate64(const char *path, off64_t length); int mkstemp64(char *template); /* flags=AT_SYMLINK_NOFOLLOW */ int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); int setgroups(size_t size, const gid_t *list); int setfsgid(gid_t fsgid); int setfsuid(uid_t fsuid); int setresgid(gid_t rgid, gid_t egid, gid_t sgid); int setresuid(uid_t ruid, uid_t euid, uid_t suid); int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); int scandir(const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)()); int getgroups(int size, gid_t *list); int lckpwdf(void); int ulckpwdf(void); int euidaccess(const char *path, int mode); int getpw(uid_t uid, char *buf); int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); pseudo-1.8.1+git20161012/ports/linux/xattr/000077500000000000000000000000001300506512600200625ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/xattr/guts/000077500000000000000000000000001300506512600210445ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/linux/xattr/guts/fgetxattr.c000066400000000000000000000004161300506512600232210ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size) * ssize_t rc = -1; */ rc = shared_getxattr(NULL, filedes, name, value, size); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/flistxattr.c000066400000000000000000000003661300506512600234210ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t flistxattr(int filedes, char *list, size_t size) * ssize_t rc = -1; */ rc = shared_listxattr(NULL, filedes, list, size); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/fremovexattr.c000066400000000000000000000003451300506512600237400ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int fremovexattr(int filedes, const char *name) * int rc = -1; */ rc = shared_removexattr(NULL, filedes, name); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/fsetxattr.c000066400000000000000000000004401300506512600232320ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int fsetxattr(int filedes, const char *name, const void *value, size_t size, int xflags) * int rc = -1; */ rc = shared_setxattr(NULL, filedes, name, value, size, xflags); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/getxattr.c000066400000000000000000000004151300506512600230520ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t getxattr(const char *path, const char *name, void *value, size_t size) * ssize_t rc = -1; */ rc = shared_getxattr(path, -1, name, value, size); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/lgetxattr.c000066400000000000000000000004161300506512600232270ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) * ssize_t rc = -1; */ rc = shared_getxattr(path, -1, name, value, size); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/listxattr.c000066400000000000000000000003651300506512600232520ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t listxattr(const char *path, char *list, size_t size) * ssize_t rc = -1; */ rc = shared_listxattr(path, -1, list, size); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/llistxattr.c000066400000000000000000000003661300506512600234270ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * ssize_t llistxattr(const char *path, char *list, size_t size) * ssize_t rc = -1; */ rc = shared_listxattr(path, -1, list, size); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/lremovexattr.c000066400000000000000000000003451300506512600237460ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int lremovexattr(const char *path, const char *name) * int rc = -1; */ rc = shared_removexattr(path, -1, name); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/lsetxattr.c000066400000000000000000000004401300506512600232400ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int lsetxattr(const char *path, const char *name, const void *value, size_t size, int xflags) * int rc = -1; */ rc = shared_setxattr(path, -1, name, value, size, xflags); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/removexattr.c000066400000000000000000000003441300506512600235710ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int removexattr(const char *path, const char *name) * int rc = -1; */ rc = shared_removexattr(path, -1, name); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/guts/setxattr.c000066400000000000000000000004371300506512600230720ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int setxattr(const char *path, const char *name, const void *value, size_t size, int xflags) * int rc = -1; */ rc = shared_setxattr(path, -1, name, value, size, xflags); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/linux/xattr/portdefs.h000066400000000000000000000000541300506512600220600ustar00rootroot00000000000000#include #include pseudo-1.8.1+git20161012/ports/linux/xattr/pseudo_wrappers.c000066400000000000000000000134041300506512600234520ustar00rootroot00000000000000/* shared functionality for the xattr code */ /* Each of these functions is expecting to get an optional name, and * a populated statbuf to use for sending messages to the server. */ /* to avoid namespace pollution and such, we now duplicate the * basic functionality of a POSIX ACL list, as used by libacl or * the kernel. Documentation was obtained from the headers of libacl * and from a page or two of _The Linux Programming Interface_, by * Michael Kerrisk. */ typedef struct { uint16_t tag; uint16_t perm; uint32_t id; } acl_entry; typedef struct { uint32_t version; acl_entry entries[]; } acl_header; enum acl_tags { ACL_UNDEFINED = 0x0, ACL_USER_OBJ = 0x1, ACL_USER = 0x2, ACL_GROUP_OBJ = 0x4, ACL_GROUP = 0x8, ACL_MASK = 0x10, ACL_OTHER = 0x20, }; static const int endian_test = 1; static const char *endian_tester = (char *) &endian_test; static inline int le16(int x16) { if (*endian_tester) { return x16; } else { return ((x16 & 0xff) << 8) | ((x16 & 0xff00) >> 8); } } static inline int le32(int x32) { if (*endian_tester) { return x32; } else { return ((x32 & 0xff) << 24) | ((x32 & 0xff00) << 8) | ((x32 & 0xff0000) >> 8) | ((x32 & 0xff000000) >> 24); } } /* set mode to match the contents of header. Return non-zero on error. * On a zero return, mode is a valid posix mode, and *extra is set to * 1 if any of the entries are not reflected by that mode. On a non-zero * return, no promises are made about *extra or *mode. */ static int posix_permissions(const acl_header *header, int entries, int *extra, int *mode) { int acl_seen = 0; if (le32(header->version) != 2) { pseudo_diag("Fatal: ACL support no available for header version %d.\n", le32(header->version)); return 1; } *mode = 0; *extra = 0; for (int i = 0; i < entries; ++i) { const acl_entry *e = &header->entries[i]; int tag = le16(e->tag); int perm = le16(e->perm); acl_seen |= tag; switch (tag) { case ACL_USER_OBJ: *mode = *mode | (perm << 6); break; case ACL_GROUP_OBJ: *mode = *mode | (perm << 3); break; case ACL_OTHER: *mode = *mode | perm; break; case ACL_USER: case ACL_GROUP: case ACL_MASK: *extra = *extra + 1; break; default: pseudo_debug(PDBGF_XATTR, "Unknown tag in ACL: 0x%x.\n", tag); return 1; } } return 0; } #define RC_AND_BUF \ int rc; \ PSEUDO_STATBUF buf; \ if (path) { \ rc = base_lstat(path, &buf); \ } else { \ rc = base_fstat(fd, &buf); \ } \ if (rc == -1) { \ return rc; \ } static ssize_t shared_getxattr(const char *path, int fd, const char *name, void *value, size_t size) { RC_AND_BUF pseudo_debug(PDBGF_XATTR, "getxattr(%s [fd %d], %s)\n", path ? path : "", fd, name); pseudo_msg_t *result = pseudo_client_op(OP_GET_XATTR, 0, fd, -1, path, &buf, name); if (result->result != RESULT_SUCCEED) { errno = ENOATTR; return -1; } if (value) { pseudo_debug(PDBGF_XATTR, "returned attributes: '%s' (%d bytes)\n", result->path, result->pathlen); if (size >= result->pathlen) { memcpy(value, result->path, result->pathlen); } else { memcpy(value, result->path, size); errno = ERANGE; } } return result->pathlen; } static int shared_setxattr(const char *path, int fd, const char *name, const void *value, size_t size, int flags) { RC_AND_BUF pseudo_op_t op; pseudo_debug(PDBGF_XATTR, "setxattr(%s [fd %d], %s => '%.*s')\n", path ? path : "", fd, name, (int) size, (char *) value); /* this may be a plain chmod */ if (!strcmp(name, "system.posix_acl_access")) { int extra; int mode; int entries = (size - sizeof(acl_header)) / sizeof(acl_entry); if (!posix_permissions(value, entries, &extra, &mode)) { pseudo_debug(PDBGF_XATTR, "posix_acl_access translated to mode %04o. Remaining attribute(s): %d.\n", mode, extra); buf.st_mode = mode; /* we want to actually issue a corresponding chmod, * as well, or else the file ends up 0600 on the * host. Using the slightly-less-efficient wrap_chmod * avoids possible misalignment. */ if (path) { wrap_chmod(path, mode); } else { wrap_fchmod(fd, mode); } /* we are sneaky, and do not actually record this using * extended attributes. */ if (!extra) { return 0; } } } if (!strcmp(name, "user.pseudo_data")) { pseudo_debug(PDBGF_XATTR | PDBGF_XATTRDB, "user.pseudo_data xattribute does not get to go in database.\n"); return -1; } switch (flags) { case XATTR_CREATE: op = OP_CREATE_XATTR; break; case XATTR_REPLACE: op = OP_REPLACE_XATTR; break; default: op = OP_SET_XATTR; break; } pseudo_msg_t *result = pseudo_client_op(op, 0, fd, -1, path, &buf, name, value, size); /* we automatically assume success */ if (op == OP_SET_XATTR) { return 0; } /* CREATE/REPLACE operations can report failure */ if (!result || result->result == RESULT_FAIL) { return -1; } return 0; } static ssize_t shared_listxattr(const char *path, int fd, char *list, size_t size) { RC_AND_BUF pseudo_msg_t *result = pseudo_client_op(OP_LIST_XATTR, 0, fd, -1, path, &buf); if (result->result != RESULT_SUCCEED) { pseudo_debug(PDBGF_XATTR, "listxattr: no success.\n"); errno = ENOATTR; return -1; } if (list) { pseudo_debug(PDBGF_XATTR, "listxattr: %d bytes of names, starting '%.*s'\n", (int) result->pathlen, (int) result->pathlen, result->path); if (size >= result->pathlen) { memcpy(list, result->path, result->pathlen); } else { memcpy(list, result->path, size); errno = ERANGE; } } return result->pathlen; } static int shared_removexattr(const char *path, int fd, const char *name) { RC_AND_BUF pseudo_msg_t *result = pseudo_client_op(OP_REMOVE_XATTR, 0, fd, -1, path, &buf, name); if (result->result != RESULT_SUCCEED) { /* docs say ENOATTR, but I don't have one */ errno = ENOENT; return -1; } return 0; } pseudo-1.8.1+git20161012/ports/linux/xattr/wrapfuncs.in000066400000000000000000000020171300506512600224220ustar00rootroot00000000000000ssize_t getxattr(const char *path, const char *name, void *value, size_t size); /* flags=0 */ ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); /* flags=AT_SYMLINK_NOFOLLOW */ ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); int setxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=0 */ int lsetxattr(const char *path, const char *name, const void *value, size_t size, int xflags); /* flags=AT_SYMLINK_NOFOLLOW */ int fsetxattr(int filedes, const char *name, const void *value, size_t size, int xflags); ssize_t listxattr(const char *path, char *list, size_t size); /* flags=0 */ ssize_t llistxattr(const char *path, char *list, size_t size); /* flags=AT_SYMLINK_NOFOLLOW */ ssize_t flistxattr(int filedes, char *list, size_t size); int removexattr(const char *path, const char *name); /* flags=0 */ int lremovexattr(const char *path, const char *name); /* flags=AT_SYMLINK_NOFOLLOW */ int fremovexattr(int filedes, const char *name); pseudo-1.8.1+git20161012/ports/uids_generic/000077500000000000000000000000001300506512600202215ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/uids_generic/guts/000077500000000000000000000000001300506512600212035ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/uids_generic/guts/COPYRIGHT000066400000000000000000000013521300506512600224770ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/endgrent.c000066400000000000000000000002631300506512600231560ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static void * wrap_endgrent(void) { * */ pseudo_grp_close(); /* return; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/endpwent.c000066400000000000000000000002631300506512600231740ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static void * wrap_endpwent(void) { * */ pseudo_pwd_close(); /* return; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getegid.c000066400000000000000000000003061300506512600227560ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static gid_t * wrap_getegid(void) { * gid_t rc = 0; */ rc = pseudo_egid; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/geteuid.c000066400000000000000000000003061300506512600227740ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static uid_t * wrap_geteuid(void) { * uid_t rc = 0; */ rc = pseudo_euid; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getgid.c000066400000000000000000000003051300506512600226100ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static gid_t * wrap_getgid(void) { * gid_t rc = 0; */ rc = pseudo_rgid; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getgrent.c000066400000000000000000000006271300506512600231730ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static struct group * * wrap_getgrent(void) { * struct group * rc = NULL; */ static struct group grp; static char grbuf[PSEUDO_PWD_MAX]; int r_rc; r_rc = wrap_getgrent_r(&grp, grbuf, PSEUDO_PWD_MAX, &rc); /* different error return conventions */ if (r_rc != 0) { errno = r_rc; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getgrgid.c000066400000000000000000000006411300506512600231440ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static struct group * * wrap_getgrgid(gid_t gid) { * struct group * rc = NULL; */ static struct group grp; static char grbuf[PSEUDO_PWD_MAX]; int r_rc; r_rc = wrap_getgrgid_r(gid, &grp, grbuf, PSEUDO_PWD_MAX, &rc); /* different error return conventions */ if (r_rc != 0) { errno = r_rc; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getgrgid_r.c000066400000000000000000000014231300506512600234640ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgrgid_r(gid_t gid, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) { * int rc = -1; */ setgrent(); while ((rc = wrap_getgrent_r(gbuf, buf, buflen, gbufp)) == 0) { /* 0 means no error occurred, and *gbufp == gbuf */ if (gbuf->gr_gid == gid) { pseudo_debug(PDBGF_CLIENT, "found group gid %d, name %s\n", gbuf->gr_gid, gbuf->gr_name); endgrent(); return rc; } } endgrent(); /* we never found a match; rc is 0 if there was no error, or * non-zero if an error occurred. Either way, set the * pwbufp pointer to NULL to indicate that we didn't find * something, and leave rc alone. */ *gbufp = NULL; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getgrnam.c000066400000000000000000000006531300506512600231570ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static struct group * * wrap_getgrnam(const char *name) { * struct group * rc = NULL; */ static struct group grp; static char grbuf[PSEUDO_PWD_MAX]; int r_rc; r_rc = wrap_getgrnam_r(name, &grp, grbuf, PSEUDO_PWD_MAX, &rc); /* different error return conventions */ if (r_rc != 0) { errno = r_rc; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getgrnam_r.c000066400000000000000000000013241300506512600234740ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getgrnam_r(const char *name, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp) { * int rc = -1; */ setgrent(); while ((rc = wrap_getgrent_r(gbuf, buf, buflen, gbufp)) == 0) { /* 0 means no error occurred, and *gbufp == gbuf */ if (gbuf->gr_name && !strcmp(gbuf->gr_name, name)) { endgrent(); return rc; } } endgrent(); /* we never found a match; rc is 0 if there was no error, or * non-zero if an error occurred. Either way, set the * pwbufp pointer to NULL to indicate that we didn't find * something, and leave rc alone. */ *gbufp = NULL; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getpwent.c000066400000000000000000000006321300506512600232050ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static struct passwd * * wrap_getpwent(void) { * struct passwd * rc = NULL; */ static struct passwd pwd; static char pwbuf[PSEUDO_PWD_MAX]; int r_rc; r_rc = wrap_getpwent_r(&pwd, pwbuf, PSEUDO_PWD_MAX, &rc); /* different error return conventions */ if (r_rc != 0) { errno = r_rc; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getpwnam.c000066400000000000000000000006541300506512600231760ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static struct passwd * * wrap_getpwnam(const char *name) { * struct passwd * rc = NULL; */ static struct passwd pwd; static char pwbuf[PSEUDO_PWD_MAX]; int r_rc; r_rc = wrap_getpwnam_r(name, &pwd, pwbuf, PSEUDO_PWD_MAX, &rc); /* different error return conventions */ if (r_rc != 0) { errno = r_rc; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getpwnam_r.c000066400000000000000000000013361300506512600235150ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) { * int rc = -1; */ setpwent(); while ((rc = wrap_getpwent_r(pwbuf, buf, buflen, pwbufp)) == 0) { /* 0 means no error occurred, and *pwbufp == pwbuf */ if (pwbuf->pw_name && !strcmp(pwbuf->pw_name, name)) { endpwent(); return rc; } } endpwent(); /* we never found a match; rc is 0 if there was no error, or * non-zero if an error occurred. Either way, set the * pwbufp pointer to NULL to indicate that we didn't find * something, and leave rc alone. */ *pwbufp = NULL; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getpwuid.c000066400000000000000000000006441300506512600232030ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static struct passwd * * wrap_getpwuid(uid_t uid) { * struct passwd * rc = NULL; */ static struct passwd pwd; static char pwbuf[PSEUDO_PWD_MAX]; int r_rc; r_rc = wrap_getpwuid_r(uid, &pwd, pwbuf, PSEUDO_PWD_MAX, &rc); /* different error return conventions */ if (r_rc != 0) { errno = r_rc; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getpwuid_r.c000066400000000000000000000012741300506512600235240ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_getpwuid_r(uid_t uid, struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp) { * int rc = -1; */ setpwent(); while ((rc = wrap_getpwent_r(pwbuf, buf, buflen, pwbufp)) == 0) { /* 0 means no error occurred, and *pwbufp == pwbuf */ if (pwbuf->pw_uid == uid) { endpwent(); return rc; } } endpwent(); /* we never found a match; rc is 0 if there was no error, or * non-zero if an error occurred. Either way, set the * pwbufp pointer to NULL to indicate that we didn't find * something, and leave rc alone. */ *pwbufp = NULL; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/getuid.c000066400000000000000000000003051300506512600226260ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static uid_t * wrap_getuid(void) { * uid_t rc = 0; */ rc = pseudo_ruid; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setegid.c000066400000000000000000000006151300506512600227750ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setegid(gid_t egid) { * int rc = -1; */ if (pseudo_euid == 0 || egid == pseudo_egid || egid == pseudo_rgid || egid == pseudo_sgid) { pseudo_egid = egid; pseudo_fgid = egid; pseudo_client_touchgid(); rc = 0; } else { rc = -1; errno = EPERM; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/seteuid.c000066400000000000000000000006151300506512600230130ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_seteuid(uid_t euid) { * int rc = -1; */ if (pseudo_euid == 0 || euid == pseudo_euid || euid == pseudo_ruid || euid == pseudo_suid) { pseudo_euid = euid; pseudo_fuid = euid; pseudo_client_touchuid(); rc = 0; } else { rc = -1; errno = EPERM; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setgid.c000066400000000000000000000010141300506512600226220ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setgid(gid_t gid) { * int rc = -1; */ if (pseudo_euid == 0) { pseudo_rgid = gid; pseudo_egid = gid; pseudo_sgid = gid; pseudo_fgid = gid; pseudo_client_touchgid(); rc = 0; } else if (pseudo_egid == gid || pseudo_sgid == gid || pseudo_rgid == gid) { pseudo_egid = gid; pseudo_fgid = gid; pseudo_client_touchgid(); rc = 0; } else { rc = -1; errno = EPERM; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setgrent.c000066400000000000000000000002621300506512600232020ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static void * wrap_setgrent(void) { * */ pseudo_grp_open(); /* return; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setpwent.c000066400000000000000000000002621300506512600232200ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static void * wrap_setpwent(void) { * */ pseudo_pwd_open(); /* return; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setregid.c000066400000000000000000000012671300506512600231630ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setregid(gid_t rgid, gid_t egid) { * int rc = -1; */ rc = 0; if (pseudo_euid != 0 && rgid != (gid_t) -1 && rgid != pseudo_egid && rgid != pseudo_rgid && rgid != pseudo_sgid) { rc = -1; errno = EPERM; } if (pseudo_euid != 0 && egid != (gid_t) -1 && egid != pseudo_egid && egid != pseudo_rgid && egid != pseudo_sgid) { rc = -1; errno = EPERM; } if (rc != -1) { if (rgid != (gid_t) -1) pseudo_rgid = rgid; if (egid != (gid_t) -1) pseudo_egid = egid; pseudo_fgid = pseudo_egid; pseudo_client_touchuid(); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setreuid.c000066400000000000000000000012671300506512600232010ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setreuid(uid_t ruid, uid_t euid) { * int rc = -1; */ rc = 0; if (pseudo_euid != 0 && ruid != (uid_t) -1 && ruid != pseudo_euid && ruid != pseudo_ruid && ruid != pseudo_suid) { rc = -1; errno = EPERM; } if (pseudo_euid != 0 && euid != (uid_t) -1 && euid != pseudo_euid && euid != pseudo_ruid && euid != pseudo_suid) { rc = -1; errno = EPERM; } if (rc != -1) { if (ruid != (uid_t) -1) pseudo_ruid = ruid; if (euid != (uid_t) -1) pseudo_euid = euid; pseudo_fuid = pseudo_euid; pseudo_client_touchuid(); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/guts/setuid.c000066400000000000000000000010141300506512600226400ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_setuid(uid_t uid) { * int rc = -1; */ if (pseudo_euid == 0) { pseudo_ruid = uid; pseudo_euid = uid; pseudo_suid = uid; pseudo_fuid = uid; pseudo_client_touchuid(); rc = 0; } else if (pseudo_euid == uid || pseudo_suid == uid || pseudo_ruid == uid) { pseudo_euid = uid; pseudo_fuid = uid; pseudo_client_touchuid(); rc = 0; } else { rc = -1; errno = EPERM; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/uids_generic/wrapfuncs.in000066400000000000000000000017631300506512600225700ustar00rootroot00000000000000# I bet you never knew there were this many of these! gid_t getegid(void); gid_t getgid(void); int getgrgid_r(gid_t gid, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); int getgrnam_r(const char *name, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); int getpwuid_r(uid_t uid, struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); int setegid(gid_t egid); int seteuid(uid_t euid); int setgid(gid_t gid); int setregid(gid_t rgid, gid_t egid); int setreuid(uid_t ruid, uid_t euid); int setuid(uid_t uid); struct group *getgrent(void); struct group *getgrgid(gid_t gid); struct group *getgrnam(const char *name); struct passwd *getpwent(void); struct passwd *getpwnam(const char *name); struct passwd *getpwuid(uid_t uid); uid_t geteuid(void); uid_t getuid(void); void endgrent(void); void endpwent(void); void setgrent(void); void setpwent(void); pseudo-1.8.1+git20161012/ports/unix/000077500000000000000000000000001300506512600165445ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/unix/guts/000077500000000000000000000000001300506512600175265ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/unix/guts/COPYRIGHT000066400000000000000000000013521300506512600210220ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ pseudo-1.8.1+git20161012/ports/unix/guts/access.c000066400000000000000000000007431300506512600211370ustar00rootroot00000000000000/* * Copyright (c) 2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_access(const char *path, int mode) { * int rc = -1; */ PSEUDO_STATBUF buf; /* note: no attempt to handle the case where user isn't * root. */ rc = base_stat(path, &buf); if (rc == -1) return rc; if (mode & X_OK) { if (buf.st_mode & 0111) { return 0; } else { errno = EPERM; return -1; } } else { return 0; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/acct.c000066400000000000000000000003131300506512600206010ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_acct(const char *path) { * int rc = -1; */ rc = real_acct(path); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/bind.c000066400000000000000000000017641300506512600206160ustar00rootroot00000000000000/* * Copyright (c) 2016 Wind River Systems; see * guts/COPYRIGHT for information. * * int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) * int rc = -1; */ /* I'm not trying to handle broken umasks for this operation right * now. Be good! */ rc = real_bind(sockfd, addr, addrlen); /* we have created a thing! we need to record it in the * database. */ if (addr && addr->sa_family == AF_UNIX && rc >= 0) { struct sockaddr_un *addr_un = (struct sockaddr_un *) addr; /* Linux supports a special hackery where the name starts * with a nul byte, I don't care about those * probably. */ if (addr_un->sun_path[0]) { /* we have to find the path, which is * relative to cwd, so we can create the database * entry. */ char *path = pseudo_root_path(__func__, __LINE__, AT_FDCWD, addr_un->sun_path, AT_SYMLINK_NOFOLLOW); PSEUDO_STATBUF buf; base_stat(path, &buf); pseudo_client_op(OP_MKNOD, 0, -1, -1, path, &buf); } } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/chdir.c000066400000000000000000000006201300506512600207610ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_chdir(const char *path) { * int rc = -1; */ pseudo_debug(PDBGF_CLIENT, "chdir: '%s'\n", path ? path : ""); if (!path) { errno = EFAULT; return -1; } rc = real_chdir(path); if (rc != -1) { pseudo_client_op(OP_CHDIR, 0, -1, -1, path, 0); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/chmod.c000066400000000000000000000003651300506512600207700ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_chmod(const char *path, mode_t mode) { * int rc = -1; */ rc = wrap_fchmodat(AT_FDCWD, path, mode, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/chown.c000066400000000000000000000004121300506512600210050ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_chown(const char *path, uid_t owner, gid_t group) { * int rc = -1; */ rc = wrap_fchownat(AT_FDCWD, path, owner, group, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/chroot.c000066400000000000000000000006431300506512600211730ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_chroot(const char *path) { * int rc = -1; */ pseudo_debug(PDBGF_CLIENT | PDBGF_CHROOT, "chroot: %s\n", path); if (!pseudo_client_op(OP_CHROOT, 0, -1, -1, path, 0)) { pseudo_debug(PDBGF_OP | PDBGF_CHROOT, "chroot failed: %s\n", strerror(errno)); rc = -1; } else { rc = 0; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/close.c000066400000000000000000000005141300506512600207770ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_close(int fd) { * int rc = -1; */ /* this cleans up an internal table, and shouldn't even * make it to the server. */ pseudo_client_op(OP_CLOSE, 0, fd, -1, 0, 0); rc = real_close(fd); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/closedir.c000066400000000000000000000005001300506512600214710ustar00rootroot00000000000000/* * Copyright (c) 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_closedir(DIR *dirp) { * int rc = -1; */ if (!dirp) { errno = EFAULT; return -1; } int fd = dirfd(dirp); pseudo_client_op(OP_CLOSE, 0, fd, -1, 0, 0); rc = real_closedir(dirp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/creat.c000066400000000000000000000003761300506512600207760ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_creat(const char *path, mode_t mode) { * int rc = -1; */ rc = wrap_open(path, O_CREAT|O_WRONLY|O_TRUNC, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/dup.c000066400000000000000000000005601300506512600204630ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_dup(int fd) { * int rc = -1; */ int save_errno; rc = real_dup(fd); save_errno = errno; pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "dup: %d->%d\n", fd, rc); pseudo_client_op(OP_DUP, 0, fd, rc, 0, 0); errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/dup2.c000066400000000000000000000011151300506512600205420ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_dup2(int oldfd, int newfd) { * int rc = -1; */ int save_errno; /* close existing one first - this also causes the socket to the * server to get moved around if someone tries to overwrite it. */ pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "dup2: %d->%d\n", oldfd, newfd); pseudo_client_op(OP_CLOSE, 0, newfd, -1, 0, 0); rc = real_dup2(oldfd, newfd); save_errno = errno; pseudo_client_op(OP_DUP, 0, oldfd, newfd, 0, 0); errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fchdir.c000066400000000000000000000004251300506512600211320ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fchdir(int dirfd) { * int rc = -1; */ rc = real_fchdir(dirfd); if (rc != -1) { pseudo_client_op(OP_CHDIR, 0, -1, dirfd, 0, 0); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fchmod.c000066400000000000000000000011121300506512600211250ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fchmod(int fd, mode_t mode) { * int rc = -1; */ PSEUDO_STATBUF buf; int save_errno = errno; if (base_fstat(fd, &buf) == -1) { /* can't stat it, can't chmod it */ return -1; } buf.st_mode = (buf.st_mode & ~07777) | (mode & 07777); pseudo_client_op(OP_FCHMOD, 0, fd, -1, 0, &buf); real_fchmod(fd, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode))); /* just pretend we worked */ errno = save_errno; rc = 0; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fchmodat.c000066400000000000000000000042641300506512600214650ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fchmodat(int dirfd, const char *path, mode_t mode, int flags) { * int rc = -1; */ PSEUDO_STATBUF buf; int save_errno = errno; if (flags & AT_SYMLINK_NOFOLLOW) { errno = ENOTSUP; return -1; } #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = base_stat(path, &buf); #else rc = base_fstatat(dirfd, path, &buf, flags); #endif if (rc == -1) { return rc; } if (S_ISLNK(buf.st_mode)) { /* we don't really support chmod of a symlink */ errno = ENOSYS; return -1; } save_errno = errno; #if 0 pseudo_msg_t *msg; /* purely for debugging purposes: check whether file * is already in database. We don't need the resulting * information for anything. This is currently ifdefed * out because it's only useful when trying to track where * files are coming from. */ msg = pseudo_client_op(OP_STAT, 0, -1, -1, path, &buf); if (!msg || msg->result != RESULT_SUCCEED) { pseudo_debug(PDBGF_FILE, "chmodat to 0%o on %d/%s, ino %llu, new file.\n", mode, dirfd, path, (unsigned long long) buf.st_ino); } #endif /* user bits added so "root" can always access files. */ #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS /* note: if path was a symlink, and AT_SYMLINK_NOFOLLOW was * specified, we already bailed previously. */ real_chmod(path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode))); #else rc = real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode)), flags); #endif /* we otherwise ignore failures from underlying fchmod, because pseudo * may believe you are permitted to change modes that the filesystem * doesn't. Note that we also don't need to know whether the * file might be a (pseudo) block device or some such; pseudo * will only modify permission bits based on an OP_CHMOD, and does * not care about device/file type mismatches, only directory/file * or symlink/file. */ buf.st_mode = (buf.st_mode & ~07777) | (mode & 07777); pseudo_client_op(OP_CHMOD, 0, -1, dirfd, path, &buf); /* don't change errno from what it was originally */ errno = save_errno; rc = 0; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fchown.c000066400000000000000000000023171300506512600211610ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fchown(int fd, uid_t owner, gid_t group) { * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF buf; int save_errno = errno; if (base_fstat(fd, &buf) == -1) { save_errno = errno; pseudo_debug(PDBGF_CONSISTENCY, "fchown failing because fstat failed: %s\n", strerror(errno)); errno = save_errno; return -1; } if (owner == (uid_t) -1 || group == (gid_t) -1) { msg = pseudo_client_op(OP_STAT, 0, fd, -1, NULL, &buf); /* copy in any existing values... */ if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(&buf, msg); } else { pseudo_debug(PDBGF_FILE, "fchown fd %d, ino %llu, unknown file.\n", fd, (unsigned long long) buf.st_ino); } } /* now override with arguments */ if (owner != (uid_t) -1) { buf.st_uid = owner; } if (group != (gid_t) -1) { buf.st_gid = group; } pseudo_debug(PDBGF_OP, "fchown, fd %d: %d:%d -> %d:%d\n", fd, owner, group, buf.st_uid, buf.st_gid); pseudo_client_op(OP_FCHOWN, 0, fd, -1, 0, &buf); /* pretend we worked, errno should be unchanged */ errno = save_errno; rc = 0; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fchownat.c000066400000000000000000000024301300506512600215020ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fchownat(int dirfd, const char *path, uid_t owner, gid_t group, int flags) { * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF buf; int save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } if (flags & AT_SYMLINK_NOFOLLOW) { rc = base_lstat(path, &buf); } else { rc = base_stat(path, &buf); } #else rc = base_fstatat(dirfd, path, &buf, flags); #endif if (rc == -1) { return rc; } save_errno = errno; if (owner == (uid_t) -1 || group == (gid_t) -1) { msg = pseudo_client_op(OP_STAT, 0, -1, -1, path, &buf); /* copy in any existing values... */ if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(&buf, msg); } else { pseudo_debug(PDBGF_FILE, "chownat to %d:%d on %d/%s, ino %llu, new file.\n", owner, group, dirfd, path, (unsigned long long) buf.st_ino); } } /* now override with arguments */ if (owner != (uid_t) -1) { buf.st_uid = owner; } if (group != (gid_t) -1) { buf.st_gid = group; } pseudo_client_op(OP_CHOWN, 0, -1, dirfd, path, &buf); /* just pretend we worked */ errno = save_errno; rc = 0; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fclose.c000066400000000000000000000004741300506512600211520ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_fclose(FILE *fp) { * int rc = -1; */ if (!fp) { errno = EFAULT; return -1; } int fd = fileno(fp); pseudo_client_op(OP_CLOSE, 0, fd, -1, 0, 0); rc = real_fclose(fp); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fdatasync.c000066400000000000000000000004111300506512600216420ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int fdatasync(int fd) * int rc = -1; */ /* note: wrapper will never call this if PSEUDO_FORCE_ASYNC * is defined. */ rc = real_fdatasync(fd); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fopen.c000066400000000000000000000016671300506512600210130ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static FILE * * wrap_fopen(const char *path, const char *mode) { * FILE * rc = 0; */ PSEUDO_STATBUF buf; int save_errno; int existed = (base_stat(path, &buf) != -1); rc = real_fopen(path, mode); save_errno = errno; if (rc) { int fd = fileno(rc); pseudo_debug(PDBGF_OP, "fopen '%s': fd %d \n", path, fd, (void *) rc); if (base_fstat(fd, &buf) != -1) { if (!existed) { real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { pseudo_debug(PDBGF_CONSISTENCY, "fopen (fd %d) succeeded, but fstat failed (%s).\n", fd, strerror(errno)); pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, 0); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/freopen.c000066400000000000000000000016571300506512600213410ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static FILE * * wrap_freopen(const char *path, const char *mode, FILE *stream) { * FILE * rc = NULL; */ PSEUDO_STATBUF buf; int save_errno; int existed = (base_stat(path, &buf) != -1); rc = real_freopen(path, mode, stream); save_errno = errno; if (rc) { int fd = fileno(rc); pseudo_debug(PDBGF_OP, "freopen '%s': fd %d\n", path, fd); if (base_fstat(fd, &buf) != -1) { if (!existed) { real_fchmod(fd, PSEUDO_FS_MODE(0666 & ~pseudo_umask, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, path, &buf); } pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, &buf); } else { pseudo_debug(PDBGF_OP, "fopen (fd %d) succeeded, but stat failed (%s).\n", fd, strerror(errno)); pseudo_client_op(OP_OPEN, pseudo_access_fopen(mode), fd, -1, path, 0); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fsync.c000066400000000000000000000004011300506512600210070ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int fsync(int fd) * int rc = -1; */ /* note: wrapper will never call this if PSEUDO_FORCE_ASYNC * is defined. */ rc = real_fsync(fd); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/fts_open.c000066400000000000000000000016501300506512600215110ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static FTS * * wrap_fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)) { * FTS * rc = NULL; */ char **rpath_argv; int args = 0; int errored = 0; int i; if (!path_argv) { errno = EFAULT; return NULL; } /* count args */ for (i = 0; path_argv[i]; ++i) { ++args; } rpath_argv = malloc((args + 1) * sizeof(*rpath_argv)); if (!rpath_argv) { errno = ENOMEM; return NULL; } for (i = 0; i < args; ++i) { rpath_argv[i] = PSEUDO_ROOT_PATH(AT_FDCWD, path_argv[i], AT_SYMLINK_NOFOLLOW); if (!rpath_argv[i]) errored = 1; else rpath_argv[i] = strdup(rpath_argv[i]); } if (errored) { errno = ENOMEM; rc = NULL; } else { rc = real_fts_open(path_argv, options, compar); } for (i = 0; i < args; ++i) free(rpath_argv[i]); free(rpath_argv); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/ftw.c000066400000000000000000000004261300506512600204740ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd) { * int rc = -1; */ rc = real_ftw(path, fn, nopenfd); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/getcwd.c000066400000000000000000000036241300506512600211540ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_getcwd(char *buf, size_t size) { * char * rc = NULL; */ pseudo_debug(PDBGF_CLIENT, "wrap_getcwd: %p, %lu\n", (void *) buf, (unsigned long) size); if (!pseudo_cwd) { pseudo_diag("Asked for CWD, but don't have it!\n"); errno = EACCES; return NULL; } /* emulate Linux semantics in case of non-Linux systems. */ if (!buf) { /* if we don't have a cwd, something's very wrong... */ if (!size) { size = pseudo_cwd_len + 1; if (pseudo_chroot_len && size >= pseudo_chroot_len && !memcmp(pseudo_cwd, pseudo_chroot, pseudo_chroot_len)) { size -= pseudo_chroot_len; /* if cwd is precisely the same as chroot, we * actually want a /, not an empty string */ if (size < 2) size = 2; } } if (size) { buf = malloc(size); } else { pseudo_diag("can't figure out CWD: length %ld + 1 - %ld => %ld\n", (unsigned long) pseudo_cwd_len, (unsigned long) pseudo_chroot_len, (unsigned long) size); } if (!buf) { pseudo_diag("couldn't allocate requested CWD buffer - need %ld byes\n", (unsigned long) size); errno = ENOMEM; return NULL; } } if (pseudo_cwd_len - (pseudo_cwd_rel - pseudo_cwd) >= size) { pseudo_debug(PDBGF_CLIENT, "only %ld bytes available, need %ld (%ld + 1 - %ld)\n", (unsigned long) size, (unsigned long) pseudo_cwd_len + 1 - pseudo_chroot_len, (unsigned long) pseudo_cwd_len, (unsigned long) pseudo_chroot_len); errno = ERANGE; return NULL; } rc = buf; pseudo_debug(PDBGF_CLIENT, "getcwd: copying %d (%d + 1 - %d) characters from <%s>.\n", (int) ((pseudo_cwd_len + 1) - pseudo_chroot_len), (int) pseudo_cwd_len, (int) pseudo_chroot_len, pseudo_cwd_rel); memcpy(buf, pseudo_cwd_rel, (pseudo_cwd_len + 1) - (pseudo_cwd_rel - pseudo_cwd)); if (!*buf) { strcpy(buf, "/"); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/getwd.c000066400000000000000000000007511300506512600210070ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_getwd(char *buf) { * char * rc = NULL; */ pseudo_debug(PDBGF_CLIENT, "getwd (getcwd)\n"); rc = wrap_getcwd(buf, pseudo_path_max()); /* because it would violate everything we have ever known about * UNIX for these functions to have the same errno semantics, * that's why. */ if (rc == NULL && errno == ERANGE ) errno = ENAMETOOLONG; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/glob.c000066400000000000000000000014751300506512600206240ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) { * int rc = -1; */ char *rpattern = NULL; int alloced = 0; /* note: no canonicalization */ if (pattern && (*pattern == '/') && pseudo_chroot_len) { size_t len = strlen(pattern) + pseudo_chroot_len + 2; rpattern = malloc(len); if (!rpattern) { errno = ENOMEM; return GLOB_NOSPACE; } snprintf(rpattern, len, "%s/%s", pseudo_chroot, pattern); alloced = 1; } rc = real_glob(alloced ? rpattern : pattern, flags, errfunc, pglob); free(rpattern); if (rc == 0) { unsigned int i; for (i = 0; i < pglob->gl_pathc; ++i) { pseudo_dechroot(pglob->gl_pathv[i], (size_t) -1); } } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/lchown.c000066400000000000000000000003511300506512600211630ustar00rootroot00000000000000/* * Copyright (c) 2008,2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int lchown(const char *path, uid_t owner, gid_t group) * int rc = -1; */ rc = wrap_chown(path, owner, group); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/link.c000066400000000000000000000012731300506512600206320ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_link(const char *oldname, const char *newname) { * int rc = -1; */ /* since 2.6.18 or so, linkat supports AT_SYMLINK_FOLLOW, which * provides the behavior link() has on most non-Linux systems, * but the default is not to follow symlinks. Better yet, it * does NOT support AT_SYMLINK_NOFOLLOW! So define this in * your port's portdefs.h or hope the default works for you. */ rc = wrap_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, PSEUDO_LINK_SYMLINK_BEHAVIOR); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/linkat.c000066400000000000000000000050241300506512600211550ustar00rootroot00000000000000/* * Copyright (c) 2012, 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int linkat(int olddirfd, const char *oldname, int newdirfd, const char *newname, int flags) * int rc = -1; */ int rc2, rflags, save_errno; pseudo_msg_t *msg; char *oldpath = NULL, *newpath = NULL; PSEUDO_STATBUF buf; /* This is gratuitously complicated. On Linux 2.6.18 and later, * flags may contain AT_SYMLINK_FOLLOW, which implies following * symlinks; otherwise, linkat() will *not* follow symlinks. FreeBSD * appears to use the same semantics. * * So on Darwin, always pass AT_SYMLINK_FOLLOW, because the * alternative doesn't work. And never pass AT_SYMLINK_NOFOLLOW * because that's not a valid flag to linkat(). * * So we need a flag for path resolution which is AT_SYMLINK_NOFOLLOW * unless AT_SYMLINK_FOLLOW was specified, in which case it's 0. */ rflags = (flags & AT_SYMLINK_FOLLOW) ? 0 : AT_SYMLINK_NOFOLLOW; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) { errno = ENOSYS; return -1; } #endif oldpath = pseudo_root_path(__func__, __LINE__, olddirfd, oldname, rflags); newpath = pseudo_root_path(__func__, __LINE__, newdirfd, newname, AT_SYMLINK_NOFOLLOW); rc = real_link(oldpath, newpath); save_errno = errno; if (rc == -1) { errno = save_errno; return rc; } /* if we got this far, the link succeeded, and oldpath and newpath * are the newly-allocated canonical paths. If OS, filesystem, or * the flags value prevent hard linking to symlinks, the resolved * path should be the target's path anyway, so lstat is safe here. */ /* find the target: */ rc2 = base_lstat(oldpath, &buf); if (rc2 == -1) { pseudo_diag("Fatal: Tried to stat '%s' after linking it, but failed: %s.\n", oldpath, strerror(errno)); errno = ENOENT; return rc; } msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, &buf); if (msg && msg->result == RESULT_SUCCEED) { pseudo_stat_msg(&buf, msg); } /* Long story short: I am pretty sure we still want OP_LINK even * if the thing linked is a symlink. */ pseudo_client_op(OP_LINK, 0, -1, -1, newpath, &buf); errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/lutimes.c000066400000000000000000000003571300506512600213610ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_lutimes(const char *path, const struct timeval *tv) { * int rc = -1; */ rc = real_lutimes(path, tv); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mkdir.c000066400000000000000000000003611300506512600210000ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_mkdir(const char *path, mode_t mode) { * int rc = -1; */ rc = wrap_mkdirat(AT_FDCWD, path, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mkdirat.c000066400000000000000000000022121300506512600213220ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_mkdirat(int dirfd, const char *path, mode_t mode) { * int rc = -1; */ /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = real_mkdir(path, PSEUDO_FS_MODE(mode, 1)); #else rc = real_mkdirat(dirfd, path, PSEUDO_FS_MODE(mode, 1)); #endif if (rc != -1) { PSEUDO_STATBUF buf; int stat_rc; int save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS stat_rc = base_lstat(path, &buf); #else stat_rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); #endif if (stat_rc != -1) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); pseudo_client_op(OP_MKDIR, 0, -1, dirfd, path, &buf); #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS real_fchmod(path, PSEUDO_FS_MODE(mode, 1)); #else real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, 1), 0); #endif } else { pseudo_debug(PDBGF_OP, "mkdir of %s succeeded, but stat failed: %s\n", path, strerror(errno)); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mkdtemp.c000066400000000000000000000016721300506512600213410ustar00rootroot00000000000000/* * Copyright (c) 2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_mkdtemp(char *template) { * char * rc = NULL; */ PSEUDO_STATBUF buf; int save_errno; size_t len; char *tmp_template; if (!template) { errno = EFAULT; return NULL; } len = strlen(template); tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW); if (!tmp_template) { errno = ENOENT; return NULL; } rc = real_mkdtemp(tmp_template); if (rc != NULL) { save_errno = errno; if (base_stat(rc, &buf) != -1) { pseudo_client_op(OP_CREAT, 0, -1, -1, tmp_template, &buf); } else { pseudo_debug(PDBGF_CONSISTENCY, "mkdtemp (path %s) succeeded, but fstat failed (%s).\n", rc, strerror(errno)); } errno = save_errno; } /* mkdtemp only changes the XXXXXX at the end. */ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6); rc = template; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mkfifo.c000066400000000000000000000003631300506512600211470ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_mkfifo(const char *path, mode_t mode) { * int rc = -1; */ rc = wrap_mkfifoat(AT_FDCWD, path, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mkfifoat.c000066400000000000000000000034231300506512600214740ustar00rootroot00000000000000/* * Copyright (c) 2015 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_mkfifoat(int dirfd, const char *path, mode_t mode) { * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF buf; int save_errno = errno; /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = base_stat(path, &buf); #else rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); #endif if (rc != -1) { /* if we can stat the file, you can't mkfifo it */ errno = EEXIST; return -1; } #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real_mkfifo(path, PSEUDO_FS_MODE(mode, 0)); if (rc == -1) { return -1; } save_errno = errno; rc = base_stat(path, &buf); real_chmod(path, PSEUDO_FS_MODE(mode, 0)); #else rc = real_mkfifoat(dirfd, path, PSEUDO_FS_MODE(mode, 0)); if (rc == -1) { return -1; } save_errno = errno; rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, 0), 0); #endif /* if the stat failed, we are going to give up and nuke * any file we may have created, and hope for the best. */ if (rc == 0) { buf.st_mode = PSEUDO_DB_MODE(buf.st_mode, mode); /* mkfifo/mknod are the same op, in that they create a file * with a non-file type. */ msg = pseudo_client_op(OP_MKNOD, 0, -1, dirfd, path, &buf); if (msg && msg->result != RESULT_SUCCEED) { errno = EPERM; rc = -1; } else { /* just pretend we worked */ errno = save_errno; rc = 0; } } if (rc == -1) { save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS real_unlink(path); #else real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); #endif errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mknod.c000066400000000000000000000003571300506512600210070ustar00rootroot00000000000000/* * Copyright (c) 2011,2014 Wind River Systems; see * guts/COPYRIGHT for information. * * int mknod(const char *path, mode_t mode, dev_t dev) * int rc = -1; */ rc = wrap_mknodat(AT_FDCWD, path, mode, dev); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mknodat.c000066400000000000000000000036511300506512600213340ustar00rootroot00000000000000/* * Copyright (c) 2011 Wind River Systems; see * guts/COPYRIGHT for information. * * int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev) * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF buf; int save_errno = errno; /* mask out mode bits appropriately */ mode = mode & ~pseudo_umask; /* if you don't specify a type, assume regular file */ if (!(mode & S_IFMT)) { mode |= S_IFREG; } pseudo_debug(PDBGF_FILE, "mknodat creating '%s', mode 0%o\n", path ? path : "", (int) mode); #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = base_stat(path, &buf); #else rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); #endif if (rc != -1) { /* if we can stat the file, you can't mknod it */ errno = EEXIST; return -1; } #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real_open(path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode, 0)); #else rc = real_openat(dirfd, path, O_CREAT | O_WRONLY | O_EXCL, PSEUDO_FS_MODE(mode, 0)); #endif if (rc == -1) { return -1; } real_fchmod(rc, PSEUDO_FS_MODE(mode, 0)); base_fstat(rc, &buf); /* mknod does not really open the file. We don't have * to use wrap_close because we've never exposed this file * descriptor to the client code. */ real_close(rc); /* mask in the mode type bits again */ buf.st_mode = (PSEUDO_DB_MODE(buf.st_mode, mode) & 07777) | (mode & ~07777); buf.st_rdev = dev; msg = pseudo_client_op(OP_MKNOD, 0, -1, dirfd, path, &buf); if (msg && msg->result != RESULT_SUCCEED) { errno = EPERM; rc = -1; } else { /* just pretend we worked */ errno = save_errno; rc = 0; } if (rc == -1) { save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS real_unlink(path); #else real_unlinkat(dirfd, path, AT_SYMLINK_NOFOLLOW); #endif errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mkstemp.c000066400000000000000000000021511300506512600213510ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_mkstemp(char *template) { * int rc = -1; */ PSEUDO_STATBUF buf; int save_errno; size_t len; char *tmp_template; if (!template) { errno = EFAULT; return 0; } len = strlen(template); tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW); if (!tmp_template) { errno = ENOENT; return -1; } rc = real_mkstemp(tmp_template); if (rc != -1) { save_errno = errno; if (base_fstat(rc, &buf) != -1) { real_fchmod(rc, PSEUDO_FS_MODE(0600, 0)); pseudo_client_op(OP_CREAT, 0, -1, -1, tmp_template, &buf); pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, &buf); } else { pseudo_debug(PDBGF_CONSISTENCY, "mkstemp (fd %d) succeeded, but fstat failed (%s).\n", rc, strerror(errno)); pseudo_client_op(OP_OPEN, PSA_READ | PSA_WRITE, rc, -1, tmp_template, 0); } errno = save_errno; } /* mkstemp only changes the XXXXXX at the end. */ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/mktemp.c000066400000000000000000000012521300506512600211670ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_mktemp(char *template) { * char * rc = NULL; */ size_t len; char *tmp_template; if (!template) { errno = EFAULT; return NULL; } len = strlen(template); tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW); if (!tmp_template) { errno = ENOENT; return NULL; } rc = real_mktemp(tmp_template); /* mktemp only changes the XXXXXX at the end, and never created * a file -- note the race condition implied here. */ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6); rc = template; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/msync.c000066400000000000000000000004601300506512600210230ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int msync(void *addr, size_t length, int flags) * int rc = -1; */ /* note: wrapper will never call this if PSEUDO_FORCE_ASYNC * is defined. */ rc = real_msync(addr, length, flags); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/nftw.c000066400000000000000000000004661300506512600206560ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag) { * int rc = -1; */ rc = real_nftw(path, fn, nopenfd, flag); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/opendir.c000066400000000000000000000011701300506512600213310ustar00rootroot00000000000000/* * Copyright (c) 2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static DIR * * wrap_opendir(const char *path) { * DIR * rc = NULL; */ PSEUDO_STATBUF buf; int save_errno; rc = real_opendir(path); if (rc) { int fd; save_errno = errno; fd = dirfd(rc); if (base_fstat(fd, &buf) == -1) { pseudo_debug(PDBGF_CONSISTENCY, "diropen (fd %d) succeeded, but fstat failed (%s).\n", fd, strerror(errno)); pseudo_client_op(OP_OPEN, PSA_READ, fd, -1, path, 0); } else { pseudo_client_op(OP_OPEN, PSA_READ, fd, -1, path, &buf); } errno = save_errno; } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/pathconf.c000066400000000000000000000003451300506512600214760ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static long * wrap_pathconf(const char *path, int name) { * long rc = -1; */ rc = real_pathconf(path, name); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/popen.c000066400000000000000000000006451300506512600210200ustar00rootroot00000000000000/* * Copyright (c) 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * FILE *popen(const char *command, const char *mode) * FILE *rc = NULL; */ /* on at least some systems, popen() calls fork and exec * in ways that avoid our usual enforcement of the environment. */ pseudo_setupenv(); if (pseudo_has_unload(NULL)) pseudo_dropenv(); rc = real_popen(command, mode); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/readlink.c000066400000000000000000000004161300506512600214640ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static ssize_t * wrap_readlink(const char *path, char *buf, size_t bufsiz) { * ssize_t rc = -1; */ rc = wrap_readlinkat(AT_FDCWD, path, buf, bufsiz); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/readlinkat.c000066400000000000000000000007371300506512600220170ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static ssize_t * wrap_readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { * ssize_t rc = -1; */ #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = real_readlink(path, buf, bufsiz); #else rc = real_readlinkat(dirfd, path, buf, bufsiz); #endif if (rc > 0) { rc = pseudo_dechroot(buf, rc); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/realpath.c000066400000000000000000000010501300506512600214660ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_realpath(const char *name, char *resolved_name) { * char * rc = NULL; */ char *rname = PSEUDO_ROOT_PATH(AT_FDCWD, name, 0); ssize_t len; if (!rname) { errno = ENAMETOOLONG; return NULL; } if ((len = strlen(rname)) >= pseudo_sys_path_max()) { errno = ENAMETOOLONG; return NULL; } if (resolved_name) { memcpy(resolved_name, rname, len + 1); rc = resolved_name; } else { rc = strdup(rname); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/remove.c000066400000000000000000000005711300506512600211720ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_remove(const char *path) { * int rc = -1; */ PSEUDO_STATBUF buf; if (base_lstat(path, &buf) == -1) { errno = ENOENT; return -1; } if (S_ISDIR(buf.st_mode)) { rc = wrap_rmdir(path); } else { rc = wrap_unlink(path); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/rename.c000066400000000000000000000056611300506512600211510ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_rename(const char *oldpath, const char *newpath) { * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF oldbuf, newbuf; int oldrc, newrc; int save_errno; int old_db_entry = 0; int may_unlinked = 0; pseudo_debug(PDBGF_OP, "rename: %s->%s\n", oldpath ? oldpath : "", newpath ? newpath : ""); if (!oldpath || !newpath) { errno = EFAULT; return -1; } save_errno = errno; newrc = base_lstat(newpath, &newbuf); oldrc = base_lstat(oldpath, &oldbuf); errno = save_errno; /* newpath must be removed. */ /* as with unlink, we have to mark that the file may get deleted */ msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, -1, newpath, newrc ? NULL : &newbuf); if (msg && msg->result == RESULT_SUCCEED) may_unlinked = 1; msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, oldrc ? NULL : &oldbuf); if (msg && msg->result == RESULT_SUCCEED) old_db_entry = 1; rc = real_rename(oldpath, newpath); save_errno = errno; if (may_unlinked) { if (rc == -1) { /* since we failed, that wasn't really unlinked -- put * it back. */ pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, newpath, &newbuf); } else { /* confirm that the file was removed */ pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, newpath, &newbuf); } } if (rc == -1) { /* and we're done. */ errno = save_errno; return rc; } save_errno = errno; /* nothing to do for a "rename" of a link to itself */ if (newrc != -1 && oldrc != -1 && newbuf.st_dev == oldbuf.st_dev && newbuf.st_ino == oldbuf.st_ino) { return rc; } /* rename(3) is not mv(1). rename(file, dir) fails; you must provide * the corrected path yourself. You can rename over a directory only * if the source is a directory. Symlinks are simply removed. * * If we got here, the real rename call succeeded. That means newpath * has been unlinked and oldpath has been linked to it. * * There are a ton of special cases to error check. I don't check * for any of them, because in every such case, the underlying rename * failed, and there is nothing to do. * The only tricky part is that, because we used to ignore symlinks, * we may have to rename or remove directory trees even though in * theory rename can never destroy a directory tree. */ if (!old_db_entry) { /* create an entry under the old name, which will then be * renamed; this way, children would get renamed too, if there * were any. */ if (newrc == 0) { if (newbuf.st_dev != oldbuf.st_dev) { oldbuf.st_dev = newbuf.st_dev; oldbuf.st_ino = newbuf.st_ino; } } pseudo_debug(PDBGF_FILE, "creating new '%s' [%llu] to rename\n", oldpath, (unsigned long long) oldbuf.st_ino); pseudo_client_op(OP_LINK, 0, -1, -1, oldpath, &oldbuf); } pseudo_client_op(OP_RENAME, 0, -1, -1, newpath, &oldbuf, oldpath); errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/renameat.c000066400000000000000000000067151300506512600214770ustar00rootroot00000000000000/* * Copyright (c) 2008-2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF oldbuf, newbuf; int oldrc, newrc; int save_errno; int old_db_entry = 0; int may_unlinked = 0; pseudo_debug(PDBGF_FILE, "renameat: %d,%s->%d,%s\n", olddirfd, oldpath ? oldpath : "", newdirfd, newpath ? newpath : ""); #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (olddirfd != AT_FDCWD || newdirfd != AT_FDCWD) { errno = ENOSYS; return -1; } #endif if (!oldpath || !newpath) { errno = EFAULT; return -1; } save_errno = errno; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS newrc = base_lstat(newpath, &newbuf); oldrc = base_lstat(oldpath, &oldbuf); #else oldrc = base_fstatat(olddirfd, oldpath, &oldbuf, AT_SYMLINK_NOFOLLOW); newrc = base_fstatat(newdirfd, newpath, &newbuf, AT_SYMLINK_NOFOLLOW); #endif errno = save_errno; /* newpath must be removed. */ /* as with unlink, we have to mark that the file may get deleted */ msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, newdirfd, newpath, newrc ? NULL : &newbuf); if (msg && msg->result == RESULT_SUCCEED) may_unlinked = 1; msg = pseudo_client_op(OP_STAT, 0, -1, olddirfd, oldpath, oldrc ? NULL : &oldbuf); if (msg && msg->result == RESULT_SUCCEED) old_db_entry = 1; rc = real_renameat(olddirfd, oldpath, newdirfd, newpath); save_errno = errno; if (may_unlinked) { if (rc == -1) { /* since we failed, that wasn't really unlinked -- put * it back. */ pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, newdirfd, newpath, &newbuf); } else { /* confirm that the file was removed */ pseudo_client_op(OP_DID_UNLINK, 0, -1, newdirfd, newpath, &newbuf); } } if (rc == -1) { /* and we're done. */ errno = save_errno; return rc; } save_errno = errno; /* nothing to do for a "rename" of a link to itself */ if (newrc != -1 && oldrc != -1 && newbuf.st_dev == oldbuf.st_dev && newbuf.st_ino == oldbuf.st_ino) { return rc; } /* rename(3) is not mv(1). rename(file, dir) fails; you must provide * the corrected path yourself. You can rename over a directory only * if the source is a directory. Symlinks are simply removed. * * If we got here, the real rename call succeeded. That means newpath * has been unlinked and oldpath has been linked to it. * * There are a ton of special cases to error check. I don't check * for any of them, because in every such case, the underlying rename * failed, and there is nothing to do. * The only tricky part is that, because we used to ignore symlinks, * we may have to rename or remove directory trees even though in * theory rename can never destroy a directory tree. */ if (!old_db_entry) { /* create an entry under the old name, which will then be * renamed; this way, children would get renamed too, if there * were any. */ if (newrc == 0) { if (newbuf.st_dev != oldbuf.st_dev) { oldbuf.st_dev = newbuf.st_dev; oldbuf.st_ino = newbuf.st_ino; } } pseudo_debug(PDBGF_OP, "creating new '%s' [%llu] to rename\n", oldpath, (unsigned long long) oldbuf.st_ino); pseudo_client_op(OP_LINK, 0, -1, olddirfd, oldpath, &oldbuf); } /* special case: use 'fd' for olddirfd, because * we know it has no other meaning for RENAME */ pseudo_client_op(OP_RENAME, 0, olddirfd, newdirfd, newpath, &oldbuf, oldpath); errno = save_errno; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/rmdir.c000066400000000000000000000014431300506512600210110ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_rmdir(const char *path) { * int rc = -1; */ pseudo_msg_t *msg; PSEUDO_STATBUF buf; int save_errno; int old_db_entry = 0; rc = base_lstat(path, &buf); if (rc == -1) { return rc; } msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, -1, path, &buf); if (msg && msg->result == RESULT_SUCCEED) old_db_entry = 1; rc = real_rmdir(path); if (old_db_entry) { if (rc == -1) { save_errno = errno; pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, path, &buf); errno = save_errno; } else { pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, path, &buf); } } else { pseudo_debug(PDBGF_FILE, "rmdir on <%s>, not in database, no effect.\n", path); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/symlink.c000066400000000000000000000004061300506512600213600ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_symlink(const char *oldname, const char *newpath) { * int rc = -1; */ rc = wrap_symlinkat(oldname, AT_FDCWD, newpath); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/symlinkat.c000066400000000000000000000023451300506512600217110ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_symlinkat(const char *oldname, int dirfd, const char *newpath) { * int rc = -1; */ PSEUDO_STATBUF buf; char *roldname = 0; if (oldname[0] == '/' && pseudo_chroot_len && !pseudo_nosymlinkexp) { size_t len = pseudo_chroot_len + strlen(oldname) + 1; roldname = malloc(len); snprintf(roldname, len, "%s%s", pseudo_chroot, oldname); } #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } rc = real_symlink(roldname ? roldname : oldname, newpath); #else rc = real_symlinkat(roldname ? roldname : oldname, dirfd, newpath); #endif if (rc == -1) { free(roldname); return rc; } #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = base_lstat(newpath, &buf); #else rc = base_fstatat(dirfd, newpath, &buf, AT_SYMLINK_NOFOLLOW); #endif if (rc == -1) { int save_errno = errno; pseudo_diag("symlinkat: couldn't stat '%s' even though symlink creation succeeded (%s).\n", newpath, strerror(errno)); errno = save_errno; free(roldname); return rc; } /* just record the entry */ pseudo_client_op(OP_SYMLINK, 0, -1, dirfd, newpath, &buf); free(roldname); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/sync.c000066400000000000000000000003571300506512600206530ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * void sync(void) * */ /* note: wrapper will never call this if PSEUDO_FORCE_ASYNC * is defined. */ (void) real_sync(); /* return; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/sync_file_range.c000066400000000000000000000004161300506512600230220ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags) * int rc = -1; */ rc = real_sync_file_range(fd, offset, nbytes, flags); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/system.c000066400000000000000000000004531300506512600212200ustar00rootroot00000000000000/* * Copyright (c) 2011, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * int system(const char *command) * int rc = -1; */ if (!command) return 1; pseudo_setupenv(); if (pseudo_has_unload(NULL)) pseudo_dropenv(); rc = real_system(command); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/tempnam.c000066400000000000000000000006201300506512600213310ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_tempnam(const char *template, const char *pfx) { * char * rc = NULL; */ /* let gcc know we ignored these on purpose */ (void) template; (void) pfx; pseudo_diag("tempnam() is so ludicrously insecure as to defy implementation."); errno = ENOMEM; rc = NULL; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/tmpnam.c000066400000000000000000000005251300506512600211700ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static char * * wrap_tmpnam(char *s) { * char * rc = NULL; */ /* let gcc know we're ignoring this */ (void) s; pseudo_diag("tmpnam() is so ludicrously insecure as to defy implementation."); errno = ENOMEM; rc = NULL; /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/truncate.c000066400000000000000000000003511300506512600215160ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_truncate(const char *path, off_t length) { * int rc = -1; */ rc = real_truncate(path, length); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/umask.c000066400000000000000000000003211300506512600210060ustar00rootroot00000000000000/* * Copyright (c) 2014 Wind River Systems; see * guts/COPYRIGHT for information. * * mode_t umask(mode_t mask) * mode_t rc = 0; */ pseudo_umask = mask; rc = real_umask(mask); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/unlink.c000066400000000000000000000003431300506512600211720ustar00rootroot00000000000000/* * Copyright (c) 2008-2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_unlink(const char *path) { * int rc = -1; */ rc = wrap_unlinkat(AT_FDCWD, path, 0); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/unlinkat.c000066400000000000000000000026711300506512600215250ustar00rootroot00000000000000/* * Copyright (c) 2008-2010, 2012 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_unlinkat(int dirfd, const char *path, int rflags) { * int rc = -1; */ pseudo_msg_t *msg; int save_errno; PSEUDO_STATBUF buf; int old_db_entry = 0; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS if (dirfd != AT_FDCWD) { errno = ENOSYS; return -1; } if (rflags) { /* the only supported flag is AT_REMOVEDIR. We'd never call * with that flag unless the real AT functions exist, so * something must have gone horribly wrong.... */ pseudo_diag("wrap_unlinkat called with flags (0x%x), path '%s'\n", rflags, path ? path : ""); errno = ENOSYS; return -1; } #endif #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = base_lstat(path, &buf); #else rc = base_fstatat(dirfd, path, &buf, AT_SYMLINK_NOFOLLOW); #endif if (rc == -1) { return rc; } msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, dirfd, path, &buf); if (msg && msg->result == RESULT_SUCCEED) old_db_entry = 1; #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS rc = real_unlink(path); #else rc = real_unlinkat(dirfd, path, rflags); #endif if (old_db_entry) { if (rc == -1) { save_errno = errno; pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, path, &buf); errno = save_errno; } else { pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, path, &buf); } } else { pseudo_debug(PDBGF_FILE, "unlink on <%s>, not in database, no effect.\n", path); } /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/utime.c000066400000000000000000000003541300506512600210170ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_utime(const char *path, const struct utimbuf *buf) { * int rc = -1; */ rc = real_utime(path, buf); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/guts/utimes.c000066400000000000000000000003621300506512600212010ustar00rootroot00000000000000/* * Copyright (c) 2010 Wind River Systems; see * guts/COPYRIGHT for information. * * static int * wrap_utimes(const char *path, const struct timeval *times) { * int rc = -1; */ rc = real_utimes(path, times); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/pseudo_wrappers.c000066400000000000000000000020441300506512600221320ustar00rootroot00000000000000FILE * popen(const char *command, const char *mode) { sigset_t saved; FILE *rc = NULL; if (!pseudo_check_wrappers() || !real_popen) { /* rc was initialized to the "failure" value */ pseudo_enosys("popen"); return rc; } pseudo_debug(PDBGF_WRAPPER, "called: popen\n"); pseudo_sigblock(&saved); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); return NULL; } int save_errno; /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; rc = wrap_popen(command, mode); save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); #if 0 /* This can cause hangs on some recentish systems which use locale * stuff for strerror... */ pseudo_debug(PDBGF_WRAPPER, "completed: popen (maybe: %s)\n", strerror(save_errno)); #endif pseudo_debug(PDBGF_WRAPPER, "completed: popen (errno: %d)\n", save_errno); errno = save_errno; return rc; } static FILE * wrap_popen(const char *command, const char *mode) { FILE *rc = NULL; #include "guts/popen.c" return rc; } pseudo-1.8.1+git20161012/ports/unix/subports000077500000000000000000000002741300506512600203560ustar00rootroot00000000000000#!/bin/sh cat > dummy.c < int main(void) { syncfs(0); return 0; } EOF if ${CC} -o dummy dummy.c > /dev/null 2>&1; then echo "unix/syncfs" fi rm -f dummy.c dummy pseudo-1.8.1+git20161012/ports/unix/syncfs/000077500000000000000000000000001300506512600200515ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/unix/syncfs/guts/000077500000000000000000000000001300506512600210335ustar00rootroot00000000000000pseudo-1.8.1+git20161012/ports/unix/syncfs/guts/syncfs.c000066400000000000000000000002611300506512600225030ustar00rootroot00000000000000/* * Copyright (c) 2013 Wind River Systems; see * guts/COPYRIGHT for information. * * int syncfs(int fd) * int rc = -1; */ rc = real_syncfs(fd); /* return rc; * } */ pseudo-1.8.1+git20161012/ports/unix/syncfs/wrapfuncs.in000066400000000000000000000001041300506512600224040ustar00rootroot00000000000000# Added around 2011 to glibc int syncfs(int fd); /* async_skip=0 */ pseudo-1.8.1+git20161012/ports/unix/wrapfuncs.in000066400000000000000000000102031300506512600211000ustar00rootroot00000000000000int creat(const char *path, mode_t mode); char *getcwd(char *buf, size_t size); char *getwd(char *buf); int close(int fd); int fchmod(int fd, mode_t mode); int fchown(int fd, uid_t owner, gid_t group); int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */ int dup2(int oldfd, int newfd); int dup(int fd); int chdir(const char *path); int fchdir(int dirfd); int access(const char *path, int mode); FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)); /* inode64=1 */ int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd); int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag); int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob); int lutimes(const char *path, const struct timeval *tv); /* flags=AT_SYMLINK_NOFOLLOW */ char *mkdtemp(char *template); char *mktemp(char *template); long pathconf(const char *path, int name); char *realpath(const char *name, char *resolved_name); /* version="GLIBC_2.3" */ int remove(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ DIR *opendir(const char *path); int closedir(DIR *dirp); char *tempnam(const char *template, const char *pfx); char *tmpnam(char *s); int truncate(const char *path, off_t length); int utime(const char *path, const struct utimbuf *buf); int utimes(const char *path, const struct timeval *times); # needed because libc stdio does horrible things with inline asm syscalls FILE *fopen(const char *path, const char *mode); int fclose(FILE *fp); FILE *freopen(const char *path, const char *mode, FILE *stream); int chroot(const char *path); int acct(const char *path); int chmod(const char *path, mode_t mode); int chown(const char *path, uid_t owner, gid_t group); int fchmodat(int dirfd, const char *path, mode_t mode, int flags); int fchownat(int dirfd, const char *path, uid_t owner, gid_t group, int flags); int link(const char *oldname, const char *newname); /* flags=AT_SYMLINK_NOFOLLOW */ int linkat(int olddirfd, const char *oldname, int newdirfd, const char *newname, int flags); int mkdir(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ int mkdirat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ int mkfifo(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ int mkfifoat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */ int mknod(const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW */ int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* flags=AT_SYMLINK_NOFOLLOW */ int mkstemp(char *template); /* flags=AT_SYMLINK_NOFOLLOW */ int rename(const char *oldpath, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ int rmdir(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ int symlink(const char *oldname, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ int symlinkat(const char *oldname, int dirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ int unlink(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ int unlinkat(int dirfd, const char *path, int rflags); /* flags=AT_SYMLINK_NOFOLLOW */ # primarily for use with chroot() ssize_t readlink(const char *path, char *buf, size_t bufsiz); /* flags=AT_SYMLINK_NOFOLLOW */ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz); /* flags=AT_SYMLINK_NOFOLLOW */ int system(const char *command); FILE *popen(const char *command, const char *mode); /* hand_wrapped=1 */ # Based on experiments by Richard Purdie: Allow pseudo to eliminate # sync-type operations globally, mostly relevant for performance reasons # during filesystem assembly. int fsync(int fd); /* async_skip=0 */ int fdatasync(int fd); /* async_skip=0 */ void sync(void); /* async_skip= */ int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags); /* async_skip=0 */ int msync(void *addr, size_t length, int flags); /* async_skip=0 */ mode_t umask(mode_t mask); int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); pseudo-1.8.1+git20161012/pseudo.1000066400000000000000000000405361300506512600160030ustar00rootroot00000000000000.\" .\" pseudo(1) man page .\" .\" Copyright (c) 2010 Wind River Systems, Inc. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the Lesser GNU General Public License version 2.1 as .\" published by the Free Software Foundation. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .\" See the Lesser GNU General Public License for more details. .\" .\" You should have received a copy of the Lesser GNU General Public License .\" version 2.1 along with this program; if not, write to the Free Software .\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA .TH pseudo 1 "pseudo - pretending to be root" .SH SYNOPSIS .B pseudo .RB [ \-dflv ] [ .B \-x .I flags ] [ .B \-P .I prefix ] [ .B \-rR .I root ] [ .B \-t .I timeout ] .RI [ command ] .PP .B pseudo \-h .PP .B pseudo .RB [ \-dflv ] [ .B \-x .I flags ] [ .B \-P .I prefix ] .RB [ \-BC ] .BR \-i\ path .PP .B pseudo .RB [ \-dflv ] [ .B \-x .I flags ] [ .B \-P .I prefix ] .RB [ \-BC ] .BR \-m\ from\ \-M\ to .PP .B pseudo .RB [ \-dflv ] [ .B \-x .I flags ] [ .B \-P .I prefix ] .B \-S .PP .B pseudo .RB [ \-dflv ] [ .B \-x .I flags ] [ .B \-P .I prefix ] .B \-V .SH DESCRIPTION The .I pseudo utility provides a virtual root environment, hereafter referred to as the .IR pseudo\ environment , allowing the creation of file system images and packages by users without root privileges. The pseudo environment is implemented by pushing a special library .RI ( libpseudo.so ) into the .B LD_PRELOAD environment variable. This library intercepts a large number of common filesystem operations and some user-id related operations, and returns values that look as though the operations had been performed by a root user. This is in turn managed by a daemon program which keeps a list of virtualized file ownership and permissions; this daemon program itself is .IR pseudo . The .I pseudo program itself can also be used as a program launcher. The launcher is used to automatically configure a working environment, then execute processes within that environment. Alternatively, you can bypass this by setting up certain environment variables (see the .B ENVIRONMENT section below). The .I pseudo client library .RI ( libpseudo.so ) can then start the server automatically. The .I pseudo command can be invoked in one of several possible modes: .TP 8 .B \-B The .B \-B option causes .I pseudo to scan its database, as with the .B \-C option, but instead of reporting mismatches, .I pseudo attempts to repair them. Specifically, device and inode number mismatches are corrected, and symlink or directory mismatches result in deletion of database entries. .TP 8 .B \-C The .B \-C option causes .I pseudo to scan its database, comparing against the filesystem, and reporting likely errors. This may be unreliable when the server is actively running. .TP 8 .B \-h The .B \-h option causes .I pseudo to print a usage message and exit. .TP 8 .B \-i The .B \-i option causes .I pseudo to attempt to correct device number mismatches by checking inodes; if .I path has the same inode number as recorded in the database, but a different device number, all instances of the device number recorded in the database are updated to the device number in the live filesystem for .IR path . This is intended to handle the mismatches that can occur when remounting an NFS filesystem. The .B \-i option implies the .B \-C option. You can also specify the .B \-B option to request that the database be rebuilt. .TP 8 .B \-m The .B \-m and .B \-M options cause .I pseudo to rename files, replacing the string .I from with the string .I to. The .B \-m option pair implies the .B \-C option. You can also specify the .B \-B option to request that the database be rebuilt. .TP 8 .B \-V The .B \-V option causes .I pseudo to print configuration information and exit immediately. .TP 8 .B \-S The .B \-S option causes .I pseudo to try to find an existing server, and if it finds one, instructs that server to shut down as soon as all clients are detached from it. Note that the server will not shut down while clients are connected to it; in this case, .I pseudo will print a list of the remaining client PIDs. .TP 8 .B \-d The .B \-d option causes pseudo to immediately detach and run in the background as a daemon. This is rarely useful except for debugging. .PP Finally, invoked without any of these options, .I pseudo sets up an emulated root environment, then invokes .I command if it was provided, otherwise a shell (using the .B SHELL environment variable if it is set, or .I /bin/sh otherwise). The following options modify the behavior of .IR pseudo : .TP 8 .BI \-d\ (daemonize) Run as a daemon; .I pseudo detaches from the calling environment and runs as a daemon. The command returns successfully if this appears to have succeeded, otherwise it produces an error message and returns a failure status. .TP 8 .BI \-f\ (foreground) Run in the foreground; .I pseudo runs as a server, and does not try to start other commands. This mode is useful for debugging. .TP 8 .BI \-l\ (log) Enable logging. The .I pseudo daemon will log every filesystem transaction in the log database. .TP 8 .BI \-r\ root .TP 8 .BI \-R\ root Set the .B PSEUDO_CHROOT environment variable, running as though the program had called .I chroot(2) on the specified path. With .BR \-r , this implies changing the working directory to the specified directory; with .BR \-R , it does not. .TP 8 .B \-t timeout Set the timeout of the .I pseudo daemon, in seconds. The default is currently 30 seconds. After this long with no attached clients, the .I pseudo daemon shuts down automatically. The server never shuts down while it has attached clients. Note that this does not prevent continued use; new clients can restart the daemon if they need it. .TP 8 .BI \-v\ (verbose) Increase the verbosity of the .I pseudo daemon, and the client library for any programs started by this invocation of .IR pseudo . This is equivalent to the numeric form of the .B PSEUDO_DEBUG environment variable; multiple .B \-v options increase the debugging level. .TP 8 .BI \-x\ (debug) Set specific deugging flags (the .I pseudo utility's help message lists them). This is equivalent to the string form of the .B PSEUDO_DEBUG environment variable. .SH EXAMPLES The two most common usages of .I pseudo are using it to run specific commands, and setting up an environment manually for running various other commands. For the first case, the usage is reasonably simple: .sp $ .I /path/to/pseudo .br # .I commands which require root privileges You may have to use the .BI \-P prefix option to tell .I pseudo where to look for its database and server. If you specify a full path, .I pseudo assumes that .B PSEUDO_PREFIX should be the path to the directory containing the .I pseudo program, or to the .I /bin directory containing the .I pseudo program. The other way to use .I pseudo is by setting up an environment. This is suitable for use in .I Makefiles or similar environments, where you want to run a series of commands in the .I pseudo environment, but not to keep invoking the .I pseudo command. To do this, set up the .BR PSEUDO_PREFIX ,\ LD_PRELOAD ,\ and\ LD_LIBRARY_PATH environment variables, then run programs normally. You do not need to separately invoke the .I pseudo daemon; the client library starts it as needed. If you have moved a directory which .I pseudo was tracking, you may be able to get the database reattached using the .B \-m option. A typical usage might be: .sp $ .I /path/to/pseudo .B \-B \-m .I oldpath .B \-M .I newpath .br This requests that .I pseudo replace the string .I oldpath with the string .I newpath at the beginnings of filenames, then regenerate the database, correcting any device/inode numbers. .SH DIAGNOSTICS Depending on invocation, diagnostic messages usually go either to standard error or to the file .B PSEUDO_PREFIX .IR /var/pseudo/pseudo.log . By default, .I pseudo daemon messages go into the log file, but messages generated by the client code go to standard error. These can be changed using the .B PSEUDO_DEBUG_FILE environment variable, documented in .BR ENVIRONMENT . At the default logging level, only critical messages are displayed. If you have raised the logging level (using the .I \-v option or the .B PSEUDO_DEBUG environment variable), additional messages are displayed. Levels higher than 2 are very unlikely to be useful outside of .I pseudo development. Diagnostic messages seen by default are those which are believed to indicate either a serious internal flaw in .I pseudo or a completely unexpected failure from the underlying operating system. In normal use, you should see no diagnostic messages. .SH ENVIRONMENT The most significant environment variables for .I pseudo are .B LD_PRELOAD and .BR LD_LIBRARY_PATH . However, these variables have no special meaning to .IR pseudo ; rather, they are used in the standard way to manipulate the dynamic linker into loading the .I libpseudo library so that it can intercept calls into the underlying C library. The following environment variables are used directly by .IR pseudo : .TP 8 .B PSEUDO_BINDIR This directory holds the path to the .I pseudo binary; by default, it is the .I bin directory under .B PSEUDO_PREFIX. .TP 8 .B PSEUDO_CHROOT This variable holds the current emulated .I chroot(2) path. Paths that are relative to this are treated as though they were instead relative to the filesystem root. .TP 8 .B PSEUDO_DEBUG This variable holds either a numeric "debug level" for .I pseudo to run at, or a set of specific debugging flags, generally letters. Use .B pseudo -h to see the available flags. In general, this is useful only for debugging .I pseudo itself. .TP 8 .B PSEUDO_DEBUG_FILE The name of a file to use for debugging messages from the pseudo client; the default is to log to standard error. If the string contains a single .BR %s , that string is replaced with the short program name, and if it contains a single .BR %d , that string is replaced with the process ID. Other format specifiers (other than '%%') are not allowed. By default, the .I pseudo server logs to the file .I pseudo.log in the .I var/pseudo directory, while clients log to standard error. .TP 8 .B PSEUDO_DISABLED If this variable is set to a value that doesn't look like f, F, n, N, s, S, or a numeric zero, the .I pseudo client library does not modify the behavior of called functions, though it continues to intercept them and block signals while processing them. This variable is reevaluated on every call to .IR fork(2) ,\ clone(2) or related functions. If the value starts with a lowercase or uppercase .I s , the pseudo client disables all server spawning and communications, but still operates locally. This means that no filesystem mode or permissions changes are actually recorded or reported, but functions like .I chown() will still report success, even though nothing happens. This function is intended for debugging of issues which are complicated by the server's involvement. .TP 8 .B PSEUDO_ALLOW_FSYNC If this variable is set, pseudo will allow .I fsync() and related system calls, even it was configured with the .I --enable-force-async option. Otherwise, that option results in all such calls being discarded silently, even when .B PSEUDO_DISABLED is set. The value specified doesn't matter. .TP 8 .B PSEUDO_ENOSYS_ABORT If this variable is set, the .I pseudo client library calls .I abort() rather than setting .I errno to .B ENOSYS in the event of a call to a missing underlying function. This variable has no function outside of debugging .I pseudo itself. .TP 8 .B PSEUDO_LIBDIR This directory holds the path to the .I pseudo shared libraries; by default, it is the .I lib directory under .BR PSEUDO_PREFIX . (On 64-bit hosts, .I lib64 is also used.) .TP 8 .B PSEUDO_LOCALSTATEDIR This directory holds the .I pseudo database files and log files; by default, it is the .I var/pseudo directory under .BR PSEUDO_PREFIX . .TP 8 .B PSEUDO_NOSYMLINKEXP By default, when chrooted, .I pseudo prepends the chroot directory to the paths used for absolute symlinks; this behavior ensures that opening symlinks produces expected results in most cases. In some cases you may want to suppress this. If this variable is unset, or set to any value other than 0, .I pseudo expands symlink paths like this. If this variable is set to 0, the behavior is disabled. .TP 8 .BR PSEUDO_OPTS This variable holds options to be passed to any new .I pseudo servers started. Typically, when .I pseudo is used as a launcher, this will be set automatically; however, you can also use it to pass options when using .B LD_PRELOAD to manually run things in the .I pseudo environment. .TP 8 .B PSEUDO_PASSWD This variable holds the path to a directory containing password and group files to use for emulation of various password and group routines. It should be the path to a directory containing the .I etc directory containing files named .I passwd and .IR group . When .I pseudo is emulating a .I chroot environment, the chroot directory is used by preference. The parallelism between these cases is why this variable points at the parent directory of .I etc rather than the directory containing the files. If there is no .I chroot environment, and this variable is also unset, .I pseudo falls back to a directory specified at configure time, with the default being the root directory. This is controlled by the .B PSEUDO_PASSWD_FALLBACK definition. .TP 8 .B PSEUDO_PREFIX If set, the variable .B PSEUDO_PREFIX is used to determine the path to use to find the .I pseudo server, in .BR PSEUDO_PREFIX /bin, and the .I pseudo data files, in .BR PSEUDO_PREFIX /var/pseudo. This variable is automatically set by the .I pseudo program when it is used as a launcher. .TP 8 .B PSEUDO_PROFILE_PATH If .I pseudo was configured with profiling enabled, specifies a path in which to write client profiling information for use with the .I pseudo_profile utility (not built by default). .TP 8 .B PSEUDO_TAG If this variable is set in a client's environment, its value is communicated to the server at the beginning of each client session, and recorded in the log database if any logging occurs related to a specific client. Note that different clients may have different tags associated with them; the tag value is per-client, not per-server. .TP 8 .BR PSEUDO_UIDS ,\ PSEUDO_GIDS These variables are used internally to pass information about the current emulated user and group identity from one process to another. .TP 8 .B PSEUDO_UNLOAD This variable is reevaluated on every call to .IR fork(2) ,\ exec(3) or related functions. If the variable exists .RI libpseudo.so will be removed from .B LD_PRELOAD and .B PSEUDO_DISABLED behavior will also be triggered. For processes that simply .IR fork(2), the behavior will be the same as if .B PSEUDO_DISABLED was set. For new processes, after a call to .IR exec(3)\ or\ system(3) pseudo will not be loaded in the new process. .TP 8 .B SHELL If set, this will be used when .I pseudo is invoked without either a command or one of the options which directs it to do something other than run a command. Otherwise, .I pseudo defaults to .I /bin/sh . .B .SH BUGS The .I pseudo database is not particularly robust in the face of whole directory trees being moved, or changes in the underlying device and inode numbers. It has a reasonable chance of recovering if only the path or the device numbers have changed, but it is not particularly designed to address this. A future release is expected to have improved resilience in these cases. The filesystem on which .I pseudo keeps its database and files must at a minimum support UNIX domain sockets and reasonable file locking semantics. Note that .I pseudo relies on .I flock(2) locking semantics; a lock has to persist into a child process. This should probably eventually be fixed. The .I pseudo client library is probably thread-safe, but has not been adequately tested or debugged in that context. Filesystem performance is noticably worse under .I pseudo than it is otherwise. This is probably because nearly every operation (other than reads and writes) involves at least one round-trip network communication with the server, and probably some kind of database activity. .SH SEE ALSO fakeroot(1), ld.so(8), pseudolog(1), sqlite3(1) .SH FURTHER READING Documentation of the internals of .I pseudo may be found in the .I doc subdirectory of the pseudo source tree. pseudo-1.8.1+git20161012/pseudo.c000066400000000000000000001057441300506512600160700ustar00rootroot00000000000000/* * pseudo.c, main pseudo utility program * * Copyright (c) 2008-2013 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_client.h" #include "pseudo_server.h" #include "pseudo_db.h" int opt_B = 0; int opt_C = 0; int opt_d = 0; int opt_f = 0; char *opt_i = NULL; int opt_l = 0; char *opt_m = NULL; char *opt_M = NULL; long opt_p = 0; char *opt_r = NULL; int opt_S = 0; static int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len); static int pseudo_db_check(int fix); void usage(int status) { FILE *f = status ? stderr : stdout; fputs("Usage: pseudo [-dflv] [-x flags] [-P prefix] [-rR root] [-t timeout] [command]\n", f); fputs(" pseudo -h\n", f); fputs(" pseudo [-dflv] [-x flags] [-P prefix] [-BC] -i path\n", f); fputs(" pseudo [-dflv] [-x flags] [-P prefix] [-BC] -m from -M to\n", f); fputs(" pseudo [-dflv] [-x flags] [-P prefix] -C\n", f); fputs(" pseudo [-dflv] [-x flags] [-P prefix] -S\n", f); fputs(" pseudo [-dflv] [-x flags] [-P prefix] -V\n", f); fputs("Debugging flags:\n", f); for (int i = 1; i < PDBG_MAX; i += 2) { unsigned char symbolics[2]; const char *descriptions[2]; symbolics[0] = pseudo_debug_type_symbolic(i); symbolics[1] = pseudo_debug_type_symbolic(i + 1); descriptions[0] = pseudo_debug_type_description(i); descriptions[1] = pseudo_debug_type_description(i + 1); if (symbolics[1]) { fprintf(f, " %c %-32s %c %-32s\n", symbolics[0], descriptions[0], symbolics[1], descriptions[1]); } else { fprintf(f, " %c %-32s\n", symbolics[0], descriptions[0]); } } exit(status); } /* main server process */ int main(int argc, char *argv[]) { int o; char *s; char *ld_env = getenv(PRELINK_LIBRARIES); int rc = 0; char opts[pseudo_path_max()], *optptr = opts; sigset_t blocked, saved; opts[0] = '\0'; pseudo_init_util(); /* The pseudo client will have blocked these, and sigprocmask * is inherited, but we want them to work. */ sigemptyset(&blocked); sigaddset(&blocked, SIGALRM); /* every-N-seconds tasks */ sigaddset(&blocked, SIGCHLD); /* reaping child processes */ sigaddset(&blocked, SIGHUP); /* idiomatically, reloading config */ sigaddset(&blocked, SIGTERM); /* shutdown/teardown operations */ sigaddset(&blocked, SIGUSR1); /* reopening log files, sometimes */ sigaddset(&blocked, SIGUSR2); /* who knows what people do */ sigprocmask(SIG_UNBLOCK, &blocked, &saved); if (ld_env && strstr(ld_env, "libpseudo")) { pseudo_debug(PDBGF_SERVER, "[server %d] can't run daemon with libpseudo in %s\n", getpid(), PRELINK_LIBRARIES); s = pseudo_get_value("PSEUDO_UNLOAD"); if (s) { pseudo_diag("pseudo: I can't seem to make %s go away. Sorry.\n", PRELINK_LIBRARIES); pseudo_diag("pseudo: %s: %s\n", PRELINK_LIBRARIES, ld_env); exit(PSEUDO_EXIT_PSEUDO_LOADED); } free(s); pseudo_set_value("PSEUDO_UNLOAD", "YES"); pseudo_setupenv(); pseudo_dropenv(); /* Drop PRELINK_LIBRARIES */ execv(argv[0], argv); exit(PSEUDO_EXIT_PSEUDO_LOADED); } /* Be sure to clean PSEUDO_UNLOAD so if we're asked to run any * programs pseudo will be active in the process... * (note: pseudo_set_value doesn't muck w/ the environment, thus * the need for the unsetenv, which is safe because "pseudo" * is the executable in this case!) */ pseudo_set_value("PSEUDO_UNLOAD", NULL); unsetenv("PSEUDO_UNLOAD"); /* we need cwd to canonicalize paths */ pseudo_client_getcwd(); /* warning: GNU getopt permutes arguments, which is just plain * wrong. The + suppresses this annoying behavior, but may not * be compatible with sane option libraries. */ while ((o = getopt(argc, argv, "+BCdfhi:lm:M:p:P:r:R:St:vVx:")) != -1) { switch (o) { case 'B': /* rebuild database */ opt_B = 1; opt_C = 1; break; case 'C': /* check database */ opt_C = 1; break; case 'd': /* run as daemon */ opt_d = 1; break; case 'f': /* run foregrounded */ opt_f = 1; break; case 'h': /* help */ usage(0); break; case 'i': /* renumber devices, assuming stable inodes */ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, 0); if (!s) { pseudo_diag("Can't resolve path '%s'\n", optarg); usage(EXIT_FAILURE); } opt_i = strdup(s); break; case 'l': /* log */ optptr += snprintf(optptr, pseudo_path_max() - (optptr - opts), "%s-l", optptr > opts ? " " : ""); opt_l = 1; break; case 'm': /* move from... (see also 'M') */ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, 0); if (!s) { pseudo_diag("Can't resolve move-from path '%s'\n", optarg); usage(EXIT_FAILURE); } opt_m = strdup(s); break; case 'M': /* move to... (see also 'm') */ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, 0); if (!s) { pseudo_diag("Can't resolve move-to path '%s'\n", optarg); usage(EXIT_FAILURE); } opt_M = strdup(s); break; case 'p': /* passwd file path */ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) { pseudo_diag("Can't resolve passwd path '%s'\n", optarg); usage(EXIT_FAILURE); } pseudo_set_value("PSEUDO_PASSWD", s); break; case 'P': /* prefix */ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) { pseudo_diag("Can't resolve prefix path '%s'\n", optarg); usage(EXIT_FAILURE); } pseudo_set_value("PSEUDO_PREFIX", s); break; case 'r': /* chroot to... (fallthrough) */ case 'R': /* pseudo root path */ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) { pseudo_diag("Can't resolve root path '%s'\n", optarg); usage(EXIT_FAILURE); } pseudo_set_value("PSEUDO_CHROOT", s); if (o == 'r') opt_r = strdup(s); break; case 'S': /* stop */ opt_S = 1; break; case 't': /* timeout */ pseudo_server_timeout = strtol(optarg, &s, 10); if (*s && !isspace(*s)) { pseudo_diag("Timeout must be an integer value.\n"); usage(EXIT_FAILURE); } optptr += snprintf(optptr, pseudo_path_max() - (optptr - opts), "%s-t %d", optptr > opts ? " " : "", pseudo_server_timeout); break; case 'v': /* verbosity */ pseudo_debug_verbose(); break; case 'V': /* version info */ printf("pseudo version %s\n", pseudo_version ? pseudo_version : ""); printf("pseudo configuration:\n prefix: %s\n", PSEUDO_PREFIX); printf("Set PSEUDO_PREFIX to run with a different prefix.\n"); exit(0); break; case 'x': /* debug flags */ pseudo_debug_set(optarg); break; case '?': default: pseudo_diag("unknown/invalid argument (option '%c').\n", optopt); usage(EXIT_FAILURE); break; } } pseudo_debug_flags_finalize(); /* Options are processed, preserve them... */ pseudo_set_value("PSEUDO_OPTS", opts); if (!pseudo_get_prefix(argv[0])) { pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n"); exit(PSEUDO_EXIT_PSEUDO_PREFIX); } /* move database */ if (opt_m || opt_M) { struct stat buf; pseudo_msg_t *msg; int rc; if (!(opt_m && opt_M)) { pseudo_diag("You cannot move the database without specifying from and to.\n"); exit(EXIT_FAILURE); } if (stat(opt_M, &buf) < 0) { pseudo_diag("stat of '%s' failed: %s\n", opt_M, strerror(errno)); pseudo_diag("The directory the database is being moved to must exist.\n"); exit(EXIT_FAILURE); } msg = pseudo_msg_new(0, opt_M); if (!msg) { pseudo_diag("Can't allocate message structure.\n"); exit(EXIT_FAILURE); } rc = pdb_rename_file(opt_m, msg); free(msg); if (rc < 0) { pseudo_diag("Warning: Database move may have failed.\n"); pseudo_diag("To try to restore, you can reverse the move.\n"); pseudo_diag("To commit to this anyway, run pseudo -C to check the database.\n"); exit(EXIT_FAILURE); } pseudo_diag("Rename looked okay, running database sanity check.\n"); opt_C = 1; } if (opt_i) { int rc; struct stat buf; pseudo_msg_t *msg; if (stat(opt_i, &buf) < 0) { pseudo_diag("stat of '%s' failed: %s\n", opt_i, strerror(errno)); pseudo_diag("The file used to renumber the database must exist.\n"); exit(EXIT_FAILURE); } msg = pseudo_msg_new(0, opt_i); if (!msg) { pseudo_diag("Couldn't allocate data structure for path.\n"); exit(EXIT_FAILURE); } if (pdb_find_file_path(msg)) { pseudo_diag("Couldn't find a database entry for '%s'.\n", opt_i); exit(EXIT_FAILURE); } if (buf.st_ino != msg->ino) { pseudo_diag("The database inode entry for '%s' doesn't match; you must use -b.\n", opt_i); exit(EXIT_FAILURE); } rc = pdb_renumber_all(msg->dev, buf.st_dev); free(msg); if (rc < 0) { pseudo_diag("Warning: Database renumber failed.\n"); exit(EXIT_FAILURE); } pseudo_diag("Renumber looked okay, running database sanity check.\n"); opt_C = 1; } if (opt_C) { /* if opt_B is set, try to fix database */ return pseudo_db_check(opt_B); } /* If you didn't specify a command, opt_S shuts down here. */ if (opt_S && argc <= optind) { return pseudo_client_shutdown(); } if (opt_d && opt_f) { pseudo_diag("You cannot run a foregrounded daemon.\n"); exit(PSEUDO_EXIT_PSEUDO_INVOCATION); } if (opt_f || opt_d) { if (argc > optind) { pseudo_diag("pseudo: running program implies spawning background daemon.\n"); exit(PSEUDO_EXIT_PSEUDO_INVOCATION); } } else { char fullpath[pseudo_path_max()]; char *path; if (opt_r) { if (chdir(opt_r) == -1) { pseudo_diag("failed to chdir to '%s': %s\n", opt_r, strerror(errno)); exit(EXIT_FAILURE); } } if (argc > optind) { pseudo_debug(PDBGF_INVOKE, "running command: %s\n", argv[optind]); argc -= optind; argv += optind; } else { static char *newargv[2]; argv = newargv; pseudo_debug(PDBGF_INVOKE, "running shell.\n"); argv[0] = getenv("SHELL"); if (!argv[0]) argv[0] = "/bin/sh"; argv[1] = NULL; } if (strchr(argv[0], '/')) { snprintf(fullpath, pseudo_path_max(), "%s", argv[0]); } else { int found = 0; if ((path = getenv("PATH")) == NULL) path = "/bin:/usr/bin"; while (*path) { struct stat buf; int len = strcspn(path, ":"); snprintf(fullpath, pseudo_path_max(), "%.*s/%s", len, path, argv[0]); path += len; if (*path == ':') ++path; if (!stat(fullpath, &buf)) { if (buf.st_mode & 0111) { found = 1; break; } } } if (!found) { pseudo_diag("Can't find '%s' in $PATH.\n", argv[0]); exit(EXIT_FAILURE); } } pseudo_setupenv(); rc = fork(); if (rc) { waitpid(rc, &rc, 0); /* try to hint that we don't think we still need * the server. */ if (opt_S) { pseudo_client_shutdown(); } if (WIFEXITED(rc)) { return WEXITSTATUS(rc); } else if (WIFSIGNALED(rc)) { kill(getpid(), WTERMSIG(rc)); exit(1); } else { exit(1); } } else { rc = execv(fullpath, argv); if (rc == -1) { pseudo_diag("pseudo: can't run %s: %s\n", argv[0], strerror(errno)); } exit(EXIT_FAILURE); } } return pseudo_server_start(opt_d); } /* * actually process operations. * This first evaluates the message, figures out what's in the DB, does some * sanity checks, then implements the fairly small DB changes required. */ int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len) { pseudo_msg_t msg_header; pseudo_msg_t by_path = { .op = 0 }, by_ino = { .op = 0 }; pseudo_msg_t db_header; char *path_by_ino = 0; char *oldpath = 0; size_t oldpathlen = 0; int found_path = 0, found_ino = 0; int prefer_ino = 0; int xattr_flags = 0; int trailing_slash = 0; if (!msg) return 1; msg->result = RESULT_SUCCEED; /* debugging message. Primary key first. */ switch (msg->op) { case OP_FCHOWN: /* FALLTHROUGH */ case OP_FCHMOD: /* FALLTHROUGH */ case OP_FSTAT: prefer_ino = 1; pseudo_debug(PDBGF_OP, "%s %llu [%s]: ", pseudo_op_name(msg->op), (unsigned long long) msg->ino, msg->pathlen ? msg->path : "no path"); break; default: pseudo_debug(PDBGF_OP, "%s %s [%llu]: ", pseudo_op_name(msg->op), msg->pathlen ? msg->path : "no path", (unsigned long long) msg->ino); break; } /* Process rename path separation, there are two paths old / new * stuff into a rename, break them apart (null seperated) */ if (msg->pathlen) { size_t initial_len; switch (msg->op) { case OP_RENAME: case OP_CREATE_XATTR: case OP_GET_XATTR: case OP_LIST_XATTR: case OP_REPLACE_XATTR: case OP_REMOVE_XATTR: case OP_SET_XATTR: /* In a rename there are two paths, null separated in msg->path */ initial_len = strlen(msg->path); oldpath = msg->path + initial_len + 1; /* for rename, the path name would be null-terminated, * but for *xattr, we don't want the null. */ oldpathlen = msg->pathlen - (oldpath - msg->path) - 1; pseudo_debug(PDBGF_OP | PDBGF_FILE | PDBGF_XATTR, "%s: path '%s', oldpath '%s' [%d/%d]\n", pseudo_op_name(msg->op), msg->path, oldpath, (int) oldpathlen, (int) msg->pathlen); /* For a rename op, we want to strip any trailing * slashes. For xattr, "oldpath" is the raw data * to be stored. */ if (oldpathlen > 0 && msg->op == OP_RENAME) { if (oldpath[oldpathlen - 1] == '/') { oldpath[--oldpathlen] = '\0'; } } /* if we got an oldpath, but a 0-length initial * path, we don't want to act as though we had * a non-empty initial path. */ msg->pathlen = initial_len; break; default: break; } } /* stash original header, in case we need it later */ msg_header = *msg; by_ino = msg_header; /* trailing slashes are kept in paths because they affect * path resolution, but we don't want them in the database * because they're optional. For now, any error-checking on * this server-side is purely advisory, but the client should * bail with ENOTDIR a lot earlier in many cases, before the * server even sees anything. */ if (msg->pathlen) { if (msg->path[msg->pathlen - 1] == '/') { msg->path[--msg->pathlen] = '\0'; trailing_slash = 1; } } /* There should usually be a path. Even for f* ops, the client * tries to provide a path from its table of known fd paths. */ /* Lookup the full path, with inode and dev if available */ if (msg->pathlen && msg->dev && msg->ino) { if (!pdb_find_file_exact(msg)) { /* restore header contents */ by_path = *msg; by_ino = *msg; *msg = msg_header; found_path = 1; found_ino = 1; /* note: we have to avoid freeing this later */ path_by_ino = msg->path; if (msg->op == OP_LINK) { pseudo_debug(PDBGF_FILE, "[matches existing link]"); } } } if (!found_path && !found_ino) { if (msg->pathlen) { /* for now, don't canonicalize paths anymore */ /* used to do it here, but now doing it in client */ if (!pdb_find_file_path(msg)) { by_path = *msg; found_path = 1; } else { if (msg->op != OP_RENAME && msg->op != OP_LINK) { pseudo_debug(PDBGF_FILE, "(new?) "); } } } /* search on original inode -- in case of mismatch */ if (msg->dev && msg->ino) { if (!pdb_find_file_dev(&by_ino, &path_by_ino)) { found_ino = 1; } } } pseudo_debug(PDBGF_OP, "incoming: '%s'%s [%llu]%s\n", msg->pathlen ? msg->path : "no path", found_path ? "+" : "-", (unsigned long long) msg_header.ino, found_ino ? "+" : "-"); /* the sanity checks are inappropriate for DID_UNLINK, since it's * completely legitimate to have a new database entry for the * same inode. */ if (found_path && msg->op != OP_DID_UNLINK) { /* This is a bad sign. We should never have a different entry * for the inode... But an inode of 0 from an EXEC is normal, * we don't track those. */ if (by_path.ino != msg_header.ino && msg_header.ino != 0) { switch (msg->op) { case OP_EXEC: break; default: /* if the path is in the database with a * different inode, but we were expecting * it to get deleted, mark the old one * as deleted. */ if (by_path.deleting != 0) { pseudo_debug(PDBGF_FILE, "inode mismatch for '%s' -- old one was marked for deletion, deleting.\n", msg->path); /* in this case, we don't trust the * existing entries, so we will do the * more expensive sweep for stray * xattrs. */ pdb_did_unlink_file(msg->path, NULL, by_path.deleting); } else { pseudo_diag("inode mismatch: '%s' ino %llu in db, %llu in request.\n", msg->path, (unsigned long long) by_path.ino, (unsigned long long) msg_header.ino); } } } /* If the database entry disagrees on S_ISDIR, it's just * plain wrong. We remove the database entry, because it * is absolutely certain to be wrong. This means found_path * is now 0, because there is no entry in db... * * This used to unlink everything with the inode from * the message -- but what if the entry in the database * had a different inode? We should nuke THAT inode, * and everything agreeing with it, which will also catch * the bogus entry that we noticed. */ if (S_ISDIR(by_path.mode) != S_ISDIR(msg_header.mode)) { pseudo_diag("dir mismatch: '%s' [%llu] db mode 0%o, header mode 0%o (unlinking db)\n", msg->path, (unsigned long long) by_path.ino, (int) by_path.mode, (int) msg_header.mode); /* unlink everything with this inode */ pdb_unlink_file_dev(&by_path); found_path = 0; } else if (S_ISLNK(by_path.mode) != S_ISLNK(msg_header.mode)) { pseudo_diag("symlink mismatch: '%s' [%llu] db mode 0%o, header mode 0%o (unlinking db)\n", msg->path, (unsigned long long) by_path.ino, (int) by_path.mode, (int) msg_header.mode); /* unlink everything with this inode */ pdb_unlink_file_dev(&by_path); found_path = 0; } if (trailing_slash && !S_ISDIR(by_path.mode)) { pseudo_diag("dir quasi-mismatch: '%s' [%llu] db mode 0%o, incoming path had trailing slash. Not unlinking.\n", msg->path, (unsigned long long) by_path.ino, (int) by_path.mode); } } /* for OP_DID_UNLINK, the reason this op exists is that the same * inode might have been reclaimed. Don't sanity-check it, and * especially don't delete the database contents! */ if (found_ino && msg->op != OP_DID_UNLINK) { /* Not always an absolute failure case. * If a file descriptor shows up unexpectedly and gets * fchown()d, you could have an entry giving the inode and * data, but not path. So, we add the path to the entry. * Any other changes from the incoming message will be applied * at leisure. */ if (msg->pathlen && !path_by_ino) { pseudo_debug(PDBGF_FILE, "db path missing: ino %llu, request '%s'.\n", (unsigned long long) msg_header.ino, msg->path); pdb_update_file_path(msg); } else if (!msg->pathlen && path_by_ino) { /* harmless */ pseudo_debug(PDBGF_FILE, "req path missing: ino %llu, db '%s'.\n", (unsigned long long) msg_header.ino, path_by_ino); } else if (msg->pathlen && path_by_ino) { /* this suggests a database error, except in LINK * cases. In those cases, it is normal for a * mismatch to occur. :) (SYMLINK shouldn't, * because the symlink gets its own inode number.) * * RENAME can get false positives on this, when * link count is greater than one. So we skip this * test for OP_LINK (always) and OP_RENAME (for link * count greater than one). For RENAME, the test * should be against the old name, though! */ int mismatch = 0; switch (msg->op) { case OP_LINK: case OP_EXEC: break; case OP_RENAME: if (msg->nlink == 1 && strcmp(oldpath, path_by_ino)) { mismatch = 1; } break; default: if (strcmp(msg->path, path_by_ino)) { mismatch = 1; } break; } if (mismatch) { /* a mismatch, but we were planning to delete * the file, so it must have gotten deleted * already. */ if (by_ino.deleting != 0) { pseudo_debug(PDBGF_FILE, "inode mismatch for '%s' -- old one was marked for deletion, deleting.\n", msg->path); pdb_did_unlink_file(path_by_ino, &by_ino, by_ino.deleting); } else { pseudo_diag("path mismatch [%d link%s]: ino %llu db '%s' req '%s'.\n", msg->nlink, msg->nlink == 1 ? "" : "s", (unsigned long long) msg_header.ino, path_by_ino ? path_by_ino : "no path", msg->path); } } } else { /* I don't think I've ever seen this one. */ pseudo_debug(PDBGF_FILE, "warning: ino %llu in db (mode 0%o, owner %d), no path known.\n", (unsigned long long) msg_header.ino, (int) by_ino.mode, (int) by_ino.uid); } /* Again, in the case of a directory mismatch, nuke the DB * entry. There is no way it can be right. */ if (S_ISDIR(by_ino.mode) != S_ISDIR(msg_header.mode)) { pseudo_diag("dir err : %llu ['%s'] (db '%s') db mode 0%o, header mode 0%o (unlinking db)\n", (unsigned long long) msg_header.ino, msg->pathlen ? msg->path : "no path", path_by_ino ? path_by_ino : "no path", (int) by_ino.mode, (int) msg_header.mode); pdb_unlink_file_dev(msg); found_ino = 0; } else if (S_ISLNK(by_ino.mode) != S_ISLNK(msg_header.mode)) { /* In the current implementation, only msg_header.mode * can ever be a symlink; the test is generic as * insurance against forgetting to fix it in a future * update. */ pseudo_diag("symlink err : %llu ['%s'] (db '%s') db mode 0%o, header mode 0%o (unlinking db)\n", (unsigned long long) msg_header.ino, msg->pathlen ? msg->path : "no path", path_by_ino ? path_by_ino : "no path", (int) by_ino.mode, (int) msg_header.mode); pdb_unlink_file_dev(msg); found_ino = 0; } } /* In the case of a stat() call, if a mismatch existed, we prefer * by-inode for fstat, by-path for stat. Nothing else actually uses * this... */ if (found_ino && (prefer_ino || !found_path)) { db_header = by_ino; } else if (found_path) { db_header = by_path; } switch (msg->op) { case OP_CHDIR: /* FALLTHROUGH */ case OP_CLOSE: /* these messages are handled entirely on the client side, * as of this writing, but might be logged by accident: */ pseudo_diag("error: op %s sent to server.\n", pseudo_op_name(msg->op)); break; case OP_EXEC: /* FALLTHROUGH */ case OP_OPEN: /* nothing to do -- just sent in case we're logging */ break; case OP_CREAT: /* implies a new file -- not a link, which would be OP_LINK */ if (found_ino) { /* CREAT should never be sent if the file existed. * So, any existing entry is an error. Nuke it. */ pseudo_diag("creat for '%s' replaces existing %llu ['%s'].\n", msg->pathlen ? msg->path : "no path", (unsigned long long) by_ino.ino, path_by_ino ? path_by_ino : "no path"); pdb_unlink_file_dev(&by_ino); } if (!found_path) { pseudo_debug(PDBGF_DB, "linking %s for OP_CREAT\n", msg->pathlen ? msg->path : "no path"); pdb_link_file(msg); } else { /* again, an error, but leaving it alone for now. */ pseudo_diag("creat ignored for existing file '%s'.\n", msg->pathlen ? msg->path : "no path"); } break; case OP_CHMOD: /* FALLTHROUGH */ case OP_FCHMOD: pseudo_debug(PDBGF_OP, "mode 0%o ", (int) msg->mode); /* if the inode is known, update it */ if (found_ino) { /* obtain the existing data, merge with mode */ *msg = by_ino; msg->mode = (msg_header.mode & 07777) | (msg->mode & ~07777); pdb_update_file(msg); } else if (found_path) { /* obtain the existing data, merge with mode */ *msg = by_path; msg->mode = (msg_header.mode & 07777) | (by_path.mode & ~07777); pdb_update_file(msg); } else { /* just in case find_file_path screwed up the msg */ msg->mode = msg_header.mode; } /* if we've never seen the file at all before, link it. * If we have it in the db by inode, but not by name, * it got fixed during the sanity checks. */ if (!found_path && !found_ino) { pseudo_debug(PDBGF_FILE, "(new) "); pseudo_debug(PDBGF_DB, "linking %s for OP_[F]CHMOD\n", msg->pathlen ? msg->path : "no path"); pdb_link_file(msg); } break; case OP_CHOWN: /* FALLTHROUGH */ case OP_FCHOWN: pseudo_debug(PDBGF_OP, "owner %d:%d ", (int) msg_header.uid, (int) msg_header.gid); /* if the inode is known, update it */ if (found_ino) { /* obtain the existing data, merge with mode */ *msg = by_ino; msg->uid = msg_header.uid; msg->gid = msg_header.gid; pdb_update_file(msg); } else if (found_path) { /* obtain the existing data, merge with mode */ *msg = by_path; msg->uid = msg_header.uid; msg->gid = msg_header.gid; pdb_update_file(msg); } else { /* just in case find_file_path screwed up the msg */ msg->uid = msg_header.uid; msg->gid = msg_header.gid; } /* if we've never seen the file at all before, link it. * If we have it in the db by inode, but not by name, * it got fixed during the sanity checks. */ if (!found_path && !found_ino) { pseudo_debug(PDBGF_FILE, "(new) "); pseudo_debug(PDBGF_DB, "linking %s for OP_[F]CHOWN\n", msg->pathlen ? msg->path : "no path"); pdb_link_file(msg); } break; case OP_STAT: /* FALLTHROUGH */ case OP_FSTAT: /* db_header will be whichever one looked best, in the rare * case where there might be a clash. */ if (found_ino || found_path) { #ifdef PSEUDO_XATTRDB if (db_header.uid == (uid_t) -1 && db_header.gid == (gid_t) -1) { /* special case: this row was created * to allow xattr lookups, and it's not * actually valid data. */ msg->result = RESULT_FAIL; } else #endif { *msg = db_header; } } else { msg->result = RESULT_FAIL; } pseudo_debug(PDBGF_OP | PDBGF_VERBOSE, "%s, ino %llu (old mode 0%o): mode 0%o\n", pseudo_op_name(msg->op), (unsigned long long) msg->ino, (int) msg_header.mode, (int) msg->mode); break; case OP_LINK: /* FALLTHROUGH */ case OP_SYMLINK: /* a successful link (client only notifies us for those) * implies that the new path did not previously exist, and * the old path did. We get the stat buffer and the new path. * So, we unlink it, then link it. Neither unlink nor link * touches the message, which was initialized from the * underlying file data in the client. */ if (found_path) { pseudo_debug(PDBGF_OP | PDBGF_FILE, "replace %slink: path %s, old ino %llu, mode 0%o, new ino %llu, mode 0%o\n", msg->op == OP_SYMLINK ? "sym" : "", msg->path, (unsigned long long) msg->ino, (int) msg->mode, (unsigned long long) msg_header.ino, (int) msg_header.mode); pdb_unlink_file(msg); } else { pseudo_debug(PDBGF_OP | PDBGF_FILE, "new %slink: path %s, ino %llu, mode 0%o\n", msg->op == OP_SYMLINK ? "sym" : "", msg->path, (unsigned long long) msg_header.ino, (int) msg_header.mode); } if (found_ino) { if (msg->op == OP_SYMLINK) { pseudo_debug(PDBGF_OP | PDBGF_FILE, "symlink: ignoring existing file %llu ['%s']\n", (unsigned long long) by_ino.ino, path_by_ino ? path_by_ino : "no path"); } else { *msg = by_ino; pseudo_debug(PDBGF_OP | PDBGF_FILE, "link: copying data from existing file %llu ['%s']\n", (unsigned long long) by_ino.ino, path_by_ino ? path_by_ino : "no path"); } } else { *msg = msg_header; } pseudo_debug(PDBGF_DB, "linking %s for %s\n", msg->pathlen ? msg->path : "no path", pseudo_op_name(msg->op)); pdb_link_file(msg); break; case OP_RENAME: /* a rename implies renaming an existing entry... and every * database entry rooted in it, if it's a directory. */ pdb_rename_file(oldpath, msg); pdb_update_inode(msg); break; case OP_MAY_UNLINK: if (pdb_may_unlink_file(msg, msg->client)) { /* harmless, but client wants to know so it knows * whether to follow up... */ msg->result = RESULT_FAIL; } break; case OP_DID_UNLINK: pdb_did_unlink_file(msg->path, msg, msg->client); break; case OP_CANCEL_UNLINK: pdb_cancel_unlink_file(msg); break; case OP_UNLINK: /* this removes any entries with the given path from the * database. No response is needed. * DO NOT try to fail if the entry is already gone -- if the * server's response didn't make it, the client would resend. */ pdb_unlink_file(msg); pdb_unlink_contents(msg); /* If we are seeing an unlink for something with only one * link, we should delete all records for that inode, even * ones through different paths. This handles the case * where something is removed through the wrong path, but * only if it didn't have multiple hard links. * * This should cease to be needed once symlinks are tracked. */ if (msg_header.nlink == 1 && found_ino) { pseudo_debug(PDBGF_FILE | PDBGF_OP, "link count 1, unlinking anything with ino %llu.\n", (unsigned long long) msg->ino); pdb_unlink_file_dev(msg); } msg->result = RESULT_NONE; break; case OP_MKDIR: /* FALLTHROUGH */ case OP_MKNOD: pseudo_debug(PDBGF_OP, "mode 0%o", (int) msg->mode); /* for us to get called, the client has to have succeeded in * a creation (of a regular file, for mknod) -- meaning this * file DID NOT exist before the call. Fix database: */ if (found_path) { pseudo_diag("mkdir/mknod: '%s' [%llu] already existed (mode 0%o), unlinking\n", msg->path, (unsigned long long) by_path.ino, (int) by_path.mode); pdb_unlink_file(msg); } if (found_ino) { pdb_unlink_file_dev(&by_ino); } *msg = msg_header; pseudo_debug(PDBGF_DB, "linking %s for %s\n", msg->pathlen ? msg->path : "no path", pseudo_op_name(msg->op)); pdb_link_file(msg); break; case OP_GET_XATTR: if (pdb_get_xattr(msg, &oldpath, &oldpathlen)) { msg->result = RESULT_FAIL; } else { *response_path = oldpath; *response_len = oldpathlen; pseudo_debug(PDBGF_XATTR, "get results: '%.*s' (%d bytes)\n", (int) *response_len, *response_path, (int) *response_len); } break; case OP_LIST_XATTR: if (pdb_list_xattr(msg, &oldpath, &oldpathlen)) { msg->result = RESULT_FAIL; } else { pseudo_debug(PDBGF_XATTR, "got %d bytes of xattrs to list: %.*s\n", (int) oldpathlen, (int) oldpathlen, oldpath); *response_path = oldpath; *response_len = oldpathlen; } break; case OP_CREATE_XATTR: case OP_REPLACE_XATTR: /* fallthrough */ if (msg->op == OP_CREATE_XATTR) { xattr_flags = XATTR_CREATE; } if (msg->op == OP_REPLACE_XATTR) { xattr_flags = XATTR_REPLACE; } case OP_SET_XATTR: if (pdb_set_xattr(msg, oldpath, oldpathlen, xattr_flags)) { msg->result = RESULT_FAIL; } break; case OP_REMOVE_XATTR: pdb_remove_xattr(msg, oldpath, oldpathlen); break; default: pseudo_diag("unknown op from client %d, op %d [%s]\n", msg->client, msg->op, msg->pathlen ? msg->path : "no path"); break; } /* in the case of an exact match, we just used the pointer * rather than allocating space. */ if (path_by_ino != msg->path) { free(path_by_ino); } pseudo_debug(PDBGF_OP, "completed %s.\n", pseudo_op_name(msg->op)); if (opt_l) pdb_log_msg(SEVERITY_INFO, msg, program, tag, NULL); return 0; } /* SHUTDOWN does not get this far, it's handled in pseudo_server.c */ int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len) { switch (msg->type) { case PSEUDO_MSG_PING: /* mad hackery: if we aren't logging, the client gets told * not to send open/exec notifications, which have no other * purpose. */ msg->result = opt_l ? RESULT_SUCCEED : RESULT_FAIL; if (opt_l) pdb_log_msg(SEVERITY_INFO, msg, program, tag, NULL); return 0; break; case PSEUDO_MSG_OP: case PSEUDO_MSG_FASTOP: return pseudo_op(msg, program, tag, response_path, response_len); break; case PSEUDO_MSG_ACK: /* FALLTHROUGH */ case PSEUDO_MSG_NAK: /* FALLTHROUGH */ default: pdb_log_msg(SEVERITY_WARN, msg, program, tag, "invalid message"); return 1; } } int pseudo_db_check(int fix) { struct stat buf; pseudo_msg_t *m; pdb_file_list l; int errors = 0; int delete_some = 0; /* magic cookie used to show who's deleting the files */ int magic_cookie = (int) getpid(); int rc = 0; l = pdb_files(); if (!l) { pseudo_diag("Couldn't start file list, can't scan.\n"); return EXIT_FAILURE; } while ((m = pdb_file(l)) != NULL) { pseudo_debug(PDBGF_DB, "m: %p (%d: %s)\n", (void *) m, m ? (int) m->pathlen : -1, m ? m->path : ""); if (m->pathlen > 0) { int fixup_needed = 0; pseudo_debug(PDBGF_DB, "Checking <%s>\n", m->path); if (lstat(m->path, &buf)) { errors = EXIT_FAILURE; pseudo_diag("can't stat <%s>\n", m->path); continue; } /* can't check for device type mismatches, uid/gid, or * permissions, because those are the very things we * can't really set. */ if (buf.st_ino != m->ino) { pseudo_debug(PDBGF_DB, "ino mismatch <%s>: ino %llu, db %llu\n", m->path, (unsigned long long) buf.st_ino, (unsigned long long) m->ino); m->ino = buf.st_ino; fixup_needed = 1; } if (buf.st_dev != m->dev) { pseudo_debug(PDBGF_DB, "dev mismatch <%s>: dev %llu, db %llu\n", m->path, (unsigned long long) buf.st_dev, (unsigned long long) m->dev); m->dev = buf.st_dev; fixup_needed = 1; } if (S_ISLNK(buf.st_mode) != S_ISLNK(m->mode)) { pseudo_debug(PDBGF_DB, "symlink mismatch <%s>: file %d, db %d\n", m->path, S_ISLNK(buf.st_mode), S_ISLNK(m->mode)); fixup_needed = 2; } if (S_ISDIR(buf.st_mode) != S_ISDIR(m->mode)) { pseudo_debug(PDBGF_DB, "symlink mismatch <%s>: file %d, db %d\n", m->path, S_ISDIR(buf.st_mode), S_ISDIR(m->mode)); fixup_needed = 2; } if (fixup_needed) { /* in fixup mode, either delete (mismatches) or * correct (dev/ino). */ if (fix) { if (fixup_needed == 1) { rc = pdb_update_inode(m); } else if (fixup_needed == 2) { /* mark for deletion */ delete_some = 1; rc = pdb_may_unlink_file(m, magic_cookie); } if (rc) { pseudo_diag("error updating file %s\n", m->path); errors = EXIT_FAILURE; } } else { errors = EXIT_FAILURE; } } } } pdb_files_done(l); /* and now delete files marked for deletion */ if (delete_some) { rc = pdb_did_unlink_files(magic_cookie); if (rc) { pseudo_diag("error nuking mismatched files.\n"); pseudo_diag("database may not be fixed.\n"); errors = EXIT_FAILURE; } } return errors; } pseudo-1.8.1+git20161012/pseudo.h000066400000000000000000000142121300506512600160620ustar00rootroot00000000000000/* * pseudo.h, shared definitions and structures for pseudo * * Copyright (c) 2008-2010, 2013 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include /* List of magic initialization functions... */ extern void pseudo_init_wrappers(void); extern void pseudo_init_util(void); extern void pseudo_init_client(void); void pseudo_dump_env(char **envp); int pseudo_set_value(const char *key, const char *value); char *pseudo_get_value(const char *key); int pseudo_has_unload(char * const *envp); #include "pseudo_tables.h" extern void pseudo_debug_verbose(void); extern void pseudo_debug_terse(void); extern void pseudo_debug_set(char *); extern void pseudo_debug_clear(char *); extern void pseudo_debug_flags_finalize(void); extern unsigned long pseudo_util_debug_flags; extern unsigned long pseudo_util_evlog_flags; extern int pseudo_util_debug_fd; extern int pseudo_disabled; extern int pseudo_allow_fsync; extern int pseudo_diag(char *, ...) __attribute__ ((format (printf, 1, 2))); extern int pseudo_evlog_internal(char *, ...) __attribute__ ((format (printf, 1, 2))); #define pseudo_evlog(x, ...) do { \ if (pseudo_util_evlog_flags & (x)) { pseudo_evlog_internal(__VA_ARGS__); } \ } while (0) extern void pseudo_evlog_dump(void); #ifndef NDEBUG #define pseudo_debug(x, ...) do { \ if ((x) & PDBGF_VERBOSE) { \ if ((pseudo_util_debug_flags & PDBGF_VERBOSE) && (pseudo_util_debug_flags & ((x) & ~PDBGF_VERBOSE))) { pseudo_diag(__VA_ARGS__); } \ } else { \ if (pseudo_util_debug_flags & (x)) { pseudo_diag(__VA_ARGS__); } \ } \ } while (0) #define pseudo_debug_call(x, fn, ...) do { \ if ((x) & PDBGF_VERBOSE) { \ if ((pseudo_util_debug_flags & PDBGF_VERBOSE) && (pseudo_util_debug_flags & ((x) & ~PDBGF_VERBOSE))) { fn(__VA_ARGS__); } \ } else { \ if (pseudo_util_debug_flags & (x)) { fn(__VA_ARGS__); } \ } \ } while (0) #else /* this used to be a static inline function, but that meant that arguments * were still evaluated for side effects. We don't want that. The ... * is a C99ism, also supported by GNU C. */ #define pseudo_debug(...) 0 #define pseudo_debug_call(...) 0 #endif extern void pseudo_dump_data(char *, const void *, size_t); void pseudo_new_pid(void); /* pseudo_fix_path resolves symlinks up to this depth */ #define PSEUDO_MAX_LINK_RECURSION 16 extern char *pseudo_fix_path(const char *, const char *, size_t, size_t, size_t *, int); extern void pseudo_dropenv(void); extern char **pseudo_dropenvp(char * const *); extern void pseudo_setupenv(void); extern char **pseudo_setupenvp(char * const *); extern char *pseudo_prefix_path(char *); extern char *pseudo_bindir_path(char *); extern char *pseudo_libdir_path(char *); extern char *pseudo_localstatedir_path(char *); extern char *pseudo_get_prefix(char *); extern char *pseudo_get_bindir(void); extern char *pseudo_get_libdir(void); extern char *pseudo_get_localstatedir(void); extern int pseudo_debug_logfile(char *defname, int prefer_fd); extern ssize_t pseudo_sys_path_max(void); extern ssize_t pseudo_path_max(void); #define PSEUDO_PWD_MAX 4096 extern int pseudo_etc_file(const char *filename, char *realname, int flags, const char **search_dirs, int dircount); extern void pseudo_stat32_from64(struct stat *, const struct stat64 *); extern void pseudo_stat64_from32(struct stat64 *, const struct stat *); extern char *pseudo_version; #ifndef PSEUDO_BINDIR #define PSEUDO_BINDIR "bin" #endif #ifndef PSEUDO_LIBDIR #define PSEUDO_LIBDIR "lib" #endif #define STARTSWITH(x, y) (!memcmp((x), (y), sizeof(y) - 1)) #ifndef PSEUDO_LOCALSTATEDIR #define PSEUDO_LOCALSTATEDIR "var/pseudo" #endif #define PSEUDO_LOCKFILE "pseudo.lock" #define PSEUDO_LOGFILE "pseudo.log" #define PSEUDO_PIDFILE "pseudo.pid" #define PSEUDO_SOCKET "pseudo.socket" /* some systems might not have *at(). We like to define operations in * terms of each other, and for instance, open(...) is the same as * openat(AT_FDCWD, ...). If no AT_FDCWD is provided, any value that can't * be a valid file descriptor will do. Using -2 because -1 could be * mistaken for a failed syscall return. AT_SYMLINK_NOFOLLOW has to be * non-zero; AT_SYMLINK_FOLLOW has to be non-zero and different. Finally, * if this happened, we set our own flag we can use to indicate that dummy * implementations of the _at functions are needed. */ #ifndef AT_FDCWD #define AT_FDCWD -2 #define AT_SYMLINK_NOFOLLOW 1 #define AT_SYMLINK_FOLLOW 2 #define PSEUDO_NO_REAL_AT_FUNCTIONS #endif /* Likewise, someone might not have O_LARGEFILE (the flag equivalent to * using open64()). Since open64() is the same as O_LARGEFILE in flags, * we implement it that way... If the system has no O_LARGEFILE, we'll * just call open() with nothing special. */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif /* Does link(2) let you create hard links to symlinks? Of course not. Who * would ever do that? Well, Linux did, and possibly as a result, linkat() * does by default too; if you are on a host with the historical Unix * behavior of following symlinks to find the link target, you will want * to set this to AT_SYMLINK_FOLLOW. Darwin does. */ #define PSEUDO_LINK_SYMLINK_BEHAVIOR 0 /* given n, pick a multiple of block enough bigger than n * to give us some breathing room. */ static inline size_t round_up(size_t n, size_t block) { return block * (((n + block / 4) / block) + 1); } #ifdef PSEUDO_PROFILING typedef struct { int processes; long long total_ops; long long messages; struct timeval op_time; struct timeval ipc_time; struct timeval wrapper_time; } pseudo_profile_t; #endif #include "pseudo_ports.h" pseudo-1.8.1+git20161012/pseudo_client.c000066400000000000000000001575771300506512600174410ustar00rootroot00000000000000/* * pseudo_client.c, pseudo client library code * * Copyright (c) 2008-2013 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PSEUDO_XATTRDB #include #endif #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_client.h" /* GNU extension */ #if PSEUDO_PORT_LINUX extern char *program_invocation_name; #else static char *program_invocation_name = "unknown"; #endif static char *base_path(int dirfd, const char *path, int leave_last); static int connect_fd = -1; static int server_pid = 0; int pseudo_prefix_dir_fd = -1; int pseudo_localstate_dir_fd = -1; int pseudo_pwd_fd = -1; int pseudo_pwd_lck_fd = -1; char *pseudo_pwd_lck_name = NULL; FILE *pseudo_pwd = NULL; int pseudo_grp_fd = -1; FILE *pseudo_grp = NULL; char *pseudo_cwd = NULL; size_t pseudo_cwd_len; char *pseudo_chroot = NULL; char *pseudo_passwd = NULL; size_t pseudo_chroot_len = 0; char *pseudo_cwd_rel = NULL; /* used for PSEUDO_DISABLED */ int pseudo_disabled = 0; int pseudo_allow_fsync = 0; static int pseudo_local_only = 0; static int pseudo_client_logging = 1; int pseudo_umask = 022; static char **fd_paths = NULL; static int nfds = 0; static const char **passwd_paths = NULL; static int npasswd_paths = 0; #ifdef PSEUDO_PROFILING int pseudo_profile_fd = -1; static int profile_interval = 1; static pseudo_profile_t profile_data; struct timeval *pseudo_wrapper_time = &profile_data.wrapper_time; #endif static int pseudo_inited = 0; static int sent_messages = 0; int pseudo_nosymlinkexp = 0; /* note: these are int, not uid_t/gid_t, so I can use 'em with scanf */ uid_t pseudo_ruid; uid_t pseudo_euid; uid_t pseudo_suid; uid_t pseudo_fuid; gid_t pseudo_rgid; gid_t pseudo_egid; gid_t pseudo_sgid; gid_t pseudo_fgid; int (*pseudo_real_fork)(void) = fork; int (*pseudo_real_execv)(const char *, char * const *) = execv; #define PSEUDO_ETC_FILE(filename, realname, flags) pseudo_etc_file(filename, realname, flags, passwd_paths, npasswd_paths) /* helper function to make a directory, just like mkdir -p. * Can't use system() because the child shell would end up trying * to do the same thing... */ static void mkdir_p(char *path) { size_t len = strlen(path); size_t i; for (i = 1; i < len; ++i) { /* try to create all the directories in path, ignoring * failures */ if (path[i] == '/') { path[i] = '\0'; (void) mkdir(path, 0755); path[i] = '/'; } } (void) mkdir(path, 0755); } /* Populating an array of unknown size is one of my least favorite * things. The idea here is to ensure that the logic flow is the same * both when counting expected items, and when populating them. */ static void build_passwd_paths(void) { int np = 0; int pass = 0; /* should never happen... */ if (passwd_paths) { free(passwd_paths); passwd_paths = 0; npasswd_paths = 0; } #define SHOW_PATH pseudo_debug(PDBGF_CHROOT | PDBGF_VERBOSE, "passwd_paths[%d]: '%s'\n", np, (passwd_paths[np])) #define ADD_PATH(p) do { if (passwd_paths) { passwd_paths[np] = (p); SHOW_PATH; } ++np; } while(0) #define NUL_BYTE(p) do { if (passwd_paths) { *(p)++ = '\0'; } else { ++(p); } } while(0) do { if (pseudo_chroot) { ADD_PATH(pseudo_chroot); } if (pseudo_passwd) { char *s = pseudo_passwd; while (s) { char *t = strchr(s, ':'); if (t) { NUL_BYTE(t); } ADD_PATH(s); s = t; } } if (PSEUDO_PASSWD_FALLBACK) { ADD_PATH(PSEUDO_PASSWD_FALLBACK); } /* allocation and/or return */ if (passwd_paths) { if (np != npasswd_paths) { pseudo_diag("internal error: path allocation was inconsistent.\n"); } else { /* yes, we allocated one extra for a trailing * null pointer. */ passwd_paths[np] = NULL; } return; } else { passwd_paths = malloc((np + 1) * sizeof(*passwd_paths)); npasswd_paths = np; if (!passwd_paths) { pseudo_diag("couldn't allocate storage for password paths.\n"); exit(1); } np = 0; } } while (++pass < 2); /* in theory the second pass already returned, but. */ pseudo_diag("should totally not have gotten here.\n"); return; } #ifdef PSEUDO_XATTRDB /* We really want to avoid calling the wrappers for these inside the * implementation. pseudo_wrappers will reinitialize these after it's * gotten the real_* found. */ ssize_t (*pseudo_real_lgetxattr)(const char *, const char *, void *, size_t) = lgetxattr; ssize_t (*pseudo_real_fgetxattr)(int, const char *, void *, size_t) = fgetxattr; int (*pseudo_real_lsetxattr)(const char *, const char *, const void *, size_t, int) = lsetxattr; int (*pseudo_real_fsetxattr)(int, const char *, const void *, size_t, int) = fsetxattr; /* Executive summary: We use an extended attribute, * user.pseudo_data, to store exactly the data we would otherwise * have stored in the database. Which is to say, uid, gid, mode, rdev. * * If we don't find a value, save an empty one with a lower version * number to indicate that we don't have data to reduce round trips. */ typedef struct { int version; uid_t uid; gid_t gid; mode_t mode; dev_t rdev; } pseudo_db_data_t; static pseudo_msg_t xattrdb_data; pseudo_msg_t * pseudo_xattrdb_save(int fd, const char *path, const struct stat64 *buf) { int rc = -1; if (!path && fd < 0) return NULL; if (!buf) return NULL; pseudo_db_data_t pseudo_db_data = { .version = 1, .uid = buf->st_uid, .gid = buf->st_gid, .mode = buf->st_mode, .rdev = buf->st_rdev }; if (path) { rc = pseudo_real_lsetxattr(path, "user.pseudo_data", &pseudo_db_data, sizeof(pseudo_db_data), 0); } else if (fd >= 0) { rc = pseudo_real_fsetxattr(fd, "user.pseudo_data", &pseudo_db_data, sizeof(pseudo_db_data), 0); } pseudo_debug(PDBGF_XATTRDB, "tried to save data for %s/%d: uid %d, mode %o, rc %d.\n", path ? path : "", fd, (int) pseudo_db_data.uid, (int) pseudo_db_data.mode, rc); /* none of the other fields are checked on save, and the value * is currently only really used by mknod. */ if (rc == 0) { xattrdb_data.result = RESULT_SUCCEED; return &xattrdb_data; } return NULL; } pseudo_msg_t * pseudo_xattrdb_load(int fd, const char *path, const struct stat64 *buf) { int rc = -1, retryrc = -1; if (!path && fd < 0) return NULL; /* don't try to getxattr on a thing unless we think it is * likely to work. */ if (buf) { if (!S_ISDIR(buf->st_mode) && !S_ISREG(buf->st_mode)) { return NULL; } } pseudo_db_data_t pseudo_db_data; if (path) { rc = pseudo_real_lgetxattr(path, "user.pseudo_data", &pseudo_db_data, sizeof(pseudo_db_data)); if (rc == -1) { pseudo_db_data = (pseudo_db_data_t) { .version = 0 }; retryrc = pseudo_real_lsetxattr(path, "user.pseudo_data", &pseudo_db_data, sizeof(pseudo_db_data), 0); } } else if (fd >= 0) { rc = pseudo_real_fgetxattr(fd, "user.pseudo_data", &pseudo_db_data, sizeof(pseudo_db_data)); if (rc == -1) { pseudo_db_data = (pseudo_db_data_t) { .version = 0 }; retryrc = pseudo_real_fsetxattr(fd, "user.pseudo_data", &pseudo_db_data, sizeof(pseudo_db_data), 0); } } pseudo_debug(PDBGF_XATTRDB, "tried to load data for %s[%d]: rc %d, version %d.\n", path ? path : "", fd, rc, pseudo_db_data.version); if (rc == -1 && retryrc == 0) { /* there's no data, but there could have been; treat * this as an empty database result. */ pseudo_debug(PDBGF_XATTRDB, "wrote version 0 for %s[%d]\n", path ? path : "", fd); xattrdb_data.result = RESULT_FAIL; return &xattrdb_data; } else if (rc == -1) { /* we can't create an extended attribute, so we may have * used the database. */ return NULL; } /* Version 0 = just recording that we looked and found * nothing. * Version 1 = actually implemented. */ switch (pseudo_db_data.version) { case 0: default: xattrdb_data.result = RESULT_FAIL; break; case 1: xattrdb_data.uid = pseudo_db_data.uid; xattrdb_data.gid = pseudo_db_data.gid; xattrdb_data.mode = pseudo_db_data.mode; xattrdb_data.rdev = pseudo_db_data.rdev; xattrdb_data.result = RESULT_SUCCEED; break; } return &xattrdb_data; } #endif #ifdef PSEUDO_PROFILING static int pseudo_profile_pid = -2; static void pseudo_profile_start(void) { /* We use -1 as a starting value, and -2 as a value * indicating not to try to open it. */ int existing_data = 0; int pid = getpid(); if (pseudo_profile_pid > 0 && pseudo_profile_pid != pid) { /* looks like there's been a fork. We intentionally * abandon any existing stats, since the parent might * want to write them, and we want to combine with * any previous stats for this pid. */ close(pseudo_profile_fd); pseudo_profile_fd = -1; } if (pseudo_profile_fd == -1) { int fd = -2; char *profile_path = pseudo_get_value("PSEUDO_PROFILE_PATH"); pseudo_profile_pid = pid; if (profile_path) { fd = open(profile_path, O_RDWR | O_CREAT, 0600); if (fd >= 0) { fd = pseudo_fd(fd, MOVE_FD); } if (fd < 0) { fd = -2; } else { if (pid > 0) { pseudo_profile_t data; off_t rc; rc = lseek(fd, pid * sizeof(data), SEEK_SET); if (rc == (off_t) (pid * sizeof(data))) { rc = read(fd, &profile_data, sizeof(profile_data)); /* cumulative with other values in same file */ if (rc == sizeof(profile_data)) { pseudo_debug(PDBGF_PROFILE, "pid %d found existing profiling data.\n", pid); existing_data = 1; ++profile_data.processes; } else { pseudo_debug(PDBGF_PROFILE, "read failed for pid %d: %d, %d\n", pid, (int) rc, errno); } profile_interval = 1; } } } } pseudo_profile_fd = fd; } else { pseudo_debug(PDBGF_PROFILE, "_start called with existing fd? (pid %d)", (int) pseudo_profile_pid); existing_data = 1; ++profile_data.processes; profile_data.total_ops = 0; profile_data.messages = 0; profile_data.wrapper_time = (struct timeval) { .tv_sec = 0 }; profile_data.op_time = (struct timeval) { .tv_sec = 0 }; profile_data.ipc_time = (struct timeval) { .tv_sec = 0 }; } if (!existing_data) { pseudo_debug(PDBGF_PROFILE, "pid %d found no existing profiling data.\n", pid); profile_data = (pseudo_profile_t) { .processes = 1, .total_ops = 0, .messages = 0, .wrapper_time = (struct timeval) { .tv_sec = 0 }, .op_time = (struct timeval) { .tv_sec = 0 }, .ipc_time = (struct timeval) { .tv_sec = 0 }, }; } } static inline void fix_tv(struct timeval *tv) { if (tv->tv_usec > 1000000) { tv->tv_sec += tv->tv_usec / 1000000; tv->tv_usec %= 1000000; } else if (tv->tv_usec < 0) { /* C99 and later guarantee truncate-towards-zero. * We want -1 through -1000000 usec to produce * -1 seconds, etcetera. Note that sec is * negative, so yes, we want to add to tv_sec and * subtract from tv_usec. */ int sec = (tv->tv_usec - 999999) / 1000000; tv->tv_sec += sec; tv->tv_usec -= 1000000 * sec; } } static int profile_reported = 0; static void pseudo_profile_report(void) { if (pseudo_profile_fd < 0) { return; } fix_tv(&profile_data.wrapper_time); fix_tv(&profile_data.op_time); fix_tv(&profile_data.ipc_time); if (pseudo_profile_pid >= 0) { int rc1, rc2; rc1 = lseek(pseudo_profile_fd, pseudo_profile_pid * sizeof(profile_data), SEEK_SET); rc2 = write(pseudo_profile_fd, &profile_data, sizeof(profile_data)); if (!profile_reported) { pseudo_debug(PDBGF_PROFILE, "pid %d (%s) saving profiling info: %d, %d.\n", pseudo_profile_pid, program_invocation_name ? program_invocation_name : "unknown", rc1, rc2); profile_reported = 1; } } } #endif void pseudo_init_client(void) { char *env; pseudo_antimagic(); pseudo_new_pid(); if (connect_fd != -1) { close(connect_fd); connect_fd = -1; } #ifdef PSEUDO_PROFILING if (pseudo_profile_fd > -1) { close(pseudo_profile_fd); } pseudo_profile_fd = -1; #endif /* in child processes, PSEUDO_DISABLED may have become set to * some truthy value, in which case we'd disable pseudo, * or it may have gone away, in which case we'd enable * pseudo (and cause it to reinit the defaults). */ env = getenv("PSEUDO_DISABLED"); if (!env) { env = pseudo_get_value("PSEUDO_DISABLED"); } if (env) { int actually_disabled = 1; switch (*env) { case '0': case 'f': case 'F': case 'n': case 'N': actually_disabled = 0; break; case 's': case 'S': actually_disabled = 0; pseudo_local_only = 1; break; } if (actually_disabled) { if (!pseudo_disabled) { pseudo_antimagic(); pseudo_disabled = 1; } pseudo_set_value("PSEUDO_DISABLED", "1"); } else { if (pseudo_disabled) { pseudo_magic(); pseudo_disabled = 0; pseudo_inited = 0; /* Re-read the initial values! */ } pseudo_set_value("PSEUDO_DISABLED", "0"); } } else { pseudo_set_value("PSEUDO_DISABLED", "0"); } /* ALLOW_FSYNC is here because some crazy hosts will otherwise * report incorrect values for st_size/st_blocks. I can sort of * understand st_blocks, but bogus values for st_size? Not cool, * dudes, not cool. */ env = getenv("PSEUDO_ALLOW_FSYNC"); if (!env) { env = pseudo_get_value("PSEUDO_ALLOW_FSYNC"); } else { pseudo_set_value("PSEUDO_ALLOW_FSYNC", env); } if (env) { pseudo_allow_fsync = 1; } else { pseudo_allow_fsync = 0; } /* in child processes, PSEUDO_UNLOAD may become set to * some truthy value, in which case we're being asked to * remove pseudo from the LD_PRELOAD. We need to make sure * this value gets loaded into the internal variables. * * If we've been told to unload, but are still available * we need to act as if unconditionally disabled. */ env = getenv("PSEUDO_UNLOAD"); if (env) { pseudo_set_value("PSEUDO_UNLOAD", env); pseudo_disabled = 1; } /* Setup global items needed for pseudo to function... */ if (!pseudo_inited) { /* Ensure that all of the values are reset */ server_pid = 0; pseudo_prefix_dir_fd = -1; pseudo_localstate_dir_fd = -1; pseudo_pwd_fd = -1; pseudo_pwd_lck_fd = -1; pseudo_pwd_lck_name = NULL; pseudo_pwd = NULL; pseudo_grp_fd = -1; pseudo_grp = NULL; pseudo_cwd = NULL; pseudo_cwd_len = 0; pseudo_chroot = NULL; pseudo_passwd = NULL; pseudo_chroot_len = 0; pseudo_cwd_rel = NULL; pseudo_nosymlinkexp = 0; } if (!pseudo_disabled && !pseudo_inited) { char *pseudo_path = 0; pseudo_umask = umask(022); umask(pseudo_umask); pseudo_path = pseudo_prefix_path(NULL); if (pseudo_prefix_dir_fd == -1) { if (pseudo_path) { pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_prefix_dir_fd == -1 && errno == ENOENT) { pseudo_debug(PDBGF_CLIENT, "prefix directory '%s' doesn't exist, trying to create\n", pseudo_path); mkdir_p(pseudo_path); pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_prefix_dir_fd = pseudo_fd(pseudo_prefix_dir_fd, MOVE_FD); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_prefix_dir_fd == -1) { pseudo_diag("Can't open prefix path '%s' for server: %s\n", pseudo_path, strerror(errno)); exit(1); } } free(pseudo_path); pseudo_path = pseudo_localstatedir_path(NULL); if (pseudo_localstate_dir_fd == -1) { if (pseudo_path) { pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_localstate_dir_fd == -1 && errno == ENOENT) { pseudo_debug(PDBGF_CLIENT, "local state directory '%s' doesn't exist, trying to create\n", pseudo_path); mkdir_p(pseudo_path); pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_localstate_dir_fd = pseudo_fd(pseudo_localstate_dir_fd, MOVE_FD); } else { pseudo_diag("No local state directory available for server/file interactions.\n"); exit(1); } if (pseudo_localstate_dir_fd == -1) { pseudo_diag("Can't open local state path '%s': %s\n", pseudo_path, strerror(errno)); exit(1); } } free(pseudo_path); env = pseudo_get_value("PSEUDO_NOSYMLINKEXP"); if (env) { char *endptr; /* if the environment variable is not an empty string, * parse it; "0" means turn NOSYMLINKEXP off, "1" means * turn it on (disabling the feature). An empty string * or something we can't parse means to set the flag; this * is a safe default because if you didn't want the flag * set, you normally wouldn't set the environment variable * at all. */ if (*env) { pseudo_nosymlinkexp = strtol(env, &endptr, 10); if (*endptr) pseudo_nosymlinkexp = 1; } else { pseudo_nosymlinkexp = 1; } } else { pseudo_nosymlinkexp = 0; } free(env); env = pseudo_get_value("PSEUDO_UIDS"); if (env) sscanf(env, "%d,%d,%d,%d", &pseudo_ruid, &pseudo_euid, &pseudo_suid, &pseudo_fuid); free(env); env = pseudo_get_value("PSEUDO_GIDS"); if (env) sscanf(env, "%d,%d,%d,%d", &pseudo_rgid, &pseudo_egid, &pseudo_sgid, &pseudo_fuid); free(env); env = pseudo_get_value("PSEUDO_CHROOT"); if (env) { pseudo_chroot = strdup(env); if (pseudo_chroot) { pseudo_chroot_len = strlen(pseudo_chroot); } else { pseudo_diag("Can't store chroot path '%s'\n", env); } } free(env); env = pseudo_get_value("PSEUDO_PASSWD"); if (env) { /* note: this means that pseudo_passwd is a * string we're allowed to modify... */ pseudo_passwd = strdup(env); } free(env); build_passwd_paths(); pseudo_inited = 1; } if (!pseudo_disabled) { pseudo_client_getcwd(); #ifdef PSEUDO_PROFILING pseudo_profile_start(); #endif } pseudo_magic(); } static void pseudo_file_close(int *fd, FILE **fp) { if (!fp || !fd) { pseudo_diag("pseudo_file_close: needs valid pointers.\n"); return; } pseudo_antimagic(); if (*fp) { #if PSEUDO_PORT_DARWIN if (*fp == pseudo_host_etc_passwd_file) { endpwent(); } else if (*fp != pseudo_host_etc_group_file) { endgrent(); } else { fclose(*fp); } #else fclose(*fp); #endif *fd = -1; *fp = 0; } #if PSEUDO_PORT_DARWIN if (*fd == pseudo_host_etc_passwd_fd || *fd == pseudo_host_etc_group_fd) { *fd = -1; } #endif /* this should be impossible */ if (*fd >= 0) { close(*fd); *fd = -1; } pseudo_magic(); } static FILE * pseudo_file_open(char *name, int *fd, FILE **fp) { if (!fp || !fd || !name) { pseudo_diag("pseudo_file_open: needs valid pointers.\n"); return NULL; } pseudo_file_close(fd, fp); pseudo_antimagic(); *fd = PSEUDO_ETC_FILE(name, NULL, O_RDONLY); #if PSEUDO_PORT_DARWIN if (*fd == pseudo_host_etc_passwd_fd) { *fp = pseudo_host_etc_passwd_file; setpwent(); } else if (*fd == pseudo_host_etc_group_fd) { *fp = pseudo_host_etc_group_file; setgrent(); } #endif if (*fd >= 0) { *fd = pseudo_fd(*fd, MOVE_FD); *fp = fdopen(*fd, "r"); if (!*fp) { close(*fd); *fd = -1; } } pseudo_magic(); return *fp; } /* there is no spec I know of requiring us to defend this fd * against being closed by the user. */ int pseudo_pwd_lck_open(void) { pseudo_pwd_lck_close(); if (!pseudo_pwd_lck_name) { pseudo_pwd_lck_name = malloc(pseudo_path_max()); if (!pseudo_pwd_lck_name) { pseudo_diag("couldn't allocate space for passwd lockfile path.\n"); return -1; } } pseudo_antimagic(); pseudo_pwd_lck_fd = PSEUDO_ETC_FILE(".pwd.lock", pseudo_pwd_lck_name, O_RDWR | O_CREAT); pseudo_magic(); return pseudo_pwd_lck_fd; } int pseudo_pwd_lck_close(void) { if (pseudo_pwd_lck_fd != -1) { pseudo_antimagic(); close(pseudo_pwd_lck_fd); if (pseudo_pwd_lck_name) { unlink(pseudo_pwd_lck_name); free(pseudo_pwd_lck_name); pseudo_pwd_lck_name = 0; } pseudo_magic(); pseudo_pwd_lck_fd = -1; return 0; } else { return -1; } } FILE * pseudo_pwd_open(void) { return pseudo_file_open("passwd", &pseudo_pwd_fd, &pseudo_pwd); } void pseudo_pwd_close(void) { pseudo_file_close(&pseudo_pwd_fd, &pseudo_pwd); } FILE * pseudo_grp_open(void) { return pseudo_file_open("group", &pseudo_grp_fd, &pseudo_grp); } void pseudo_grp_close(void) { pseudo_file_close(&pseudo_grp_fd, &pseudo_grp); } void pseudo_client_touchuid(void) { static char uidbuf[256]; snprintf(uidbuf, 256, "%d,%d,%d,%d", pseudo_ruid, pseudo_euid, pseudo_suid, pseudo_fuid); pseudo_set_value("PSEUDO_UIDS", uidbuf); } void pseudo_client_touchgid(void) { static char gidbuf[256]; snprintf(gidbuf, 256, "%d,%d,%d,%d", pseudo_rgid, pseudo_egid, pseudo_sgid, pseudo_fgid); pseudo_set_value("PSEUDO_GIDS", gidbuf); } int pseudo_client_chroot(const char *path) { /* free old value */ free(pseudo_chroot); pseudo_debug(PDBGF_CLIENT | PDBGF_CHROOT, "client chroot: %s\n", path); if (!strcmp(path, "/")) { pseudo_chroot_len = 0; pseudo_chroot = 0; pseudo_set_value("PSEUDO_CHROOT", NULL); return 0; } /* allocate new value */ pseudo_chroot_len = strlen(path); pseudo_chroot = malloc(pseudo_chroot_len + 1); if (!pseudo_chroot) { pseudo_diag("Couldn't allocate chroot directory buffer.\n"); pseudo_chroot_len = 0; errno = ENOMEM; return -1; } memcpy(pseudo_chroot, path, pseudo_chroot_len + 1); pseudo_set_value("PSEUDO_CHROOT", pseudo_chroot); return 0; } char * pseudo_root_path(const char *func, int line, int dirfd, const char *path, int leave_last) { char *rc; pseudo_antimagic(); rc = base_path(dirfd, path, leave_last); pseudo_magic(); if (!rc) { pseudo_diag("couldn't allocate absolute path for '%s'.\n", path); } pseudo_debug(PDBGF_CHROOT, "root_path [%s, %d]: '%s' from '%s'\n", func, line, rc ? rc : "", path ? path : ""); return rc; } int pseudo_client_getcwd(void) { char *cwd; cwd = malloc(pseudo_path_max()); if (!cwd) { pseudo_diag("Can't allocate CWD buffer!\n"); return -1; } pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "getcwd: trying to find cwd.\n"); if (getcwd(cwd, pseudo_path_max())) { /* cwd now holds a canonical path to current directory */ free(pseudo_cwd); pseudo_cwd = cwd; pseudo_cwd_len = strlen(pseudo_cwd); pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "getcwd okay: [%s] %d bytes\n", pseudo_cwd, (int) pseudo_cwd_len); if (pseudo_chroot_len && pseudo_cwd_len >= pseudo_chroot_len && !memcmp(pseudo_cwd, pseudo_chroot, pseudo_chroot_len) && (pseudo_cwd[pseudo_chroot_len] == '\0' || pseudo_cwd[pseudo_chroot_len] == '/')) { pseudo_cwd_rel = pseudo_cwd + pseudo_chroot_len; } else { pseudo_cwd_rel = pseudo_cwd; } pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "abscwd: <%s>\n", pseudo_cwd); if (pseudo_cwd_rel != pseudo_cwd) { pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "relcwd: <%s>\n", pseudo_cwd_rel); } return 0; } else { pseudo_diag("Can't get CWD: %s\n", strerror(errno)); return -1; } } static char * fd_path(int fd) { if (fd >= 0 && fd < nfds) { return fd_paths[fd]; } if (fd == AT_FDCWD) { return pseudo_cwd; } return 0; } static void pseudo_client_path(int fd, const char *path) { if (fd < 0) return; if (fd >= nfds) { int i; pseudo_debug(PDBGF_CLIENT, "expanding fds from %d to %d\n", nfds, fd + 1); fd_paths = realloc(fd_paths, (fd + 1) * sizeof(char *)); for (i = nfds; i < fd + 1; ++i) fd_paths[i] = 0; nfds = fd + 1; } else { if (fd_paths[fd]) { pseudo_debug(PDBGF_CLIENT, "reopening fd %d [%s] -- didn't see close\n", fd, fd_paths[fd]); } /* yes, it is safe to free null pointers. yay for C89 */ free(fd_paths[fd]); fd_paths[fd] = 0; } if (path) { fd_paths[fd] = strdup(path); } } static void pseudo_client_close(int fd) { if (fd < 0 || fd >= nfds) return; free(fd_paths[fd]); fd_paths[fd] = 0; } /* spawn server */ static int client_spawn_server(void) { int status; FILE *fp; char * pseudo_pidfile; if ((server_pid = pseudo_real_fork()) != 0) { if (server_pid == -1) { pseudo_diag("couldn't fork server: %s\n", strerror(errno)); return 1; } pseudo_evlog(PDBGF_CLIENT, "spawned new server, pid %d\n", server_pid); pseudo_debug(PDBGF_CLIENT | PDBGF_SERVER, "spawned server, pid %d\n", server_pid); /* wait for the child process to terminate, indicating server * is ready */ waitpid(server_pid, &status, 0); if (WIFEXITED(status)) { pseudo_evlog(PDBGF_CLIENT, "server exited status %d\n", WEXITSTATUS(status)); } if (WIFSIGNALED(status)) { pseudo_evlog(PDBGF_CLIENT, "server exited from signal %d\n", WTERMSIG(status)); } server_pid = -2; if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { pseudo_evlog(PDBGF_CLIENT, "server reports successful startup, reading pid.\n"); pseudo_pidfile = pseudo_localstatedir_path(PSEUDO_PIDFILE); fp = fopen(pseudo_pidfile, "r"); if (fp) { if (fscanf(fp, "%d", &server_pid) != 1) { pseudo_debug(PDBGF_CLIENT, "Opened server PID file, but didn't get a pid.\n"); } fclose(fp); } else { pseudo_debug(PDBGF_CLIENT, "no pid file (%s): %s\n", pseudo_pidfile, strerror(errno)); } pseudo_debug(PDBGF_CLIENT, "read new pid file: %d\n", server_pid); free(pseudo_pidfile); /* at this point, we should have a new server_pid */ return 0; } else { pseudo_evlog(PDBGF_CLIENT, "server startup apparently unsuccessful. setting server pid to -1.\n"); server_pid = -1; return 1; } } else { char *base_args[] = { NULL, NULL, NULL }; char **argv; char *option_string = pseudo_get_value("PSEUDO_OPTS"); int args; int fd; pseudo_new_pid(); base_args[0] = pseudo_bindir_path("pseudo"); base_args[1] = "-d"; if (option_string) { char *s; int arg; /* count arguments in PSEUDO_OPTS, starting at 2 * for pseudo/-d/NULL, plus one for the option string. * The number of additional arguments may be less * than the number of spaces, but can't be more. */ args = 4; for (s = option_string; *s; ++s) if (*s == ' ') ++args; argv = malloc(args * sizeof(char *)); argv[0] = base_args[0]; argv[1] = base_args[1]; arg = 2; while ((s = strsep(&option_string, " ")) != NULL) { if (*s) { argv[arg++] = strdup(s); } } argv[arg] = 0; } else { argv = base_args; } /* close any higher-numbered fds which might be open, * such as sockets. We don't have to worry about 0 and 1; * the server closes them already, and more importantly, * they can't have been opened or closed without us already * having spawned a server... The issue is just socket() * calls which could result in fds being left open, and those * can't overwrite fds 0-2 unless we closed them... * * No, really. It works. */ for (fd = 3; fd < 1024; ++fd) { if (fd != pseudo_util_debug_fd) close(fd); } /* and now, execute the server */ pseudo_debug(PDBGF_CLIENT | PDBGF_SERVER | PDBGF_INVOKE, "calling execv on %s\n", argv[0]); /* don't try to log this exec, because it'll cause the process * that is supposed to be spawning the server to try to spawn * a server. Whoops. This is because the exec wrapper doesn't * respect antimagic, which I believe is intentional. */ pseudo_client_logging = 0; /* manual setup of environment, so we can call real-execv * instead of the wrapper. */ pseudo_set_value("PSEUDO_UNLOAD", "YES"); pseudo_setupenv(); pseudo_dropenv(); pseudo_real_execv(argv[0], argv); pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno)); exit(1); } } static int client_ping(void) { pseudo_msg_t ping; pseudo_msg_t *ack; char tagbuf[pseudo_path_max()]; char *tag = pseudo_get_value("PSEUDO_TAG"); memset(&ping, 0, sizeof(ping)); ping.type = PSEUDO_MSG_PING; ping.op = OP_NONE; ping.pathlen = snprintf(tagbuf, sizeof(tagbuf), "%s%c%s", program_invocation_name ? program_invocation_name : "", 0, tag ? tag : ""); free(tag); ping.client = getpid(); ping.result = 0; errno = 0; pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "sending ping\n"); if (pseudo_msg_send(connect_fd, &ping, ping.pathlen, tagbuf)) { pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "error pinging server: %s\n", strerror(errno)); return 1; } ack = pseudo_msg_receive(connect_fd); if (!ack) { pseudo_debug(PDBGF_CLIENT, "no ping response from server: %s\n", strerror(errno)); /* and that's not good, so... */ server_pid = 0; return 1; } if (ack->type != PSEUDO_MSG_ACK) { pseudo_debug(PDBGF_CLIENT, "invalid ping response from server: expected ack, got %d\n", ack->type); /* and that's not good, so... */ server_pid = 0; return 1; } else { /* The server tells us whether or not to log things. */ if (ack->result == RESULT_SUCCEED) { pseudo_client_logging = 1; } else { pseudo_client_logging = 0; } } pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "ping ok\n"); return 0; } static void void_client_ping(void) { client_ping(); } int pseudo_fd(int fd, int how) { int newfd; if (fd < 0) return(-1); /* If already above PSEUDO_MIN_FD, no need to move */ if ((how == MOVE_FD) && (fd >= PSEUDO_MIN_FD)) { newfd = fd; } else { newfd = fcntl(fd, F_DUPFD, PSEUDO_MIN_FD); if (how == MOVE_FD) close(fd); } /* Set close on exec, even if we didn't move it. */ if ((newfd >= 0) && (fcntl(newfd, F_SETFD, FD_CLOEXEC) < 0)) pseudo_diag("Can't set close on exec flag: %s\n", strerror(errno)); return(newfd); } static int client_connect(void) { /* we have a server pid, is it responsive? */ struct sockaddr_un sun = { .sun_family = AF_UNIX, .sun_path = PSEUDO_SOCKET }; int cwd_fd; #if PSEUDO_PORT_DARWIN sun.sun_len = strlen(PSEUDO_SOCKET) + 1; #endif connect_fd = socket(PF_UNIX, SOCK_STREAM, 0); connect_fd = pseudo_fd(connect_fd, MOVE_FD); pseudo_evlog(PDBGF_CLIENT, "creating socket %s.\n", sun.sun_path); if (connect_fd == -1) { char *e = strerror(errno); pseudo_diag("Can't create socket: %s (%s)\n", sun.sun_path, e); pseudo_evlog(PDBGF_CLIENT, "failed to create socket: %s\n", e); return 1; } pseudo_debug(PDBGF_CLIENT, "connecting socket...\n"); cwd_fd = open(".", O_RDONLY); if (cwd_fd == -1) { pseudo_diag("Couldn't stash directory before opening socket: %s", strerror(errno)); close(connect_fd); connect_fd = -1; return 1; } if (fchdir(pseudo_localstate_dir_fd) == -1) { pseudo_diag("Couldn't chdir to server directory [%d]: %s\n", pseudo_localstate_dir_fd, strerror(errno)); close(connect_fd); close(cwd_fd); connect_fd = -1; return 1; } if (connect(connect_fd, (struct sockaddr *) &sun, sizeof(sun)) == -1) { char *e = strerror(errno); pseudo_debug(PDBGF_CLIENT, "Can't connect socket to pseudo.socket: (%s)\n", e); pseudo_evlog(PDBGF_CLIENT, "connect failed: %s\n", e); close(connect_fd); if (fchdir(cwd_fd) == -1) { pseudo_diag("return to previous directory failed: %s\n", strerror(errno)); } close(cwd_fd); connect_fd = -1; return 1; } if (fchdir(cwd_fd) == -1) { pseudo_diag("return to previous directory failed: %s\n", strerror(errno)); } close(cwd_fd); pseudo_evlog(PDBGF_CLIENT, "socket connect OK.\n"); pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "connected socket.\n"); return 0; } static int pseudo_client_setup(void) { char * pseudo_pidfile; FILE *fp; server_pid = 0; /* avoid descriptor leak, I hope */ if (connect_fd >= 0) { close(connect_fd); connect_fd = -1; } if (client_connect() == 0) { pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "connection started.\n"); if (client_ping() == 0) { pseudo_debug(PDBGF_CLIENT, "connection ping okay, server ready.\n"); return 0; } else { pseudo_debug(PDBGF_CLIENT, "connection up, but ping failed.\n"); /* and now we'll close the connection, and try to * kick the server. */ close(connect_fd); connect_fd = -1; } } pseudo_debug(PDBGF_CLIENT, "connection failed, checking server...\n"); /* This whole section is purely informational; we want to know * what might be up with the server, but we're going to try to * start one anyway. */ pseudo_pidfile = pseudo_localstatedir_path(PSEUDO_PIDFILE); fp = fopen(pseudo_pidfile, "r"); free(pseudo_pidfile); if (fp) { if (fscanf(fp, "%d", &server_pid) != 1) { pseudo_debug(PDBGF_CLIENT, "Opened server PID file, but didn't get a pid.\n"); } fclose(fp); } if (server_pid > 0) { if (kill(server_pid, 0) == -1) { pseudo_debug(PDBGF_CLIENT, "couldn't find server at pid %d: %s\n", server_pid, strerror(errno)); server_pid = 0; } else { pseudo_debug(PDBGF_CLIENT, "server pid should be %d, which exists, but connection failed.\n", server_pid); /* and we'll restart the server anyway. */ } } else { pseudo_debug(PDBGF_CLIENT, "no server pid available.\n"); } if (client_spawn_server()) { pseudo_evlog(PDBGF_CLIENT, "attempted respawn, failed.\n"); pseudo_debug(PDBGF_CLIENT, "failed to spawn server, waiting for retry.\n"); return 1; } else { pseudo_evlog(PDBGF_CLIENT, "restarted, new pid %d, will retry message\n", server_pid); pseudo_debug(PDBGF_CLIENT, "restarted, new pid %d, will retry message\n", server_pid); /* so we think a server has started. Now we'll retry the * connect/ping, because if they work we'll have set * connect_fd correctly, and will have succeeded. */ if (!client_connect()) { pseudo_debug(PDBGF_CLIENT, "connect okay to restarted server.\n"); pseudo_evlog(PDBGF_CLIENT, "connect okay to restarted server.\n"); if (!client_ping()) { pseudo_debug(PDBGF_CLIENT, "ping okay to restarted server.\n"); pseudo_evlog(PDBGF_CLIENT, "ping okay to restarted server.\n"); return 0; } else { pseudo_debug(PDBGF_CLIENT, "ping failed to restarted server.\n"); pseudo_evlog(PDBGF_CLIENT, "ping failed to restarted server.\n"); close(connect_fd); connect_fd = -1; return 1; } } else { pseudo_debug(PDBGF_CLIENT, "connect failed to restarted server.\n"); pseudo_evlog(PDBGF_CLIENT, "connect failed to restarted server.\n"); return 1; } } } #define PSEUDO_RETRIES 20 static pseudo_msg_t * pseudo_client_request(pseudo_msg_t *msg, size_t len, const char *path) { pseudo_msg_t *response = 0; int tries = 0; int rc; extern char *program_invocation_short_name; #if 0 if (!strcmp(program_invocation_short_name, "pseudo")) abort(); #endif if (!msg) return 0; /* Try to send a message. If sending fails, try to spawn a server, * and whether or not we succeed, wait a little bit and retry sending. * It's okay if we can't start a server sometimes, because another * client may have done it. */ pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "sending a message: ino %llu\n", (unsigned long long) msg->ino); for (tries = 0; tries < PSEUDO_RETRIES; ++tries) { pseudo_evlog(PDBGF_CLIENT, "try %d, connect fd is %d\n", tries, connect_fd); rc = pseudo_msg_send(connect_fd, msg, len, path); if (rc != 0) { pseudo_evlog(PDBGF_CLIENT, "msg_send failed [%d].\n", rc); pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "msg_send: %d%s\n", rc, rc == -1 ? " (sigpipe)" : " (short write)"); pseudo_debug(PDBGF_CLIENT, "trying to get server, try %d\n", tries); /* try to open server; if we fail, wait a bit before * retry. */ if (pseudo_client_setup()) { int ms = (getpid() % 5) + (3 * tries); struct timespec delay = { .tv_sec = 0, .tv_nsec = ms * 1000000 }; pseudo_evlog(PDBGF_CLIENT, "setup failed, delaying %d ms.\n", ms); nanosleep(&delay, NULL); } else { pseudo_evlog(PDBGF_CLIENT, "setup apparently successful, retrying message immediately.\n"); } continue; } /* note "continue" above; we only get here if rc was 0, * indicating a successful send. */ pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "sent!\n"); if (msg->type != PSEUDO_MSG_FASTOP) { response = pseudo_msg_receive(connect_fd); if (!response) { pseudo_debug(PDBGF_CLIENT, "expected response did not occur; retrying\n"); } else { if (response->type != PSEUDO_MSG_ACK) { pseudo_debug(PDBGF_CLIENT, "got non-ack response %d\n", response->type); return 0; } else { pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "got response type %d\n", response->type); return response; } } } else { return 0; } } pseudo_diag("pseudo: server connection persistently failed, aborting.\n"); pseudo_evlog_dump(); pseudo_diag("event log dumped, aborting.\n"); abort(); pseudo_diag("aborted.\n"); return 0; } int pseudo_client_shutdown(void) { pseudo_msg_t msg; pseudo_msg_t *ack; char *pseudo_path; pseudo_debug(PDBGF_INVOKE, "attempting to shut down server.\n"); pseudo_path = pseudo_prefix_path(NULL); if (pseudo_prefix_dir_fd == -1) { if (pseudo_path) { pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_prefix_dir_fd == -1 && errno == ENOENT) { pseudo_debug(PDBGF_CLIENT, "prefix directory doesn't exist, trying to create\n"); mkdir_p(pseudo_path); pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_prefix_dir_fd = pseudo_fd(pseudo_prefix_dir_fd, COPY_FD); free(pseudo_path); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_prefix_dir_fd == -1) { pseudo_diag("Can't open prefix path (%s) for server. (%s)\n", pseudo_prefix_path(NULL), strerror(errno)); exit(1); } } pseudo_path = pseudo_localstatedir_path(NULL); mkdir_p(pseudo_path); if (pseudo_localstate_dir_fd == -1) { if (pseudo_path) { pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); /* directory is missing? */ if (pseudo_localstate_dir_fd == -1 && errno == ENOENT) { pseudo_debug(PDBGF_CLIENT, "local state dir doesn't exist, trying to create\n"); mkdir_p(pseudo_path); pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); } pseudo_localstate_dir_fd = pseudo_fd(pseudo_localstate_dir_fd, COPY_FD); free(pseudo_path); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_localstate_dir_fd == -1) { pseudo_diag("Can't open local state path (%s) for server. (%s)\n", pseudo_localstatedir_path(NULL), strerror(errno)); exit(1); } } if (client_connect()) { pseudo_debug(PDBGF_INVOKE, "Pseudo server seems to be already offline.\n"); return 0; } memset(&msg, 0, sizeof(pseudo_msg_t)); msg.type = PSEUDO_MSG_SHUTDOWN; msg.op = OP_NONE; msg.client = getpid(); pseudo_debug(PDBGF_CLIENT | PDBGF_SERVER, "sending shutdown request\n"); if (pseudo_msg_send(connect_fd, &msg, 0, NULL)) { pseudo_debug(PDBGF_CLIENT | PDBGF_SERVER, "error requesting shutdown: %s\n", strerror(errno)); return 1; } ack = pseudo_msg_receive(connect_fd); if (!ack) { pseudo_diag("server did not respond to shutdown query.\n"); return 1; } if (ack->type == PSEUDO_MSG_ACK) { return 0; } pseudo_diag("Server refused shutdown. Remaining client fds: %d\n", ack->fd); pseudo_diag("Client pids: %s\n", ack->path); pseudo_diag("Server will shut down after all clients exit.\n"); return 0; } static char * base_path(int dirfd, const char *path, int leave_last) { char *basepath = 0; size_t baselen = 0; size_t minlen = 0; char *newpath; if (!path) return NULL; if (!*path) return ""; if (path[0] != '/') { if (dirfd != -1 && dirfd != AT_FDCWD) { if (dirfd >= 0) { basepath = fd_path(dirfd); } if (basepath) { baselen = strlen(basepath); } else { pseudo_diag("got *at() syscall for unknown directory, fd %d\n", dirfd); } } else { basepath = pseudo_cwd; baselen = pseudo_cwd_len; } if (!basepath) { pseudo_diag("unknown base path for fd %d, path %s\n", dirfd, path); return 0; } /* if there's a chroot path, and it's the start of basepath, * flag it for pseudo_fix_path */ if (pseudo_chroot_len && baselen >= pseudo_chroot_len && !memcmp(basepath, pseudo_chroot, pseudo_chroot_len) && (basepath[pseudo_chroot_len] == '\0' || basepath[pseudo_chroot_len] == '/')) { minlen = pseudo_chroot_len; } } else if (pseudo_chroot_len) { /* "absolute" is really relative to chroot path */ basepath = pseudo_chroot; baselen = pseudo_chroot_len; minlen = pseudo_chroot_len; } newpath = pseudo_fix_path(basepath, path, minlen, baselen, NULL, leave_last); pseudo_debug(PDBGF_PATH, "base_path: %s%s\n", basepath ? basepath : "", path ? path : ""); return newpath; } pseudo_msg_t * pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path, const PSEUDO_STATBUF *buf, ...) { pseudo_msg_t *result = 0; pseudo_msg_t msg = { .type = PSEUDO_MSG_OP }; size_t pathlen = -1; int do_request = 0; char *path_extra_1 = 0; size_t path_extra_1len = 0; char *path_extra_2 = 0; size_t path_extra_2len = 0; static char *alloced_path = 0; static size_t alloced_len = 0; int strip_slash; #ifdef PSEUDO_PROFILING struct timeval tv1_op, tv2_op; gettimeofday(&tv1_op, NULL); ++profile_data.total_ops; #endif /* disable wrappers */ pseudo_antimagic(); /* note: I am not entirely sure this should happen even if no * messages have actually been sent. */ if (!sent_messages) { sent_messages = 1; atexit(void_client_ping); } /* if path isn't available, try to find one? */ if (!path && fd >= 0 && fd <= nfds) { path = fd_path(fd); if (!path) { pathlen = 0; } else { pathlen = strlen(path) + 1; } } #ifdef PSEUDO_XATTRDB if (buf) { struct stat64 bufcopy = *buf; int do_save = 0; /* maybe use xattr instead */ /* note: if we use xattr, logging won't work reliably * because the server won't get messages if these work. */ switch (op) { case OP_CHMOD: case OP_FCHMOD: case OP_CHOWN: case OP_FCHOWN: /* for these, we want to start with the existing db * values. */ bufcopy = *buf; result = pseudo_xattrdb_load(fd, path, buf); if (result && result->result == RESULT_SUCCEED) { pseudo_debug(PDBGF_XATTR, "merging existing values for xattr\n"); switch (op) { case OP_CHMOD: case OP_FCHMOD: bufcopy.st_uid = result->uid; bufcopy.st_gid = result->gid; break; case OP_CHOWN: case OP_FCHOWN: bufcopy.st_rdev = result->rdev; bufcopy.st_mode = result->mode; break; default: break; } } else { switch (op) { case OP_CHMOD: case OP_FCHMOD: bufcopy.st_uid = pseudo_fuid; bufcopy.st_gid = pseudo_fgid; break; default: break; } } result = NULL; do_save = 1; break; case OP_CREAT: case OP_MKDIR: case OP_MKNOD: bufcopy.st_uid = pseudo_fuid; bufcopy.st_gid = pseudo_fgid; do_save = 1; break; case OP_LINK: do_save = 1; break; case OP_FSTAT: case OP_STAT: result = pseudo_xattrdb_load(fd, path, buf); break; default: break; } if (do_save) { result = pseudo_xattrdb_save(fd, path, &bufcopy); } if (result) goto skip_path; } #endif if (op == OP_RENAME) { va_list ap; if (!path) { pseudo_diag("rename (%s) without new path.\n", path ? path : ""); pseudo_magic(); return 0; } va_start(ap, buf); path_extra_1 = va_arg(ap, char *); va_end(ap); /* last argument is the previous path of the file */ if (!path_extra_1) { pseudo_diag("rename (%s) without old path.\n", path ? path : ""); pseudo_magic(); return 0; } path_extra_1len = strlen(path_extra_1); pseudo_debug(PDBGF_PATH | PDBGF_FILE, "rename: %s -> %s\n", path_extra_1, path); } /* we treat the "create" and "replace" flags as logically * distinct operations, because they can fail when set can't. */ if (op == OP_SET_XATTR || op == OP_CREATE_XATTR || op == OP_REPLACE_XATTR) { va_list ap; va_start(ap, buf); path_extra_1 = va_arg(ap, char *); path_extra_1len = strlen(path_extra_1); path_extra_2 = va_arg(ap, char *); path_extra_2len = va_arg(ap, size_t); va_end(ap); pseudo_debug(PDBGF_XATTR, "setxattr, name '%s', value %d bytes\n", path_extra_1, (int) path_extra_2len); pseudo_debug_call(PDBGF_XATTR | PDBGF_VERBOSE, pseudo_dump_data, "xattr value", path_extra_2, path_extra_2len); } if (op == OP_GET_XATTR || op == OP_REMOVE_XATTR) { va_list ap; va_start(ap, buf); path_extra_1 = va_arg(ap, char *); path_extra_1len = strlen(path_extra_1); va_end(ap); pseudo_debug(PDBGF_XATTR, "%sxattr, name '%s'\n", op == OP_GET_XATTR ? "get" : "remove", path_extra_1); } if (path) { if (pathlen == (size_t) -1) { pathlen = strlen(path) + 1; } /* path fixup has to happen in the specific functions, * because they may have to make calls which have to be * fixed up for chroot stuff already. * ... but wait! / in chroot should not have that * trailing /. * (no attempt is made to handle a rename of "/" occurring * in a chroot...) */ strip_slash = (pathlen > 2 && (path[pathlen - 2]) == '/'); } else { path = ""; pathlen = 0; strip_slash = 0; } /* f*xattr operations can result in needing to send a path * value even though we don't have one available. We use an * empty path for that. */ if (path_extra_1) { size_t full_len = path_extra_1len + 1 + pathlen; size_t partial_len = pathlen - 1 - strip_slash; if (path_extra_2) { full_len += path_extra_2len + 1; } if (full_len > alloced_len) { free(alloced_path); alloced_path = malloc(full_len); alloced_len = full_len; if (!alloced_path) { pseudo_diag("Can't allocate space for paths for a rename operation. Sorry.\n"); alloced_len = 0; pseudo_magic(); return 0; } } memcpy(alloced_path, path, partial_len); alloced_path[partial_len] = '\0'; memcpy(alloced_path + partial_len + 1, path_extra_1, path_extra_1len); alloced_path[partial_len + path_extra_1len + 1] = '\0'; if (path_extra_2) { memcpy(alloced_path + partial_len + path_extra_1len + 2, path_extra_2, path_extra_2len); } alloced_path[full_len - 1] = '\0'; path = alloced_path; pathlen = full_len; pseudo_debug_call(PDBGF_IPC | PDBGF_VERBOSE, pseudo_dump_data, "combined path buffer", path, pathlen); } else { if (strip_slash) { if (pathlen > alloced_len) { free(alloced_path); alloced_path = malloc(pathlen); alloced_len = pathlen; if (!alloced_path) { pseudo_diag("Can't allocate space for paths for a rename operation. Sorry.\n"); alloced_len = 0; pseudo_magic(); return 0; } } memcpy(alloced_path, path, pathlen); alloced_path[pathlen - 2] = '\0'; path = alloced_path; } } #ifdef PSEUDO_XATTRDB /* If we were able to store things in xattr, we can easily skip * most of the fancy path computations and such. */ skip_path: #endif if (buf) pseudo_msg_stat(&msg, buf); if (pseudo_util_debug_flags & PDBGF_OP) { pseudo_debug(PDBGF_OP, "%s%s", pseudo_op_name(op), (dirfd != -1 && dirfd != AT_FDCWD && op != OP_DUP) ? "at" : ""); if (path_extra_1) { pseudo_debug(PDBGF_OP, " %s ->", path_extra_1); } if (path) { pseudo_debug(PDBGF_OP, " %s", path); } /* for OP_RENAME in renameat, "fd" is also used for the * second dirfd. */ if (fd != -1 && op != OP_RENAME) { pseudo_debug(PDBGF_OP, " [fd %d]", fd); } if (buf) { pseudo_debug(PDBGF_OP, " (+buf)"); if (fd != -1) { pseudo_debug(PDBGF_OP, " [dev/ino: %d/%llu]", (int) buf->st_dev, (unsigned long long) buf->st_ino); } pseudo_debug(PDBGF_OP, " (0%o)", (int) buf->st_mode); } pseudo_debug(PDBGF_OP, ": "); } msg.type = PSEUDO_MSG_OP; msg.op = op; msg.fd = fd; msg.access = access; msg.result = RESULT_NONE; msg.client = getpid(); /* do stuff */ pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "processing request [ino %llu]\n", (unsigned long long) msg.ino); switch (msg.op) { case OP_CHDIR: pseudo_client_getcwd(); do_request = 0; break; case OP_CHROOT: if (pseudo_client_chroot(path) == 0) { /* return a non-zero value to show non-failure */ result = &msg; } do_request = 0; break; case OP_OPEN: pseudo_client_path(fd, path); case OP_EXEC: /* fallthrough */ do_request = pseudo_client_logging; break; case OP_CLOSE: /* no request needed */ if (fd >= 0) { if (fd == connect_fd) { connect_fd = pseudo_fd(connect_fd, COPY_FD); if (connect_fd == -1) { pseudo_diag("tried to close connection, couldn't dup: %s\n", strerror(errno)); } } else if (fd == pseudo_util_debug_fd) { pseudo_util_debug_fd = pseudo_fd(fd, COPY_FD); } else if (fd == pseudo_prefix_dir_fd) { pseudo_prefix_dir_fd = pseudo_fd(fd, COPY_FD); } else if (fd == pseudo_localstate_dir_fd) { pseudo_localstate_dir_fd = pseudo_fd(fd, COPY_FD); } else if (fd == pseudo_pwd_fd) { pseudo_pwd_fd = pseudo_fd(fd, COPY_FD); /* since we have a FILE * on it, we close that... */ fclose(pseudo_pwd); /* and open a new one on the copy */ pseudo_pwd = fdopen(pseudo_pwd_fd, "r"); } else if (fd == pseudo_grp_fd) { pseudo_grp_fd = pseudo_fd(fd, COPY_FD); /* since we have a FILE * on it, we close that... */ fclose(pseudo_grp); /* and open a new one on the copy */ pseudo_grp = fdopen(pseudo_grp_fd, "r"); } } pseudo_client_close(fd); do_request = 0; break; case OP_DUP: /* just copy the path over */ pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "dup: fd_path(%d) = %p [%s], dup to %d\n", fd, fd_path(fd), fd_path(fd) ? fd_path(fd) : "", dirfd); pseudo_client_path(dirfd, fd_path(fd)); break; /* operations for which we should use the magic uid/gid */ case OP_CHMOD: case OP_CREAT: case OP_FCHMOD: case OP_MKDIR: case OP_MKNOD: case OP_SYMLINK: msg.uid = pseudo_fuid; msg.gid = pseudo_fgid; pseudo_debug(PDBGF_OP, "fuid: %d ", pseudo_fuid); /* FALLTHROUGH */ /* chown/fchown uid/gid already calculated, and * a link or rename should not change a file's ownership. * (operations which can create should be CREAT or MKNOD * or MKDIR) */ case OP_CHOWN: case OP_FCHOWN: case OP_FSTAT: case OP_LINK: case OP_RENAME: case OP_STAT: case OP_UNLINK: case OP_DID_UNLINK: case OP_CANCEL_UNLINK: case OP_MAY_UNLINK: case OP_GET_XATTR: case OP_LIST_XATTR: case OP_SET_XATTR: case OP_REMOVE_XATTR: do_request = 1; break; default: pseudo_diag("error: unknown or unimplemented operator %d (%s)", op, pseudo_op_name(op)); break; } /* result can only be set when PSEUDO_XATTRDB resulted in a * successful store to or read from the local database. */ if (do_request && !result) { #ifdef PSEUDO_PROFILING struct timeval tv1_ipc, tv2_ipc; #endif if (!pseudo_op_wait(msg.op)) msg.type = PSEUDO_MSG_FASTOP; pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "sending request [ino %llu]\n", (unsigned long long) msg.ino); #ifdef PSEUDO_PROFILING gettimeofday(&tv1_ipc, NULL); #endif if (pseudo_local_only) { /* disable server */ result = NULL; } else { result = pseudo_client_request(&msg, pathlen, path); } #ifdef PSEUDO_PROFILING gettimeofday(&tv2_ipc, NULL); ++profile_data.messages; profile_data.ipc_time.tv_sec += (tv2_ipc.tv_sec - tv1_ipc.tv_sec); profile_data.ipc_time.tv_usec += (tv2_ipc.tv_usec - tv1_ipc.tv_usec); #endif if (result) { pseudo_debug(PDBGF_OP, "(%d) %s", getpid(), pseudo_res_name(result->result)); if (op == OP_STAT || op == OP_FSTAT) { pseudo_debug(PDBGF_OP, " mode 0%o uid %d:%d", (int) result->mode, (int) result->uid, (int) result->gid); } else if (op == OP_CHMOD || op == OP_FCHMOD) { pseudo_debug(PDBGF_OP, " mode 0%o", (int) result->mode); } else if (op == OP_CHOWN || op == OP_FCHOWN) { pseudo_debug(PDBGF_OP, " uid %d:%d", (int) result->uid, (int) result->gid); } } else if (msg.type != PSEUDO_MSG_FASTOP) { pseudo_debug(PDBGF_OP, "(%d) no answer", getpid()); } } else if (!result) { pseudo_debug(PDBGF_OP, "(%d) (no request)", getpid()); } #ifdef PSEUDO_XATTRDB else { pseudo_debug(PDBGF_OP, "(%d) (handled through xattrdb: %s)", getpid(), pseudo_res_name(result->result)); } #endif pseudo_debug(PDBGF_OP, "\n"); #ifdef PSEUDO_PROFILING gettimeofday(&tv2_op, NULL); profile_data.op_time.tv_sec += (tv2_op.tv_sec - tv1_op.tv_sec); profile_data.op_time.tv_usec += (tv2_op.tv_usec - tv1_op.tv_usec); if (profile_data.total_ops % profile_interval == 0) { pseudo_profile_report(); if (profile_interval < 100) { profile_interval = profile_interval * 10; } } #endif /* reenable wrappers */ pseudo_magic(); return result; } /* stuff for handling paths and execs */ static char *previous_path; static char *previous_path_segs; static char **path_segs; static size_t *path_lens; /* Makes an array of strings which are the path components * of previous_path. Does this by duping the path, then replacing * colons with null terminators, and storing the addresses of the * starts of the substrings. */ static void populate_path_segs(void) { size_t len = 0; char *s; int c = 0; free(path_segs); free(previous_path_segs); free(path_lens); path_segs = NULL; path_lens = NULL; previous_path_segs = NULL; if (!previous_path) return; for (s = previous_path; *s; ++s) { if (*s == ':') ++c; } path_segs = malloc((c+2) * sizeof(*path_segs)); if (!path_segs) { pseudo_diag("warning: failed to allocate space for %d path segments.\n", c); return; } path_lens = malloc((c + 2) * sizeof(*path_lens)); if (!path_lens) { pseudo_diag("warning: failed to allocate space for %d path lengths.\n", c); free(path_segs); path_segs = 0; return; } previous_path_segs = strdup(previous_path); if (!previous_path_segs) { pseudo_diag("warning: failed to allocate space for path copy.\n"); free(path_segs); path_segs = NULL; free(path_lens); path_lens = NULL; return; } c = 0; path_segs[c++] = previous_path; len = 0; for (s = previous_path; *s; ++s) { if (*s == ':') { *s = '\0'; path_lens[c - 1] = len; len = 0; path_segs[c++] = s + 1; } else { ++len; } } path_lens[c - 1] = len; path_segs[c] = NULL; path_lens[c] = 0; } const char * pseudo_exec_path(const char *filename, int search_path) { char *path = getenv("PATH"); char *candidate; int i; struct stat buf; if (!filename) return NULL; pseudo_antimagic(); if (!path) { free(path_segs); free(previous_path); path_segs = 0; previous_path = 0; } else if (!previous_path || strcmp(previous_path, path)) { free(previous_path); previous_path = strdup(path); populate_path_segs(); } /* absolute paths just get canonicalized. */ if (*filename == '/') { candidate = pseudo_fix_path(NULL, filename, 0, 0, NULL, 0); pseudo_magic(); return candidate; } if (!search_path || !path_segs) { candidate = pseudo_fix_path(pseudo_cwd, filename, 0, pseudo_cwd_len, NULL, 0); /* executable or not, it's the only thing we can try */ pseudo_magic(); return candidate; } for (i = 0; path_segs[i]; ++i) { path = path_segs[i]; pseudo_debug(PDBGF_CLIENT, "exec_path: checking %s for %s\n", path, filename); if (!*path || (*path == '.' && path_lens[i] == 1)) { /* empty path or . is cwd */ candidate = pseudo_fix_path(pseudo_cwd, filename, 0, pseudo_cwd_len, NULL, 0); pseudo_debug(PDBGF_CLIENT, "exec_path: in cwd, got %s\n", candidate); } else if (*path == '/') { candidate = pseudo_fix_path(path, filename, 0, path_lens[i], NULL, 0); pseudo_debug(PDBGF_CLIENT, "exec_path: got %s\n", candidate); } else { /* oh you jerk, making me do extra work */ size_t len; char *dir = pseudo_fix_path(pseudo_cwd, path, 0, pseudo_cwd_len, &len, 0); if (dir) { candidate = pseudo_fix_path(dir, filename, 0, len, NULL, 0); pseudo_debug(PDBGF_CLIENT, "exec_path: got %s for non-absolute path\n", candidate); } else { pseudo_diag("couldn't allocate intermediate path.\n"); candidate = NULL; } } if (candidate && !stat(candidate, &buf) && !S_ISDIR(buf.st_mode) && (buf.st_mode & 0111)) { pseudo_debug(PDBGF_CLIENT | PDBGF_VERBOSE, "exec_path: %s => %s\n", filename, candidate); pseudo_magic(); return candidate; } } /* blind guess being as good as anything */ pseudo_magic(); return filename; } pseudo-1.8.1+git20161012/pseudo_client.h000066400000000000000000000074021300506512600174230ustar00rootroot00000000000000/* * pseudo_client.h, shared declarations for client * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ extern pseudo_msg_t *pseudo_client_op(pseudo_op_t op, int access, int fd, int dirfd, const char *path, const PSEUDO_STATBUF *buf, ...); #if PSEUDO_STATBUF_64 #define base_lstat real_lstat64 #define base_fstat real_fstat64 #define base_stat real_stat64 #define base_fstatat(dirfd, path, buf, flags) real___fxstatat64(_STAT_VER, dirfd, path, buf, flags) #else #define base_lstat real_lstat #define base_fstat real_fstat #define base_stat real_stat #define base_fstatat(dirfd, path, buf, flags) real___fxstatat(_STAT_VER, dirfd, path, buf, flags) #endif extern void pseudo_antimagic(void); extern void pseudo_magic(void); extern void pseudo_client_touchuid(void); extern void pseudo_client_touchgid(void); extern char *pseudo_client_fdpath(int fd); extern int pseudo_client_shutdown(void); extern int pseudo_fd(int fd, int how); #define MOVE_FD 0 #define COPY_FD 1 #define PSEUDO_MIN_FD 20 extern uid_t pseudo_euid; extern uid_t pseudo_fuid; extern uid_t pseudo_suid; extern uid_t pseudo_ruid; extern gid_t pseudo_egid; extern gid_t pseudo_sgid; extern gid_t pseudo_rgid; extern gid_t pseudo_fgid; extern int pseudo_prefix_dir_fd; extern int pseudo_localstate_dir_fd; extern FILE *pseudo_pwd_open(void); extern FILE *pseudo_grp_open(void); extern void pseudo_pwd_close(void); extern void pseudo_grp_close(void); extern int pseudo_pwd_lck_open(void); extern int pseudo_pwd_lck_close(void); extern FILE *pseudo_pwd; extern FILE *pseudo_grp; /* pseudo_wrappers will try to initialize these */ extern int (*pseudo_real_lstat)(const char *path, PSEUDO_STATBUF *buf); extern int (*pseudo_real_unsetenv)(const char *); extern char * (*pseudo_real_getenv)(const char *); extern int (*pseudo_real_setenv)(const char *, const char *, int); extern int (*pseudo_real_fork)(void); extern int (*pseudo_real_execv)(const char *, char * const *); /* support related to chroot/getcwd/etc. */ extern int pseudo_client_getcwd(void); extern int pseudo_client_chroot(const char *); extern char *pseudo_root_path(const char *, int, int, const char *, int); extern const char *pseudo_exec_path(const char *filename, int); #define PSEUDO_ROOT_PATH(x, y, z) pseudo_root_path(__func__, __LINE__, (x), (y), (z)); extern char *pseudo_cwd; extern size_t pseudo_cwd_len; extern char *pseudo_cwd_rel; extern char *pseudo_chroot; extern char *pseudo_passwd; extern size_t pseudo_chroot_len; extern int pseudo_nosymlinkexp; extern int pseudo_umask; /* Root can read and write files, and enter directories which have no * read, write, or execute permissions. (But can't execute files without * execute permissions!) * * A non-root user can't. * * When doing anything which actually writes to the filesystem, we add in * the user read/write/execute bits. When storing to the database, though, * we mask out any such bits which weren't in the original mode. */ #define PSEUDO_FS_MODE(mode, isdir) (((mode) | S_IRUSR | S_IWUSR | ((isdir) ? S_IXUSR : 0)) & ~(S_IWGRP | S_IWOTH)) #define PSEUDO_DB_MODE(fs_mode, user_mode) (((fs_mode) & ~0722) | ((user_mode & 0722))) pseudo-1.8.1+git20161012/pseudo_db.c000066400000000000000000002216701300506512600165320ustar00rootroot00000000000000/* * pseudo_db.c, sqlite3 interface * * Copyright (c) 2008-2010,2013 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_db.h" /* #define NPROFILE */ #ifdef NPROFILE void xProfile(void * pArg, const char * pQuery, sqlite3_uint64 pTimeTaken) { pseudo_diag("profile: %lld %s\n", pTimeTaken, pQuery); } #endif struct log_history { int rc; unsigned long fields; sqlite3_stmt *stmt; }; struct pdb_file_list { int rc; sqlite3_stmt *stmt; }; static int file_db_dirty = 0; #ifdef USE_MEMORY_DB static sqlite3 *real_file_db = 0; #endif static sqlite3 *file_db = 0; static sqlite3 *log_db = 0; /* What's going on here, you might well ask? * This contains a template to build the database. I suppose maybe it * should have been elegantly done as a big chunk of embedded SQL, but * this looked like a good idea at the time. */ typedef struct { char *fmt; int arg; } id_row; /* So the migrations were getting to be large and complicated. Phase * one of getting rid of that is to merge their effects in and set up * a dummy migration to indicate that this was done. */ static id_row files_default_migrations[] = { { "%d, -1, 'N/A (initial setup)'", 8 }, { NULL, 0 }, }; static id_row logs_default_migrations[] = { { "%d, -1, 'N/A (initial setup)'", 7 }, { NULL, 0 }, }; /* This seemed like a really good idea at the time. The idea is that these * structures let me write semi-abstract code to "create a database" without * duplicating as much of the code. */ static struct sql_table { char *name; char *sql; char *names; id_row *values; } file_tables[] = { { "files", "id INTEGER PRIMARY KEY, " "path VARCHAR, " "dev INTEGER, " "ino INTEGER, " "uid INTEGER, " "gid INTEGER, " "mode INTEGER, " "rdev INTEGER, " "deleting INTEGER", NULL, NULL }, { "xattrs", "id INTEGER PRIMARY KEY, " "dev INTEGER, " "ino INTEGER, " "name VARCHAR, " "value VARCHAR", NULL, NULL }, { "migrations", "id INTEGER PRIMARY KEY, " "version INTEGER, " "stamp INTEGER, " "sql VARCHAR", "version, stamp, sql", files_default_migrations }, { NULL, NULL, NULL, NULL }, }, log_tables[] = { { "logs", "id INTEGER PRIMARY KEY, " "stamp INTEGER, " "op INTEGER, " "client INTEGER, " "fd INTEGER, " "dev INTEGER, " "ino INTEGER, " "mode INTEGER, " "path VARCHAR, " "result INTEGER, " "severity INTEGER, " "text VARCHAR, " "tag VARCHAR, " "uid INTEGER, " "gid INTEGER, " "access INTEGER, " "program VARCHAR, " "type INTEGER" , NULL, NULL }, { "migrations", "id INTEGER PRIMARY KEY, " "version INTEGER, " "stamp INTEGER, " "sql VARCHAR", "version, stamp, sql", logs_default_migrations }, { NULL, NULL, NULL, NULL }, }; /* similarly, this creates indexes generically. */ static struct sql_index { char *name; char *table; char *keys; } file_indexes[] = { { "files__path_dev_ino", "files", "path, dev, ino" }, { "files__dev_ino", "files", "dev, ino" }, { "migration__version", "migrations", "version" }, { "xattrs_dev_ino", "xattrs", "dev, ino" }, { "xattrs_dev_ino_name", "xattrs", "dev, ino, name" }, { NULL, NULL, NULL }, }, log_indexes[] = { { "migration__version", "migrations", "version" }, { NULL, NULL, NULL }, }; static char *file_pragmas[] = { "PRAGMA legacy_file_format = OFF;", "PRAGMA journal_mode = OFF;", /* the default page size produces painfully bad behavior * for memory databases with some versions of sqlite. */ "PRAGMA page_size = 8192;", "PRAGMA locking_mode = EXCLUSIVE;", /* Setting this to NORMAL makes pseudo noticably slower * than fakeroot, but is perhaps more secure. However, * note that sqlite always flushes to the OS; what is lacking * in non-synchronous mode is waiting for the OS to * confirm delivery to media, and also a bunch of cache * flushing and reloading which we probably don't really * need. */ "PRAGMA synchronous = OFF;", "PRAGMA foreign_keys = ON;", NULL }; static char *log_pragmas[] = { "PRAGMA legacy_file_format = OFF;", "PRAGMA journal_mode = OFF;", "PRAGMA locking_mode = EXCLUSIVE;", "PRAGMA synchronous = OFF;", NULL }; /* table migrations: */ /* If there is no migration table, we assume "version -1" -- the * version shipped with wrlinux 3.0, which had no version * number. Otherwise, we check it for the highest version recorded. * We then perform, and then record, each migration in sequence. * The first migration is the migration to create the migrations * table; this way, it'll work on existing databases. It'll also * work for new databases -- the migrations get performed in order * before the databases are considered to be set up. */ static char create_migration_table[] = "CREATE TABLE migrations (" "id INTEGER PRIMARY KEY, " "version INTEGER, " "stamp INTEGER, " "sql VARCHAR" ");"; static char index_migration_table[] = "CREATE INDEX migration__version ON migrations (version)"; /* This used to be a { version, sql } pair, but version was always * the same as index into the table, so I removed it. * The first migration in each database is migration #0 -- the * creation of the migration table now being used for versioning. * The second is indexing on version -- sqlite3 can grab MAX(version) * faster if it's indexed. (Indexing this table is very cheap, since * there are very few migrations and each one produces exactly * one insert.) */ static struct sql_migration { char *sql; } file_migrations[] = { { create_migration_table }, { index_migration_table }, { "ALTER TABLE files ADD deleting INTEGER;" }, /* oops. I made a design mistake, and the table is in the wrong * format. But sqlite doesn't support modifying constraints on an * existing table, or dropping columns. Soooooo. * * Also, you can't have a foreign key relationship to data which * aren't actually unique, but the entire problem is that dev/ino * are not unique. * * Note that the name entry is not really all that useful; it's mostly * there to help with debugging, now. */ { "CREATE TABLE xattrs_new (" "id INTEGER PRIMARY KEY, " "dev INTEGER, " "ino INTEGER, " "name VARCHAR, " "value VARCHAR" ");", }, { "INSERT INTO xattrs_new (dev, ino, name, value) " "SELECT " "(SELECT dev FROM files WHERE id = file_id), " "(SELECT ino FROM files WHERE id = file_id), " "name, " "value " "FROM xattrs;" }, { "DROP TABLE xattrs;" }, { "ALTER TABLE xattrs_new RENAME TO xattrs;", }, { "CREATE INDEX xattrs__dev_ino ON xattrs (dev, ino);", }, { "CREATE UNIQUE INDEX xattrs__dev_ino_name ON xattrs (dev, ino, name);", }, { NULL }, }, log_migrations[] = { { create_migration_table }, { index_migration_table }, /* support for hostdeps merge -- this allows us to log "tags" * along with events. */ { "ALTER TABLE logs ADD tag VARCHAR;" }, /* the logs table was defined so early I hadn't realized I cared * about UID and GID. */ { "ALTER TABLE logs ADD uid INTEGER;" }, { "ALTER TABLE logs ADD gid INTEGER;" }, /* track access types (read/write, etc) */ { "ALTER TABLE logs ADD access INTEGER;" }, /* client program/path */ { "ALTER TABLE logs ADD program VARCHAR;" }, /* message type (ping, op) */ { "ALTER TABLE logs ADD type INTEGER;" }, { NULL }, }; /* cleanup database before getting started * * On a large build, the logs database gets GIGANTIC... And * we rarely-if-ever delete things from it. So instead of * doing the vacuum operation on it at startup, which can impose * a several-minute delay, we do it only on deletions. * * There's no setup for log database right now. */ char *file_setups[] = { "VACUUM;", NULL, }; struct database_info { char *pathname; struct sql_index *indexes; struct sql_table *tables; struct sql_migration *migrations; char **pragmas; char **setups; struct sqlite3 **db; }; static struct database_info db_infos[] = { { "logs.db", log_indexes, log_tables, log_migrations, log_pragmas, NULL, &log_db }, { "files.db", file_indexes, file_tables, file_migrations, file_pragmas, file_setups, #ifdef USE_MEMORY_DB &real_file_db }, { ":memory:", file_indexes, file_tables, file_migrations, file_pragmas, file_setups, #endif &file_db }, { 0, 0, 0, 0, 0, 0, 0 } }; /* pretty-print error along with the underlying SQL error. */ static void dberr(sqlite3 *db, char *fmt, ...) { va_list ap; char debuff[8192]; int len; va_start(ap, fmt); len = vsnprintf(debuff, 8192, fmt, ap); va_end(ap); len = write(pseudo_util_debug_fd, debuff, len); if (db) { len = snprintf(debuff, 8192, ": %s\n", sqlite3_errmsg(db)); len = write(pseudo_util_debug_fd, debuff, len); } else { len = write(pseudo_util_debug_fd, " (no db)\n", 9); } } #ifdef USE_MEMORY_DB static void pdb_backup() { sqlite3_backup *pBackup; /* no point in doing this if we don't have a database to back up, * or nothing's changed. */ if (!file_db || !real_file_db || !file_db_dirty) return; pBackup = sqlite3_backup_init(real_file_db, "main", file_db, "main"); if (pBackup) { int rc; (void)sqlite3_backup_step(pBackup, -1); rc = sqlite3_backup_finish(pBackup); if (rc != SQLITE_OK) { dberr(real_file_db, "error during flush to disk"); } } file_db_dirty = 0; } static void pdb_restore() { sqlite3_backup *pBackup; /* no point in doing this if we don't have a database to back up */ if (!file_db || !real_file_db) return; pBackup = sqlite3_backup_init(file_db, "main", real_file_db, "main"); if (pBackup) { int rc; (void)sqlite3_backup_step(pBackup, -1); rc = sqlite3_backup_finish(pBackup); if (rc != SQLITE_OK) { dberr(file_db, "error during load from disk"); } } file_db_dirty = 0; } int pdb_maybe_backup(void) { static int occasional = 0; if (file_db && real_file_db) { occasional = (occasional + 1) % 10; if (occasional == 0) { pdb_backup(); return 1; } } return 0; } #else /* USE_MEMORY_DB */ int pdb_maybe_backup(void) { return 0; } #endif /* those who enjoy children, sausages, and databases, should not watch * them being made. */ static int make_tables(sqlite3 *db, struct sql_table *sql_tables, struct sql_index *sql_indexes, struct sql_migration *sql_migrations, char **existing, int rows) { static sqlite3_stmt *stmt; sqlite3_stmt *update_version = 0; struct sql_migration *m; int available_migrations; int version = -1; int i, j; char *sql; char *errmsg; int rc; int found = 0; for (i = 0; sql_tables[i].name; ++i) { found = 0; printf("considering table %s\n", sql_tables[i].name); for (j = 1; j <= rows; ++j) { if (!strcmp(existing[j], sql_tables[i].name)) { found = 1; break; } } if (found) continue; /* now to create the table */ sql = sqlite3_mprintf("CREATE TABLE %s ( %s );", sql_tables[i].name, sql_tables[i].sql); rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg); sqlite3_free(sql); if (rc) { dberr(db, "error trying to create %s", sql_tables[i].name); return 1; } if (sql_tables[i].values) { for (j = 0; sql_tables[i].values[j].fmt; ++j) { char buffer[256]; snprintf(buffer, sizeof(buffer), sql_tables[i].values[j].fmt, sql_tables[i].values[j].arg); sql = sqlite3_mprintf("INSERT INTO %s ( %s ) VALUES ( %s );", sql_tables[i].name, sql_tables[i].names, buffer); pseudo_debug(PDBGF_DB, "data setup: %s\n", sql); rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg); sqlite3_free(sql); if (rc) { dberr(db, "error trying to populate %s", sql_tables[i].name); return 1; } } } for (j = 0; sql_indexes[j].name; ++j) { if (strcmp(sql_indexes[j].table, sql_tables[i].name)) continue; sql = sqlite3_mprintf("CREATE INDEX %s ON %s ( %s );", sql_indexes[j].name, sql_indexes[j].table, sql_indexes[j].keys); rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg); sqlite3_free(sql); if (rc) { dberr(db, "error trying to index %s", sql_tables[i].name); return 1; } } } /* the migrations table is assumed to exist, because we now * create it automatically; in a previous implementation, it was * only created by the first migration. */ sql = "SELECT MAX(version) FROM migrations;"; rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL); if (rc) { dberr(db, "couldn't examine migrations table"); return 1; } rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { version = (unsigned long) sqlite3_column_int64(stmt, 0); rc = sqlite3_step(stmt); } else { version = -1; } if (rc != SQLITE_DONE) { dberr(db, "not done after the single row we expected?", rc); return 1; } pseudo_debug(PDBGF_DB, "existing database version: %d\n", version); rc = sqlite3_finalize(stmt); if (rc) { dberr(db, "couldn't finalize version check"); return 1; } for (m = sql_migrations; m->sql; ++m) ; /* m points to the null, so available migrations is one lower */ available_migrations = m - sql_migrations - 1; /* I am pretty sure this can never happen. */ if (version < -1) version = -1; /* I hope this can never happen. */ pseudo_debug(PDBGF_DB, "version %d, available_migrations %d\n", version, available_migrations); if (version > available_migrations) version = available_migrations; for (m = sql_migrations + (version + 1); m->sql; ++m) { int migration = (m - sql_migrations); pseudo_debug(PDBGF_DB, "considering migration %d\n", migration); if (version >= migration) continue; pseudo_debug(PDBGF_DB, "running migration %d\n", migration); rc = sqlite3_prepare_v2(db, m->sql, strlen(m->sql), &stmt, NULL); if (rc) { dberr(log_db, "couldn't prepare migration %d (%s)", migration, m->sql); return 1; } rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { dberr(file_db, "migration %d failed", migration); return 1; } sqlite3_finalize(stmt); /* this has to occur here, because the first migration * executed CREATES the migration table, so you can't * prepare this statement if you haven't already executed * the first migration. * * Lesson learned: Yes, it actually WILL be a sort of big * deal to add versioning later. */ static char *update_sql = "INSERT INTO migrations (" "version, stamp, sql" ") VALUES (?, ?, ?);"; rc = sqlite3_prepare_v2(db, update_sql, strlen(update_sql), &update_version, NULL); if (rc) { dberr(db, "couldn't prepare statement to update migrations"); return 1; } sqlite3_bind_int(update_version, 1, migration); sqlite3_bind_int(update_version, 2, time(NULL)); sqlite3_bind_text(update_version, 3, m->sql, -1, SQLITE_STATIC); rc = sqlite3_step(update_version); if (rc != SQLITE_DONE) { dberr(db, "couldn't update migrations table (after migration to version %d)", migration); sqlite3_finalize(update_version); return 1; } else { pseudo_debug(PDBGF_DB, "update of migrations (after %d) fine.\n", migration); } sqlite3_finalize(update_version); update_version = 0; version = migration; } return 0; } /* registered with atexit */ static void cleanup_db(void) { char datebuf[64]; struct tm stamp_tm; struct timeval stamp; gettimeofday(&stamp, NULL); localtime_r(&stamp.tv_sec, &stamp_tm); strftime(datebuf, 64, "%H:%M:%S", &stamp_tm); pseudo_diag("db cleanup for server shutdown, %s.%03d\n", datebuf, (int) (stamp.tv_usec / 1000)); #ifdef USE_MEMORY_DB if (real_file_db) { pdb_backup(); sqlite3_close(real_file_db); } gettimeofday(&stamp, NULL); localtime_r(&stamp.tv_sec, &stamp_tm); strftime(datebuf, 64, "%H:%M:%S", &stamp_tm); pseudo_diag("memory-to-file backup complete, %s.%03d.\n", datebuf, (int) (stamp.tv_usec / 1000)); #endif if (file_db) sqlite3_close(file_db); if (log_db) sqlite3_close(log_db); gettimeofday(&stamp, NULL); localtime_r(&stamp.tv_sec, &stamp_tm); strftime(datebuf, 64, "%H:%M:%S", &stamp_tm); pseudo_diag("db cleanup finished, %s.%03d\n", datebuf, (int) (stamp.tv_usec / 1000)); } /* This function has been rewritten and I no longer hate it. I just feel * like it's important to say that. */ static int get_db(struct database_info *dbinfo) { int rc; int i; char *sql; char **results; int rows, columns; char *errmsg; static int registered_cleanup = 0; char *dbfile; sqlite3 *db; if (!dbinfo) return 1; if (!dbinfo->db) return 1; /* this database is perhaps already initialized? */ if (*(dbinfo->db)) return 0; pseudo_debug(PDBGF_DB, "get_db(%s)\n", dbinfo->pathname); dbfile = pseudo_localstatedir_path(dbinfo->pathname); #ifdef USE_MEMORY_DB if (!strcmp(dbinfo->pathname, ":memory:")) { rc = sqlite3_open(dbinfo->pathname, &db); } else #endif rc = sqlite3_open(dbfile, &db); free(dbfile); if (rc) { pseudo_diag("Failed: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); *(dbinfo->db) = NULL; return 1; } /* we store this in the database_info, but hereafter we'll just use * the name db, because it is shorter. */ *dbinfo->db = db; if (!registered_cleanup) { atexit(cleanup_db); registered_cleanup = 1; } if (dbinfo->pragmas) { for (i = 0; dbinfo->pragmas[i]; ++i) { rc = sqlite3_exec(db, dbinfo->pragmas[i], NULL, NULL, &errmsg); pseudo_debug(PDBGF_SQL | PDBGF_VERBOSE, "executed pragma: '%s', rc %d.\n", dbinfo->pragmas[i], rc); if (rc) { dberr(db, dbinfo->pragmas[i]); } } } /* create database tables or die trying */ sql = "SELECT name FROM sqlite_master " "WHERE type = 'table' " "ORDER BY name;"; rc = sqlite3_get_table(db, sql, &results, &rows, &columns, &errmsg); if (rc) { pseudo_diag("Failed: %s\n", errmsg); } else { rc = make_tables(db, dbinfo->tables, dbinfo->indexes, dbinfo->migrations, results, rows); sqlite3_free_table(results); } /* as of now, the only setup is a vacuum operation which we don't care about * the results of. */ if (dbinfo->setups) { for (i = 0; dbinfo->setups[i]; ++i) { sqlite3_exec(db, dbinfo->setups[i], NULL, NULL, &errmsg); } } return rc; } static int get_dbs(void) { int err = 0; int i; #ifdef USE_MEMORY_DB int already_loaded = 0; if (file_db) already_loaded = 1; #endif for (i = 0; db_infos[i].db; ++i) { if (get_db(&db_infos[i])) { pseudo_diag("Error getting '%s' database.\n", db_infos[i].pathname); err = 1; } } #ifdef USE_MEMORY_DB if (!already_loaded && file_db) pdb_restore(); #endif return err; } /* put a prepared log entry into the database */ int pdb_log_traits(pseudo_query_t *traits) { pseudo_query_t *trait; log_entry *e; int rc; if (!log_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 1; } e = calloc(sizeof(*e), 1); if (!e) { pseudo_diag("can't allocate space for log entry."); return 1; } for (trait = traits; trait; trait = trait->next) { switch (trait->field) { case PSQF_ACCESS: e->access = trait->data.ivalue; break; case PSQF_CLIENT: e->client = trait->data.ivalue; break; case PSQF_DEV: e->dev = trait->data.ivalue; break; case PSQF_FD: e->fd = trait->data.ivalue; break; case PSQF_FTYPE: e->mode |= (trait->data.ivalue & S_IFMT); break; case PSQF_GID: e->gid = trait->data.ivalue; break; case PSQF_INODE: e->ino = trait->data.ivalue; break; case PSQF_MODE: e->mode = trait->data.ivalue; break; case PSQF_OP: e->op = trait->data.ivalue; break; case PSQF_PATH: e->path = trait->data.svalue ? strdup(trait->data.svalue) : NULL; break; case PSQF_PERM: e->mode |= (trait->data.ivalue & ~(S_IFMT) & 0177777); break; case PSQF_PROGRAM: e->program = trait->data.svalue ? strdup(trait->data.svalue) : NULL; break; case PSQF_RESULT: e->result = trait->data.ivalue; break; case PSQF_SEVERITY: e->severity = trait->data.ivalue; break; case PSQF_STAMP: e->stamp = trait->data.ivalue; break; case PSQF_TAG: e->tag = trait->data.svalue ? strdup(trait->data.svalue) : NULL; break; case PSQF_TEXT: e->text = trait->data.svalue ? strdup(trait->data.svalue) : NULL; break; case PSQF_TYPE: e->type = trait->data.ivalue; break; case PSQF_UID: e->uid = trait->data.ivalue; break; case PSQF_ID: case PSQF_ORDER: default: pseudo_diag("Invalid trait %s for log creation.\n", pseudo_query_field_name(trait->field)); free(e); return 1; break; } } rc = pdb_log_entry(e); log_entry_free(e); return rc; } /* create a log from a given log entry, with tag and text */ int pdb_log_entry(log_entry *e) { char *sql = "INSERT INTO logs " "(stamp, op, access, client, dev, gid, ino, mode, path, result, severity, text, program, tag, type, uid)" " VALUES " "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; static sqlite3_stmt *insert; int field; int rc; if (!log_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 1; } if (!insert) { rc = sqlite3_prepare_v2(log_db, sql, strlen(sql), &insert, NULL); if (rc) { dberr(log_db, "couldn't prepare INSERT statement"); return 1; } } field = 1; if (e) { if (e->stamp) { sqlite3_bind_int(insert, field++, e->stamp); } else { sqlite3_bind_int(insert, field++, (unsigned long) time(NULL)); } sqlite3_bind_int(insert, field++, e->op); sqlite3_bind_int(insert, field++, e->access); sqlite3_bind_int(insert, field++, e->client); sqlite3_bind_int(insert, field++, e->dev); sqlite3_bind_int(insert, field++, e->gid); sqlite3_bind_int64(insert, field++, e->ino); sqlite3_bind_int(insert, field++, e->mode); if (e->path) { sqlite3_bind_text(insert, field++, e->path, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } sqlite3_bind_int(insert, field++, e->result); sqlite3_bind_int(insert, field++, e->severity); if (e->text) { sqlite3_bind_text(insert, field++, e->text, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } if (e->program) { sqlite3_bind_text(insert, field++, e->program, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } if (e->tag) { sqlite3_bind_text(insert, field++, e->tag, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } sqlite3_bind_int(insert, field++, e->type); sqlite3_bind_int(insert, field++, e->uid); } else { sqlite3_bind_int(insert, field++, (unsigned long) time(NULL)); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_null(insert, field++); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_null(insert, field++); sqlite3_bind_null(insert, field++); sqlite3_bind_null(insert, field++); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); } rc = sqlite3_step(insert); if (rc != SQLITE_DONE) { dberr(log_db, "insert may have failed"); } sqlite3_reset(insert); sqlite3_clear_bindings(insert); return rc != SQLITE_DONE; } /* create a log from a given message, with tag and text */ int pdb_log_msg(pseudo_sev_t severity, pseudo_msg_t *msg, const char *program, const char *tag, const char *text, ...) { char *sql = "INSERT INTO logs " "(stamp, op, access, client, dev, gid, ino, mode, path, result, uid, severity, text, program, tag, type)" " VALUES " "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; static sqlite3_stmt *insert; char buffer[8192]; int field; int rc; va_list ap; if (text) { va_start(ap, text); vsnprintf(buffer, 8192, text, ap); va_end(ap); text = buffer; } if (!log_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 1; } if (!insert) { rc = sqlite3_prepare_v2(log_db, sql, strlen(sql), &insert, NULL); if (rc) { dberr(log_db, "couldn't prepare INSERT statement"); return 1; } } field = 1; sqlite3_bind_int(insert, field++, (unsigned long) time(NULL)); if (msg) { sqlite3_bind_int(insert, field++, msg->op); sqlite3_bind_int(insert, field++, msg->access); sqlite3_bind_int(insert, field++, msg->client); sqlite3_bind_int(insert, field++, msg->dev); sqlite3_bind_int(insert, field++, msg->gid); sqlite3_bind_int64(insert, field++, msg->ino); sqlite3_bind_int(insert, field++, msg->mode); if (msg->pathlen) { sqlite3_bind_text(insert, field++, msg->path, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } sqlite3_bind_int(insert, field++, msg->result); sqlite3_bind_int(insert, field++, msg->uid); } else { sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_null(insert, field++); sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); } sqlite3_bind_int(insert, field++, severity); if (text) { sqlite3_bind_text(insert, field++, text, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } if (program) { sqlite3_bind_text(insert, field++, program, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } if (tag) { sqlite3_bind_text(insert, field++, tag, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } if (msg) { sqlite3_bind_int(insert, field++, msg->type); } else { sqlite3_bind_int(insert, field++, 0); } rc = sqlite3_step(insert); if (rc != SQLITE_DONE) { dberr(log_db, "insert may have failed"); } sqlite3_reset(insert); sqlite3_clear_bindings(insert); return rc != SQLITE_DONE; } #define BFSZ 8192 typedef struct { size_t buflen; char *data; char *tail; } buffer; static int frag(buffer *b, char *fmt, ...) { va_list ap; static size_t curlen; int rc; if (!b) { pseudo_diag("frag called without buffer.\n"); return -1; } curlen = b->tail - b->data; va_start(ap, fmt); rc = vsnprintf(b->tail, b->buflen - curlen, fmt, ap); va_end(ap); if ((rc > 0) && ((size_t) rc >= (b->buflen - curlen))) { size_t newlen = b->buflen; while (newlen <= (rc + curlen)) newlen *= 2; char *newbuf = malloc(newlen); if (!newbuf) { pseudo_diag("failed to allocate SQL buffer.\n"); return -1; } memcpy(newbuf, b->data, curlen + 1); b->tail = newbuf + curlen; free(b->data); b->data = newbuf; b->buflen = newlen; /* try again */ va_start(ap, fmt); rc = vsnprintf(b->tail, b->buflen - curlen, fmt, ap); va_end(ap); if ((rc > 0) && ((size_t) rc >= (b->buflen - curlen))) { pseudo_diag("tried to reallocate larger buffer, failed. giving up.\n"); return -1; } } if (rc >= 0) b->tail += rc; return rc; } sqlite3_stmt * pdb_query(char *stmt_type, pseudo_query_t *traits, unsigned long fields, int unique, int want_results) { pseudo_query_t *trait; sqlite3_stmt *stmt; int done_any = 0; int field = 0; const char *order_by = "id"; char *order_dir = "ASC"; int rc; pseudo_query_field_t f; static buffer *sql; if (!log_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return NULL; } if (!stmt_type) { pseudo_diag("can't prepare a statement without a type.\n"); } if (!sql) { sql = malloc(sizeof *sql); if (!sql) { pseudo_diag("can't allocate SQL buffer.\n"); return NULL; } sql->buflen = 512; sql->data = malloc(sql->buflen); if (!sql->data) { pseudo_diag("can't allocate SQL text buffer.\n"); free(sql); sql = 0; return NULL; } } sql->tail = sql->data; /* should be DELETE or SELECT */ frag(sql, "%s ", stmt_type); if (want_results) { if (unique) frag(sql, "DISTINCT "); done_any = 0; for (f = PSQF_NONE + 1; f < PSQF_MAX; ++f) { if (fields & (1 << f)) { frag(sql, "%s%s", done_any ? ", " : "", pseudo_query_field_name(f)); done_any = 1; } } } frag(sql, " FROM logs "); /* first, build up an SQL string with the fields and operators */ done_any = 0; for (trait = traits; trait; trait = trait->next) { if (trait->field != PSQF_ORDER) { if (done_any) { frag(sql, "AND "); } else { frag(sql, "WHERE "); done_any = 1; } } switch (trait->field) { case PSQF_PROGRAM: case PSQF_TEXT: case PSQF_TAG: case PSQF_PATH: switch (trait->type) { case PSQT_LIKE: frag(sql, "%s %s ('%' || ? || '%')", pseudo_query_field_name(trait->field), pseudo_query_type_sql(trait->type)); break; case PSQT_NOTLIKE: case PSQT_SQLPAT: frag(sql, "%s %s ?", pseudo_query_field_name(trait->field), pseudo_query_type_sql(trait->type)); break; default: frag(sql, "%s %s ? ", pseudo_query_field_name(trait->field), pseudo_query_type_sql(trait->type)); break; } break; case PSQF_PERM: switch (trait->type) { case PSQT_LIKE: case PSQT_NOTLIKE: case PSQT_SQLPAT: pseudo_diag("Error: Can't use a LIKE match on non-text fields.\n"); return 0; break; default: break; } /* mask out permission bits */ frag(sql, "(%s & %d) %s ? ", "mode", ~(S_IFMT) & 0177777, pseudo_query_type_sql(trait->type)); break; case PSQF_FTYPE: switch (trait->type) { case PSQT_LIKE: case PSQT_NOTLIKE: case PSQT_SQLPAT: pseudo_diag("Error: Can't use a LIKE match on non-text fields.\n"); return 0; break; default: break; } /* mask out permission bits */ frag(sql, "(%s & %d) %s ? ", "mode", S_IFMT, pseudo_query_type_sql(trait->type)); break; case PSQF_ORDER: order_by = pseudo_query_field_name(trait->data.ivalue); switch (trait->type) { case PSQT_LESS: order_dir = "DESC"; break; case PSQT_EXACT: /* this was already the default */ break; case PSQT_GREATER: order_dir = "ASC"; break; default: pseudo_diag("Ordering must be < or >.\n"); return 0; break; } break; default: switch (trait->type) { case PSQT_LIKE: case PSQT_NOTLIKE: case PSQT_SQLPAT: pseudo_diag("Error: Can't use a LIKE match on non-text fields.\n"); return 0; break; default: frag(sql, "%s %s ? ", pseudo_query_field_name(trait->field), pseudo_query_type_sql(trait->type)); break; } break; } } if (want_results) frag(sql, "ORDER BY %s %s;", order_by, order_dir); pseudo_debug(PDBGF_SQL, "created SQL: <%s>\n", sql->data); /* second, prepare it */ rc = sqlite3_prepare_v2(log_db, sql->data, strlen(sql->data), &stmt, NULL); if (rc) { dberr(log_db, "couldn't prepare %s statement", stmt_type); return NULL; } /* third, bind the fields */ field = 1; for (trait = traits; trait; trait = trait->next) { switch (trait->field) { case PSQF_ORDER: /* this just creates a hunk of SQL above */ break; case PSQF_PROGRAM: case PSQF_PATH: case PSQF_TAG: case PSQF_TEXT: sqlite3_bind_text(stmt, field++, trait->data.svalue, -1, SQLITE_STATIC); break; case PSQF_ACCESS: case PSQF_CLIENT: case PSQF_DEV: case PSQF_FD: case PSQF_FTYPE: case PSQF_INODE: case PSQF_GID: case PSQF_PERM: case PSQF_MODE: case PSQF_OP: case PSQF_RESULT: case PSQF_SEVERITY: case PSQF_STAMP: case PSQF_TYPE: case PSQF_UID: sqlite3_bind_int64(stmt, field++, trait->data.ivalue); break; default: pseudo_diag("Inexplicably invalid field type %d\n", trait->field); sqlite3_finalize(stmt); return NULL; } } return stmt; } int pdb_delete(pseudo_query_t *traits, unsigned long fields) { sqlite3_stmt *stmt; stmt = pdb_query("DELETE", traits, fields, 0, 0); /* no need to return it, so... */ if (stmt) { file_db_dirty = 1; int rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { dberr(log_db, "deletion failed"); return -1; } else { pseudo_diag("Deleted records, vacuuming log database (may take a while).\n"); /* we can't do anything about it if this fails... */ sqlite3_exec(log_db, "VACUUM;", NULL, NULL, NULL); } sqlite3_finalize(stmt); return 0; } return -1; } log_history pdb_history(pseudo_query_t *traits, unsigned long fields, int unique) { log_history h = NULL; sqlite3_stmt *stmt; stmt = pdb_query("SELECT", traits, fields, unique, 1); if (stmt) { /* fourth, return the statement, now ready to be stepped through */ h = malloc(sizeof(*h)); if (h) { h->rc = 0; h->fields = fields; h->stmt = stmt; } else { pseudo_diag("failed to allocate memory for log_history\n"); sqlite3_finalize(stmt); } return h; } else { return NULL; } } log_entry * pdb_history_entry(log_history h) { log_entry *l; const unsigned char *s; int column; pseudo_query_field_t f; if (!h || !h->stmt) return 0; /* in case someone tries again after we're already done */ if (h->rc == SQLITE_DONE) { return 0; } h->rc = sqlite3_step(h->stmt); if (h->rc == SQLITE_DONE) { return 0; } else if (h->rc != SQLITE_ROW) { dberr(log_db, "statement failed"); return 0; } l = calloc(sizeof(log_entry), 1); if (!l) { pseudo_diag("couldn't allocate log entry.\n"); return 0; } column = 0; for (f = PSQF_NONE + 1; f < PSQF_MAX; ++f) { if (!(h->fields & (1 << f))) continue; switch (f) { case PSQF_ACCESS: l->access = sqlite3_column_int64(h->stmt, column++); break; case PSQF_CLIENT: l->client = sqlite3_column_int64(h->stmt, column++); break; case PSQF_DEV: l->dev = sqlite3_column_int64(h->stmt, column++); break; case PSQF_FD: l->fd = sqlite3_column_int64(h->stmt, column++); break; case PSQF_GID: l->gid = sqlite3_column_int64(h->stmt, column++); break; case PSQF_INODE: l->ino = sqlite3_column_int64(h->stmt, column++); break; case PSQF_MODE: l->mode = sqlite3_column_int64(h->stmt, column++); break; case PSQF_OP: l->op = sqlite3_column_int64(h->stmt, column++); break; case PSQF_PATH: s = sqlite3_column_text(h->stmt, column++); if (s) l->path = strdup((char *) s); break; case PSQF_PROGRAM: s = sqlite3_column_text(h->stmt, column++); if (s) l->program = strdup((char *) s); break; case PSQF_RESULT: l->result = sqlite3_column_int64(h->stmt, column++); break; case PSQF_SEVERITY: l->severity = sqlite3_column_int64(h->stmt, column++); break; case PSQF_STAMP: l->stamp = sqlite3_column_int64(h->stmt, column++); break; case PSQF_TAG: s = sqlite3_column_text(h->stmt, column++); if (s) l->tag = strdup((char *) s); break; case PSQF_TEXT: s = sqlite3_column_text(h->stmt, column++); if (s) l->text = strdup((char *) s); break; case PSQF_TYPE: l->type = sqlite3_column_int64(h->stmt, column++); break; case PSQF_UID: l->uid = sqlite3_column_int64(h->stmt, column++); break; case PSQF_ORDER: case PSQF_FTYPE: case PSQF_PERM: pseudo_diag("field %s should not be in the fields list.\n", pseudo_query_field_name(f)); return 0; break; default: pseudo_diag("unknown field %d\n", f); return 0; break; } } return l; } void pdb_history_free(log_history h) { if (!h) return; if (h->stmt) { sqlite3_reset(h->stmt); sqlite3_finalize(h->stmt); } free(h); } void log_entry_free(log_entry *e) { if (!e) return; free(e->text); free(e->path); free(e->program); free(e->tag); free(e); } /* Now for the actual file handling code! */ /* nuke any unlinked xattrs. Only used from the rare case where we try * to nuke a whole bunch of "deleting" files. */ static void pdb_clear_unused_xattrs(void) { static sqlite3_stmt *delete; char *delete_sql = "DELETE FROM xattrs WHERE (SELECT COUNT(*) FROM files WHERE dev = xattrs.dev AND ino = xattrs.ino) = 0;"; int rc; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return; } if (!delete) { rc = sqlite3_prepare_v2(file_db, delete_sql, strlen(delete_sql), &delete, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement for unused xattrs"); return; } } rc = sqlite3_step(delete); if (rc != SQLITE_DONE) { dberr(file_db, "delete of unused xattrs may have failed"); } sqlite3_reset(delete); } /* we want to delete extended attributes for a device/inode if there * are no longer any links with it. Anything that deletes at least one * link for a given device/inode should then call this. */ static void pdb_clear_xattrs(pseudo_msg_t *msg) { static sqlite3_stmt *delete; char *delete_sql = "DELETE FROM xattrs WHERE dev = ? AND ino = ?;"; int rc; if (!msg) return; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return; } if (!delete) { rc = sqlite3_prepare_v2(file_db, delete_sql, strlen(delete_sql), &delete, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement for unused xattrs"); return; } } sqlite3_bind_int(delete, 1, msg->dev); sqlite3_bind_int(delete, 2, msg->ino); rc = sqlite3_step(delete); if (rc != SQLITE_DONE) { dberr(file_db, "delete of unused xattrs may have failed"); } sqlite3_reset(delete); sqlite3_clear_bindings(delete); } static void pdb_copy_xattrs(pseudo_msg_t *oldmsg, pseudo_msg_t *msg) { static sqlite3_stmt *copy; char *copy_sql = "INSERT INTO xattrs (dev, ino, name, value) " "SELECT " "?, " "?, " "name, " "value " "FROM xattrs WHERE dev = ? AND ino = ?;"; int rc; if (!oldmsg || !msg) return; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return; } if (!copy) { rc = sqlite3_prepare_v2(file_db, copy_sql, strlen(copy_sql), ©, NULL); if (rc) { dberr(file_db, "couldn't prepare INSERT statement for copying xattrs"); return; } } sqlite3_bind_int(copy, 1, msg->dev); sqlite3_bind_int(copy, 2, msg->ino); sqlite3_bind_int(copy, 3, oldmsg->dev); sqlite3_bind_int(copy, 4, oldmsg->ino); rc = sqlite3_step(copy); if (rc != SQLITE_DONE) { dberr(file_db, "copy of xattrs may have failed"); } sqlite3_reset(copy); sqlite3_clear_bindings(copy); } static void pdb_check_xattrs(pseudo_msg_t *msg) { static sqlite3_stmt *scan; char *scan_sql = "SELECT COUNT(*) FROM files WHERE dev = ? AND ino = ?;"; int rc; if (!msg) return; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return; } if (!scan) { rc = sqlite3_prepare_v2(file_db, scan_sql, strlen(scan_sql), &scan, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement for checking for existing files"); return; } } int existing; sqlite3_bind_int(scan, 1, msg->dev); sqlite3_bind_int(scan, 2, msg->ino); rc = sqlite3_step(scan); if (rc == SQLITE_ROW) { existing = (int) sqlite3_column_int64(scan, 0); pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "scan for existing files: %d\n", existing); rc = sqlite3_step(scan); } else { pseudo_debug(PDBGF_DB, "scan for existing files: no count?\n"); existing = 0; } if (rc != SQLITE_DONE) { dberr(file_db, "not done after the single row we expected?", rc); } sqlite3_reset(scan); sqlite3_clear_bindings(scan); /* if there are any matching entries, don't delete xattrs */ if (existing == 0) pdb_clear_xattrs(msg); } /* pdb_link_file: Creates a new file from msg, using the provided path * or 'NAMELESS FILE'. */ int pdb_link_file(pseudo_msg_t *msg) { static sqlite3_stmt *insert; int rc; char *sql = "INSERT INTO files " " ( path, dev, ino, uid, gid, mode, rdev, deleting ) " " VALUES (?, ?, ?, ?, ?, ?, ?, 0);"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!insert) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &insert, NULL); if (rc) { dberr(file_db, "couldn't prepare INSERT statement"); return 1; } } if (!msg) { return 1; } if (msg->pathlen) { sqlite3_bind_text(insert, 1, msg->path, -1, SQLITE_STATIC); } else { sqlite3_bind_text(insert, 1, "NAMELESS FILE", -1, SQLITE_STATIC); } sqlite3_bind_int(insert, 2, msg->dev); sqlite3_bind_int64(insert, 3, msg->ino); sqlite3_bind_int(insert, 4, msg->uid); sqlite3_bind_int(insert, 5, msg->gid); sqlite3_bind_int(insert, 6, msg->mode); sqlite3_bind_int(insert, 7, msg->rdev); pseudo_debug(PDBGF_DB | PDBGF_FILE, "linking %s: dev %llu, ino %llu, mode %o, owner %d\n", (msg->pathlen ? msg->path : " (as NAMELESS FILE)"), (unsigned long long) msg->dev, (unsigned long long) msg->ino, (int) msg->mode, msg->uid); file_db_dirty = 1; rc = sqlite3_step(insert); if (rc != SQLITE_DONE) { dberr(file_db, "insert may have failed (rc %d)", rc); } sqlite3_reset(insert); sqlite3_clear_bindings(insert); return rc != SQLITE_DONE; } /* pdb_unlink_file_dev: Delete every instance of a dev/inode pair. */ int pdb_unlink_file_dev(pseudo_msg_t *msg) { static sqlite3_stmt *sql_delete; int rc; char *sql = "DELETE FROM files WHERE dev = ? AND ino = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!sql_delete) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &sql_delete, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (!msg) { return 1; } sqlite3_bind_int(sql_delete, 1, msg->dev); sqlite3_bind_int64(sql_delete, 2, msg->ino); file_db_dirty = 1; rc = sqlite3_step(sql_delete); if (rc != SQLITE_DONE) { dberr(file_db, "delete by inode may have failed"); } sqlite3_reset(sql_delete); sqlite3_clear_bindings(sql_delete); pdb_clear_xattrs(msg); return rc != SQLITE_DONE; } /* provide a path for a 'NAMELESS FILE' entry */ int pdb_update_file_path(pseudo_msg_t *msg) { static sqlite3_stmt *update; int rc; char *sql = "UPDATE files SET path = ? " "WHERE path = 'NAMELESS FILE' and dev = ? AND ino = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!update) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &update, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } if (!msg || !msg->pathlen) { pseudo_debug(PDBGF_DB, "can't update a file without a message or path.\n"); return 1; } sqlite3_bind_text(update, 1, msg->path, -1, SQLITE_STATIC); sqlite3_bind_int(update, 2, msg->dev); sqlite3_bind_int64(update, 3, msg->ino); file_db_dirty = 1; rc = sqlite3_step(update); if (rc != SQLITE_DONE) { dberr(file_db, "update path by inode may have failed"); } sqlite3_reset(update); sqlite3_clear_bindings(update); return rc != SQLITE_DONE; } /* mark a file for pending deletion by a given client */ int pdb_may_unlink_file(pseudo_msg_t *msg, int deleting) { static sqlite3_stmt *mark_file; int rc, exact; char *sql_mark_file = "UPDATE files SET deleting = ? WHERE path = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!mark_file) { rc = sqlite3_prepare_v2(file_db, sql_mark_file, strlen(sql_mark_file), &mark_file, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (!msg) { return 1; } if (msg->pathlen) { sqlite3_bind_int(mark_file, 1, deleting); sqlite3_bind_text(mark_file, 2, msg->path, -1, SQLITE_STATIC); } else { pseudo_debug(PDBGF_DB, "cannot mark a file for pending deletion without a path."); return 1; } file_db_dirty = 1; rc = sqlite3_step(mark_file); if (rc != SQLITE_DONE) { dberr(file_db, "mark for deletion may have failed"); return 1; } exact = sqlite3_changes(file_db); pseudo_debug(PDBGF_DB, "(exact %d) ", exact); sqlite3_reset(mark_file); sqlite3_clear_bindings(mark_file); /* indicate whether we marked something */ if (exact > 0) return 0; else return 1; } /* unmark a file for pending deletion */ int pdb_cancel_unlink_file(pseudo_msg_t *msg) { static sqlite3_stmt *mark_file; int rc, exact; char *sql_mark_file = "UPDATE files SET deleting = 0 WHERE path = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!mark_file) { rc = sqlite3_prepare_v2(file_db, sql_mark_file, strlen(sql_mark_file), &mark_file, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (!msg) { return 1; } if (msg->pathlen) { sqlite3_bind_text(mark_file, 1, msg->path, -1, SQLITE_STATIC); } else { pseudo_debug(PDBGF_DB, "cannot unmark a file for pending deletion without a path."); return 1; } file_db_dirty = 1; rc = sqlite3_step(mark_file); if (rc != SQLITE_DONE) { dberr(file_db, "unmark for deletion may have failed"); } exact = sqlite3_changes(file_db); pseudo_debug(PDBGF_DB, "(exact %d) ", exact); sqlite3_reset(mark_file); sqlite3_clear_bindings(mark_file); pdb_check_xattrs(msg); return rc != SQLITE_DONE; } /* delete all files attached to a given cookie; * used for database fixup passes. */ int pdb_did_unlink_files(int deleting) { static sqlite3_stmt *delete_exact; int rc, exact; char *sql_delete_exact = "DELETE FROM files WHERE deleting = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!delete_exact) { rc = sqlite3_prepare_v2(file_db, sql_delete_exact, strlen(sql_delete_exact), &delete_exact, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (deleting == 0) { pseudo_diag("did_unlink_files: deleting must be non-zero.\n"); return 0; } sqlite3_bind_int(delete_exact, 1, deleting); file_db_dirty = 1; rc = sqlite3_step(delete_exact); if (rc != SQLITE_DONE) { dberr(file_db, "cleanup of files marked for deletion may have failed"); } exact = sqlite3_changes(file_db); pseudo_debug(PDBGF_DB, "(exact %d)\n", exact); sqlite3_reset(delete_exact); sqlite3_clear_bindings(delete_exact); pdb_clear_unused_xattrs(); return rc != SQLITE_DONE; } /* confirm deletion of a specific file by a given client */ int pdb_did_unlink_file(char *path, pseudo_msg_t *msg, int deleting) { static sqlite3_stmt *delete_exact; int rc, exact; char *sql_delete_exact = "DELETE FROM files WHERE path = ? AND deleting = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!delete_exact) { rc = sqlite3_prepare_v2(file_db, sql_delete_exact, strlen(sql_delete_exact), &delete_exact, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (!path) { pseudo_debug(PDBGF_DB, "cannot unlink a file without a path."); return 1; } sqlite3_bind_text(delete_exact, 1, path, -1, SQLITE_STATIC); sqlite3_bind_int(delete_exact, 2, deleting); file_db_dirty = 1; rc = sqlite3_step(delete_exact); if (rc != SQLITE_DONE) { dberr(file_db, "cleanup of file marked for deletion may have failed"); } exact = sqlite3_changes(file_db); pseudo_debug(PDBGF_DB, "(exact %d)\n", exact); sqlite3_reset(delete_exact); if (msg) { pdb_clear_xattrs(msg); } else { /* we have to clean everything because we don't know for sure the * device/inode... */ pdb_clear_unused_xattrs(); } return rc != SQLITE_DONE; } /* unlink a file, by path */ int pdb_unlink_file(pseudo_msg_t *msg) { static sqlite3_stmt *delete_exact; int rc, exact; char *sql_delete_exact = "DELETE FROM files WHERE path = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!delete_exact) { rc = sqlite3_prepare_v2(file_db, sql_delete_exact, strlen(sql_delete_exact), &delete_exact, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (!msg) { return 1; } if (msg->pathlen) { sqlite3_bind_text(delete_exact, 1, msg->path, -1, SQLITE_STATIC); } else { pseudo_debug(PDBGF_DB, "cannot unlink a file without a path."); return 1; } file_db_dirty = 1; rc = sqlite3_step(delete_exact); if (rc != SQLITE_DONE) { dberr(file_db, "delete exact by path may have failed"); } exact = sqlite3_changes(file_db); pseudo_debug(PDBGF_DB, "(exact %d) ", exact); sqlite3_reset(delete_exact); sqlite3_clear_bindings(delete_exact); pdb_check_xattrs(msg); return rc != SQLITE_DONE; } /* Unlink all the contents of directory * SQLite performance limitations: * path LIKE foo '/%' -> can't use index * path = A OR path = B -> can't use index * Solution: * 1. From web http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html * Use > and < instead of a glob at the end. */ int pdb_unlink_contents(pseudo_msg_t *msg) { static sqlite3_stmt *delete_sub; int rc, sub; char *sql_delete_sub = "DELETE FROM files WHERE " "(path > (? || '/') AND path < (? || '0'));"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!delete_sub) { rc = sqlite3_prepare_v2(file_db, sql_delete_sub, strlen(sql_delete_sub), &delete_sub, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } if (!msg) { return 1; } if (msg->pathlen) { sqlite3_bind_text(delete_sub, 1, msg->path, -1, SQLITE_STATIC); sqlite3_bind_text(delete_sub, 2, msg->path, -1, SQLITE_STATIC); } else { pseudo_debug(PDBGF_DB, "cannot unlink a file without a path."); return 1; } file_db_dirty = 1; rc = sqlite3_step(delete_sub); if (rc != SQLITE_DONE) { dberr(file_db, "delete sub by path may have failed"); } sub = sqlite3_changes(file_db); pseudo_debug(PDBGF_DB, "(sub %d) ", sub); sqlite3_reset(delete_sub); sqlite3_clear_bindings(delete_sub); /* we have no idea how many things might have been affected, so. */ pdb_clear_unused_xattrs(); return rc != SQLITE_DONE; } /* rename a file. * If there are any other files with paths that are rooted in "file", then * file must really be a directory, and they should be renamed. * * This is tricky: * You have to rename everything starting with "path/", but also "path" itself * with no slash. Luckily for us, SQL can replace the old path with the * new path. */ int pdb_rename_file(const char *oldpath, pseudo_msg_t *msg) { static sqlite3_stmt *update_exact, *update_sub; int rc; char *sql_update_exact = "UPDATE files SET path = ? WHERE path = ?;"; char *sql_update_sub = "UPDATE files SET path = replace(path, ?, ?) " "WHERE (path > (? || '/') AND path < (? || '0'));"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!update_exact) { rc = sqlite3_prepare_v2(file_db, sql_update_exact, strlen(sql_update_exact), &update_exact, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } if (!update_sub) { rc = sqlite3_prepare_v2(file_db, sql_update_sub, strlen(sql_update_sub), &update_sub, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } if (!msg) { return 1; } if (!msg->pathlen) { pseudo_debug(PDBGF_DB, "rename: No path provided (ino %llu)\n", (unsigned long long) msg->ino); return 1; } if (!oldpath) { pseudo_debug(PDBGF_DB, "rename: No old path for %s\n", msg->path); return 1; } pseudo_debug(PDBGF_DB, "rename: Changing %s to %s\n", oldpath, msg->path); rc = sqlite3_bind_text(update_exact, 1, msg->path, -1, SQLITE_STATIC); rc = sqlite3_bind_text(update_exact, 2, oldpath, -1, SQLITE_STATIC); rc = sqlite3_bind_text(update_sub, 1, oldpath, -1, SQLITE_STATIC); rc = sqlite3_bind_text(update_sub, 2, msg->path, -1, SQLITE_STATIC); rc = sqlite3_bind_text(update_sub, 3, oldpath, -1, SQLITE_STATIC); rc = sqlite3_bind_text(update_sub, 4, oldpath, -1, SQLITE_STATIC); rc = sqlite3_exec(file_db, "BEGIN;", NULL, NULL, NULL); file_db_dirty = 1; rc = sqlite3_step(update_exact); if (rc != SQLITE_DONE) { dberr(file_db, "update exact may have failed: rc %d", rc); } rc = sqlite3_step(update_sub); if (rc != SQLITE_DONE) { dberr(file_db, "update sub may have failed: rc %d", rc); } sqlite3_reset(update_exact); sqlite3_reset(update_sub); rc = sqlite3_exec(file_db, "END;", NULL, NULL, NULL); sqlite3_clear_bindings(update_exact); sqlite3_clear_bindings(update_sub); return rc != SQLITE_DONE; } /* renumber device only. * this is used if the filesystem moves to a new device, without changing * inode allocations. */ int pdb_renumber_all(dev_t from, dev_t to) { static sqlite3_stmt *files_update, *xattrs_update; int rc; char *files_sql = "UPDATE files " " SET dev = ? " " WHERE dev = ?;"; char *xattrs_sql = "UPDATE xattrs " " SET dev = ? " " WHERE dev = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!files_update) { rc = sqlite3_prepare_v2(file_db, files_sql, strlen(files_sql), &files_update, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } if (!xattrs_update) { rc = sqlite3_prepare_v2(file_db, xattrs_sql, strlen(xattrs_sql), &xattrs_update, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } rc = sqlite3_bind_int(files_update, 1, to); if (rc) { dberr(file_db, "error binding device numbers to update"); } rc = sqlite3_bind_int(files_update, 2, from); if (rc) { dberr(file_db, "error binding device numbers to update"); } rc = sqlite3_bind_int(xattrs_update, 1, to); if (rc) { dberr(file_db, "error binding device numbers to update"); } rc = sqlite3_bind_int(xattrs_update, 2, from); if (rc) { dberr(file_db, "error binding device numbers to update"); } file_db_dirty = 1; pseudo_debug(PDBGF_DB, "updating device dev %llu to %llu\n", (unsigned long long) from, (unsigned long long) to); rc = sqlite3_step(files_update); if (rc != SQLITE_DONE) { dberr(file_db, "update may have failed: rc %d", rc); } sqlite3_reset(files_update); sqlite3_clear_bindings(files_update); rc = sqlite3_step(xattrs_update); if (rc != SQLITE_DONE) { dberr(file_db, "update may have failed: rc %d", rc); } sqlite3_reset(xattrs_update); sqlite3_clear_bindings(xattrs_update); return rc != SQLITE_DONE; } /* change dev/inode for a given path -- used only by RENAME for now. * So, if you rename something with xattrs, we want to keep the xattrs. * But. If you renamed one of multiple hard links, we may end up wanting * to keep the xattrs on both the old name and the new one. But if there's * only one link, we want to drop the xattrs from the old one. Eww. */ int pdb_update_inode(pseudo_msg_t *msg) { static sqlite3_stmt *update; int rc; static pseudo_msg_t *oldmsg; int found_existing; if (!oldmsg) { oldmsg = malloc(pseudo_path_max()); } char *sql = "UPDATE files " " SET dev = ?, ino = ? " " WHERE path = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!update) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &update, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } if (!msg) { return 1; } if (!msg->pathlen) { pseudo_diag("Can't update the inode of a file without its path.\n"); return 1; } memcpy(oldmsg, msg, sizeof(msg) + msg->pathlen); found_existing = !pdb_find_file_path(oldmsg); if (found_existing) { /* we have an existing file entry */ pdb_copy_xattrs(oldmsg, msg); } sqlite3_bind_int(update, 1, msg->dev); sqlite3_bind_int64(update, 2, msg->ino); rc = sqlite3_bind_text(update, 3, msg->path, -1, SQLITE_STATIC); if (rc) { /* msg->path can never be null, and if msg didn't * have a non-zero pathlen, we'd already have exited * above */ dberr(file_db, "error binding %s to select", msg->path); } file_db_dirty = 1; rc = sqlite3_step(update); if (rc != SQLITE_DONE) { dberr(file_db, "update may have failed: rc %d", rc); } sqlite3_reset(update); sqlite3_clear_bindings(update); if (found_existing) { /* possibly delete old message's xattrs */ pdb_check_xattrs(oldmsg); } pseudo_debug(PDBGF_DB, "updating path %s to dev %llu, ino %llu\n", msg->path, (unsigned long long) msg->dev, (unsigned long long) msg->ino); return rc != SQLITE_DONE; } /* change uid/gid/mode/rdev in any existing entries matching a given * dev/inode pair. */ int pdb_update_file(pseudo_msg_t *msg) { static sqlite3_stmt *update; int rc; char *sql = "UPDATE files " " SET uid = ?, gid = ?, mode = ?, rdev = ? " " WHERE dev = ? AND ino = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!update) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &update, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } if (!msg) { return 1; } sqlite3_bind_int(update, 1, msg->uid); sqlite3_bind_int(update, 2, msg->gid); sqlite3_bind_int(update, 3, msg->mode); sqlite3_bind_int(update, 4, msg->rdev); sqlite3_bind_int(update, 5, msg->dev); sqlite3_bind_int64(update, 6, msg->ino); file_db_dirty = 1; rc = sqlite3_step(update); if (rc != SQLITE_DONE) { dberr(file_db, "update may have failed: rc %d", rc); } sqlite3_reset(update); sqlite3_clear_bindings(update); pseudo_debug(PDBGF_DB, "updating dev %llu, ino %llu, new mode %o, owner %d\n", (unsigned long long) msg->dev, (unsigned long long) msg->ino, (int) msg->mode, msg->uid); return rc != SQLITE_DONE; } /* find file using both path AND dev/inode as key */ int pdb_find_file_exact(pseudo_msg_t *msg) { static sqlite3_stmt *select; int rc; char *sql = "SELECT * FROM files WHERE path = ? AND dev = ? AND ino = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } if (!msg) { return 1; } rc = sqlite3_bind_text(select, 1, msg->path, -1, SQLITE_STATIC); if (rc) { dberr(file_db, "error binding %s to select", msg->pathlen ? msg->path : ""); } sqlite3_bind_int(select, 2, msg->dev); sqlite3_bind_int64(select, 3, msg->ino); rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: msg->uid = (unsigned long) sqlite3_column_int64(select, 4); msg->gid = (unsigned long) sqlite3_column_int64(select, 5); msg->mode = (unsigned long) sqlite3_column_int64(select, 6); msg->rdev = (unsigned long) sqlite3_column_int64(select, 7); msg->deleting = (int) sqlite3_column_int64(select, 8); rc = 0; break; case SQLITE_DONE: pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_exact: sqlite_done on first row\n"); rc = 1; break; default: dberr(file_db, "find_exact: select returned neither a row nor done"); rc = 1; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); return rc; } /* find file using path as a key */ int pdb_find_file_path(pseudo_msg_t *msg) { static sqlite3_stmt *select; int rc; char *sql = "SELECT * FROM files WHERE path = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 1; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } if (!msg) { return 1; } if (!msg->pathlen) { return 1; } rc = sqlite3_bind_text(select, 1, msg->path, -1, SQLITE_STATIC); if (rc) { dberr(file_db, "error binding %s to select", msg->pathlen ? msg->path : ""); } rc = sqlite3_column_count(select); rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: msg->dev = sqlite3_column_int64(select, 2); msg->ino = sqlite3_column_int64(select, 3); msg->uid = sqlite3_column_int64(select, 4); msg->gid = sqlite3_column_int64(select, 5); msg->mode = sqlite3_column_int64(select, 6); msg->rdev = sqlite3_column_int64(select, 7); msg->deleting = (int) sqlite3_column_int64(select, 8); rc = 0; break; case SQLITE_DONE: pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_path: sqlite_done on first row\n"); rc = 1; break; default: dberr(file_db, "find_path: select returned neither a row nor done"); rc = 1; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); return rc; } /* find path for a file, given dev and inode as keys */ char * pdb_get_file_path(pseudo_msg_t *msg) { static sqlite3_stmt *select; int rc; char *sql = "SELECT path FROM files WHERE dev = ? AND ino = ?;"; char *response; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 0; } } if (!msg) { return 0; } sqlite3_bind_int(select, 1, msg->dev); sqlite3_bind_int64(select, 2, msg->ino); rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: response = (char *) sqlite3_column_text(select, 0); if (response) { if (strcmp(response, "NAMELESS FILE")) { response = strdup(response); } else { response = 0; } } break; case SQLITE_DONE: pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_dev: sqlite_done on first row\n"); response = 0; break; default: dberr(file_db, "find_dev: select returned neither a row nor done"); response = 0; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); return response; } /* find file using dev/inode as key */ int pdb_find_file_dev(pseudo_msg_t *msg, char **path) { static sqlite3_stmt *select; int rc; char *sql = "SELECT * FROM files WHERE dev = ? AND ino = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } if (!msg) { return 1; } sqlite3_bind_int(select, 1, msg->dev); sqlite3_bind_int64(select, 2, msg->ino); rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: msg->uid = (unsigned long) sqlite3_column_int64(select, 4); msg->gid = (unsigned long) sqlite3_column_int64(select, 5); msg->mode = (unsigned long) sqlite3_column_int64(select, 6); msg->rdev = (unsigned long) sqlite3_column_int64(select, 7); msg->deleting = (int) sqlite3_column_int64(select, 8); /* stash path */ if (path) { *path = strdup((char *) sqlite3_column_text(select, 1)); pseudo_debug(PDBGF_FILE, "find_file_dev: path %s\n", *path ? *path : ""); } rc = 0; break; case SQLITE_DONE: pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_dev: sqlite_done on first row\n"); rc = 1; break; default: dberr(file_db, "find_dev: select returned neither a row nor done"); rc = 1; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); return rc; } int pdb_get_xattr(pseudo_msg_t *msg, char **value, size_t *len) { static sqlite3_stmt *select; int rc; char *response; size_t length; char *sql = "SELECT value FROM xattrs WHERE dev = ? AND ino = ? AND name = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } pseudo_debug(PDBGF_XATTR, "requested xattr named '%s' for ino %lld\n", *value, (long long) msg->ino); sqlite3_bind_int(select, 1, msg->dev); sqlite3_bind_int(select, 2, msg->ino); rc = sqlite3_bind_text(select, 3, *value, -1, SQLITE_STATIC); if (rc) { dberr(file_db, "couldn't bind xattr name to SELECT."); return 1; } rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: response = (char *) sqlite3_column_text(select, 0); length = sqlite3_column_bytes(select, 0); pseudo_debug(PDBGF_XATTR, "got %d-byte results: '%s'\n", (int) length, response); if (response && length >= 1) { /* not a strdup because the values can contain * arbitrary bytes. */ *value = malloc(length); memcpy(*value, response, length); *len = length; rc = 0; } else { *value = NULL; *len = 0; rc = 1; } break; case SQLITE_DONE: pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_exact: sqlite_done on first row\n"); rc = 1; break; default: dberr(file_db, "find_exact: select returned neither a row nor done"); rc = 1; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); return rc; } int pdb_list_xattr(pseudo_msg_t *msg, char **value, size_t *len) { static sqlite3_stmt *select; size_t allocated = 0; size_t used = 0; char *buffer = 0; int rc; char *sql = "SELECT name FROM xattrs WHERE dev = ? AND ino = ? ORDER BY name;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } sqlite3_bind_int(select, 1, msg->dev); sqlite3_bind_int(select, 2, msg->ino); do { rc = sqlite3_step(select); if (rc == SQLITE_ROW) { char *value = (char *) sqlite3_column_text(select, 0); size_t len = sqlite3_column_bytes(select, 0); if (!buffer) { allocated = round_up(len, 256); buffer = malloc(allocated); } if (used + len + 2 > allocated) { size_t new_allocated = round_up(used + len + 2, 256); char *new_buffer = malloc(new_allocated); memcpy(new_buffer, buffer, used); free(buffer); allocated = new_allocated; buffer = new_buffer; } memcpy(buffer + used, value, len); buffer[used + len] = '\0'; used = used + len + 1; } else if (rc == SQLITE_DONE) { *value = buffer; *len = used; } else { dberr(file_db, "non-row response from select?"); free(buffer); *value = NULL; *len = 0; } } while (rc == SQLITE_ROW); sqlite3_reset(select); sqlite3_clear_bindings(select); return rc != SQLITE_DONE; } int pdb_remove_xattr(pseudo_msg_t *msg, char *value, size_t len) { static sqlite3_stmt *delete; int rc; char *sql = "DELETE FROM xattrs WHERE dev = ? AND ino = ? AND name = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!delete) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &delete, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; } } sqlite3_bind_int(delete, 1, msg->dev); sqlite3_bind_int(delete, 2, msg->ino); rc = sqlite3_bind_text(delete, 3, value, len, SQLITE_STATIC); if (rc) { dberr(file_db, "couldn't bind xattr name to DELETE."); return 1; } file_db_dirty = 1; rc = sqlite3_step(delete); if (rc != SQLITE_DONE) { dberr(file_db, "delete xattr may have failed"); } sqlite3_reset(delete); sqlite3_clear_bindings(delete); return rc != SQLITE_DONE; } int pdb_set_xattr(pseudo_msg_t *msg, char *value, size_t len, int flags) { static sqlite3_stmt *select, *update, *insert; int rc; long long existing_row = -1; char *select_sql = "SELECT id FROM xattrs WHERE dev = ? AND ino = ? AND name = ?;"; char *insert_sql = "INSERT INTO xattrs " " ( dev, ino, name, value ) " " VALUES (?, ?, ?, ?);"; char *update_sql = "UPDATE xattrs SET value = ? WHERE id = ?;"; char *vname = value; size_t vlen; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, select_sql, strlen(select_sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } sqlite3_bind_int(select, 1, msg->dev); sqlite3_bind_int(select, 2, msg->ino); rc = sqlite3_bind_text(select, 3, value, -1, SQLITE_STATIC); if (rc) { dberr(file_db, "couldn't bind xattr name to SELECT."); return 1; } rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: existing_row = sqlite3_column_int64(select, 0); break; case SQLITE_DONE: pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_exact: sqlite_done on first row\n"); existing_row = -1; break; default: dberr(file_db, "set_xattr: select returned neither a row nor done"); rc = 1; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); if (flags == XATTR_CREATE && existing_row != -1) { pseudo_debug(PDBGF_DB, "XATTR_CREATE with an existing row, failing."); return 1; } if (flags == XATTR_REPLACE && existing_row == -1) { pseudo_debug(PDBGF_DB, "XATTR_REPLACE with no existing row, failing."); return 1; } /* the material after the name is the value */ vlen = strlen(value); len = len - (vlen + 1); value = value + vlen + 1; pseudo_debug(PDBGF_XATTR, "trying to set a value for ino %lld: name is '%s' [%d/%d bytes], value is '%s' [%d bytes]. Existing row %lld.\n", (long long) msg->ino, vname, (int) vlen, (int) (len + vlen + 1), value, (int) len, existing_row); if (existing_row != -1) { /* update */ if (!update) { rc = sqlite3_prepare_v2(file_db, update_sql, strlen(update_sql), &update, NULL); if (rc) { dberr(file_db, "couldn't prepare UPDATE statement"); return 1; } } rc = sqlite3_bind_text(update, 1, value, len, SQLITE_STATIC); if (rc) { dberr(file_db, "couldn't bind xattr value to UPDATE."); return 1; } sqlite3_bind_int(update, 2, existing_row); file_db_dirty = 1; rc = sqlite3_step(update); if (rc != SQLITE_DONE) { dberr(file_db, "update xattr may have failed"); } sqlite3_reset(update); sqlite3_clear_bindings(update); return rc != SQLITE_DONE; } else { /* insert */ if (!insert) { rc = sqlite3_prepare_v2(file_db, insert_sql, strlen(insert_sql), &insert, NULL); if (rc) { dberr(file_db, "couldn't prepare INSERT statement"); return 1; } } sqlite3_bind_int64(insert, 1, msg->dev); sqlite3_bind_int64(insert, 2, msg->ino); rc = sqlite3_bind_text(insert, 3, vname, -1, SQLITE_STATIC); if (rc) { dberr(file_db, "couldn't bind xattr name to INSERT statement"); return 1; } rc = sqlite3_bind_text(insert, 4, value, len, SQLITE_STATIC); if (rc) { dberr(file_db, "couldn't bind xattr value to INSERT statement"); return 1; } file_db_dirty = 1; rc = sqlite3_step(insert); if (rc != SQLITE_DONE) { dberr(file_db, "insert xattr may have failed"); } sqlite3_reset(insert); sqlite3_clear_bindings(insert); return rc != SQLITE_DONE; } return rc; } /* find file using only inode as key. Unused for now, planned to come * in for NFS usage. */ int pdb_find_file_ino(pseudo_msg_t *msg) { static sqlite3_stmt *select; int rc; char *sql = "SELECT * FROM files WHERE ino = ?;"; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } if (!select) { rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); if (rc) { dberr(file_db, "couldn't prepare SELECT statement"); return 1; } } if (!msg) { return 1; } sqlite3_bind_int64(select, 1, msg->ino); rc = sqlite3_step(select); switch (rc) { case SQLITE_ROW: msg->dev = (unsigned long) sqlite3_column_int64(select, 2); msg->uid = (unsigned long) sqlite3_column_int64(select, 4); msg->gid = (unsigned long) sqlite3_column_int64(select, 5); msg->mode = (unsigned long) sqlite3_column_int64(select, 6); msg->rdev = (unsigned long) sqlite3_column_int64(select, 7); msg->deleting = (int) sqlite3_column_int64(select, 8); rc = 0; break; case SQLITE_DONE: pseudo_debug(PDBGF_DB, "find_ino: sqlite_done on first row\n"); rc = 1; break; default: dberr(file_db, "find_ino: select returned neither a row nor done"); rc = 1; break; } sqlite3_reset(select); sqlite3_clear_bindings(select); return rc; } pdb_file_list pdb_files(void) { pdb_file_list l; if (!file_db && get_dbs()) { pseudo_diag("%s: database error.\n", __func__); return 0; } l = malloc(sizeof(*l)); if (!l) return NULL; l->rc = sqlite3_prepare_v2(file_db, "SELECT path, dev, ino, uid, gid, mode, rdev FROM files", -1, &l->stmt, NULL); if (l->rc) { dberr(file_db, "Couldn't start SELECT from files.\n"); free(l); return NULL; } return l; } pseudo_msg_t * pdb_file(pdb_file_list l) { const unsigned char *s; pseudo_msg_t *m; int column = 0; if (!l || !l->stmt) return 0; /* in case someone tries again after we're already done */ if (l->rc == SQLITE_DONE) { return 0; } l->rc = sqlite3_step(l->stmt); if (l->rc == SQLITE_DONE) { return 0; } else if (l->rc != SQLITE_ROW) { dberr(log_db, "statement failed"); return 0; } s = sqlite3_column_text(l->stmt, column++); m = pseudo_msg_new(0, (const char *) s); if (!m) { pseudo_diag("couldn't allocate file message.\n"); return NULL; } pseudo_debug(PDBGF_DB, "pdb_file: '%s'\n", s ? (const char *) s : ""); m->dev = sqlite3_column_int64(l->stmt, column++); m->ino = sqlite3_column_int64(l->stmt, column++); m->uid = sqlite3_column_int64(l->stmt, column++); m->gid = sqlite3_column_int64(l->stmt, column++); m->mode = sqlite3_column_int64(l->stmt, column++); m->rdev = sqlite3_column_int64(l->stmt, column++); return m; } void pdb_files_done(pdb_file_list l) { if (!l) return; if (l->stmt) { sqlite3_reset(l->stmt); sqlite3_finalize(l->stmt); } free(l); } pseudo-1.8.1+git20161012/pseudo_db.h000066400000000000000000000066251300506512600165400ustar00rootroot00000000000000/* * pseudo_db.h, declarations and definitions for database use * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ typedef struct { time_t stamp; pseudo_msg_type_t type; pseudo_op_t op; int access; unsigned long client; unsigned long fd; unsigned long long dev; unsigned long long ino; unsigned long mode; unsigned long gid; unsigned long uid; char *path; pseudo_res_t result; pseudo_sev_t severity; char *text; char *tag; char *program; } log_entry; extern int pdb_maybe_backup(void); extern int pdb_cancel_unlink_file(pseudo_msg_t *msg); extern int pdb_did_unlink_file(char *path, pseudo_msg_t *msg, int deleting); extern int pdb_did_unlink_files(int deleting); extern int pdb_link_file(pseudo_msg_t *msg); extern int pdb_may_unlink_file(pseudo_msg_t *msg, int deleting); extern int pdb_unlink_file(pseudo_msg_t *msg); extern int pdb_unlink_file_dev(pseudo_msg_t *msg); extern int pdb_update_file(pseudo_msg_t *msg); extern int pdb_update_file_path(pseudo_msg_t *msg); extern int pdb_update_inode(pseudo_msg_t *msg); extern int pdb_unlink_contents(pseudo_msg_t *msg); extern int pdb_rename_file(const char *oldpath, pseudo_msg_t *msg); extern int pdb_renumber_all(dev_t from, dev_t to); extern int pdb_find_file_exact(pseudo_msg_t *msg); extern int pdb_find_file_path(pseudo_msg_t *msg); extern int pdb_find_file_dev(pseudo_msg_t *msg, char **path); extern int pdb_find_file_ino(pseudo_msg_t *msg); extern char *pdb_get_file_path(pseudo_msg_t *msg); extern int pdb_get_xattr(pseudo_msg_t *msg, char **value, size_t *len); extern int pdb_list_xattr(pseudo_msg_t *msg, char **value, size_t *len); extern int pdb_remove_xattr(pseudo_msg_t *msg, char *value, size_t len); extern int pdb_set_xattr(pseudo_msg_t *msg, char *value, size_t len, int flags); struct log_history; typedef struct log_history *log_history; union pseudo_query_data { unsigned long long ivalue; char *svalue; }; typedef struct pseudo_query { pseudo_query_type_t type; pseudo_query_field_t field; union pseudo_query_data data; struct pseudo_query *next; } pseudo_query_t; extern int pdb_log_entry(log_entry *e); extern int pdb_log_msg(pseudo_sev_t severity, pseudo_msg_t *msg, const char *program, const char *tag, const char *text, ...); extern int pdb_log_traits(pseudo_query_t *traits); struct pdb_file_list; typedef struct pdb_file_list *pdb_file_list; extern pdb_file_list pdb_files(void); extern pseudo_msg_t *pdb_file(pdb_file_list); extern void pdb_files_done(pdb_file_list); extern int pdb_delete(pseudo_query_t *traits, unsigned long fields); extern log_history pdb_history(pseudo_query_t *traits, unsigned long fields, int unique); extern log_entry *pdb_history_entry(log_history h); extern void pdb_history_free(log_history h); extern void log_entry_free(log_entry *); pseudo-1.8.1+git20161012/pseudo_ipc.c000066400000000000000000000147311300506512600167160ustar00rootroot00000000000000/* * pseudo_ipc.c, IPC code for pseudo client/server * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" /* Short reads or writes can cause a sigpipe, killing the program, so we * trap it and report that something happened. */ static sig_atomic_t pipe_error = 0; static void (*old_handler)(int) = SIG_DFL; static void sigpipe_trap(int unused __attribute__((unused))) { pipe_error = 1; } static void ignore_sigpipe(void) { pipe_error = 0; old_handler = signal(SIGPIPE, sigpipe_trap); } static void allow_sigpipe(void) { signal(SIGPIPE, old_handler); } #if 0 /* useful only when debugging crazy stuff */ static void display_msg_header(pseudo_msg_t *msg) { pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "type: %d\n", msg->type); pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "inode: %llu\n", (unsigned long long) msg->ino); pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "uid: %d\n", msg->uid); pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "pathlen: %d\n", (int) msg->pathlen); if (msg->pathlen) { pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "path: %s\n", msg->path); } } #endif /* * send message on fd * return: * 0 on success * >0 on error * <0 on error suggesting other end is dead */ int pseudo_msg_send(int fd, pseudo_msg_t *msg, size_t len, const char *path) { int r; if (!msg) return 1; if (fd < 0) return -1; if (path) { pseudo_debug(PDBGF_IPC, "msg type %d (%s), external path %s, mode 0%o\n", msg->type, pseudo_op_name(msg->op), path, (int) msg->mode); if (len == (size_t) -1) len = strlen(path) + 1; msg->pathlen = len; ignore_sigpipe(); r = write(fd, msg, PSEUDO_HEADER_SIZE); if (r == PSEUDO_HEADER_SIZE) { r += write(fd, path, len); } allow_sigpipe(); pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "wrote %d bytes\n", r); if (pipe_error || (r == -1 && errno == EBADF)) return -1; return ((size_t) r != PSEUDO_HEADER_SIZE + len); } else { pseudo_debug(PDBGF_IPC, "msg type %d (%s), result %d (%s), path %.*s, mode 0%o\n", msg->type, pseudo_op_name(msg->op), msg->result, pseudo_res_name(msg->result), msg->pathlen, msg->path, (int) msg->mode); // display_msg_header(msg); ignore_sigpipe(); r = write(fd, msg, PSEUDO_HEADER_SIZE + msg->pathlen); allow_sigpipe(); pseudo_debug(PDBGF_IPC | PDBGF_VERBOSE, "wrote %d bytes\n", r); if (pipe_error || (r == -1 && errno == EBADF)) return -1; return ((size_t) r != PSEUDO_HEADER_SIZE + msg->pathlen); } } /* attempts to receive a message from fd * return is allocated message if one is provided */ pseudo_msg_t * pseudo_msg_receive(int fd) { static pseudo_msg_t *incoming; static size_t incoming_pathlen; pseudo_msg_t *newmsg, header; int r; if (fd < 0) return 0; errno = 0; r = read(fd, &header, PSEUDO_HEADER_SIZE); if (r == -1) { pseudo_debug(PDBGF_IPC, "read failed: %s\n", strerror(errno)); return 0; } if (r < (int) PSEUDO_HEADER_SIZE) { pseudo_debug(PDBGF_IPC, "got only %d bytes (%s)\n", r, strerror(errno)); return 0; } pseudo_debug(PDBGF_IPC, "got header, type %d, pathlen %d\n", header.type, (int) header.pathlen); // display_msg_header(&header); if (!incoming || header.pathlen >= incoming_pathlen) { newmsg = pseudo_msg_new(header.pathlen + 128, 0); if (!newmsg) { pseudo_diag("Couldn't allocate header for path of %d bytes.\n", (int) header.pathlen); return 0; } free(incoming); incoming = newmsg; incoming_pathlen = header.pathlen + 128; } *incoming = header; if (incoming->pathlen) { r = read(fd, incoming->path, incoming->pathlen); if (r < (int) incoming->pathlen) { pseudo_debug(PDBGF_IPC, "short read on path, expecting %d, got %d\n", (int) incoming->pathlen, r); return 0; } /* ensure null termination */ incoming->path[r] = '\0'; } // display_msg_header(incoming); return incoming; } /* duplicate a message -- currently totally unused */ pseudo_msg_t * pseudo_msg_dup(pseudo_msg_t *old) { pseudo_msg_t *newmsg; if (!old) return NULL; newmsg = malloc(sizeof(pseudo_msg_t) + old->pathlen); if (!newmsg) return NULL; memcpy(newmsg, old, sizeof(pseudo_msg_t) + old->pathlen); return newmsg; } /* allocate a message either with pathlen chars of storage or with enough * storage for path */ pseudo_msg_t * pseudo_msg_new(size_t pathlen, const char *path) { pseudo_msg_t *newmsg; if (pathlen) { newmsg = malloc(sizeof(pseudo_msg_t) + pathlen); if (newmsg) { newmsg->pathlen = pathlen; if (path) memcpy(newmsg->path, path, pathlen); newmsg->path[pathlen - 1] = '\0'; } return newmsg; } else { if (!path) { /* no pathlen, no path == purely informational */ newmsg = malloc(sizeof(pseudo_msg_t)); if (newmsg) { newmsg->pathlen = 0; } return newmsg; } else { pathlen = strlen(path) + 1; newmsg = malloc(sizeof(pseudo_msg_t) + pathlen); if (newmsg) { memcpy(newmsg->path, path, pathlen); newmsg->pathlen = pathlen; } return newmsg; } } } /* The following functions populate messages from statbufs and vice versa. * It is intentional that copying a message into a stat doesn't touch nlink; * the nlink value was not stored in the database (it is in the message at * all only so the server can do sanity checks). */ void pseudo_msg_stat(pseudo_msg_t *msg, const PSEUDO_STATBUF *buf) { if (!msg || !buf) return; msg->uid = buf->st_uid; msg->gid = buf->st_gid; msg->dev = buf->st_dev; msg->ino = buf->st_ino; msg->mode = buf->st_mode; msg->rdev = buf->st_rdev; msg->nlink = buf->st_nlink; } void pseudo_stat_msg(PSEUDO_STATBUF *buf, const pseudo_msg_t *msg) { if (!msg || !buf) return; buf->st_uid = msg->uid; buf->st_gid = msg->gid; buf->st_mode = msg->mode; buf->st_rdev = msg->rdev; } pseudo-1.8.1+git20161012/pseudo_ipc.h000066400000000000000000000045131300506512600167200ustar00rootroot00000000000000/* * pseudo_ipc.h, definitions and declarations for pseudo IPC code * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* The [] item at the end of the struct is a C99 feature, replacing the * old (and unportable) "struct hack". */ typedef struct { pseudo_msg_type_t type; pseudo_op_t op; pseudo_res_t result; int access; int client; int fd; dev_t dev; unsigned long long ino; uid_t uid; gid_t gid; unsigned long long mode; dev_t rdev; unsigned int pathlen; int nlink; int deleting; char path[]; } pseudo_msg_t; enum { PSA_EXEC = 1, PSA_WRITE = (PSA_EXEC << 1), PSA_READ = (PSA_WRITE << 1), PSA_APPEND = (PSA_READ << 1), } pseudo_access_t; #define PSEUDO_ACCESS_MAP(mode, fcntl_access, pseudo_access) ((((mode) & O_ACCMODE) == (fcntl_access)) ? (pseudo_access) : (0)) #define PSEUDO_ACCESS_FLAG(mode, fcntl_access, pseudo_access) (((mode) & (fcntl_access)) ? (pseudo_access) : (0)) #define PSEUDO_ACCESS(mode) ( \ PSEUDO_ACCESS_MAP(mode, O_RDONLY, PSA_READ) | \ PSEUDO_ACCESS_MAP(mode, O_WRONLY, PSA_WRITE) | \ PSEUDO_ACCESS_MAP(mode, O_RDWR, PSA_READ | PSA_WRITE) | \ PSEUDO_ACCESS_FLAG(mode, O_APPEND, PSA_APPEND)) extern int pseudo_access_fopen(const char *); #define PSEUDO_HEADER_SIZE (offsetof(pseudo_msg_t, path)) extern pseudo_msg_t *pseudo_msg_receive(int fd); extern pseudo_msg_t *pseudo_msg_dup(pseudo_msg_t *); extern pseudo_msg_t *pseudo_msg_dupheader(pseudo_msg_t *); extern pseudo_msg_t *pseudo_msg_new(size_t, const char *); extern int pseudo_msg_send(int fd, pseudo_msg_t *, size_t, const char *); void pseudo_msg_stat(pseudo_msg_t *msg, const PSEUDO_STATBUF *buf); void pseudo_stat_msg(PSEUDO_STATBUF *buf, const pseudo_msg_t *msg); pseudo-1.8.1+git20161012/pseudo_profile.c000066400000000000000000000036161300506512600176030ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include "pseudo.h" int main(int argc, char **argv) { int fd; pseudo_profile_t totals = { .total_ops = 0 }, item; int count = 0; if (argc < 2) { fprintf(stderr, "usage: pseudo_profile \n"); exit(1); } fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "Can't open '%s': %s.\n", argv[1], strerror(errno)); exit(1); } while (read(fd, &item, sizeof(item)) == sizeof(item)) { if (item.total_ops > 0) { ++count; totals.processes += item.processes; totals.total_ops += item.total_ops; totals.messages += item.messages; totals.op_time.tv_sec += item.op_time.tv_sec; totals.op_time.tv_usec += item.op_time.tv_usec; if (totals.op_time.tv_usec >= 1000000) { ++totals.op_time.tv_sec; totals.op_time.tv_usec -= 1000000; } totals.ipc_time.tv_sec += item.ipc_time.tv_sec; totals.ipc_time.tv_usec += item.ipc_time.tv_usec; if (totals.ipc_time.tv_usec >= 1000000) { ++totals.ipc_time.tv_sec; totals.ipc_time.tv_usec -= 1000000; } totals.wrapper_time.tv_sec += item.wrapper_time.tv_sec; totals.wrapper_time.tv_usec += item.wrapper_time.tv_usec; if (totals.wrapper_time.tv_usec >= 1000000) { ++totals.wrapper_time.tv_sec; totals.wrapper_time.tv_usec -= 1000000; } } } printf("Found data for %d PIDs, %d processes.\n", count, totals.processes); printf("%lld messages for %lld ops.\n", totals.messages, totals.total_ops); double otime = totals.op_time.tv_sec + (totals.op_time.tv_usec / 1000000.0); double itime = totals.ipc_time.tv_sec + (totals.ipc_time.tv_usec / 1000000.0); double wtime = totals.wrapper_time.tv_sec + (totals.wrapper_time.tv_usec / 1000000.0); printf("%.4f msec wrapper time, %.4f msec op time, %.4f msec IPC time.\n", wtime * 1000, otime * 1000, itime * 1000); return 0; } pseudo-1.8.1+git20161012/pseudo_server.c000066400000000000000000000525421300506512600174530ustar00rootroot00000000000000/* * pseudo_server.c, pseudo's server-side logic and message handling * * Copyright (c) 2008-2010, 2013 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_server.h" #include "pseudo_client.h" #include "pseudo_db.h" static int listen_fd = -1; typedef struct { int fd; pid_t pid; char *tag; char *program; } pseudo_client_t; pseudo_client_t *clients; /* active_clients: Number of clients we actually have right now. * highest_client: Highest index into clients table of an active client. * max_clients: Size of table. */ static int active_clients = 0, highest_client = 0, max_clients = 0; #define LOOP_DELAY 2 #define DEFAULT_PSEUDO_SERVER_TIMEOUT 30 int pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT; static int die_peacefully = 0; static int die_forcefully = 0; /* when the client is linked with pseudo_wrappers, these are defined there. * when it is linked with pseudo_server, though, we have to provide different * versions (pseudo_wrappers must not be linked with the server, or Bad Things * happen). */ void pseudo_magic(void) { } void pseudo_antimagic(void) { } void quit_now(int signal) { pseudo_diag("Received signal %d, quitting.\n", signal); die_forcefully = 1; } static int messages = 0, responses = 0; static struct timeval message_time = { .tv_sec = 0 }; static void pseudo_server_loop(void); /* helper function to make a directory, just like mkdir -p. * Can't use system() because the child shell would end up trying * to do the same thing... */ static void mkdir_p(char *path) { size_t len = strlen(path); size_t i; for (i = 1; i < len; ++i) { /* try to create all the directories in path, ignoring * failures */ if (path[i] == '/') { path[i] = '\0'; (void) mkdir(path, 0755); path[i] = '/'; } } (void) mkdir(path, 0755); } static int pseudo_server_write_pid(pid_t pid) { char *pseudo_path; FILE *fp; pseudo_path = pseudo_localstatedir_path(PSEUDO_PIDFILE); if (!pseudo_path) { pseudo_diag("Couldn't get path for prefix/%s\n", PSEUDO_PIDFILE); return 1; } fp = fopen(pseudo_path, "w"); if (!fp) { pseudo_diag("Couldn't open %s: %s\n", pseudo_path, strerror(errno)); return 1; } fprintf(fp, "%lld\n", (long long) pid); fclose(fp); free(pseudo_path); return 0; } static sig_atomic_t got_sigusr1 = 0; static sig_atomic_t got_sigalrm = 0; static void handle_sigusr1(int sig) { (void) sig; got_sigusr1 = 1; } static void handle_sigalrm(int sig) { (void) sig; got_sigalrm = 1; } #define PSEUDO_CHILD_PROCESS_TIMEOUT 2 int pseudo_server_start(int daemonize) { struct sockaddr_un sun = { .sun_family = AF_UNIX, .sun_path = PSEUDO_SOCKET }; char *pseudo_path; int newfd, lockfd; int rc, save_errno; char *lockname; char *lockpath; struct flock lock_data; /* we want a sane umask for server operations; this is what * would control the modes of database files, sockets, and so * on. */ umask(022); /* parent process will wait for child process, or until it gets * SIGUSR1, or until too much time has passed. */ if (daemonize) { int child; /* make startup messages go away when invoked-as-daemon */ pseudo_debug_logfile(PSEUDO_LOGFILE, 2); child = fork(); if (child == -1) { pseudo_diag("Couldn't fork child process: %s\n", strerror(errno)); exit(PSEUDO_EXIT_FORK_FAILED); } if (child) { int status; int rc; int save_errno; got_sigusr1 = 0; signal(SIGUSR1, handle_sigusr1); signal(SIGALRM, handle_sigalrm); alarm(PSEUDO_CHILD_PROCESS_TIMEOUT); int tries = 0; do { rc = waitpid(child, &status, WNOHANG); save_errno = errno; if (rc != child && !got_sigalrm && !got_sigusr1) { struct timespec delay = { .tv_sec = 0, .tv_nsec = 100000 }; nanosleep(&delay, NULL); ++tries; } } while (!got_sigalrm && !got_sigusr1 && rc != child); alarm(0); pseudo_debug(PDBGF_SERVER, "pid waited: %d/%d [%d tries], status %d, usr1 %d, alrm %d\n", rc, save_errno, tries, status, got_sigusr1, got_sigalrm); if (got_sigusr1) { pseudo_debug(PDBGF_SERVER, "server says it's ready.\n"); exit(0); } if (got_sigalrm) { pseudo_diag("Child process timeout after %d seconds.\n", PSEUDO_CHILD_PROCESS_TIMEOUT); exit(PSEUDO_EXIT_TIMEOUT); } if (rc == -1) { pseudo_diag("Failure in waitpid(): %s\n", strerror(save_errno)); exit(PSEUDO_EXIT_WAITPID); } if (WIFSIGNALED(status)) { status = WTERMSIG(status); pseudo_diag("Child process exited from signal %d.\n", status); kill(getpid(), status); /* can't use +128 because that's not valid */ exit(status + 64); } if (WIFEXITED(status)) { status = WEXITSTATUS(status); pseudo_diag("Child process exit status %d: %s\n", status, pseudo_exit_status_name(status)); if (status == 0) { pseudo_diag("Hang on, server should not have exited 0 without sending us sigusr1?\n"); } exit(status); } pseudo_diag("Unknown exit status %d.\n", status); exit(PSEUDO_EXIT_GENERAL); } else { /* detach from parent session */ setsid(); fclose(stdin); fclose(stdout); /* and then just execute the server code normally. */ /* Any logging will presumably go to logfile, but * exit status will make it back to the parent for * reporting. */ } } pseudo_diag("pid %d [parent %d], doing new pid setup and server start\n", getpid(), getppid()); pseudo_new_pid(); pseudo_debug(PDBGF_SERVER, "opening lock.\n"); lockpath = pseudo_localstatedir_path(NULL); if (!lockpath) { pseudo_diag("Couldn't allocate a file path.\n"); exit(PSEUDO_EXIT_LOCK_PATH); } mkdir_p(lockpath); lockname = pseudo_localstatedir_path(PSEUDO_LOCKFILE); if (!lockname) { pseudo_diag("Couldn't allocate a file path.\n"); exit(PSEUDO_EXIT_LOCK_PATH); } lockfd = open(lockname, O_RDWR | O_CREAT, 0644); if (lockfd < 0) { pseudo_diag("Can't open or create lockfile %s: %s\n", lockname, strerror(errno)); exit(PSEUDO_EXIT_LOCK_FAILED); } free(lockname); /* the lock shuffle has to happen before an fcntl lock, which * is automatically dropped if *any* file descriptor on the file * is closed... */ if (lockfd <= 2) { newfd = fcntl(lockfd, F_DUPFD, 3); if (newfd < 0) { pseudo_diag("Can't move lockfile to safe descriptor, leaving it on %d: %s\n", lockfd, strerror(errno)); } else { close(lockfd); lockfd = newfd; } } pseudo_debug(PDBGF_SERVER, "acquiring lock.\n"); memset(&lock_data, 0, sizeof(lock_data)); lock_data.l_type = F_WRLCK; lock_data.l_whence = SEEK_SET; lock_data.l_start = 0; lock_data.l_len = 0; rc = fcntl(lockfd, F_SETLK, &lock_data); if (rc < 0) { save_errno = errno; if (save_errno == EACCES || save_errno == EAGAIN) { rc = fcntl(lockfd, F_GETLK, &lock_data); if (rc == 0 && lock_data.l_type != F_UNLCK) { pseudo_diag("lock already held by existing pid %d.\n", lock_data.l_pid); } } pseudo_diag("Couldn't obtain lock: %s.\n", strerror(save_errno)); exit(PSEUDO_EXIT_LOCK_HELD); } else { pseudo_debug(PDBGF_SERVER, "Acquired lock.\n"); } #if PSEUDO_PORT_DARWIN sun.sun_len = strlen(PSEUDO_SOCKET) + 1; #endif listen_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (listen_fd < 0) { pseudo_diag("couldn't create listening socket: %s\n", strerror(errno)); exit(PSEUDO_EXIT_SOCKET_CREATE); } if (listen_fd <= 2) { newfd = fcntl(listen_fd, F_DUPFD, 3); if (newfd < 0) { pseudo_diag("couldn't dup listening socket: %s\n", strerror(errno)); close(listen_fd); exit(PSEUDO_EXIT_SOCKET_FD); } else { close(listen_fd); listen_fd = newfd; } } /* cd to the data directory */ pseudo_path = pseudo_localstatedir_path(NULL); if (!pseudo_path || chdir(pseudo_path) == -1) { pseudo_diag("can't get to '%s': %s\n", pseudo_path, strerror(errno)); exit(PSEUDO_EXIT_SOCKET_PATH); } free(pseudo_path); /* remove existing socket -- if it exists */ rc = unlink(sun.sun_path); if (rc == -1 && errno != ENOENT) { pseudo_diag("Can't unlink existing socket: %s.\n", strerror(errno)); exit(PSEUDO_EXIT_SOCKET_UNLINK); } if (bind(listen_fd, (struct sockaddr *) &sun, sizeof(sun)) == -1) { pseudo_diag("couldn't bind listening socket: %s\n", strerror(errno)); exit(PSEUDO_EXIT_SOCKET_BIND); } if (listen(listen_fd, 5) == -1) { pseudo_diag("couldn't listen on socket: %s\n", strerror(errno)); exit(PSEUDO_EXIT_SOCKET_LISTEN); } rc = pseudo_server_write_pid(getpid()); if (rc != 0) { pseudo_diag("warning: couldn't write pid file.\n"); } signal(SIGHUP, quit_now); signal(SIGINT, quit_now); signal(SIGALRM, quit_now); signal(SIGQUIT, quit_now); signal(SIGTERM, quit_now); /* tell parent process to stop waiting */ if (daemonize) { pid_t ppid = getppid(); if (ppid == 1) { pseudo_diag("Setup complete, but parent is init, not sending SIGUSR1.\n"); } else { pseudo_diag("Setup complete, sending SIGUSR1 to pid %d.\n", ppid); kill(ppid, SIGUSR1); } } pseudo_server_loop(); return 0; } /* mess with internal tables as needed */ static void open_client(int fd) { pseudo_client_t *new_clients; int i; /* if possible, use first open client slot */ for (i = 0; i < max_clients; ++i) { if (clients[i].fd == -1) { pseudo_debug(PDBGF_SERVER, "reusing client %d for fd %d\n", i, fd); clients[i].fd = fd; clients[i].pid = 0; clients[i].tag = NULL; clients[i].program = NULL; ++active_clients; if (i > highest_client) highest_client = i; return; } } /* otherwise, allocate a new one */ new_clients = malloc(sizeof(*new_clients) * (max_clients + 16)); if (new_clients) { memcpy(new_clients, clients, max_clients * sizeof(*clients)); free(clients); for (i = max_clients; i < max_clients + 16; ++i) { new_clients[i].fd = -1; new_clients[i].pid = 0; new_clients[i].tag = NULL; new_clients[i].program = NULL; } clients = new_clients; clients[max_clients].fd = fd; clients[max_clients].pid = 0; clients[max_clients].tag = NULL; clients[max_clients].program = NULL; highest_client = max_clients + 1; max_clients += 16; ++active_clients; } else { pseudo_diag("error allocating new client, fd %d\n", fd); close(fd); } } /* clear pid/fd. If this was the highest client, iterate downwards looking * for a lower one to be the new highest client. */ static void close_client(int client) { pseudo_debug(PDBGF_SERVER, "lost client %d [%d], closing fd %d\n", client, clients[client].pid, clients[client].fd); /* client went away... */ if (client > highest_client || client <= 0) { pseudo_diag("tried to close client %d (highest is %d)\n", client, highest_client); return; } close(clients[client].fd); clients[client].fd = -1; free(clients[client].tag); free(clients[client].program); clients[client].pid = 0; clients[client].tag = NULL; clients[client].program = NULL; --active_clients; if (client == highest_client) while (clients[highest_client].fd != -1 && highest_client > 0) --highest_client; } /* Actually process a request. */ static int serve_client(int i) { pseudo_msg_t *in; int rc; pseudo_debug(PDBGF_SERVER, "message from client %d [%d:%s - %s] fd %d\n", i, (int) clients[i].pid, clients[i].program ? clients[i].program : "???", clients[i].tag ? clients[i].tag : "NO TAG", clients[i].fd); in = pseudo_msg_receive(clients[i].fd); if (in) { char *response_path = 0; size_t response_pathlen; int send_response = 1; pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "got a message (%d): %s\n", in->type, (in->pathlen ? in->path : "")); /* handle incoming ping */ if (in->type == PSEUDO_MSG_PING && !clients[i].pid) { pseudo_debug(PDBGF_SERVER, "new client: %d -> %d", i, in->client); clients[i].pid = in->client; if (in->pathlen) { size_t proglen; proglen = strlen(in->path); pseudo_debug(PDBGF_SERVER, " <%s>", in->path); free(clients[i].program); clients[i].program = malloc(proglen + 1); if (clients[i].program) { snprintf(clients[i].program, proglen + 1, "%s", in->path); } if (in->pathlen > proglen) { pseudo_debug(PDBGF_SERVER, " [%s]", in->path + proglen + 1); clients[i].tag = malloc(in->pathlen - proglen); if (clients[i].tag) snprintf(clients[i].tag, in->pathlen - proglen, "%s", in->path + proglen + 1); } } pseudo_debug(PDBGF_SERVER, "\n"); } /* sanity-check client ID */ if (in->client != clients[i].pid) { pseudo_debug(PDBGF_SERVER, "uh-oh, expected pid %d for client %d, got %d\n", (int) clients[i].pid, i, in->client); } /* regular requests are processed in place by * pseudo_server_response. */ if (in->type != PSEUDO_MSG_SHUTDOWN) { if (in->type == PSEUDO_MSG_FASTOP) send_response = 0; /* most messages don't need these, but xattr may */ response_path = 0; response_pathlen = -1; if (pseudo_server_response(in, clients[i].program, clients[i].tag, &response_path, &response_pathlen)) { in->type = PSEUDO_MSG_NAK; } else { in->type = PSEUDO_MSG_ACK; pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "response: %d (%s)\n", in->result, pseudo_res_name(in->result)); } in->client = i; if (response_path) { in->pathlen = response_pathlen; } else { in->pathlen = 0; } } else { /* the server's listen fd is "a client", and * so is the program connecting to request a shutdown. * it should never be less than 2, but crazy things * happen. >2 implies some other active client, * though. */ if (active_clients > 2) { int j; char *s; response_path = malloc(8 * active_clients); in->type = PSEUDO_MSG_NAK; in->fd = active_clients - 2; s = response_path; for (j = 1; j <= highest_client; ++j) { if (clients[j].fd != -1 && j != i) { s += snprintf(s, 8, "%d ", (int) clients[j].pid); } } in->pathlen = (s - response_path) + 1; /* exit quickly once clients go away, though */ pseudo_server_timeout = 3; } else { in->type = PSEUDO_MSG_ACK; in->pathlen = 0; in->client = i; die_peacefully = 1; } } if (send_response) { if ((rc = pseudo_msg_send(clients[i].fd, in, in->pathlen, response_path)) != 0) { pseudo_debug(PDBGF_SERVER, "failed to send response to client %d [%d]: %d (%s)\n", i, (int) clients[i].pid, rc, strerror(errno)); } } else { rc = 1; } free(response_path); return rc; } else { /* this should not be happening, but the exceptions aren't * being detected in select() for some reason. */ pseudo_debug(PDBGF_SERVER, "client %d: no message\n", (int) clients[i].pid); close_client(i); return 0; } } /* get clients, handle messages, shut down. * This doesn't actually do any work, it just calls a ton of things which * do work. */ static void pseudo_server_loop(void) { struct sockaddr_un client; socklen_t len; fd_set reads, writes, events; int max_fd, current_clients; struct timeval timeout; int i; int rc; int fd; int loop_timeout = pseudo_server_timeout; clients = malloc(16 * sizeof(*clients)); clients[0].fd = listen_fd; clients[0].pid = getpid(); for (i = 1; i < 16; ++i) { clients[i].fd = -1; clients[i].pid = 0; clients[i].tag = NULL; clients[i].program = NULL; } active_clients = 1; max_clients = 16; highest_client = 0; pseudo_debug(PDBGF_SERVER, "server loop started.\n"); if (listen_fd < 0) { pseudo_diag("got into loop with no valid listen fd.\n"); exit(PSEUDO_EXIT_LISTEN_FD); } pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server started (pid %d)", getpid()); FD_ZERO(&reads); FD_ZERO(&writes); FD_ZERO(&events); FD_SET(clients[0].fd, &reads); FD_SET(clients[0].fd, &events); max_fd = clients[0].fd; timeout = (struct timeval) { .tv_sec = LOOP_DELAY, .tv_usec = 0 }; /* EINTR tends to come from profiling, so it is not a good reason to * exit; other signals are caught and set the flag causing a graceful * exit. */ while ((rc = select(max_fd + 1, &reads, &writes, &events, &timeout)) >= 0 || (errno == EINTR)) { if (rc == 0 || (rc == -1 && errno == EINTR)) { /* If there's no clients, start timing out. If there * are active clients, never time out. */ if (active_clients == 1) { loop_timeout -= LOOP_DELAY; /* maybe flush database to disk */ pdb_maybe_backup(); if (loop_timeout <= 0) { pseudo_debug(PDBGF_SERVER, "no more clients, got bored.\n"); die_peacefully = 1; } else { /* display this if not exiting */ pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "%d messages handled in %.4f seconds, %d responses\n", messages, (double) message_time.tv_sec + (double) message_time.tv_usec / 1000000.0, responses); } } } else if (rc > 0) { loop_timeout = pseudo_server_timeout; for (i = 1; i <= highest_client; ++i) { if (clients[i].fd == -1) continue; if (FD_ISSET(clients[i].fd, &events)) { /* this should happen but doesn't... */ close_client(i); } else if (FD_ISSET(clients[i].fd, &reads)) { struct timeval tv1, tv2; int rc; gettimeofday(&tv1, NULL); rc = serve_client(i); gettimeofday(&tv2, NULL); ++messages; if (rc == 0) ++responses; message_time.tv_sec += (tv2.tv_sec - tv1.tv_sec); message_time.tv_usec += (tv2.tv_usec - tv1.tv_usec); if (message_time.tv_usec < 0) { message_time.tv_usec += 1000000; --message_time.tv_sec; } else while (message_time.tv_usec > 1000000) { message_time.tv_usec -= 1000000; ++message_time.tv_sec; } } if (die_forcefully) break; } if (!die_forcefully && (FD_ISSET(clients[0].fd, &events) || FD_ISSET(clients[0].fd, &reads))) { len = sizeof(client); if ((fd = accept(listen_fd, (struct sockaddr *) &client, &len)) != -1) { /* Don't allow clients to end up on fd 2, because glibc's * malloc debug uses that fd unconditionally. */ if (fd == 2) { int newfd = fcntl(fd, F_DUPFD, 3); close(fd); fd = newfd; } pseudo_debug(PDBGF_SERVER, "new client fd %d\n", fd); open_client(fd); /* A new client implicitly cancels any * previous shutdown request, or a * shutdown for lack of clients. */ pseudo_server_timeout = DEFAULT_PSEUDO_SERVER_TIMEOUT; die_peacefully = 0; } } pseudo_debug(PDBGF_SERVER, "server loop complete [%d clients left]\n", active_clients); } if (die_peacefully || die_forcefully) { pseudo_debug(PDBGF_SERVER, "quitting.\n"); pseudo_debug(PDBGF_SERVER | PDBGF_BENCHMARK, "server %d exiting: handled %d messages in %.4f seconds\n", getpid(), messages, (double) message_time.tv_sec + (double) message_time.tv_usec / 1000000.0); pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server %d exiting: handled %d messages in %.4f seconds", getpid(), messages, (double) message_time.tv_sec + (double) message_time.tv_usec / 1000000.0); /* and at this point, we'll start refusing connections */ close(clients[0].fd); /* This is a good place to insert a delay for * debugging race conditions during startup. */ /* usleep(300000); */ exit(0); } FD_ZERO(&reads); FD_ZERO(&writes); FD_ZERO(&events); FD_SET(clients[0].fd, &reads); FD_SET(clients[0].fd, &events); max_fd = clients[0].fd; /* current_clients is a sanity check; note that for * purposes of select(), the server is one of the fds, * and thus, "a client". */ current_clients = 1; for (i = 1; i <= highest_client; ++i) { if (clients[i].fd != -1) { ++current_clients; FD_SET(clients[i].fd, &reads); FD_SET(clients[i].fd, &events); if (clients[i].fd > max_fd) max_fd = clients[i].fd; } } if (current_clients != active_clients) { pseudo_debug(PDBGF_SERVER, "miscount of current clients (%d) against active_clients (%d)?\n", current_clients, active_clients); } /* reinitialize timeout because Linux select alters it */ timeout = (struct timeval) { .tv_sec = LOOP_DELAY, .tv_usec = 0 }; } pseudo_diag("select failed: %s\n", strerror(errno)); } pseudo-1.8.1+git20161012/pseudo_server.h000066400000000000000000000020151300506512600174460ustar00rootroot00000000000000/* * pseudo_server.h, pseudo server declarations and definitions * * Copyright (c) 2008-2009 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ extern int pseudo_server_start(int); extern int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len); extern int pseudo_server_timeout; extern int opt_l; pseudo-1.8.1+git20161012/pseudo_util.c000066400000000000000000001303411300506512600171140ustar00rootroot00000000000000/* * pseudo_util.c, miscellaneous utility functions * * Copyright (c) 2008-2013 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* we need access to RTLD_NEXT for a horrible workaround */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include /* see the comments below about (*real_regcomp)() */ #include #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_db.h" struct pseudo_variables { char *key; size_t key_len; char *value; }; /* The order below is not arbitrary, but based on an assumption * of how often things will be used. */ static struct pseudo_variables pseudo_env[] = { { "PSEUDO_PREFIX", 13, NULL }, { "PSEUDO_BINDIR", 13, NULL }, { "PSEUDO_LIBDIR", 13, NULL }, { "PSEUDO_LOCALSTATEDIR", 20, NULL }, { "PSEUDO_PASSWD", 13, NULL }, { "PSEUDO_CHROOT", 13, NULL }, { "PSEUDO_UIDS", 11, NULL }, { "PSEUDO_GIDS", 11, NULL }, { "PSEUDO_OPTS", 11, NULL }, { "PSEUDO_DEBUG", 12, NULL }, { "PSEUDO_DEBUG_FILE", 17, NULL }, { "PSEUDO_TAG", 10, NULL }, { "PSEUDO_ENOSYS_ABORT", 19, NULL }, { "PSEUDO_NOSYMLINKEXP", 19, NULL }, { "PSEUDO_DISABLED", 15, NULL }, { "PSEUDO_UNLOAD", 13, NULL }, { "PSEUDO_ALLOW_FSYNC", 18, NULL }, #ifdef PSEUDO_PROFILING { "PSEUDO_PROFILE_PATH", 19, NULL }, #endif { "PSEUDO_EVLOG", 12, NULL }, { "PSEUDO_EVLOG_FILE", 17, NULL }, { NULL, 0, NULL } /* Magic terminator */ }; typedef struct { struct timeval stamp; int len; char *data; } pseudo_evlog_entry; /* so bash overrides getenv/unsetenv/etcetera, preventing them from * actually modifying environ, so we have pseudo_wrappers try to dlsym * the right values. This could fail, in which case we'd get null * pointers, and we'll just call whatever the linker gives us and * hope for the best. */ #define SETENV(x, y, z) (pseudo_real_setenv ? pseudo_real_setenv : setenv)(x, y, z) #define GETENV(x) (pseudo_real_getenv ? pseudo_real_getenv : getenv)(x) #define UNSETENV(x) (pseudo_real_unsetenv ? pseudo_real_unsetenv : unsetenv)(x) #define PSEUDO_EVLOG_ENTRIES 250 #define PSEUDO_EVLOG_LENGTH 256 static pseudo_evlog_entry event_log[PSEUDO_EVLOG_ENTRIES]; static char *pseudo_evlog_buffer; static int pseudo_evlog_next_entry = 0; static void pseudo_evlog_set(char *); static void pseudo_evlog_flags_finalize(void); static unsigned long pseudo_debug_flags_in(char *); /* -1 - init hasn't been run yet * 0 - init has been run * 1 - init is running * * There are cases where the constructor is run AFTER the * program starts playing with things, so we need to do our * best to handle that case. */ static int pseudo_util_initted = -1; /* Not yet run */ /* bypass wrapper logic on path computations */ int (*pseudo_real_lstat)(const char *path, PSEUDO_STATBUF *buf) = NULL; /* bash workaround */ int (*pseudo_real_unsetenv)(const char *) = unsetenv; char * (*pseudo_real_getenv)(const char *) = getenv; int (*pseudo_real_setenv)(const char *, const char *, int) = setenv; #if 0 static void dump_env(char **envp) { size_t i = 0; for (i = 0; envp[i]; i++) { pseudo_debug(PDBGF_ENV, "dump_envp: [%d]%s\n", (int) i, envp[i]); } for (i = 0; pseudo_env[i].key; i++) { pseudo_debug(PDBGF_ENV, "dump_envp: {%d}%s=%s\n", (int) i, pseudo_env[i].key, pseudo_env[i].value); } pseudo_debug(PDBGF_ENV, "dump_envp: _in_init %d\n", pseudo_util_initted); } #endif int pseudo_has_unload(char * const *envp) { static const char unload[] = "PSEUDO_UNLOAD"; static size_t unload_len = sizeof(unload) - 1; size_t i = 0; /* Is it in the caller environment? */ if (NULL != GETENV(unload)) return 1; /* Is it in the environment cache? */ if (pseudo_util_initted == -1) pseudo_init_util(); while (pseudo_env[i].key && strcmp(pseudo_env[i].key, unload)) ++i; if (pseudo_env[i].key && pseudo_env[i].value) return 1; /* Is it in the operational environment? */ while (envp && *envp) { if ((!strncmp(*envp, unload, unload_len)) && ('=' == (*envp)[unload_len])) return 1; ++envp; } return 0; } /* Caller must free memory! */ char * pseudo_get_value(const char *key) { size_t i = 0; char * value; if (pseudo_util_initted == -1) pseudo_init_util(); for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ; /* Check if the environment has it and we don't ... * if so, something went wrong... so we'll attempt to recover */ if (pseudo_env[i].key && !pseudo_env[i].value && GETENV(pseudo_env[i].key)) pseudo_init_util(); if (pseudo_env[i].value) value = strdup(pseudo_env[i].value); else value = NULL; if (!pseudo_env[i].key) pseudo_diag("Unknown variable %s.\n", key); return value; } /* We make a copy, so the original values should be freed. */ int pseudo_set_value(const char *key, const char *value) { int rc = 0; size_t i = 0; if (pseudo_util_initted == -1) pseudo_init_util(); for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ; if (pseudo_env[i].key) { if (pseudo_env[i].value) free(pseudo_env[i].value); if (value) { char *new = strdup(value); if (new) pseudo_env[i].value = new; else pseudo_diag("warning: failed to save new value (%s) for key %s\n", value, key); } else pseudo_env[i].value = NULL; } else { if (!pseudo_util_initted) pseudo_diag("Unknown variable %s.\n", key); rc = -EINVAL; } return rc; } void pseudo_init_util(void) { size_t i = 0; char * env; pseudo_util_initted = 1; for (i = 0; pseudo_env[i].key; i++) { if (GETENV(pseudo_env[i].key)) pseudo_set_value(pseudo_env[i].key, GETENV(pseudo_env[i].key)); } pseudo_util_initted = 0; /* Somewhere we have to set the debug level.. */ env = pseudo_get_value("PSEUDO_DEBUG"); if (env) { int i; int level = atoi(env); if (level > 0) { for (i = 0; i < level; ++i) { pseudo_debug_verbose(); } } else { pseudo_debug_set(env); } pseudo_debug_flags_finalize(); } free(env); env = pseudo_get_value("PSEUDO_EVLOG"); if (env) { pseudo_evlog_set(env); pseudo_evlog_flags_finalize(); } free(env); } unsigned long pseudo_util_debug_flags = 0; unsigned long pseudo_util_evlog_flags = 0; int pseudo_util_debug_fd = 2; int pseudo_util_evlog_fd = 2; static int debugged_newline = 1; static char pid_text[32]; static size_t pid_len; static int pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurrent, const char *element, size_t elen, int leave_this); static int pseudo_append_elements(char *newpath, char *root, size_t allocated, char **current, const char *elements, size_t elen, int leave_last); extern char **environ; static ssize_t pseudo_max_pathlen = -1; static ssize_t pseudo_sys_max_pathlen = -1; /* in our installed system, we usually use a name of the form * libpseudoCHECKSUM.so, where CHECKSUM is an md5 checksum of the host * libc.so -- this forces rebuilds of the library when the C library * changes. The problem is that the pseudo binary may be * a prebuilt, in which case it doesn't know about CHECKSUM, so it * has to determine whether a given PRELINK_LIBRARIES contains libpseudo.so * or libpseudoCHECKSUM.so, without prior knowledge... Fancy! * * We search for anything matching libpseudo*.so, where * is any * sequence of non-spaces (including an empty string), with either * the beginning of the string or a space in front of it, and either * the end of the string or a space after it. */ static char *libpseudo_name = "libpseudo.so"; /* this used to look for a "libpseudo*.so", but it turns out you can * specify a path even on Linux. */ static char *libpseudo_pattern = "(^|=| )[^ ]*libpseudo[^ ]*\\.so($| )"; static regex_t libpseudo_regex; static int libpseudo_regex_compiled = 0; /* Okay, so, there's a funny story behind this. On one of the systems * we need to run on, /usr/bin/find happens to provide its own * definitions of regcomp and regexec which are INCOMPATIBLE with the * ones in the C library, and not only that, but which have buggy and/or * incompatible semantics, such that they trash elements of the pmatch * array. So we do our best to call the "real" regcomp/regexec in the * C library. If we can't find them, we just do our best and hope that * no one called us from a program with incompatible variants. * */ #if PSEUDO_PORT_LINUX static int (*real_regcomp)(regex_t *__restrict __preg, const char *__restrict __pattern, int __cflags); static int (*real_regexec)(const regex_t *__restrict __preg, const char *__restrict __string, size_t __nmatch, regmatch_t __pmatch[__restrict_arr], int __eflags); #else #define real_regcomp regcomp #define real_regexec regexec #endif /* PSEUDO_PORT_LINUX */ static int libpseudo_regex_init(void) { int rc; if (libpseudo_regex_compiled) return 0; #if PSEUDO_PORT_LINUX real_regcomp = dlsym(RTLD_NEXT, "regcomp"); if (!real_regcomp) real_regcomp = regcomp; real_regexec = dlsym(RTLD_NEXT, "regexec"); if (!real_regexec) real_regexec = regexec; #endif rc = (*real_regcomp)(&libpseudo_regex, libpseudo_pattern, REG_EXTENDED); if (rc == 0) libpseudo_regex_compiled = 1; return rc; } /* given a space-or-colon-separated list of files, ala PRELINK_LIBRARIES, # return that list without any variants of libpseudo*.so. */ static char * without_libpseudo(char *list) { regmatch_t pmatch[1]; int counter = 0; int skip_start = 0; if (libpseudo_regex_init()) return NULL; if (list[0] == '=' || list[0] == PSEUDO_LINKPATH_SEPARATOR[0]) skip_start = 1; if ((*real_regexec)(&libpseudo_regex, list, 1, pmatch, 0)) { return list; } list = strdup(list); while (!(*real_regexec)(&libpseudo_regex, list, 1, pmatch, 0)) { char *start = list + pmatch[0].rm_so; char *end = list + pmatch[0].rm_eo; /* don't copy over the space or = */ start += skip_start; memmove(start, end, strlen(end) + 1); ++counter; if (counter > 5) { pseudo_diag("Found way too many libpseudo.so in environment, giving up.\n"); return list; } } return list; } static char * with_libpseudo(char *list, char *libdir_path) { regmatch_t pmatch[1]; if (libpseudo_regex_init()) return NULL; if ((*real_regexec)(&libpseudo_regex, list, 1, pmatch, 0)) { size_t len; #if PSEUDO_PORT_DARWIN /* <%s:%s/%s\0> */ len = strlen(list) + 1 + strlen(libdir_path) + 1 + strlen(libpseudo_name) + 1; #else /* suppress warning */ (void) libdir_path; /* <%s %s\0> */ len = strlen(list) + 1 + strlen(libpseudo_name) + 1; #endif char *new = malloc(len); if (new) { /* insert space only if there were previous bits */ /* on Darwin, we have to provide the full path to * libpseudo */ #if PSEUDO_PORT_DARWIN snprintf(new, len, "%s%s%s/%s", list, *list ? PSEUDO_LINKPATH_SEPARATOR : "", libdir_path ? libdir_path : "", libpseudo_name); #else snprintf(new, len, "%s%s%s", list, *list ? PSEUDO_LINKPATH_SEPARATOR : "", libpseudo_name); #endif } return new; } else { return strdup(list); } } char *pseudo_version = PSEUDO_VERSION; /* going away soon */ static int max_debug_level = 0; void pseudo_debug_terse(void) { char s[2] = { pseudo_debug_type_symbolic(max_debug_level) }; if (max_debug_level > 0) { --max_debug_level; pseudo_debug_clear(s); } } void pseudo_debug_verbose(void) { char s[2] = { pseudo_debug_type_symbolic(max_debug_level + 1) }; if (s[0]) { pseudo_debug_set(s); ++max_debug_level; } } void pseudo_debug_set(char *s) { pseudo_util_debug_flags = pseudo_debug_flags_in(s); } static void pseudo_evlog_set(char *s) { pseudo_util_evlog_flags = pseudo_debug_flags_in(s); } /* This exists because we don't want to allocate a bunch of strings * and free them immediately if you have several flags set. */ static void pseudo_flags_finalize(unsigned long flags, char *value) { char buf[PDBG_MAX + 1] = "", *s = buf; for (int i = 0; i < PDBG_MAX; ++i) { if (flags & (1 << i)) { *s++ = pseudo_debug_type_symbolic(i); } } pseudo_set_value(value, buf); } void pseudo_debug_flags_finalize(void) { pseudo_flags_finalize(pseudo_util_debug_flags, "PSEUDO_DEBUG"); } void pseudo_evlog_flags_finalize(void) { pseudo_flags_finalize(pseudo_util_evlog_flags, "PSEUDO_EVLOG"); } static unsigned long pseudo_debug_flags_in(char *s) { unsigned long flags = 0; if (!s) return flags; for (; *s; ++s) { int id = pseudo_debug_type_symbolic_id(*s); if (id > 0) { flags |= (1 << id); } } return flags; } void pseudo_debug_clear(char *s) { if (!s) return; for (; *s; ++s) { int id = pseudo_debug_type_symbolic_id(*s); if (id > 0) { pseudo_util_debug_flags &= ~(1 << id); } } } int pseudo_diag(char *fmt, ...) { va_list ap; char debuff[8192]; int len; /* gcc on Ubuntu 8.10 requires that you examine the return from * write(), and won't let you cast it to void. Of course, if you * can't print error messages, there's nothing to do. */ int wrote = 0; va_start(ap, fmt); len = vsnprintf(debuff, 8192, fmt, ap); va_end(ap); if (len > 8192) len = 8192; if (debugged_newline && (pseudo_util_debug_flags & PDBGF_PID)) { wrote += write(pseudo_util_debug_fd, pid_text, pid_len); } debugged_newline = (debuff[len - 1] == '\n'); wrote += write(pseudo_util_debug_fd, debuff, len); return wrote; } void pseudo_evlog_dump(void) { char scratch[256], firstdate[64], lastdate[64]; time_t first = 0, last = 0; int len; int entries = 0; struct tm first_tm, last_tm; int wrote; /* ignoring write errors because there's nothing we can do */ for (int i = 0; i < PSEUDO_EVLOG_ENTRIES; ++i) { pseudo_evlog_entry *e = &event_log[i]; if (!e->data || e->len < 0 || e->stamp.tv_sec == 0) continue; ++entries; if (!first || e->stamp.tv_sec < first) first = e->stamp.tv_sec; if (!last || e->stamp.tv_sec > last) last = e->stamp.tv_sec; } localtime_r(&first, &first_tm); localtime_r(&last, &last_tm); strftime(firstdate, 64, "%Y-%M-%D %H:%M:%S", &first_tm); strftime(lastdate, 64, "%Y-%M-%D %H:%M:%S", &last_tm); len = snprintf(scratch, 256, "event log for pid %d [%d entries]\n", getpid(), entries); if (len > 256) len = 256; wrote = write(pseudo_util_evlog_fd, scratch, len); len = snprintf(scratch, 256, " first entry %s\n", firstdate); wrote = write(pseudo_util_evlog_fd, scratch, len); len = snprintf(scratch, 256, " last entry %s\n", lastdate); wrote = write(pseudo_util_evlog_fd, scratch, len); for (int i = 0; i < PSEUDO_EVLOG_ENTRIES; ++i) { int entry = (pseudo_evlog_next_entry + i) % PSEUDO_EVLOG_ENTRIES; pseudo_evlog_entry *ev = &event_log[entry]; if (!ev->data || ev->len <= 0) continue; localtime_r(&ev->stamp.tv_sec, &first_tm); len = strftime(firstdate, 64, "%H:%M:%S", &first_tm); if (len) { len = snprintf(scratch, 256, "%s.%03d: ", firstdate, (int) (ev->stamp.tv_usec / 1000)); wrote = write(pseudo_util_evlog_fd, scratch, len); } else { wrote = write(pseudo_util_evlog_fd, "no timestamp: ", 14); } wrote = write(pseudo_util_evlog_fd, ev->data, ev->len); } (void) wrote; } int pseudo_evlog_internal(char *fmt, ...) { va_list ap; pseudo_evlog_entry *ev = &event_log[pseudo_evlog_next_entry++]; pseudo_evlog_next_entry %= PSEUDO_EVLOG_ENTRIES; if (!ev->data) { pseudo_evlog_buffer = malloc(PSEUDO_EVLOG_ENTRIES * PSEUDO_EVLOG_LENGTH); if (pseudo_evlog_buffer) { for (int i = 0; i < PSEUDO_EVLOG_ENTRIES; ++i) { event_log[i].data = pseudo_evlog_buffer + (PSEUDO_EVLOG_LENGTH * i); } } else { pseudo_diag("fatal: can't allocate event log storage.\n"); } } va_start(ap, fmt); ev->len = vsnprintf(ev->data, PSEUDO_EVLOG_LENGTH, fmt, ap); va_end(ap); if (ev->len > PSEUDO_EVLOG_LENGTH) { strcpy(ev->data + PSEUDO_EVLOG_LENGTH - 5, "...\n"); ev->len = PSEUDO_EVLOG_LENGTH - 1; } gettimeofday(&ev->stamp, NULL); return ev->len; } /* store pid in text form for prepending to messages */ void pseudo_new_pid() { #if PSEUDO_PORT_LINUX extern char *program_invocation_short_name; /* glibcism */ #else char *program_invocation_short_name = "unknown"; #endif int pid = getpid(); pid_len = snprintf(pid_text, 32, "%d: ", pid); pseudo_debug(PDBGF_PID, "new pid: %d [%s]\n", pid, program_invocation_short_name); } /* helper function for pseudo_fix_path * adds "element" to "newpath" at location current, if it can, then * checks whether this now points to a symlink. If it does, expand * the symlink, appending each element in turn the same way. */ static int pseudo_append_element(char *newpath, char *root, size_t allocated, char **pcurrent, const char *element, size_t elen, int leave_this) { static int link_recursion = 0; size_t curlen; char *current; PSEUDO_STATBUF buf; if (!newpath || !pcurrent || !*pcurrent || !root || !element) { pseudo_diag("pseudo_append_element: invalid args.\n"); return -1; } current = *pcurrent; /* sanity-check: ignore // or /./ */ if (elen == 0 || (elen == 1 && *element == '.')) { return 1; } /* backtrack for .. */ if (elen == 2 && element[0] == '.' && element[1] == '.') { /* if newpath's whole contents are '/', do nothing */ if (current <= root + 1) return 1; /* backtrack to the character before the / */ current -= 2; /* now find the previous slash */ while (current > root && *current != '/') { --current; } /* and point to the nul just past it */ *(++current) = '\0'; *pcurrent = current; return 1; } curlen = current - newpath; /* current length, plus / / \0 */ /* => curlen + elen + 3 */ if (curlen + elen + 3 > allocated) { pseudo_diag("pseudo_append_element: path too long (wanted %lu bytes).\n", (unsigned long) curlen + elen + 3); return -1; } memcpy(current, element, elen); current += elen; /* nul-terminate, and we now point to the nul after the slash */ *current = '\0'; /* now, the moment of truth... is that a symlink? */ /* if lstat fails, that's fine -- nonexistent files aren't symlinks */ if (!leave_this) { int is_link; is_link = (pseudo_real_lstat) && (pseudo_real_lstat(newpath, &buf) != -1) && S_ISLNK(buf.st_mode); if (link_recursion >= PSEUDO_MAX_LINK_RECURSION && is_link) { pseudo_diag("link recursion too deep, not expanding path '%s'.\n", newpath); is_link = 0; } if (is_link) { char linkbuf[pseudo_path_max() + 1]; ssize_t linklen; int retval; linklen = readlink(newpath, linkbuf, pseudo_path_max()); if (linklen == -1) { pseudo_diag("uh-oh! '%s' seems to be a symlink, but I can't read it. Ignoring.", newpath); return 0; } /* null-terminate buffer */ linkbuf[linklen] = '\0'; /* absolute symlink means start over! */ if (*linkbuf == '/') { current = newpath + 1; } else { /* point back at the end of the previous path... */ current -= elen; } /* null terminate at the new pointer */ *current = '\0'; /* append all the elements in series */ *pcurrent = current; ++link_recursion; retval = pseudo_append_elements(newpath, root, allocated, pcurrent, linkbuf, linklen, 0); --link_recursion; return retval; } } /* okay, not a symlink, go ahead and append a slash */ *(current++) = '/'; *current = '\0'; *pcurrent = current; return 1; } static int pseudo_append_elements(char *newpath, char *root, size_t allocated, char **current, const char *element, size_t elen, int leave_last) { int retval = 1; const char * start = element; if (!newpath || !root || !current || !*current || !element) { pseudo_diag("pseudo_append_elements: invalid arguments."); return -1; } while (element < (start + elen) && *element) { size_t this_elen; int leave_this = 0; char *next = strchr(element, '/'); if (!next) { next = strchr(element, '\0'); leave_this = leave_last; } this_elen = next - element; switch (this_elen) { case 0: /* path => '/' */ break; case 1: /* path => '?/' */ if (*element != '.') { if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, leave_this) == -1) { retval = -1; } } break; default: if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, leave_this) == -1) { retval = -1; } break; } /* and now move past the separator */ element += this_elen + 1; } return retval; } /* don't do so many allocations */ #define PATHBUFS 16 static char *pathbufs[PATHBUFS] = { 0 }; static int pathbuf = 0; /* Canonicalize path. "base", if present, is an already-canonicalized * path of baselen characters, presumed not to end in a /. path is * the new path to be canonicalized. The tricky part is that path may * contain symlinks, which must be resolved. * if "path" starts with a /, then it is an absolute path, and * we ignore base. */ char * pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t baselen, size_t *lenp, int leave_last) { size_t newpathlen, pathlen; char *newpath; char *current; char *effective_root; int trailing_slash = 0; if (!path) { pseudo_diag("can't fix empty path.\n"); return 0; } newpathlen = pseudo_path_max(); if (!pathbufs[pathbuf]) { pathbufs[pathbuf] = malloc(newpathlen); } newpath = pathbufs[pathbuf]; pathbuf = (pathbuf + 1) % PATHBUFS; pathlen = strlen(path); /* a trailing slash has special meaning */ if (pathlen > 0 && path[pathlen - 1] == '/') { trailing_slash = 1; } /* allow a bit of slush. overallocating a bit won't * hurt. rounding to 256's in the hopes that it makes life * easier for the library. */ if (!newpath) { pseudo_diag("allocation failed seeking memory for path (%s).\n", path); return 0; } newpath[0] = '\0'; current = newpath; if (baselen && (path[0] != '/' || rootlen)) { memcpy(current, base, baselen); current += baselen; } /* "root" is a pointer to the beginning of the *modifiable* * part of the string; you can't back up over it. */ effective_root = newpath + rootlen; *current++ = '/'; *current = '\0'; /* at any given point: * current points to just after the last / of newpath * path points to the next element of path * newpathlen is the total allocated length of newpath * (current - newpath) is the used length of newpath */ if (pseudo_append_elements(newpath, effective_root, newpathlen, ¤t, path, pathlen, leave_last) != -1) { --current; if (*current == '/' && current > effective_root && !trailing_slash) { *current = '\0'; } pseudo_debug(PDBGF_PATH, "%s + %s => <%s>\n", base ? base : "", path ? path : "", newpath ? newpath : ""); if (lenp) { *lenp = current - newpath; } return newpath; } else { return 0; } } /* remove the libpseudo stuff from the environment (leaving other preloads * alone). * There's an implicit memory leak here, but this is called only right * before an exec(), or at most once in a given run. * * we don't try to fix the library path. */ void pseudo_dropenv() { char *ld_preload = GETENV(PRELINK_LIBRARIES); if (ld_preload) { ld_preload = without_libpseudo(ld_preload); if (!ld_preload) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES); } if (ld_preload && strlen(ld_preload)) { SETENV(PRELINK_LIBRARIES, ld_preload, 1); } else { UNSETENV(PRELINK_LIBRARIES); } } } char ** pseudo_dropenvp(char * const *envp) { char **new_envp; int i, j; for (i = 0; envp[i]; ++i) ; new_envp = malloc((i + 1) * sizeof(*new_envp)); if (!new_envp) { pseudo_diag("fatal: can't allocate new environment.\n"); return NULL; } j = 0; for (i = 0; envp[i]; ++i) { if (STARTSWITH(envp[i], PRELINK_LIBRARIES "=")) { char *new_val = without_libpseudo(envp[i]); if (!new_val) { pseudo_diag("fatal: can't allocate new environment variable.\n"); return 0; } else { /* don't keep an empty value; if the whole string is * PRELINK_LIRBARIES=, we just drop it. */ if (strcmp(new_val, PRELINK_LIBRARIES "=")) { new_envp[j++] = new_val; } } } else { new_envp[j++] = envp[i]; } } new_envp[j++] = NULL; return new_envp; } /* add pseudo stuff to the environment. */ void pseudo_setupenv() { size_t i = 0; pseudo_debug(PDBGF_CLIENT, "setting up pseudo environment.\n"); /* Make sure everything has been evaluated */ free(pseudo_get_prefix(NULL)); free(pseudo_get_bindir()); free(pseudo_get_libdir()); free(pseudo_get_localstatedir()); while (pseudo_env[i].key) { if (pseudo_env[i].value) { SETENV(pseudo_env[i].key, pseudo_env[i].value, 0); pseudo_debug(PDBGF_ENV | PDBGF_VERBOSE, "pseudo_env: %s => %s\n", pseudo_env[i].key, pseudo_env[i].value); } i++; } const char *ld_library_path = GETENV(PRELINK_PATH); char *libdir_path = pseudo_libdir_path(NULL); if (!ld_library_path) { size_t len = strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; char *newenv = malloc(len); if (!newenv) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_PATH); } snprintf(newenv, len, "%s:%s64", libdir_path, libdir_path); SETENV(PRELINK_PATH, newenv, 1); } else if (!strstr(ld_library_path, libdir_path)) { size_t len = strlen(ld_library_path) + 1 + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; char *newenv = malloc(len); if (!newenv) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_PATH); } snprintf(newenv, len, "%s:%s:%s64", ld_library_path, libdir_path, libdir_path); SETENV(PRELINK_PATH, newenv, 1); } else { /* nothing to do, ld_library_path exists and contains * our preferred path */ } char *ld_preload = GETENV(PRELINK_LIBRARIES); if (ld_preload) { ld_preload = with_libpseudo(ld_preload, libdir_path); if (!ld_preload) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES); } SETENV(PRELINK_LIBRARIES, ld_preload, 1); free(ld_preload); } else { ld_preload = with_libpseudo("", libdir_path); if (!ld_preload) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES); } SETENV(PRELINK_LIBRARIES, ld_preload, 1); free(ld_preload); } /* we kept libdir path until now because with_libpseudo might * need it */ free(libdir_path); #if PSEUDO_PORT_DARWIN char *force_flat = GETENV("DYLD_FORCE_FLAT_NAMESPACE"); if (!force_flat) { SETENV("DYLD_FORCE_FLAT_NAMESPACE", "1", 1); } #endif } /* add pseudo stuff to the environment. * We can't just use setenv(), because one use case is that we're trying * to modify the environment of a process about to be forked through * execve(). */ char ** pseudo_setupenvp(char * const *envp) { char **new_envp; size_t i, j, k; size_t env_count = 0; size_t size_pseudoenv = 0; char *ld_preload = NULL, *ld_library_path = NULL; pseudo_debug(PDBGF_ENV, "setting up envp environment.\n"); /* Make sure everything has been evaluated */ free(pseudo_get_prefix(NULL)); free(pseudo_get_bindir()); free(pseudo_get_libdir()); free(pseudo_get_localstatedir()); for (i = 0; envp[i]; ++i) { if (STARTSWITH(envp[i], PRELINK_LIBRARIES "=")) { ld_preload = envp[i]; } if (STARTSWITH(envp[i], PRELINK_PATH "=")) { ld_library_path = envp[i]; } ++env_count; } for (i = 0; pseudo_env[i].key; i++) { size_pseudoenv++; } env_count += size_pseudoenv; /* We're going to over allocate */ j = 0; new_envp = malloc((env_count + 1) * sizeof(*new_envp)); if (!new_envp) { pseudo_diag("fatal: can't allocate new environment.\n"); return NULL; } char *libdir_path = pseudo_libdir_path(NULL); if (!ld_library_path) { size_t len = strlen(PRELINK_PATH "=") + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; char *newenv = malloc(len); if (!newenv) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_PATH); } snprintf(newenv, len, PRELINK_PATH "=%s:%s64", libdir_path, libdir_path); new_envp[j++] = newenv; } else if (!strstr(ld_library_path, libdir_path)) { size_t len = strlen(ld_library_path) + 1 + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; char *newenv = malloc(len); if (!newenv) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_PATH); } snprintf(newenv, len, "%s:%s:%s64", ld_library_path, libdir_path, libdir_path); new_envp[j++] = newenv; } else { /* keep old value */ new_envp[j++] = ld_library_path; } if (ld_preload) { ld_preload = with_libpseudo(ld_preload, libdir_path); if (!ld_preload) { pseudo_diag("fatal: can't allocate new %s variable.\n", PRELINK_LIBRARIES); } new_envp[j++] = ld_preload; } else { ld_preload = with_libpseudo("", libdir_path); size_t len = strlen(PRELINK_LIBRARIES "=") + strlen(ld_preload) + 1; char *newenv = malloc(len); snprintf(newenv, len, PRELINK_LIBRARIES "=%s", ld_preload); new_envp[j++] = newenv; free(ld_preload); } free(libdir_path); for (i = 0; envp[i]; ++i) { if (STARTSWITH(envp[i], PRELINK_LIBRARIES "=")) continue; if (STARTSWITH(envp[i], PRELINK_PATH "=")) continue; new_envp[j++] = envp[i]; } for (i = 0; pseudo_env[i].key; i++) { int found = 0; for (k = 0; k < j; k++) { if (!strncmp(pseudo_env[i].key,new_envp[k],strlen(pseudo_env[i].key))) { found = 1; break; } } if (!found && pseudo_env[i].key && pseudo_env[i].value) { size_t len = strlen(pseudo_env[i].key) + 1 + strlen(pseudo_env[i].value) + 1; char *newenv = malloc(len); if (!newenv) { pseudo_diag("fatal: can't allocate new variable.\n"); } snprintf(newenv, len, "%s=%s", pseudo_env[i].key, pseudo_env[i].value); new_envp[j++] = newenv; } } new_envp[j++] = NULL; return new_envp; } /* Append the file value to the prefix value. */ char * pseudo_append_path(const char * prefix, size_t prefix_len, char *file) { char *path; if (!file) { return strdup(prefix); } else { size_t len = prefix_len + strlen(file) + 2; path = malloc(len); if (path) { char *endptr; int rc; rc = snprintf(path, len, "%s", prefix); /* this certainly SHOULD be impossible */ if ((size_t) rc >= len) rc = len - 1; endptr = path + rc; /* strip extra slashes. * This probably has no real effect, but I don't like * seeing "//" in paths. */ while ((endptr > path) && (endptr[-1] == '/')) --endptr; snprintf(endptr, len - (endptr - path), "/%s", file); } return path; } } /* get the full path to a file under $PSEUDO_PREFIX. Other ways of * setting the prefix all set it in the environment. */ char * pseudo_prefix_path(char *file) { char * rc; char * prefix = pseudo_get_prefix(NULL); if (!prefix) { pseudo_diag("You must set the PSEUDO_PREFIX environment variable to run pseudo.\n"); exit(1); } rc = pseudo_append_path(prefix, strlen(prefix), file); free(prefix); return rc; } /* get the full path to a file under $PSEUDO_BINDIR. */ char * pseudo_bindir_path(char *file) { char * rc; char * bindir = pseudo_get_bindir(); if (!bindir) { pseudo_diag("You must set the PSEUDO_BINDIR environment variable to run pseudo.\n"); exit(1); } rc = pseudo_append_path(bindir, strlen(bindir), file); free(bindir); return rc; } /* get the full path to a file under $PSEUDO_LIBDIR. */ char * pseudo_libdir_path(char *file) { char * rc; char * libdir = pseudo_get_libdir(); if (!libdir) { pseudo_diag("You must set the PSEUDO_LIBDIR environment variable to run pseudo.\n"); exit(1); } rc = pseudo_append_path(libdir, strlen(libdir), file); free(libdir); return rc; } /* get the full path to a file under $PSEUDO_LOCALSTATEDIR. */ char * pseudo_localstatedir_path(char *file) { char * rc; char * localstatedir = pseudo_get_localstatedir(); if (!localstatedir) { pseudo_diag("You must set the PSEUDO_LOCALSTATEDIR environment variable to run pseudo.\n"); exit(1); } rc = pseudo_append_path(localstatedir, strlen(localstatedir), file); free(localstatedir); return rc; } char * pseudo_get_prefix(char *pathname) { char *s = pseudo_get_value("PSEUDO_PREFIX"); /* Generate the PSEUDO_PREFIX if necessary, and possible... */ if (!s && pathname) { char mypath[pseudo_path_max()]; char *dir; char *tmp_path; if (pathname[0] == '/') { snprintf(mypath, pseudo_path_max(), "%s", pathname); s = mypath + strlen(mypath); } else { if (!getcwd(mypath, pseudo_path_max())) { mypath[0] = '\0'; } s = mypath + strlen(mypath); s += snprintf(s, pseudo_path_max() - (s - mypath), "/%s", pathname); } tmp_path = pseudo_fix_path(NULL, mypath, 0, 0, 0, AT_SYMLINK_NOFOLLOW); /* point s to the end of the fixed path */ if ((int) strlen(tmp_path) >= pseudo_path_max()) { pseudo_diag("Can't expand path '%s' -- expansion exceeds %d.\n", mypath, (int) pseudo_path_max()); } else { s = mypath + snprintf(mypath, pseudo_path_max(), "%s", tmp_path); } while (s > (mypath + 1) && *s != '/') --s; *s = '\0'; dir = s - 1; while (dir > mypath && *dir != '/') { --dir; } /* strip bin directory, if any */ if (!strncmp(dir, "/bin", 4)) { *dir = '\0'; } /* degenerate case: /bin/pseudo should yield a pseudo_prefix "/" */ if (*mypath == '\0') { strcpy(mypath, "/"); } pseudo_diag("Warning: PSEUDO_PREFIX unset, defaulting to %s.\n", mypath); pseudo_set_value("PSEUDO_PREFIX", mypath); s = pseudo_get_value("PSEUDO_PREFIX"); } return s; } char * pseudo_get_bindir(void) { char *s = pseudo_get_value("PSEUDO_BINDIR"); if (!s) { char *pseudo_bindir = pseudo_prefix_path(PSEUDO_BINDIR); if (pseudo_bindir) { pseudo_set_value("PSEUDO_BINDIR", pseudo_bindir); s = pseudo_bindir; } } return s; } char * pseudo_get_libdir(void) { char *s = pseudo_get_value("PSEUDO_LIBDIR"); if (!s) { char *pseudo_libdir; pseudo_libdir = pseudo_prefix_path(PSEUDO_LIBDIR); if (pseudo_libdir) { pseudo_set_value("PSEUDO_LIBDIR", pseudo_libdir); s = pseudo_libdir; } } #if PSEUDO_PORT_DARWIN /* on Darwin, we need lib64, because dyld won't search */ #else /* If we somehow got lib64 in there, clean it down to just lib... */ if (s) { size_t len = strlen(s); if (s[len-2] == '6' && s[len-1] == '4') { s[len-2] = '\0'; pseudo_set_value("PSEUDO_LIBDIR", s); } } #endif return s; } char * pseudo_get_localstatedir() { char *s = pseudo_get_value("PSEUDO_LOCALSTATEDIR"); if (!s) { char *pseudo_localstatedir = pseudo_prefix_path(PSEUDO_LOCALSTATEDIR); if (pseudo_localstatedir) { pseudo_set_value("PSEUDO_LOCALSTATEDIR", pseudo_localstatedir); s = pseudo_localstatedir; } } return s; } /* these functions define the sizes pseudo will try to use * when trying to allocate space, or guess how much space * other people will have allocated; see the GNU man page * for realpath(3) for an explanation of why the sys_path_max * functions exists, approximately -- it's there to be a size * that I'm pretty sure the user will have allocated if they * provided a buffer to that defective function. */ /* I'm pretty sure this will be larger than real PATH_MAX */ #define REALLY_BIG_PATH 16384 /* A likely common value for PATH_MAX */ #define SORTA_BIG_PATH 4096 ssize_t pseudo_path_max(void) { if (pseudo_max_pathlen == -1) { long l = pathconf("/", _PC_PATH_MAX); if (l < 0) { if (_POSIX_PATH_MAX > 0) { pseudo_max_pathlen = _POSIX_PATH_MAX; } else { pseudo_max_pathlen = REALLY_BIG_PATH; } } else { if (l <= REALLY_BIG_PATH) { pseudo_max_pathlen = l; } else { pseudo_max_pathlen = REALLY_BIG_PATH; } } } return pseudo_max_pathlen; } ssize_t pseudo_sys_path_max(void) { if (pseudo_sys_max_pathlen == -1) { long l = pathconf("/", _PC_PATH_MAX); if (l < 0) { if (_POSIX_PATH_MAX > 0) { pseudo_sys_max_pathlen = _POSIX_PATH_MAX; } else { pseudo_sys_max_pathlen = SORTA_BIG_PATH; } } else { if (l <= SORTA_BIG_PATH) { pseudo_sys_max_pathlen = l; } else { pseudo_sys_max_pathlen = SORTA_BIG_PATH; } } } return pseudo_sys_max_pathlen; } /* complicated because in theory you can have modes like * 'ab+' * which is the same as 'a+' in POSIX. The first letter really does have * to be one of r, w, a, though. */ int pseudo_access_fopen(const char *mode) { int access = 0; for (; *mode; ++mode) { switch (*mode) { case 'a': access |= (PSA_APPEND | PSA_WRITE); break; case 'r': access |= PSA_READ; break; case 'w': access |= PSA_WRITE; break; case 'x': /* special case -- note that this conflicts with a * rarely-used glibc extension */ access |= PSA_EXEC; break; case 'b': /* binary mode */ break; case 'c': case 'e': case 'm': /* glibc extensions */ break; case '+': /* one of these will already be set, presumably */ access |= (PSA_READ | PSA_WRITE); break; default: access = -1; break; } } return access; } /* find a passwd/group file to use * uses in order: * - PSEUDO_CHROOT/etc/ (only if CHROOT is set) * - PSEUDO_PASSWD/etc/ * - /etc/ */ #if PSEUDO_PORT_DARWIN /* on Darwin, you can't just use /etc/passwd for system lookups, * you have to use the real library calls because they know about * Directory Services. So... * * We make up fake fds and FILE * objects that can't possibly be * valid. */ int pseudo_host_etc_passwd_fd = -3; int pseudo_host_etc_group_fd = -4; static FILE pseudo_fake_passwd_file; static FILE pseudo_fake_group_file; FILE *pseudo_host_etc_passwd_file = &pseudo_fake_passwd_file; FILE *pseudo_host_etc_group_file = &pseudo_fake_group_file; #endif int pseudo_etc_file(const char *file, char *realname, int flags, const char **search_dirs, int dircount) { char filename[pseudo_path_max()]; int rc = -1; if (!file) { pseudo_debug(PDBGF_CHROOT, "pseudo_etc_file: needs argument, usually passwd/group\n"); errno = ENOENT; return -1; } int i; if (!search_dirs || dircount == 0) { pseudo_debug(PDBGF_CHROOT, "pseudo_etc_file: no search dirs.\n"); errno = ENOENT; return -1; } for (i = 0; i < dircount; ++i) { const char *s = search_dirs[i]; /* we used to pass in some paths as NULL when unset, * so we skipped those. Now NULL entries don't get * put in, so the only NULL should be the sentinel * value, and this should never get hit. * * "should" is not comforting to me. */ if (!s) break; #if PSEUDO_PORT_DARWIN /* special magic: empty string implies our emulation * of the passwd/group files. */ if (!*s) { if (!strcmp("passwd", file)) { pseudo_debug(PDBGF_CHROOT, "Darwin hackery: pseudo_etc_passwd returning magic passwd fd\n"); return pseudo_host_etc_passwd_fd; } else if (!strcmp("group", file)) { pseudo_debug(PDBGF_CHROOT, "Darwin hackery: pseudo_etc_passwd returning magic group fd\n"); return pseudo_host_etc_group_fd; } } #endif snprintf(filename, pseudo_path_max(), "%s/etc/%s", s, file); rc = open(filename, flags, 0600); if (rc >= 0) { if (realname) strcpy(realname, filename); pseudo_debug(PDBGF_CHROOT, "pseudo_etc_file: using '%s' for '%s'.\n", filename, file); return rc; } else { pseudo_debug(PDBGF_CHROOT | PDBGF_VERBOSE, "didn't find <%s>\n", filename); } } return rc; } /* set up a log file */ static int pseudo_logfile(char *filename, char *defname, int prefer_fd) { char *pseudo_path; char *s; #if PSEUDO_PORT_LINUX extern char *program_invocation_short_name; /* glibcism */ #else char *program_invocation_short_name = "unknown"; #endif int fd; if (!filename) { if (!defname) { pseudo_debug(PDBGF_INVOKE, "no special log file requested, using stderr.\n"); return -1; } pseudo_path = pseudo_localstatedir_path(defname); if (!pseudo_path) { pseudo_diag("can't get path for prefix/%s\n", PSEUDO_LOGFILE); return -1; } } else { char *pid = NULL, *prog = NULL; size_t len; for (s = filename; *s; ++s) { if (s[0] == '%') { switch (s[1]) { case '%': /* skip the %% */ ++s; break; case 'd': if (pid) { pseudo_diag("found second %%d in PSEUDO_DEBUG_FILE, ignoring.\n"); return -1; } else { pid = s; } break; case 's': if (prog) { pseudo_diag("found second %%s in PSEUDO_DEBUG_FILE, ignoring.\n"); return -1; } else { prog = s; } break; default: if (isprint(s[1])) { pseudo_diag("found unknown format character '%c' in PSEUDO_DEBUG_FILE, ignoring.\n", s[1]); } else { pseudo_diag("found unknown format character '\\x%02x' in PSEUDO_DEBUG_FILE, ignoring.\n", (unsigned char) s[1]); } return -1; break; } } } len = strlen(filename) + 1; if (pid) len += 8; if (prog) len += strlen(program_invocation_short_name); pseudo_path = malloc(len); if (!pseudo_path) { pseudo_diag("can't allocate space for debug file name.\n"); return -1; } if (pid && prog) { if (pid < prog) { snprintf(pseudo_path, len, filename, getpid(), program_invocation_short_name); } else { snprintf(pseudo_path, len, filename, program_invocation_short_name, getpid()); } } else if (pid) { snprintf(pseudo_path, len, filename, getpid()); } else if (prog) { snprintf(pseudo_path, len, filename, program_invocation_short_name); } else { strcpy(pseudo_path, filename); } free(filename); } fd = open(pseudo_path, O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd == -1) { pseudo_diag("help: can't open log file %s: %s\n", pseudo_path, strerror(errno)); } else { /* try to force fd to prefer_fd. We do this because glibc's malloc * debug unconditionally writes to fd 2, and we don't want * a client process ending op on fd 2, or server debugging * becomes a nightmare. So, server sets prefer_fd to 2. Client * leaves it at -1. */ if (prefer_fd >= 0 && fd != prefer_fd) { int newfd; close(prefer_fd); newfd = dup2(fd, prefer_fd); if (newfd != -1) { fd = newfd; } } pseudo_util_debug_fd = fd; } free(pseudo_path); if (fd == -1) return -1; else return fd; } int pseudo_debug_logfile(char *defname, int prefer_fd) { char *filename = pseudo_get_value("PSEUDO_DEBUG_FILE"); int fd; fd = pseudo_logfile(filename, defname, prefer_fd); if (fd > -1) { pseudo_diag("debug_logfile: fd %d\n", fd); pseudo_util_debug_fd = fd; return 0; } return 1; } int pseudo_evlog_logfile(char *defname, int prefer_fd) { char *filename = pseudo_get_value("PSEUDO_EVLOG_FILE"); int fd; fd = pseudo_logfile(filename, defname, prefer_fd); if (fd > -1) { pseudo_util_evlog_fd = fd; return 0; } return 1; } void pseudo_stat32_from64(struct stat *buf32, const struct stat64 *buf) { buf32->st_dev = buf->st_dev; buf32->st_ino = buf->st_ino; buf32->st_mode = buf->st_mode; buf32->st_nlink = buf->st_nlink; buf32->st_uid = buf->st_uid; buf32->st_gid = buf->st_gid; buf32->st_rdev = buf->st_rdev; buf32->st_size = buf->st_size; buf32->st_blksize = buf->st_blksize; buf32->st_blocks = buf->st_blocks; buf32->st_atime = buf->st_atime; buf32->st_mtime = buf->st_mtime; buf32->st_ctime = buf->st_ctime; } void pseudo_stat64_from32(struct stat64 *buf64, const struct stat *buf) { buf64->st_dev = buf->st_dev; buf64->st_ino = buf->st_ino; buf64->st_mode = buf->st_mode; buf64->st_nlink = buf->st_nlink; buf64->st_uid = buf->st_uid; buf64->st_gid = buf->st_gid; buf64->st_rdev = buf->st_rdev; buf64->st_size = buf->st_size; buf64->st_blksize = buf->st_blksize; buf64->st_blocks = buf->st_blocks; buf64->st_atime = buf->st_atime; buf64->st_mtime = buf->st_mtime; buf64->st_ctime = buf->st_ctime; } /* pretty-dump some data. * expects to be called using pseudo_debug_call() so it doesn't have * to do debug checks. */ void pseudo_dump_data(char *name, const void *v, size_t len) { char hexbuf[128]; char asciibuf[32]; const unsigned char *base = v; const unsigned char *data = base; int remaining = len; pseudo_diag("%s at %p [%d byte%s]:\n", name ? name : "data", v, (int) len, len == 1 ? "" : "s"); while (remaining > 0) { char *hexptr = hexbuf; char *asciiptr = asciibuf; for (int i = 0; i < 16 && i < remaining; ++i) { hexptr += snprintf(hexptr, 4, "%02x ", data[i]); if (isprint(data[i])) { *asciiptr++ = data[i]; } else { *asciiptr++ = '.'; } if (i % 4 == 3) { *hexptr++ = ' '; } } *hexptr = '\0'; *asciiptr = '\0'; pseudo_diag("0x%06x %-50.50s '%.16s'\n", (int) (data - base), hexbuf, asciibuf); remaining = remaining - 16; data = data + 16; } } pseudo-1.8.1+git20161012/pseudo_wrappers.c000066400000000000000000000172771300506512600200160ustar00rootroot00000000000000/* * pseudo_wrappers.c, shared code for wrapper functions * * Copyright (c) 2008-2012 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* used for various specific function arguments */ #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_wrapfuncs.h" #include "pseudo_ipc.h" #include "pseudo_client.h" /* Types and declarations we need in advance. */ #include "pseudo_wrapper_table.c" static void pseudo_enosys(const char *); static int pseudo_check_wrappers(void); static volatile int antimagic = 0; static pthread_mutex_t pseudo_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t pseudo_mutex_holder; static int pseudo_mutex_recursion = 0; static int pseudo_getlock(void); static void pseudo_droplock(void); static size_t pseudo_dechroot(char *, size_t); static void pseudo_sigblock(sigset_t *); extern char *program_invocation_short_name; static sigset_t pseudo_saved_sigmask; /* Constructor only exists in libpseudo */ static void _libpseudo_init(void) __attribute__ ((constructor)); static int _libpseudo_initted = 0; #ifdef PSEUDO_PROFILING extern struct timeval *pseudo_wrapper_time; /* profiling shared postamble */ #define PROFILE_START \ struct timeval tv1, tv2; \ do { gettimeofday(&tv1, NULL); } while(0) #define PROFILE_DONE do { \ gettimeofday(&tv2, NULL); \ pseudo_wrapper_time->tv_sec += tv2.tv_sec - tv1.tv_sec; \ pseudo_wrapper_time->tv_usec += tv2.tv_usec - tv1.tv_usec; } while(0) #else #define PROFILE_START do {} while(0) #define PROFILE_DONE do {} while(0) #endif /* later, the init code can change these to refer to the real calls and * skip the wrappers. */ #ifdef PSEUDO_XATTRDB extern ssize_t (*pseudo_real_lgetxattr)(const char *, const char *, void *, size_t); extern ssize_t (*pseudo_real_fgetxattr)(int, const char *, void *, size_t); extern int (*pseudo_real_lsetxattr)(const char *, const char *, const void *, size_t, int); extern int (*pseudo_real_fsetxattr)(int, const char *, const void *, size_t, int); #endif static void _libpseudo_init(void) { pseudo_getlock(); pseudo_antimagic(); _libpseudo_initted = 1; pseudo_init_util(); pseudo_init_wrappers(); pseudo_init_client(); pseudo_magic(); pseudo_droplock(); } void pseudo_reinit_libpseudo(void) { _libpseudo_init(); } static void pseudo_init_one_wrapper(pseudo_function *func) { int (*f)(void) = (int (*)(void)) NULL; char *e; if (*func->real != NULL) { /* already initialized */ return; } dlerror(); #if PSEUDO_PORT_LINUX if (func->version) f = dlvsym(RTLD_NEXT, func->name, func->version); /* fall through to the general case, if that failed */ if (!f) #endif f = dlsym(RTLD_NEXT, func->name); if (f) { *func->real = f; } else { #ifdef PSEUDO_NO_REAL_AT_FUNCTIONS char *s = func->name; s += strlen(s) - 2; /* *at() don't have to exist */ if (!strcmp(s, "at")) { return; } #endif e = dlerror(); if (e != NULL) { pseudo_diag("No real function for %s: %s\n", func->name, e); } else { pseudo_diag("No real function for %s, but dlerror NULL.\n", func->name); } } } void pseudo_init_wrappers(void) { int i; static int done = 0; pseudo_getlock(); pseudo_antimagic(); /* We only ever want to run this once, even though we might want to * "re-init" at specific times... */ if (!done) { for (i = 0; pseudo_functions[i].name; ++i) { pseudo_init_one_wrapper(&pseudo_functions[i]); } done = 1; } #ifdef PSEUDO_XATTRDB pseudo_real_lgetxattr = real_lgetxattr; pseudo_real_fgetxattr = real_fgetxattr; pseudo_real_lsetxattr = real_lsetxattr; pseudo_real_fsetxattr = real_fsetxattr; #endif pseudo_real_lstat = base_lstat; /* bash has its own local copies of these which it uses * instead of ours... */ pseudo_real_unsetenv = dlsym(RTLD_NEXT, "unsetenv"); pseudo_real_getenv = dlsym(RTLD_NEXT, "getenv"); pseudo_real_setenv = dlsym(RTLD_NEXT, "setenv"); /* and these are used so the client's server spawn can bypass * wrappers. */ pseudo_real_fork = dlsym(RTLD_NEXT, "fork"); pseudo_real_execv = dlsym(RTLD_NEXT, "execv"); /* Once the wrappers are setup, we can now use open... so * setup the logfile, if necessary... */ pseudo_debug_logfile(NULL, -1); pseudo_magic(); pseudo_droplock(); } static void pseudo_sigblock(sigset_t *saved) { sigset_t blocked; /* these are signals for which the handlers often * invoke operations, such as close(), which are handled * by pseudo and could result in a deadlock. */ sigemptyset(&blocked); sigaddset(&blocked, SIGALRM); /* every-N-seconds tasks */ sigaddset(&blocked, SIGCHLD); /* reaping child processes */ sigaddset(&blocked, SIGHUP); /* idiomatically, reloading config */ sigaddset(&blocked, SIGTERM); /* shutdown/teardown operations */ sigaddset(&blocked, SIGUSR1); /* reopening log files, sometimes */ sigaddset(&blocked, SIGUSR2); /* who knows what people do */ sigprocmask(SIG_BLOCK, &blocked, saved); } static int pseudo_getlock(void) { if (pthread_equal(pseudo_mutex_holder, pthread_self())) { ++pseudo_mutex_recursion; return 0; } else { if (pthread_mutex_lock(&pseudo_mutex) == 0) { pseudo_mutex_recursion = 1; pseudo_mutex_holder = pthread_self(); return 0; } else { return -1; } } } static void pseudo_droplock(void) { if (--pseudo_mutex_recursion == 0) { pseudo_mutex_holder = 0; pthread_mutex_unlock(&pseudo_mutex); } } void pseudo_antimagic() { ++antimagic; } void pseudo_magic() { if (antimagic > 0) --antimagic; } static void pseudo_enosys(const char *func) { pseudo_diag("pseudo: ENOSYS for '%s'.\n", func ? func : ""); char * value = pseudo_get_value("PSEUDO_ENOSYS_ABORT"); if (value) abort(); free(value); errno = ENOSYS; } /* de-chroot a string. * note that readlink() yields an unterminated buffer, so * must pass in the correct length. Buffers are null-terminated * unconditionally if they are modified -- the modification would * shorten the string, so there will be space for the NUL, so * this is safe even for stuff like readlink(). */ static size_t pseudo_dechroot(char *s, size_t len) { if (len == (size_t) -1) len = strlen(s); if (pseudo_chroot_len && len >= pseudo_chroot_len && !memcmp(s, pseudo_chroot, pseudo_chroot_len)) { if (s[pseudo_chroot_len] == '/') { memmove(s, s + pseudo_chroot_len, len - pseudo_chroot_len); len -= pseudo_chroot_len; s[len] = '\0'; } else if (s[pseudo_chroot_len] == '\0') { s[0] = '/'; len = 1; s[len] = '\0'; } /* otherwise, it's not really a match... */ } return len; } static int pseudo_check_wrappers(void) { if (!_libpseudo_initted) pseudo_reinit_libpseudo(); return _libpseudo_initted; } /* the generated code goes here */ #include "port_wrappers.c" #include "pseudo_wrapfuncs.c" pseudo-1.8.1+git20161012/pseudodb.c000066400000000000000000000025101300506512600163610ustar00rootroot00000000000000/* * pseudodb.c, (unimplemented) database maintenance utility. * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_db.h" int main(int argc, char **argv) { pseudo_msg_t *msg; int rc; if (argc < 2) { fprintf(stderr, "Usage: pseudodb \n"); exit(1); } msg = pseudo_msg_new(0, argv[1]); rc = pdb_find_file_path(msg); if (rc) { printf("error.\n"); return 1; } else { printf("%s: %o %x\n", msg->path, (int) msg->mode, (int) msg->rdev); return 0; } } pseudo-1.8.1+git20161012/pseudolog.1000066400000000000000000000245261300506512600165060ustar00rootroot00000000000000.\" .\" pseudolog(1) man page .\" .\" Copyright (c) 2010 Wind River Systems, Inc. .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the Lesser GNU General Public License version 2.1 as .\" published by the Free Software Foundation. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .\" See the Lesser GNU General Public License for more details. .\" .\" You should have received a copy of the Lesser GNU General Public License .\" version 2.1 along with this program; if not, write to the Free Software .\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA .TH pseudolog 1 "pseudo - pretending to be root" .SH SYNOPSIS .B pseudolog \-l .RB [ \-Pv ] [ .B \-E .I timeformat ] [ .B \-x .I flags ] .RI [ SPECIFICATIONS ] .PP .B pseudolog .RB [ \-UPv ] [ .B \-E .I timeformat ] [ .B \-F .I format ] [ .B \-x .I flags ] .PP .B pseudolog \-h .PP .B pseudolog \-D .RB [ \-Pv ] [ .B \-E .I timeformat ] [ .B \-x .I flags ] .RI [ SPECIFICATIONS ] .RI [ SPECIFICATIONS ] .SH DESCRIPTION The .I pseudolog utility displays, creates, or deletes log entries associated with the .I pseudo daemon. Creation of log entries is useful only to create timestamps or notes; for instance, you could create a log entry before beginning a process, so there would be a timestamp for the beginning of that process. There are a number of special options used to match or create the components of a log entry; these are called .IR specifications , and are detailed in the .B SPECIFICATIONS section below. The following other options are supported: .TP 8 .B \-h Print a usage message and exit. .TP 8 .B \-D Delete rows selected by the query. This is not reversible. .TP 8 .BI \-E \ timeformat Specify a format string (for .I strptime(3) or .I strftime(3) to use) for displaying or interpreting time stamps. The same format is used both for parsing and displaying stamps. .TP 8 .BI \-F \ format Specifies a format string for displaying log entries. This format cannot be used to create log entries, only for display. The format string is a .I printf(3) type format string, with format specifiers matching the option characters used in specifications (see .BR SPECIFICATIONS ). There are some limitations on allowed formats, and misuse of this feature could cause interesting or surprising failures. .TP 8 .B \-l Create a log entry. This option is mutually exclusive with the .B \-F option, or with any relative specifications (see below). .TP 8 .BI \-P \ path Specify that .I path should be used as the .B PSEUDO_PREFIX value, overriding any environment setting. .TP 8 .B \-U Restrict query output to unique rows. Rows will have members defined by the .B \-F (format) option. If all members are the same between two rows, only one is displayed. Applies only to queries. .TP 8 .B \-v Increase verbosity (debug level). Not useful except when debugging pseudo. Deprecated; use .BR \-x . .TP 8 .BI \-x flags Specify debugging flags of interest. Not useful except when debugging pseudo. Other option characters are defined as specifications, and all of those require arguments to specify their values. .SH SPECIFICATIONS The various components of a log entry can be specified, either as command-line options, or as format specifiers. In either case, the same character is used for a given component of a log entry. When querying values, one of the following prefixes may be prepended to a value; otherwise, the value is used for a literal match (an SQL .B = operator). .TP 8 .B > Greater than; true if the related field is greater than the provided value. .TP 8 .B < Less than; true if the related field is less than the provided value. .TP 8 .B & Bitwise and; true if the related field, bitwise-and the provided value, is non-zero. (This is useful primarily for permissions or modes.) .TP 8 .B = Equal to. (This is a no-op, as of this writing.) .TP 8 .B ! Not equal to. .TP 8 .B % Similar to .BR ~ . This is valid only on text fields, and is equivalent to the SQL .B LIKE operator, with .B % patterns on the ends; it performs an unanchored, case-insensitive match. .TP 8 .B ~ Similar to .BR % . This is valid only on text fields, and is equivalent to the SQL .B LIKE operator, but performs an anchored match. The match is case-insensitive. The specifier .B ~%foo% is equivalent to the specifier .BR %foo . .TP 8 .B ^ Unlike. This is the inverse of ~; it specifies .BR NOT\ LIKE . .TP 8 .B \\ Escape the string. This is useful if you want to have one of the other modifiers at the beginning of the string. .PP Only .BR = and \\ modifiers may be used in conjunction with the .B \-l option. The following characters correspond to specific fields in a log entry. In general, numeric values are parsed in the standard C idiom (where a leading .B 0 indicates an octal value, and a leading .B 0x indicates a hexadecimal value, and any other number is decimal). A few fields are parsed or displayed in other ways, as detailed in their entries. .TP 8 .B a Access mode. This is an access mode specified in the form used by .IR fopen(3) , such as "r+" to indicate read/write access. Note that specifying .B \&a as an access mode will include non-append writes, as the "a" mode implies write and append both. This feature is slightly experimental and may not correctly identify the access type of every access. The string .B x may be specified to indicate execute access. .TP 8 .B c Client ID (the PID of a client). .TP 8 .B d Device number (from a stat buffer). .TP 8 .B f File descriptor. In some cases, messages have an associated file descriptor identified. .TP 8 .B g GID. The group ID associated with an entry. .TP 8 .B G Tag. This is a text field. In log entries created by .IR pseudo , this field holds the value that the environment variable .B PSEUDO_TAG had in the client's environment. .TP 8 .B i Inode number (from a stat buffer). .TP 8 .TP 8 .B I ID. This is the database row number. Normally these are assigned as monotonically increasing values as rows are inserted, making them a more reliable sorting mechanism than timestamps. The default ordering is by ID. .B m Permissions. These can be entered as an octal value or as a symbolic mode string, similar to the output of .I ls(1) .BR -l. The file type component is ignored. .TP 8 .B M Mode. This can be entered as an octal value or as a symbolic mode string, similar to the output of .I ls(1) .BR -l. This is tested against the whole file mode, including both the type and permissions bits. In general, it is more useful to use the .B m or .B t specifiers. .TP 8 .B o Operation. This is the name of the file system operation (e.g., "open" or "rename"). .TP 8 .B O Order. This takes another specification character as the field on which to order results. A '<' implies a descending order sort, a '>' or no modifier specifies an ascending order sort. By default, records are sorted by ID. .TP 8 .B p File path. This is a text field. .TP 8 .B r Result. This is the .I pseudo result code, most often "fail" or "succeed". Note that "fail" doesn't mean that an underlying operation failed; for instance, if a "stat" operation fails, it usually means that there was no entry in the .I pseudo database. .TP 8 .B R Program. This is the program name (as retrieved by glibc's .I program_invocation_name variable), which has the full path if and only if the program was invoked by full path name. .TP 8 .B s Timestamp. The format of this field is controlled by the .B \-E format string, which is used with .I strftime(3) when displaying entries, or with .I strptime(3) when interpreting command line values. There is a small selection of common default time formats understood by the parser. Time fields not specified default to the current time. Note that specifying a time stamp when creating a log entry may yield confusing results. .TP 8 .B S Severity. Log messages can have a severity, with the default for file operations being "info". .B t File type. This corresponds to the first letter of a mode string, or the values accepted by the .B \-type option to .IR find(1) . This is compared only against the file type bits of a mode. .TP 8 .B T Text. This is an optional field available for user use when creating log entries, or to hold the text of an error message when an error is logged. It is, of course, a text field. .TP 8 .B u UID. The user ID associated with an entry. .TP 8 .B y Type. This is usually "op" for operations, or "ping" for the ping messages clients send to confirm server availability. Other types should rarely occur, but include "ack" and "nak" for server responses (which are never logged), and "halt" for shutdown messages (currently not logged). .SH EXAMPLES The following examples illustrate some of the likely usage patterns for .IR pseudolog . .TP 8 .B pseudolog -m '&020' -t d Report on all directories which are group-writeable. .TP 8 .B pseudolog -m 755 -t f Report on all plain files which have the mode rwxr-xr-x. .TP 8 .B pseudolog -s '>03:19:00' -s '<03:20:00' Report on all entries created after 03:19:00 and before 03:20:00 on the current date. .TP 8 .B pseudolog -p '~/usr/bin/%' -F '%-8o %p' Report on every entry with a path beginning with the string '/usr/bin', displaying the operation name (in a space-padded field of eight characters, left-adjusted) followed by the path. .TP 8 .B pseudolog -l -T 'stamp test' Create an entry with all fields zero or blank, except for the text field, which is set to the text "stamp test", and the timestamp, which is set to the current time. .TP 8 .B pseudolog -D -r succeed -F '%p' -O p Display all paths for which operations succeeded, sorted by path value. .SH ENVIRONMENT The only environment variable supported by .I pseudolog is: .TP 8 .B PSEUDO_PREFIX If set, the variable .B PSEUDO_PREFIX is used to determine the path to use to find the .I logs.db database file, in .BR PSEUDO_PREFIX /var/pseudo. .SH BUGS The user might think our intent is to replace all of SQL. It's not. If the options here aren't enough, rather than adding more options to this already fairly elaborate program, just do raw SQL queries on the .I logs.db file. The formatting options are handled by converting them into .I printf(3) format strings, without much checking. As a result, it is possible for a malformed format string to cause .I printf() to explode unexpectedly. .SH SEE ALSO pseudo(1), sqlite3(1) pseudo-1.8.1+git20161012/pseudolog.c000066400000000000000000000526601300506512600165700ustar00rootroot00000000000000/* * pseudolog.c, pseudo database viewer (preliminary) * * Copyright (c) 2008-2010 Wind River Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License version 2.1 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * version 2.1 along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* We need _XOPEN_SOURCE for strptime(), but if we define that, * we then don't get S_IFSOCK... _GNU_SOURCE turns on everything. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "pseudo.h" #include "pseudo_ipc.h" #include "pseudo_client.h" #include "pseudo_db.h" static int opt_D = 0; static int opt_U = 0; static int opt_l = 0; /* when the client is linked with pseudo_wrappers, these are defined there. * when it is linked with pseudo_server, though, we have to provide different * versions (pseudo_wrappers must not be linked with the server, or Bad Things * happen). */ void pseudo_magic(void) { } void pseudo_antimagic(void) { } static void display(log_entry *, char *format); static int format_scan(char *format); void usage(int status) { static char *options[] = { "a access (rwax)", "c client pid", "d device number", "f file descriptor", "g gid", "G tag (text)", "i inode number", "I id (database row)", "m permission bits (octal)", "M file mode (octal)", "o operation (e.g. 'open')", "O order by (< DESC > ASC)", "p file path", "P program", "r result (e.g. 'succeed')", "s timestamp", "S severity", "t type (like find -type)", "T text (text field)", "u uid", "y type (op/ping/shutown)", NULL, }; FILE *f = (status == EXIT_SUCCESS) ? stdout : stderr; int i; fputs("pseudolog: create or report log entries. usage:\n", f); fputs("pseudolog -l [SPECIFIERS] -- create entries\n", f); fputs("pseudolog [-U] [-F format] [SPECIFIERS] -- report entries\n", f); fputs("pseudolog -D [SPECIFIERS] -- delete entries\n", f); fputs("shared options: [-P prefix] [-E timeformat] [-x flags]\n", f); fputs(" format is a printf-like format string using the option letters\n", f); fputs(" listed below as format specifiers for the corresponding field.\n", f); fputs(" timeformat is a strftime-like format string, the default is '%x %X'.\n", f); fputs(" prefix is the PSEUDO_PREFIX in which to find the database.\n", f); fputs(" flags are characters from the same debug flag set used by pseudo.\n", f); fputs("\n", f); fputs("SPECIFIERS are options of the form -X , where X is one of\n", f); fputs("the following option letters, and value is the value to match.\n", f); fputs("values may be prefixed with ! (not equal to), > (greater than),\n", f); fputs("< (less than), & (bitwise and), ~ (LIKE match, anchored at both\n", f); fputs("ends, text fields only), ^ (NOT LIKE match, anchored at both\n", f); fputs("ends, or % (LIKE match, text fields only).\n", f); fputs("\n", f); fputs("OPTION LETTERS:\n", f); for (i = 0; options[i]; ++i) { fprintf(f, " %-28s%s", options[i], (i % 2) ? "\n" : " "); } if (i % 2 == 1) { fprintf(f, "\n"); } exit(status); } pseudo_query_field_t opt_to_field[UCHAR_MAX + 1] = { ['a'] = PSQF_ACCESS, ['c'] = PSQF_CLIENT, ['d'] = PSQF_DEV, ['f'] = PSQF_FD, ['g'] = PSQF_GID, ['G'] = PSQF_TAG, ['I'] = PSQF_ID, ['i'] = PSQF_INODE, ['m'] = PSQF_PERM, ['M'] = PSQF_MODE, ['o'] = PSQF_OP, ['O'] = PSQF_ORDER, ['p'] = PSQF_PATH, ['r'] = PSQF_RESULT, ['R'] = PSQF_PROGRAM, ['s'] = PSQF_STAMP, ['S'] = PSQF_SEVERITY, ['t'] = PSQF_FTYPE, ['T'] = PSQF_TEXT, ['u'] = PSQF_UID, ['y'] = PSQF_TYPE, }; pseudo_query_type_t plog_query_type(char **string) { pseudo_query_type_t type = PSQT_EXACT; if (!string || !*string) return PSQT_UNKNOWN; switch (**string) { case '\0': pseudo_diag("Error: Value may not be an empty string."); return PSQT_UNKNOWN; break; case '>': type = PSQT_GREATER; ++*string; break; case '<': type = PSQT_LESS; ++*string; break; case '!': type = PSQT_NOTEQUAL; ++*string; break; case '=': ++*string; break; case '&': type = PSQT_BITAND; ++*string; break; case '%': type = PSQT_LIKE; ++*string; break; case '^': type = PSQT_NOTLIKE; ++*string; break; case '~': type = PSQT_SQLPAT; ++*string; break; case '\\': /* no special type, but allows one of the others to be the * first character of the effective string */ ++*string; break; } if (opt_l && type != PSQT_EXACT) { pseudo_diag("Error: Non-exact match requested while trying to create a log entry.\n"); type = PSQT_UNKNOWN; } return type; } static char *time_formats[] = { "%s", "%F %r", "%F %T", "%m-%d %r", "%m-%d %T", "%r", "%T", NULL, }; static char *timeformat = "%X"; mode_t parse_file_type(char *string) { switch (*string) { case 'b': return S_IFBLK; break; case 'c': return S_IFCHR; break; case 'd': return S_IFDIR; break; case '-': /* FALLTHROUGH */ case 'f': return S_IFREG; break; case 'l': return S_IFLNK; break; case 'p': return S_IFIFO; break; case 's': return S_IFSOCK; break; default: pseudo_diag("unknown file type %c; should be one of [-bcdflps]\n", isprint(*string) ? *string : '?'); return -1; break; } } mode_t parse_partial_mode(char *string) { mode_t mode = 0; switch (string[0]) { case 'r': mode |= 04; break; case '-': break; default: pseudo_diag("unknown mode character: %c\n", string[0]); return -1; break; } switch (string[1]) { case 'w': mode |= 02; break; case '-': break; default: pseudo_diag("unknown mode character: %c\n", string[1]); return -1; break; } switch (string[2]) { case 'x': mode |= 01; break; case 't': /* FALLTHROUGH */ case 's': mode |= 011; break; case 'T': /* FALLTHROUGH */ case 'S': mode |= 010; break; case '-': break; default: pseudo_diag("unknown mode character: %c\n", string[2]); return -1; break; } return mode; } mode_t parse_mode_string(char *string) { size_t len = strlen(string); mode_t mode = 0; mode_t bits = 0; if (len != 9 && len != 10) { pseudo_diag("mode strings must be of the form [-]rwxr-xr-x\n"); return -1; } if (len == 10) { mode |= parse_file_type(string); ++string; if (mode == (mode_t) -1) { pseudo_diag("mode strings with a file type must use a valid type [-bcdflps]\n"); return -1; } } bits = parse_partial_mode(string); if (bits == (mode_t) -1) return -1; if (bits & 010) { mode |= S_ISUID; bits &= ~010; } mode |= bits << 6; string += 3; bits = parse_partial_mode(string); if (bits == (mode_t) -1) return -1; if (bits & 010) { mode |= S_ISGID; bits &= ~010; } mode |= bits << 3; string += 3; bits = parse_partial_mode(string); if (bits == (mode_t) -1) return -1; if (bits & 010) { mode |= S_ISVTX; bits &= ~010; } mode |= bits; return mode; } static time_t parse_timestamp(char *string) { time_t stamp_sec; struct tm stamp_tm; int i; char *s; char timebuf[4096]; stamp_sec = time(0); /* try the user's provided time format first, if there is one: */ localtime_r(&stamp_sec, &stamp_tm); s = strptime(string, timeformat, &stamp_tm); if (s && !*s) { return mktime(&stamp_tm); } for (i = 0; time_formats[i]; ++i) { char *s; localtime_r(&stamp_sec, &stamp_tm); s = strptime(string, time_formats[i], &stamp_tm); if (s && !*s) { break; } } if (!time_formats[i]) { pseudo_diag("Couldn't parse <%s> as a time. Current time in known formats is:\n", string); localtime_r(&stamp_sec, &stamp_tm); for (i = 0; time_formats[i]; ++i) { strftime(timebuf, sizeof(timebuf), time_formats[i], &stamp_tm); pseudo_diag("\t%s\n", timebuf); } pseudo_diag("Or, specify your own with -E; see strptime(3).\n"); return -1; } return mktime(&stamp_tm); } pseudo_query_t * plog_trait(int opt, char *string) { pseudo_query_t *new_trait; char *endptr; if (opt < 0 || opt > UCHAR_MAX) { pseudo_diag("Unknown/invalid option value: %d\n", opt); return 0; } if (!opt_to_field[opt]) { if (isprint(opt)) { pseudo_diag("Unknown option: -%c\n", opt); } else { pseudo_diag("Unknown option: 0x%02x\n", opt); } return 0; } if (!*string) { pseudo_diag("invalid empty string for -%c\n", opt); return 0; } new_trait = calloc(sizeof(*new_trait), 1); if (!new_trait) { pseudo_diag("Couldn't allocate requested trait (for -%c %s)\n", opt, string ? string : ""); return 0; } new_trait->field = opt_to_field[opt]; new_trait->type = plog_query_type(&string); if (new_trait->type == PSQT_UNKNOWN) { pseudo_diag("Couldn't comprehend trait type for '%s'\n", string ? string : ""); free(new_trait); return 0; } switch (new_trait->field) { case PSQF_ACCESS: new_trait->data.ivalue = pseudo_access_fopen(string); if (new_trait->data.ivalue == (unsigned long long) -1) { pseudo_diag("access flags should be specified like fopen(3) mode strings (or x for exec).\n"); free(new_trait); return 0; } break; case PSQF_FTYPE: /* special magic: allow file types ala find */ /* This is implemented by additional magic over in the database code */ /* must not be more than one character. The test against * the first character is because in theory, if the * first character is the terminating NUL, we may not * access the second. */ if (string[0] && string[1]) { pseudo_diag("file type must be a single character [-bcdflps].\n"); free(new_trait); return 0; } new_trait->data.ivalue = parse_file_type(string); if (new_trait->data.ivalue == (mode_t) -1) { free(new_trait); return 0; } break; case PSQF_OP: new_trait->data.ivalue = pseudo_op_id(string); break; case PSQF_ORDER: if (string[0] && string[1]) { pseudo_diag("order type must be a single specifier character.\n"); free(new_trait); return 0; } new_trait->data.ivalue = opt_to_field[(unsigned char) string[0]]; if (!new_trait->data.ivalue) { pseudo_diag("Unknown field type: %c\n", string[0]); } break; case PSQF_RESULT: new_trait->data.ivalue = pseudo_res_id(string); break; case PSQF_SEVERITY: new_trait->data.ivalue = pseudo_sev_id(string); break; case PSQF_STAMP: new_trait->data.ivalue = parse_timestamp(string); if ((time_t) new_trait->data.ivalue == (time_t) -1) { free(new_trait); return 0; } break; case PSQF_TYPE: new_trait->data.ivalue = pseudo_msg_type_id(string); break; case PSQF_CLIENT: case PSQF_DEV: case PSQF_FD: case PSQF_GID: case PSQF_INODE: case PSQF_UID: new_trait->data.ivalue = strtoll(string, &endptr, 0); if (*endptr) { pseudo_diag("Unexpected garbage after number (%llu): '%s'\n", new_trait->data.ivalue, endptr); free(new_trait); return 0; } break; case PSQF_MODE: case PSQF_PERM: new_trait->data.ivalue = strtoll(string, &endptr, 8); if (!*endptr) { break; } /* maybe it's a mode string? */ new_trait->data.ivalue = parse_mode_string(string); if (new_trait->data.ivalue == (mode_t) -1) { free(new_trait); return 0; } if (new_trait->field == PSQF_PERM) { /* mask out file type */ new_trait->data.ivalue &= ~S_IFMT; } break; case PSQF_PATH: /* FALLTHROUGH */ case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_TEXT: /* FALLTHROUGH */ case PSQF_TAG: /* Plain strings */ new_trait->data.svalue = strdup(string); break; default: pseudo_diag("I don't know how I got here. Unknown field type %d.\n", new_trait->field); free(new_trait); return 0; break; } return new_trait; } /* You can either create a query or create a log entry. They use very * similar syntax, but: * - if you're making a query, you can use >, <, etc. * - if you're logging, you can't. * This is tracked by recording whether any non-exact relations * have been requested ("query_only"), and refusing to set the -l * flag if they have, and refusing to accept any such relation * if the -l flag is already set. */ int main(int argc, char **argv) { pseudo_query_t *traits = 0, *current = 0, *new_trait = 0; char *s; log_history history; int query_only = 0; int o; int bad_args = 0; char *format = "%s %-12.12R %-4y %7o: [mode %04m, %2a] %p %T"; while ((o = getopt(argc, argv, "vla:c:d:DE:f:F:g:G:hi:I:m:M:o:O:p:P:r:R:s:S:t:T:u:Ux:y:")) != -1) { switch (o) { case 'P': s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) pseudo_diag("Can't resolve prefix path '%s'\n", optarg); pseudo_set_value("PSEUDO_PREFIX", s); break; case 'v': pseudo_debug_verbose(); break; case 'x': pseudo_debug_set(optarg); break; case 'l': opt_l = 1; break; case 'D': opt_D = 1; query_only = 1; break; case 'E': timeformat = strdup(optarg); break; case 'F': /* disallow specifying -F with -l */ format = strdup(optarg); query_only = 1; break; case 'U': opt_U = 1; query_only = 1; break; case 'I': /* PSQF_ID */ query_only = 1; /* FALLTHROUGH */ case 'a': /* PSQF_ACCESS */ case 'c': /* PSQF_CLIENT */ case 'd': /* PSQF_DEV */ case 'f': /* PSQF_FD */ case 'g': /* PSQF_GID */ case 'G': /* PSQF_TAG */ case 'i': /* PSQF_INODE */ case 'm': /* PSQF_PERM */ case 'M': /* PSQF_MODE */ case 'o': /* PSQF_OP */ case 'O': /* PSQF_ORDER */ case 'p': /* PSQF_PATH */ case 'r': /* PSQF_RESULT */ case 'R': /* PSQF_PROGRAM */ case 's': /* PSQF_STAMP */ case 'S': /* PSQF_SEVERITY */ case 't': /* PSQF_FTYPE */ case 'T': /* PSQF_TEXT */ case 'u': /* PSQF_UID */ case 'y': /* PSQF_TYPE */ new_trait = plog_trait(o, optarg); if (!new_trait) { bad_args = 1; } break; case 'h': usage(EXIT_SUCCESS); break; case '?': /* FALLTHROUGH */ default: fprintf(stderr, "unknown option '%c'\n", optopt); usage(EXIT_FAILURE); break; } if (new_trait) { if (current) { current->next = new_trait; current = current->next; } else { traits = new_trait; current = new_trait; } new_trait = 0; } } pseudo_debug_flags_finalize(); if (optind < argc) { pseudo_diag("Error: Extra arguments not associated with any option.\n"); usage(EXIT_FAILURE); } if (query_only && opt_l) { pseudo_diag("Error: -l cannot be used with query-only options or flags.\n"); bad_args = 1; } /* should be set only if we have already diagnosed the bad arguments. */ if (bad_args) exit(EXIT_FAILURE); if (!pseudo_get_prefix(argv[0])) { pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n"); exit(EXIT_FAILURE); } if (!pseudo_get_bindir()) { pseudo_diag("Can't figure out bindir. Set PSEUDO_BINDIR.\n"); exit(EXIT_FAILURE); } if (!pseudo_get_libdir()) { pseudo_diag("Can't figure out libdir. Set PSEUDO_LIBDIR.\n"); exit(EXIT_FAILURE); } if (!pseudo_get_localstatedir()) { pseudo_diag("Can't figure out localstatedir. Set PSEUDO_LOCALSTATEDIR.\n"); exit(EXIT_FAILURE); } if (opt_l) { pdb_log_traits(traits); } else { int fields; fields = format_scan(format); if (fields == -1) { pseudo_diag("couldn't parse format string (%s).\n", format); return EXIT_FAILURE; } if (opt_D) { if (pdb_delete(traits, fields)) { pseudo_diag("errors occurred trying to delete entries.\n"); } } else { history = pdb_history(traits, fields, opt_U); if (history) { log_entry *e; while ((e = pdb_history_entry(history)) != NULL) { display(e, format); log_entry_free(e); } pdb_history_free(history); } else { pseudo_diag("could not retrieve history.\n"); return EXIT_FAILURE; } } } return 0; } /* print a single member of log, based on a single format specifier; * returns the address of the last character of the format specifier. */ static char * format_one(log_entry *e, char *format) { char fmtbuf[256]; size_t len = strcspn(format, "acdfgGimMoprRsStTuy"), real_len; char scratch[4096]; time_t stamp_sec; struct tm stamp_tm; char *s; if (!e || !format) { pseudo_diag("invalid log entry or format specifier.\n"); return 0; } real_len = snprintf(fmtbuf, sizeof(fmtbuf), "%.*s", (int) len + 1, format); if (real_len >= sizeof(fmtbuf) - 1) { pseudo_diag("Format string way too long starting at %.10s", format - 1); return 0; } /* point to the last character */ s = fmtbuf + real_len - 1; /* The * modifier for width or precision requires additional * parameters -- this doesn't make sense here. */ if (strchr(fmtbuf, '*') || strchr(fmtbuf, 'l') || strchr(fmtbuf, 'h')) { pseudo_diag("Sorry, you can't use *, h, or l format modifiers.\n"); return 0; } switch (*s) { case 'a': /* PSQF_ACCESS */ *scratch = '\0'; if (e->access == -1) { strcpy(scratch, "invalid"); } else if (e->access != 0) { if (e->access & PSA_READ) { strcpy(scratch, "r"); if (e->access & PSA_WRITE) strcat(scratch, "+"); } else if (e->access & PSA_WRITE) { if (e->access & PSA_APPEND) { strcpy(scratch, "a"); } else { strcpy(scratch, "w"); } if (e->access & PSA_READ) strcat(scratch, "+"); } else if (e->access & PSA_EXEC) { strcpy(scratch, "x"); } /* this should be impossible... should. */ if (e->access & PSA_APPEND && !(e->access & PSA_WRITE)) { strcat(scratch, "?a"); } } else { strcpy(scratch, "-"); } strcpy(s, "s"); printf(fmtbuf, scratch); break; case 'c': /* PSQF_CLIENT */ strcpy(s, "d"); printf(fmtbuf, (int) e->client); break; case 'd': /* PSQF_DEV */ strcpy(s, "d"); printf(fmtbuf, (int) e->dev); break; case 'f': /* PSQF_FD */ strcpy(s, "d"); printf(fmtbuf, (int) e->fd); break; case 'g': /* PSQF_GID */ strcpy(s, "d"); printf(fmtbuf, (int) e->gid); break; case 'G': /* PSQF_TAG */ strcpy(s, "s"); printf(fmtbuf, e->tag ? e->tag : ""); break; case 'i': /* PSQF_INODE */ strcpy(s, "llu"); printf(fmtbuf, (unsigned long long) e->ino); break; case 'm': /* PSQF_PERM */ strcpy(s, "o"); printf(fmtbuf, (unsigned int) e->mode & ALLPERMS); break; case 'M': /* PSQF_MODE */ strcpy(s, "o"); printf(fmtbuf, (unsigned int) e->mode); break; case 'o': /* PSQF_OP */ strcpy(s, "s"); printf(fmtbuf, pseudo_op_name(e->op)); break; case 'p': /* PSQF_PATH */ strcpy(s, "s"); printf(fmtbuf, e->path ? e->path : ""); break; case 'r': /* PSQF_RESULT */ strcpy(s, "s"); printf(fmtbuf, pseudo_res_name(e->result)); break; case 'R': /* PSQF_PROGRAM */ strcpy(s, "s"); printf(fmtbuf, e->program ? e->program : ""); break; case 's': /* PSQF_STAMP */ strcpy(s, "s"); stamp_sec = e->stamp; localtime_r(&stamp_sec, &stamp_tm); strftime(scratch, sizeof(scratch), timeformat, &stamp_tm); printf(fmtbuf, scratch); break; case 'S': /* PSQF_SEVERITY */ strcpy(s, "s"); printf(fmtbuf, pseudo_sev_name(e->severity)); break; case 't': /* PSQF_FTYPE */ strcpy(s, "s"); if (S_ISREG(e->mode)) { strcpy(scratch, "file"); } if (S_ISLNK(e->mode)) { strcpy(scratch, "link"); } else if (S_ISDIR(e->mode)) { strcpy(scratch, "dir"); } else if (S_ISFIFO(e->mode)) { strcpy(scratch, "fifo"); } else if (S_ISBLK(e->mode)) { strcpy(scratch, "block"); } else if (S_ISCHR(e->mode)) { strcpy(scratch, "char"); } else { snprintf(scratch, sizeof(scratch), "?%o", (unsigned int) e->mode & S_IFMT); } printf(fmtbuf, scratch); break; case 'T': /* PSQF_TEXT */ strcpy(s, "s"); printf(fmtbuf, e->text ? e->text : ""); break; case 'u': /* PSQF_UID */ strcpy(s, "d"); printf(fmtbuf, (int) e->uid); break; case 'y': /* PSQF_TYPE */ strcpy(s, "s"); printf(fmtbuf, pseudo_msg_type_name(e->type)); break; } return format + len; } static int format_scan(char *format) { char *s; size_t len; int fields = 0; pseudo_query_field_t field; for (s = format; (s = strchr(s, '%')) != NULL; ++s) { len = strcspn(s, "acdfgGimMoprRsStTuy"); s += len; if (!*s) { pseudo_diag("Unknown format: '%.3s'\n", (s - len)); return -1; } field = opt_to_field[(unsigned char) *s]; switch (field) { case PSQF_PERM: /* FALLTHROUGH */ case PSQF_FTYPE: fields |= (1 << PSQF_MODE); break; case PSQF_ACCESS: /* FALLTHROUGH */ case PSQF_CLIENT: /* FALLTHROUGH */ case PSQF_DEV: /* FALLTHROUGH */ case PSQF_FD: /* FALLTHROUGH */ case PSQF_GID: /* FALLTHROUGH */ case PSQF_TAG: /* FALLTHROUGH */ case PSQF_INODE: /* FALLTHROUGH */ case PSQF_MODE: /* FALLTHROUGH */ case PSQF_OP: /* FALLTHROUGH */ case PSQF_PATH: /* FALLTHROUGH */ case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_RESULT: /* FALLTHROUGH */ case PSQF_STAMP: /* FALLTHROUGH */ case PSQF_SEVERITY: /* FALLTHROUGH */ case PSQF_TEXT: /* FALLTHROUGH */ case PSQF_TYPE: /* FALLTHROUGH */ case PSQF_UID: fields |= (1 << field); break; case '\0': /* if there are no more formats, that may be wrong, but * we can ignore it here */ break; default: pseudo_diag("error: invalid format specifier %c (at %s)\n", *s, s); return -1; break; } } return fields; } static void display(log_entry *e, char *format) { for (; *format; ++format) { switch (*format) { case '%': format = format_one(e, format); if (!format) return; break; default: putchar(*format); break; } } putchar('\n'); } pseudo-1.8.1+git20161012/run_tests.sh000077500000000000000000000016541300506512600170050ustar00rootroot00000000000000#!/bin/bash opt_verbose= usage() { echo >&2 "usage:" echo >&2 " run_tests [-v|--verbose]" exit 1 } for arg do case $arg in --) shift; break ;; -v | --verbose) opt_verbose=-v ;; *) usage ;; esac done #The tests will be run on the build dir, not the installed versions #This requires to following be set properly. export PSEUDO_PREFIX=${PWD} num_tests=0 num_passed_tests=0 for file in test/test*.sh do filename=${file#test/} let num_tests++ mkdir -p var/pseudo ./bin/pseudo $file ${opt_verbose} if [ "$?" -eq "0" ]; then let num_passed_tests++ if [ "${opt_verbose}" == "-v" ]; then echo "${filename%.sh}: Passed." fi else echo "${filename/%.sh}: Failed." fi rm -rf var/pseudo/* done echo "${num_passed_tests}/${num_tests} test(s) passed." pseudo-1.8.1+git20161012/table_templates/000077500000000000000000000000001300506512600175575ustar00rootroot00000000000000pseudo-1.8.1+git20161012/table_templates/pseudo_tables.c000066400000000000000000000013631300506512600225570ustar00rootroot00000000000000@name pseudo_tables.c @header /* Tables matching enums to strings */ /* This file is generated and should not be modified. See the maketables * script if you want to modify this. */ #include "pseudo_tables.h" @body /* tables for ${name} */ static const char *${name}_id_to_name[] = { "none", ${names}, NULL }; ${column_names} /* functions for ${name} */ extern const char * pseudo_${name}_name(pseudo_${name}_t id) { if (id < 0 || id >= ${prefix}_MAX) return "unknown"; return ${name}_id_to_name[id]; } extern pseudo_${name}_t pseudo_${name}_id(const char *name) { int id; if (!name) return -1; for (id = 0; id < ${prefix}_MAX; ++id) if (!strcmp(${name}_id_to_name[id], name)) return id; return -1; } ${column_funcs} @footer pseudo-1.8.1+git20161012/table_templates/pseudo_tables.h000066400000000000000000000010141300506512600225550ustar00rootroot00000000000000@name pseudo_tables.h @header /* standard ranges/values/keys */ /* This file is generated and should not be modified. See the maketables * script if you want to modify this. */ /* NULL, strcmp */ #include @body /* tables for ${name} */ ${comment} typedef enum { ${prefix}_UNKNOWN = -1, ${prefix}_NONE = 0, ${enums}, ${prefix}_MAX } pseudo_${name}_t; ${flag_enums} extern const char *pseudo_${name}_name(pseudo_${name}_t); extern pseudo_${name}_t pseudo_${name}_id(const char *); ${column_protos} @footer pseudo-1.8.1+git20161012/templatefile.py000066400000000000000000000072721300506512600174470ustar00rootroot00000000000000from string import Template import os class TemplateFile: """A template for creating a source file""" def __init__(self, path): # default values... # no name or file yet self.name = '' self.sections = {} self.file = None self.path = None # open a new file for each item self.file_per_item = False # empty footer if none specified: self.sections['footer'] = [] # empty per-port if none specified: self.sections['port'] = [] # lines appended to body by default self.sections['body'] = [] current = self.sections['body'] self.template = open(path) for line in self.template: line = line.rstrip() if line.startswith('@'): if ' ' in line: leading, trailing = line.split(' ', 1) else: leading, trailing = line, None if leading == '@name': if not trailing: raise Exception("@name requires a file name.") self.path = trailing if '$' in self.path: self.file_per_item = True else: section = leading[1:] if section not in self.sections: self.sections[section] = [] current = self.sections[section] else: current.append(line) self.template.close() for section, data in self.sections.items(): if len(data) > 0: self.sections[section] = Template("\n".join(data)) else: self.sections[section] = None # You need a file if this isn't a file-per-item if not self.file_per_item: self.file = open(self.path, 'w') def close(self): """Close the associated file.""" if self.file: self.file.close() self.file = None def __repr__(self): strings = [] if self.file_per_item: strings.append("path: %s (per item)" % self.path) else: strings.append("path: %s" % self.path) for name, data in self.sections.items(): strings.append("%s:" % name) strings.append(data.safe_substitute({})) return "\n".join(strings) def get_file(self, item): if self.file_per_item: if not item: return path = Template(self.path).safe_substitute(item) if os.path.exists(path): # print "We don't overwrite existing files." return self.file = open(path, 'w') if not self.file: print "Couldn't open '%s' (expanded from %s), " \ "not emitting '%s'." % \ (path, self.path, template) return def emit(self, template, item=None): """Emit a template, with optional interpolation of an item.""" if template == "copyright": # hey, at least it's not a global variable, amirite? self.get_file(item) if self.file: self.file.write(TemplateFile.copyright) elif template in self.sections: templ = self.sections[template] if templ: self.get_file(item) if self.file: self.file.write(templ.safe_substitute(item)) self.file.write("\n") else: print "Warning: Unknown template '%s'." % template if self.file_per_item: if self.file: self.file.close() self.file = None pseudo-1.8.1+git20161012/templates/000077500000000000000000000000001300506512600164105ustar00rootroot00000000000000pseudo-1.8.1+git20161012/templates/func_deps000066400000000000000000000001111300506512600202720ustar00rootroot00000000000000@name func_deps.mk ${funcdeps} @header # Per-function extra dependencies pseudo-1.8.1+git20161012/templates/guts000066400000000000000000000003611300506512600173150ustar00rootroot00000000000000@name ports/${port}/guts/${name}.c @header @body /* * Copyright (c) ${date} Wind River Systems; see * guts/COPYRIGHT for information. * * ${comment} * ${rc_decl} */ ${rc_assign} real_${name}(${call_args}); /* ${rc_return} * } */ pseudo-1.8.1+git20161012/templates/port_deps000066400000000000000000000001011300506512600203220ustar00rootroot00000000000000@name port_deps.mk @port ${portdeps} @header # port dependencies pseudo-1.8.1+git20161012/templates/port_wrappers000066400000000000000000000001631300506512600212420ustar00rootroot00000000000000@name port_wrappers.c @header /* additional hand-written wrappers for ports which provide them */ @port ${include} pseudo-1.8.1+git20161012/templates/pseudo_ports000066400000000000000000000001421300506512600210560ustar00rootroot00000000000000@name pseudo_ports.h @header /* #defines for port-specific hackery */ @port ${define} ${portdefs} pseudo-1.8.1+git20161012/templates/wrapfuncs.c000066400000000000000000000046771300506512600206020ustar00rootroot00000000000000@name pseudo_wrapfuncs.c @header /* wrapper functions. generated automatically. */ /* IF YOU ARE SEEING COMPILER ERRORS IN THIS FILE: * If you are seeing a whole lot of errors, make sure you aren't actually * trying to compile pseudo_wrapfuncs.c directly. This file is #included * from pseudo_wrappers.c, which has a lot of needed include files and * static declarations. */ /* This file is generated and should not be modified. See the makewrappers * script if you want to modify this. */ @body static ${type} (*real_${name})(${decl_args}) = ${real_init}; ${maybe_skip} ${type} ${name}(${decl_args}) { sigset_t saved; ${variadic_decl} ${rc_decl} PROFILE_START; ${maybe_async_skip} if (!pseudo_check_wrappers() || !real_$name) { /* rc was initialized to the "failure" value */ pseudo_enosys("${name}"); PROFILE_DONE; ${rc_return} } ${variadic_start} if (pseudo_disabled) { ${rc_assign} (*real_${name})(${call_args}); ${variadic_end} PROFILE_DONE; ${rc_return} } pseudo_debug(PDBGF_WRAPPER, "wrapper called: ${name}\n"); pseudo_sigblock(&saved); pseudo_debug(PDBGF_WRAPPER | PDBGF_VERBOSE, "${name} - signals blocked, obtaining lock\n"); if (pseudo_getlock()) { errno = EBUSY; sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER, "${name} failed to get lock, giving EBUSY.\n"); PROFILE_DONE; ${def_return} } int save_errno; if (antimagic > 0) { /* call the real syscall */ pseudo_debug(PDBGF_SYSCALL, "${name} calling real syscall.\n"); ${rc_assign} (*real_${name})(${call_args}); } else { ${fix_paths} /* exec*() use this to restore the sig mask */ pseudo_saved_sigmask = saved; ${rc_assign} wrap_$name(${call_args}); } ${variadic_end} save_errno = errno; pseudo_droplock(); sigprocmask(SIG_SETMASK, &saved, NULL); pseudo_debug(PDBGF_WRAPPER | PDBGF_VERBOSE, "${name} - yielded lock, restored signals\n"); #if 0 /* This can cause hangs on some recentish systems which use locale * stuff for strerror... */ pseudo_debug(PDBGF_WRAPPER, "wrapper completed: ${name} returns ${rc_format} (errno: %s)\n", ${rc_value}, strerror(save_errno)); #endif pseudo_debug(PDBGF_WRAPPER, "wrapper completed: ${name} returns ${rc_format} (errno: %d)\n", ${rc_value}, save_errno); errno = save_errno; PROFILE_DONE; ${rc_return} } static ${type} wrap_${name}(${wrap_args}) { $rc_decl ${maybe_variadic_decl} ${maybe_variadic_start} #include "ports/${port}/guts/${name}.c" ${rc_return} } ${end_maybe_skip} pseudo-1.8.1+git20161012/templates/wrapfuncs.h000066400000000000000000000005211300506512600205670ustar00rootroot00000000000000@name pseudo_wrapfuncs.h @header /* wrapper functions. generated automatically. */ /* This file is generated and should not be modified. See the makewrappers * script if you want to modify this. */ @body /* ${comment} */ static ${type} wrap_${name}(${wrap_args}); static ${type} (*real_${name})(${decl_args}); ${real_predecl} @footer pseudo-1.8.1+git20161012/templates/wrapper_table000066400000000000000000000012231300506512600211600ustar00rootroot00000000000000@name pseudo_wrapper_table.c @header /* The table of wrapper functions to populate */ /* This file is generated and should not be modified. See the makewrappers * script if you want to modify this. */ typedef struct { char *name; /* the name */ int (**real)(void); /* the underlying syscall */ int (*wrapper)(void); /* the wrapper from guts/name.c */ char *version; /* the version, if we know and care */ } pseudo_function; static pseudo_function pseudo_functions[] = { @body { /* ${comment}; */ "${name}${maybe_inode64}", (int (**)(void)) &real_${name}, (int (*)(void)) wrap_${name}, ${version} }, @footer { NULL, NULL, NULL, NULL }, }; pseudo-1.8.1+git20161012/test/000077500000000000000000000000001300506512600153715ustar00rootroot00000000000000pseudo-1.8.1+git20161012/test/test-chroot.sh000077500000000000000000000007541300506512600202110ustar00rootroot00000000000000#!/bin/bash # Return vals: 2 - invalid arg list # 1 - chroot failed # 0 - chroot succeeded cat > chroot_test.c << EOF #include int main(int argc, char *argv[]) { if (argc != 2) return 2; return (chroot(argv[1]) == -1); } EOF gcc -o chroot_test chroot_test.c ./chroot_test `pwd` if [ "$?" = "0" ] then #echo "Passed." rm -f chroot_test chroot_test.c exit 0 fi #echo "Failed" rm -f chroot_test chroot_test.c exit 1 pseudo-1.8.1+git20161012/test/test-dir-move.sh000077500000000000000000000003101300506512600204210ustar00rootroot00000000000000#!/bin/bash mkdir d1 touch d1/f1 mv d1 d2 fileuid=`\ls -n1 d2/f1 | awk '{ print $3 }'` if [ "$fileuid" == "$UID" ] then #echo "Passed." rm -rf d2 exit 0 fi rm -rf d2 #echo "Failed" exit 1 pseudo-1.8.1+git20161012/test/test-env_i.sh000077500000000000000000000002101300506512600177760ustar00rootroot00000000000000#!/bin/bash env -i A=A B=B C=C env | grep -q "PSEUDO_" if [ "$?" = "0" ] then #echo "Passed." exit 0 fi #echo "Failed" exit 1 pseudo-1.8.1+git20161012/test/test-execl.sh000077500000000000000000000005541300506512600200110ustar00rootroot00000000000000#!/bin/bash cat > execl_test.c << EOF #include int main() { return execl("/usr/bin/env", "/usr/bin/env", "A=A", "B=B", "C=C", NULL); } EOF gcc -o execl_test execl_test.c ./execl_test | grep -q "C=C" if [ "$?" = "0" ] then #echo "Passed." rm -f execl_test execl_test.c exit 0 fi #echo "Failed" rm -f execl_test execl_test.c exit 1 pseudo-1.8.1+git20161012/test/test-pseudo_disable-fork-env_i.sh000077500000000000000000000012511300506512600237230ustar00rootroot00000000000000#!/bin/bash # Verify normal operation... uid=`env -i id -u` gid=`env -i id -g` if [ $uid -ne 0 -o $gid -ne 0 ]; then exit 1 fi export PSEUDO_DISABLED=1 # Verify we dropped OUT of pseudo control, even with env -i # This checks that env -i replacement functionality still works # as expected uid=`env -i id -u` gid=`env -i id -g` if [ $uid -eq 0 -o $gid -eq 0 ]; then exit 1 fi export PSEUDO_DISABLED=1 # Verify we can change PSEUDO_DISABLED, even with env -i # This checks that env -i replacement functionality still works # as expected uid=`env -i PSEUDO_DISABLED=0 id -u` gid=`env -i PSEUDO_DISABLED=0 id -g` if [ $uid -ne 0 -o $gid -ne 0 ]; then exit 1 fi exit 0 pseudo-1.8.1+git20161012/test/test-pseudo_disable-fork.sh000077500000000000000000000004161300506512600226270ustar00rootroot00000000000000#!/bin/bash # Verify normal operation... uid=`id -u` gid=`id -g` if [ $uid -ne 0 -o $gid -ne 0 ]; then exit 1 fi export PSEUDO_DISABLED=1 # Verify we dropped OUT of pseudo control uid=`id -u` gid=`id -g` if [ $uid -eq 0 -o $gid -eq 0 ]; then exit 1 fi exit 0 pseudo-1.8.1+git20161012/test/test-pseudo_unload-fork-env_i.sh000077500000000000000000000011731300506512600236050ustar00rootroot00000000000000#!/bin/bash # Verify normal operation... uid=`env -i id -u` gid=`env -i id -g` if [ $uid -ne 0 -o $gid -ne 0 ]; then exit 1 fi export PSEUDO_UNLOAD=1 # Verify we dropped OUT of pseudo control, even with env -i # This checks that env -i replacement functionality still works # as expected uid=`env -i id -u` gid=`env -i id -g` if [ $uid -eq 0 -o $gid -eq 0 ]; then exit 1 fi export PSEUDO_UNLOAD=1 # Verify that once PSEUDO_UNLOAD has been issued, that # we can't restore pseudo into memory uid=`env -i PSEUDO_DISABLED=0 id -u` gid=`env -i PSEUDO_DISABLED=0 id -g` if [ $uid -eq 0 -o $gid -eq 0 ]; then exit 1 fi exit 0 pseudo-1.8.1+git20161012/test/test-pseudo_unload-fork.sh000077500000000000000000000004141300506512600225040ustar00rootroot00000000000000#!/bin/bash # Verify normal operation... uid=`id -u` gid=`id -g` if [ $uid -ne 0 -o $gid -ne 0 ]; then exit 1 fi export PSEUDO_UNLOAD=1 # Verify we dropped OUT of pseudo control uid=`id -u` gid=`id -g` if [ $uid -eq 0 -o $gid -eq 0 ]; then exit 1 fi exit 0 pseudo-1.8.1+git20161012/test/test-reexec-chroot.sh000077500000000000000000000011731300506512600214560ustar00rootroot00000000000000#!/bin/bash # Test if we re-invoke pseudo that chroot still works # Return vals: 2 - invalid arg list # 1 - chroot failed # 0 - chroot succeeded cat > chroot_test.c << EOF #include int main(int argc, char *argv[]) { if (argc != 2) return 2; return (chroot(argv[1]) == -1); } EOF gcc -o chroot_test chroot_test.c # The following should just run chroot_test since pseudo is already loaded ./bin/pseudo ./chroot_test `pwd` if [ "$?" = "0" ] then #echo "Passed." rm -f chroot_test chroot_test.c exit 0 fi #echo "Failed" rm -f chroot_test chroot_test.c exit 1 pseudo-1.8.1+git20161012/test/test-umask.sh000077500000000000000000000005421300506512600200260ustar00rootroot00000000000000#!/bin/bash mode() { ls -l "$1" | awk '{ print $1 }' } # Verify normal operation... umask 022 touch a umask 0 touch b case $(mode a) in -rw-r--r--*) ;; *) exit 1;; esac case $(mode b) in -rw-rw-rw-*) ;; *) exit 1;; esac export PSEUDO_DISABLED=1 case $(mode a) in -rw-r--r--*) ;; *) exit 1;; esac case $(mode b) in -rw-r--r--*) ;; *) exit 1;; esac pseudo-1.8.1+git20161012/test/test-xattr.sh000077500000000000000000000016441300506512600200540ustar00rootroot00000000000000#!/bin/bash # Return vals: 2 - Unable to run xattr commands # 1 - Invalid return value # 0 - Pass touch f1 attrs=`getfattr -d f1 | grep -v '^#'` if [ -n "$attrs" ] then #echo "Fail, unexpected getfattr result '$attr'" rm -f f1 exit 1 fi setfattr -n "user.dummy" -v "test_f1" f1 if [ $? -ne 0 ] then #echo "Fail, unable to call setfattr" rm -f f1 exit 2 fi attrs=`getfattr -d f1 | grep -v '^#'` if [ "$attrs" != 'user.dummy="test_f1"' ] then #echo "Fail, unexpected getfattr result '$attr'" rm -f f1 exit 1 fi setfattr -n "security.dummy" -v "test_f2" f1 if [ $? -ne 0 ] then #echo "Fail, unable to call setfattr" rm -f f1 exit 2 fi attrs=`getfattr -n "security.dummy" f1 | grep -v '^#'` if [ "$attrs" != 'security.dummy="test_f2"' ] then #echo "Fail, unexpected getfattr result '$attr'" rm -f f1 exit 1 fi #echo "Passed." rm -f f1 exit 0