diff options
author | Corinna Vinschen <vinschen@redhat.com> | 2007-11-12 15:31:45 +0000 |
---|---|---|
committer | Corinna Vinschen <vinschen@redhat.com> | 2007-11-12 15:31:45 +0000 |
commit | 3868726bb8a58ee9466ad022a2adf74cab715961 (patch) | |
tree | 794d75155a7f1f04b449a618023fa9d8231957f5 | |
parent | e62ab8f1f4f65459d52959afc912eaaf0bc2767c (diff) | |
download | gdb-3868726bb8a58ee9466ad022a2adf74cab715961.tar.gz |
2007-08-31 Dave Korn <dave.korn@artimi.com>
* mkgroup.c (enum_groups): Use MAX_PREFERRED_LENGTH in netgroupenum
call so that it will automatically size returned buffer sufficiently.
2007-08-03 Dave Korn <dave.korn@artimi.com>
* Makefile.in (cygcheck.exe): Add bloda.o as prerequisite, adjusting
dependency-filtering $(wordlist ...) call appropriately. Link ntdll.
(bloda.o): New rule to build bloda.o
* cygcheck.cc (dump_sysinfo): Call bloda function dump_dodgy_apps().
* bloda.cc: New file implements detection of applications from the
Big List Of Dodgy Apps.
2007-07-24 Corinna Vinschen <corinna@vinschen.de>
* COPYING.dumper: New file.
* dumper.cc: Change license to plain GPLv2 + later.
* dumper.h: Ditto.
* parse_pe.cc: Ditto.
2007-07-23 Christopher Faylor <me+cygwin@cgf.cx>
* strace.cc (create_child): Don't convert a path from cygwin format
unless it has a slash.
2007-07-09 Christopher Faylor <me+cygwin@cgf.cx>
* strace.cc (usage): Add missing description for -q.
2007-05-29 Pedro Alves <pedro_alves@portugalmail.pt>
* dumper.cc (dumper::prepare_core_dump): Record a phdr for each section.
2007-03-30 Mark Mitchell <mark@codesourcery.com>
* utils/cygpath.cc (get_long_path_name_w32impl): Close handles returned
by FindFirstFile.
2006-09-11 Eric Blake <ebb9@byu.net>
* cygcheck.cc (main): Restore POSIXLY_CORRECT before displaying user's
environment.
2006-08-03 Corinna Vinschen <corinna@vinschen.de>
* path.cc (vconcat): Don't convert backslahes to slashes.
(cygpath): Return native path with all backslashes.
2006-07-30 Ilya Bobir <ilya@po4ta.com>
* cygpath.cc (get_long_name): Fallback to get_long_path_name_w32impl.
2006-07-27 Corinna Vinschen <corinna@vinschen.de>
* cygpath.c (get_long_name): Cover the case that GetLongPathName
doesn't return valid information for non-existant files. Just return
incoming filename in that case.
-rw-r--r-- | winsup/utils/COPYING.dumper | 340 | ||||
-rw-r--r-- | winsup/utils/ChangeLog | 1977 | ||||
-rw-r--r-- | winsup/utils/Makefile.in | 225 | ||||
-rw-r--r-- | winsup/utils/bloda.cc | 410 | ||||
-rw-r--r-- | winsup/utils/cygcheck.cc | 1958 | ||||
-rw-r--r-- | winsup/utils/cygpath.cc | 831 | ||||
-rw-r--r-- | winsup/utils/dumper.cc | 947 | ||||
-rw-r--r-- | winsup/utils/dumper.h | 142 | ||||
-rw-r--r-- | winsup/utils/mkgroup.c | 797 | ||||
-rw-r--r-- | winsup/utils/parse_pe.cc | 104 | ||||
-rw-r--r-- | winsup/utils/path.cc | 353 | ||||
-rw-r--r-- | winsup/utils/strace.cc | 1061 |
12 files changed, 9145 insertions, 0 deletions
diff --git a/winsup/utils/COPYING.dumper b/winsup/utils/COPYING.dumper new file mode 100644 index 00000000000..623b6258a13 --- /dev/null +++ b/winsup/utils/COPYING.dumper @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog new file mode 100644 index 00000000000..d12dd9c4a0e --- /dev/null +++ b/winsup/utils/ChangeLog @@ -0,0 +1,1977 @@ +2007-08-31 Dave Korn <dave.korn@artimi.com> + + * mkgroup.c (enum_groups): Use MAX_PREFERRED_LENGTH in netgroupenum + call so that it will automatically size returned buffer sufficiently. + +2007-08-03 Dave Korn <dave.korn@artimi.com> + + * Makefile.in (cygcheck.exe): Add bloda.o as prerequisite, adjusting + dependency-filtering $(wordlist ...) call appropriately. Link ntdll. + (bloda.o): New rule to build bloda.o + * cygcheck.cc (dump_sysinfo): Call bloda function dump_dodgy_apps(). + * bloda.cc: New file implements detection of applications from the + Big List Of Dodgy Apps. + +2007-07-24 Corinna Vinschen <corinna@vinschen.de> + + * COPYING.dumper: New file. + * dumper.cc: Change license to plain GPLv2 + later. + * dumper.h: Ditto. + * parse_pe.cc: Ditto. + +2007-07-23 Christopher Faylor <me+cygwin@cgf.cx> + + * strace.cc (create_child): Don't convert a path from cygwin format + unless it has a slash. + +2007-07-09 Christopher Faylor <me+cygwin@cgf.cx> + + * strace.cc (usage): Add missing description for -q. + +2007-05-29 Pedro Alves <pedro_alves@portugalmail.pt> + + * dumper.cc (dumper::prepare_core_dump): Record a phdr for each section. + +2007-03-30 Mark Mitchell <mark@codesourcery.com> + + * utils/cygpath.cc (get_long_path_name_w32impl): Close handles returned + by FindFirstFile. + +2006-09-11 Eric Blake <ebb9@byu.net> + + * cygcheck.cc (main): Restore POSIXLY_CORRECT before displaying user's + environment. + +2006-08-03 Corinna Vinschen <corinna@vinschen.de> + + * path.cc (vconcat): Don't convert backslahes to slashes. + (cygpath): Return native path with all backslashes. + +2006-07-30 Ilya Bobir <ilya@po4ta.com> + + * cygpath.cc (get_long_name): Fallback to get_long_path_name_w32impl. + +2006-07-27 Corinna Vinschen <corinna@vinschen.de> + + * cygpath.c (get_long_name): Cover the case that GetLongPathName + doesn't return valid information for non-existant files. Just return + incoming filename in that case. + +2006-07-03 Kazuhiro Fujieda <fujieda@jaist.ac.jp> + + * Makefile.in: Build setmetamode.exe. + * setmetamode.c: New file. + +2006-05-24 Christopher Faylor <cgf@timesys.com> + + * configure.in: Update to newer autoconf. + (thanks to Steve Ellcey) + * configure: Regenerate. + * aclocal.m4: New file. + +2006-05-24 Christopher Faylor <cgf@timesys.com> + + * strace.cc (proc_child): Propagate return code from child process. + (dostrace): Ditto. + (main): Ditto. + +2006-03-03 Christian Franke <franke@computer.org> + + * regtool.cc (options): Add 'binary'. + (usage): Document 'load|unload|save' and '-b'. + (find_key): Add 'options' parameter, add load/unload. + (cmd_set): Add KT_BINARY case. + (cmd_get): Add hex output in KT_BINARY case. + (cmd_load): New function. + (cmd_unload): New function. + (set_privilege): New function. + (cmd_save): New function. + (commands): Add load, unload and save. + (main): Add '-b' + * utils.sgml (regtool): Document it. + +2006-02-17 Corinna Vinschen <corinna@vinschen.de> + + * cygpath.cc (get_long_name): Load GetLongPathNameA instead of incorrect + GetLongPathName. + (doit): Create mixed filename after converting to short or long pathname + respectively. + +2006-02-17 Jerry D. Hedden <jerry@hedden.us> + + * ps.cc (main): Set aflag if -p option is given. + +2006-02-16 Jerry D. Hedden <jerry@hedden.us> + + * ps.cc (longopts): Add --process option. + (opts): Add -p option. + (usage): Mention -p/--process option. + (main): Handle -p option. + * utils.sgml: Describe -p/--process option. + +2006-02-15 Igor Peshansky <pechtcha@cs.nyu.edu> + + * regtool.cc (usage): Clarify help for "-K". + +2006-02-08 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (find_on_path): Update copyright text. + +2006-02-08 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (find_on_path): Create copy of rv on successful access + check, since rv can be reused in subsequent calls. + +2006-01-19 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (common_paths): Add "patch". + +2006-01-19 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (init_paths): Record first_nonsys_path. + (find_on_path): Start on first nonsys path when !search_sysdirs. + +2006-01-18 Christopher Faylor <cgf@timesys.com> + + * Makefile.in (sysconfdir): Remove unneeded variable. + + * mkgroup.c: Replace ` with ' throughout. + * mkpasswd.c: Ditto. + * setfacl.c: Ditto. + * ssp.c: Ditto. + +2006-01-18 Dave Korn <dave.korn@artimi.com> + + * cygpath.cc (dowin): Remove stray debugging printf statement. + +2006-01-17 Corinna Vinschen <corinna@vinschen.de> + + * Makefile.in: Replace etcdir with sysconfdir. + +2006-01-13 Brian Dessent <brian@dessent.net> + + * Makefile.in (cygcheck.exe): Do not link against libwininet.a. + * cygcheck.cc (pInternetCloseHandle): Define global function pointer. + (display_internet_error): Use it. + (package_grep): Attempt to load wininet.dll at runtime. Call WinInet + API through function pointers throughout. + +2006-01-13 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Convert k32 to HMODULE. + +2006-01-13 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Convert osname to const and remove casts + in subsequent assignments. Recognize Longhorn/Vista and report as not + yet supported. Recognize when running under WOW64 and report native + CPU type. Slightly rearrange \n printing. + +2006-01-12 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc: Replace ` with ' throughout. + +2006-01-05 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (dump_sysinfo): Report the failing drive to prevent + having to guess. + +2005-12-29 Christopher Faylor <cgf@timesys.com> + + * strace.cc (nprocesses): Make static global. + (quiet): New variable. + (strace_active): Ditto. + (add_child): Increment nprocesses here. Don't add a child if it is + already added (windows bug?). Report on child if not quiet. + (get_child): Just return NULL if child not found. + (remove_child): Report on child if not quiet. + (attach_process): Don't complain if given a windows process. Use + windows pid in error. + (handle_output_debug_string): Issue error if trying to manipulate a process that we don't know about. + Handle _STRACE_CHILD_PID - attach to reported child when we get this. + (proc_child): Move nprocesses to file scope. + Report on exceptions. + (longopts): Implement "--quiet". + (opts): Implement "-q". + (main): Manipulate quiet flag. + * utils.sgml (strace): Add words describing '-q'. + +2005-12-29 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (common_apps): Add crontab, vi, vim. + +2005-12-19 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * dump_setup.cc (package_find): Fix is_alias computation for "/usr/lib". + +2005-12-14 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (common_apps): Add a few more binaries to look for. + +2005-12-05 Christopher Faylor <cgf@timesys.com> + + * strace.cc (show_usecs): Renamed from 'usecs'. + (main): Use show_usecs rather than usecs. Toggle delta if '-u' is + specified. + (handle_output_debug_string): Avoid printing microsecond timestamp if + show_usecs == 0. + * utils.sgml (strace): Add words to describe '-u'. + +2005-11-22 Brian Dessent <brian@dessent.net> + + * Makefile.in: Link cygcheck with libwininet.a. + * cygcheck.cc: Add includes. + (grep_packages): New global variable. + (display_internet_error): New function. + (safe_chars): New global variable. + (base_url): Ditto. + (package_grep): New function. + (usage): Reword --help output for clarity. Document new argument. + (longopts): Add 'package-query' option. + (opts): Add 'p' option, reorder to be consistent with 'longopts'. + (main): Accommodate new option. + * utils.sgml (cygcheck): Update --help output. Document new -p option. + +2005-09-22 Corinna Vinschen <corinna@vinschen.de> + + Align error message handling to mkpasswd's error messages throughout. + * mkgroup.c (print_win_error): Create macro calling ... + (_print_win_error): ... this function created from former + print_win_error. Move up in source. + (PDOMAIN_CONTROLLER_INFOW): Define return type of DsGetDcNameW. + (dsgetdcname): New function pointer for DsGetDcNameW. + (load_netapi): Get DsGetDcNameW address. + (main): If DsGetDcNameW is available, use it. + * mkpasswd.c (PDOMAIN_CONTROLLER_INFOW): Define return type of + DsGetDcNameW. + (dsgetdcname): New function pointer for DsGetDcNameW. + (load_netapi): Get DsGetDcNameW address. + (main): If DsGetDcNameW is available, use it. Rename local variable + domain_name_specified to domain_specified as in mkgroup.c. + +2005-09-08 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * regtool.cc: Extend copyright-years. + (print_version): Ditto. + (cmd_list): Don't depend on terminating '\0' being present on + string-values. + (cmd_get): Don't attempt to read more than present, but keep + extra space for terminating '\0'. Really output REG_BINARY. + Don't leak memory. + (cmd_set): Include trailing '\0' in string's length. + +2005-08-18 Corinna Vinschen <corinna@vinschen.de> + + * passwd.c (longopts): Add --logonserver option. + (opts): Add -d option. + (GetPW): Add server parameter which is given to Net functions. + (ChangePW): Ditto. + (PrintPW): Ditto. + (SetModals): Ditto. + (usage): Add description for -d/--logonserver option. + (main): Add option handling for -d/--logonserver. Use LOGONSERVER + environment variable content for server to contact, unless + -d/--logonserver option is given. If both is missing, use NULL. + +2005-08-16 Brian Dessent <brian@dessent.net> + + * cygcheck.cc (dump_sysinfo_services): Properly null-terminate 'buf'. + Avoid extraneous cygrunsrv invocation if 'verbose' is true. + +2005-08-03 Corinna Vinschen <corinna@vinschen.de> + + * mount.cc (longopts): Fix typo which disallows --options option. + +2005-07-19 Christopher Faylor <cgf@timesys.com> + Eric Blake <ebb9@byu.net> + + * cygcheck.cc (find_on_path): Perform .exe magic on non-PATH search. + +2005-07-05 Eric Blake <ebb9@byu.net> + + * cygcheck.cc (track_down, cygcheck): Return true on success. + (main): Reflect cygcheck failures in exit status. + +2005-06-14 Corinna Vinschen <corinna@vinschen.de> + + * parse_pe.c (exclusion::sort_and_check): Remove crude cast. + +2005-05-30 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Recognize XP Media Center and Tablet PC + Editions. Change .NET to 2003 throughout. Recognize 2003 Web Server + and Datacenter Server. Report when running in terminal server session. + +2005-05-27 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (load_cygwin): Make half-hearted attempt to work with + older DLLs. + * strace.cc (load_cygwin): Ditto. + +2005-05-22 Brian Dessent <brian@dessent.net> + + * cygcheck.cc (dump_sysinfo_services): Add new function that uses + new cygrunsrv options to dump service info. + (dump_sysinfo): Call dump_sysinfo_services if running under NT. + Change 'Cygnus' to 'Cygwin' in output. + +2005-05-20 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (load_cygwin): Remove debugging statement. + +2005-05-20 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (dump_sysinfo): Don't attempt to use path if it is not + set. + (nuke): Fix off by one error in allocation of environment variable. + (load_cygwin): Always set PATH even if cygwin environment is empty. + +2005-05-16 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (cygwin_internal): Define as a "C" function. + * strace.cc (cygwin_internal): Ditto. + + * cygpath.cc (print_version): Update copyright. + +2005-05-15 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (load_cygwin): Avoid calling putenv with a NULL path. + +2005-05-15 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (load_cygwin): Don't touch $PATH for now. + (print_version): Fix copyright. + * strace.cc (print_version): Ditto. + +2005-05-13 Christopher Faylor <cgf@timesys.com> + + * mount.cc (mount_commands): Display "-X" option when appropriate. + +2005-05-08 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (nuke): New function. + (load_cygwin): New function. + (main): Use load_cygwin to load argv/envp from cygwin environment, if + appropriate. + +2005-05-08 Christopher Faylor <cgf@timesys.com> + + * strace.cc (attach_process): Don't call load_cygwin(). Assume that + it's already happened. + (dotoggle): Ditto. + (main): Set argv from cygwin environment, if it exists. + +2005-05-01 Christopher Faylor <cgf@timesys.com> + + * mount.cc (usage): Clarify action of -m option. + +2005-04-29 Dave Korn <dave.korn@artimi.com> + + * path.cc (getmntent): Add previously-omitted 'noexec' and 'managed' + flags to mnt_opts string if present in mount flags. + +2005-04-20 Brian Dessent <brian@dessent.net> + + * utils.sgml (mount): Clarify setting cygdrive prefix for user + and system-wide. + +2005-03-24 Brian Dessent <brian@dessent.net> + + * cygcheck.cc (init_paths): Use full path instead of "." for the + current directory. Do not add "." if present in $PATH. + (dump_sysinfo): Skip placeholder first value of paths[]. + +2005-03-07 Christopher Faylor <cgf@timesys.com> + + * kill.cc (getsig): Rectify bug introduced by 2005-02-26 change. Don't + pad signal name with spaces. + +2005-03-07 Corinna Vinschen <corinna@vinschen.de> + + * strace.cc (handle_output_debug_string): Fix compiler warning. + +2005-03-06 Christopher Faylor <cgf@timesys.com> + + * cygpath.cc (usage): Pass in one more copy of program_name to printf + to avoid a SEGV. + +2005-02-27 Christopher Faylor <cgf@timesys.com> + + * regtool.cc (opts): The argument to 'K' is not optional. + (main): Revert previous change. Just let getopt deal with missing + argument. + +2005-02-27 Christopher Faylor <cgf@timesys.com> + + * regtool.cc (main): Avoid a SEGV when nothing follows -K. + +2005-02-26 Christopher Faylor <cgf@timesys.com> + + * kill.cc (getsig): Avoid buffer overflow when generating a signal name. + + * strace.cc (handle_output_debug_string): Make error a little more specific. + +2005-02-23 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * cygpath.cc (usage): Clarify help output to indicate acceptance + of multiple file names as arguments. + +2005-01-16 Christopher Faylor <cgf@timesys.com> + + * ps.cc (main): Eliminate use of PID_ZOMBIE. + * strace.cc (main): Recognize new option for displaying hex value of + strace type. + (handle_output_debug_string): Prepend output with hex value of strace + message if -H is specified. + +2005-01-11 Pierre Humblet <pierre.humblet@ieee.org> + + * mkpasswd.c (print_win_error): Transform into macro. + (_print_win_error): Upgrade former print_win_error by + printing the line. + (current_user): Call _print_win_error. + (enum_users): Print name in case of lookup failure. + (enum_local_groups): Ditto. + +2004-11-18 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (eprintf): New function. + +2004-11-16 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (find_on_path): Clear show_error and/or print_failed + parameters to display_error. + (rva_to_offset): Ditto. + (dll_info): Ditto. + (track_down): Ditto. + +2004-11-13 Pierre Humblet <pierre.humblet@ieee.org> + + * kill.cc (forcekill): Do not pass negative pids to + cygwin_internal. Check if sig == 0. Improve error messages. + (main): Make pid a long long and distinguish between pids, gpids + (i.e. negative pids) and Win9x pids. + +2004-11-11 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc: Change "keyeprint" to "display_error" throughout. + +2004-11-11 Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (main): Allow a '-l' without an additional argument. + +2004-11-11 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (keyeprint): New optional parameters: show_error and + print_failed. + +2004-10-31 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (get_dword): Fix errormessage. + (cygwin_info): Ditto. + (track_down): Ditto. + (check_keys): Ditto. + +2004-10-27 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): Warn about missing or multiple cygwin1 + dlls. + +2004-10-25 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): Add leading newline before legend for + drive-list. + +2004-10-22 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): In legend for drive-list: Add ``ram'' and + ``unk''. Use single puts. Add leading newline. Line up equal-signs. + +2004-10-20 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): Allow for larger drives in drive-list. + Change ``Used'' to ``Free'' in helptext-title for drive-list. + +2004-10-18 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (pretty_id): Don't let i become negative. Fix + printf-format. + +2004-10-17 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (pretty_id): Allocate space for ')' in uid and guid. + +2004-10-17 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (pretty_id): Allocate space for trailing '\0' in uid and + guid. + +2004-10-15 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + Christopher Faylor <cgf@timesys.com> + + * cygcheck.cc (pretty_id): Don't exit on id error. Fix size + calculations. + +2004-10-14 Christopher Faylor <cgf@timesys.com> + Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (pretty_id): Correct column calculations. Abort if id is + acting funny. + +2004-10-10 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (add_path): Don't leak memory when path is already in + ``paths''. + +2004-10-10 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): Warn about trailing (back)slash on mount + entries. + +2004-10-10 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): Don't warn about empty path components, + just display ``.''. + +2004-10-06 Corinna Vinschen <corinna@vinschen.de> + + * strace.cc (_impure_ptr): Remove. + +2004-10-06 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (dump_sysinfo): Warn about empty path components. + +2004-10-04 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * cygcheck.cc (pretty_id): Close pipe. + +2004-09-23 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * dump_setup.cc (dump_setup): Remove unneeded strlen when check_files + is not set. + +2004-09-22 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + Corinna Vinschen <corinna@vinschen.de> + + * dump_setup.cc (dump_setup): Avoid trailing spaces on package-list. + +2004-09-15 Bas van Gompel <cygwin-patch.buzz@bavag.tmfweb.nl> + + * getfacl.c (main): Correct layout when nopt is set. + +2004-07-12 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * mkgroup.cc (netapibufferallocate,netgroupgetinfo): New function + pointers. + (load_netapi): Load NetApiBufferAllocate and NetGroupGetInfo. + (enum_local_groups,enum_groups): Add disp_groupname parameter. + Load info for disp_groupname if specified. + (usage): Add description of "-g/--group" option. + (longopts,opts): Add "-g/--group" option. + (main): Process "-g/--group" option. + * utils.sgml (mkgroup): Add description of "-g/--group" option + +2004-06-15 Alan Modra <amodra@bigpond.net.au> + + * dumper.cc (dumper::prepare_core_dump): Use bfd_get_section_size + instead of _raw_size. + (dumper::write_core_dump): Likewise. + * parse_pe.cc (select_data_section): Likewise. + +2004-05-29 John Paul Wallington <jpw@gnu.org> + + * ssp.c (usage): Add missing linefeed. + +2004-05-27 Christopher Faylor <cgf@alum.bu.edu> + + * kill.cc (listsig): Fix "kill -l" segv. NSIG includes Signal 0, so we + need to avoid calling strtosigno with NSIG - 1. + +2004-03-21 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (dump_sysinfo): Remove "Win95/NT" from output. + +2004-03-15 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc (usage): Don't issue helpful message if -i is used. + (from Igor Pechtchanski) + +2004-03-13 Rob Siklos <rob2@siklos.ca> + + * kill.cc (get_debug_priv): New function. + (forcekill): Call get_debug_priv before trying to kill process. + +2004-02-24 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc (long_options): Add "mode" option. + (options): Ditto. + (usage): Report on new option. + (report_mode): New function. + (main): Implement -M option. + +2004-02-20 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (build_dumper): Detect missing iconv library. + + * cygpath.cc (dowin): Report on filename conversion errors. + (doit): Ditto. + + * strace.cc (main): Use symbolic constant for _STRACE_ALL when setting + mask. + +2004-02-14 Corinna Vinschen <corinna@vinschen.de> + + * ssp.c (opts): Add leading '+' to force posixly correct behaviour. + +2004-02-11 Corinna Vinschen <corinna@vinschen.de> + + * strace.cc (opts): Add leading '+' to force posixly correct + behaviour. + (main): Revert POSIXLY_CORRECT manipulations. + +2004-02-10 Christopher Faylor <cgf@redhat.com> + + * strace.cc (main): Guard against previous setting of POSIXLY_CORRECT. + +2004-02-10 Christopher Faylor <cgf@redhat.com> + + * strace.cc: Update copyrights. + * cygcheck.cc: Update copyrights. + +2004-02-10 Christopher Faylor <cgf@redhat.com> + + * strace.cc (main): Ensure POSIXLY_CORRECT argument ordering. + * cygcheck.cc (main): Ditto. + +2004-01-23 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (pretty_id): Make more robust in absence of id.exe. + +2004-01-22 Corinna Vinschen <corinna@vinschen.de> + + * cygpath.cc (dowin): Revert accidental checkin from November. + +2003-11-26 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c (main): Print "root" group with local admins group sid + and gid 0. + +2003-11-19 Corinna Vinschen <corinna@vinschen.de> + + * Makefile.in: Add rules to build ipcrm and ipcs. + * ipcrm.c: New file. + * ipcs.c: New file. + +2003-11-14 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c: Avoid compiler warnings throughout. + * mkpasswd.c: Ditto. + * passwd.c: Ditto. + +2003-11-07 Mark Blackburn <marklist@fangorn.ca> + + * cygpath.cc (main): Allow multiple pathnames on command line. + +2003-11-04 Corinna Vinschen <corinna@vinschen.de> + + * passwd.c (PrintPW): Turn around meaning printed for "Password not + required" option to be in line with Windows meaning. + (main): Use more appropriate 1008 option when calling + NetUserSetInfo to change flag values. + +2003-11-04 Corinna Vinschen <corinna@vinschen.de> + + * passwd.c (UF_LOCKOUT): Remove (defined in lmaccess.h). + (version): Bump version number to 1.5. + (longopts): Add -c, -C, -e, -E, -p, -P options. + (opts): Ditto. + (PrintPW): Print values of UF_PASSWD_CANT_CHANGE and + UF_DONT_EXPIRE_PASSWD flags. Slightly reformat output. Don't suppress + correct system password settings just because the account has admin + privileges. + (usage): Define as "noreturn" function. Restructure and rephrase + output. Accommodate new options. + (print_version): Fix copyright dates. + (main): Accommodate new options for setting UF_PASSWD_CANT_CHANGE, + UF_DONT_EXPIRE_PASSWD and UF_PASSWD_NOTREQD settings. + +2003-10-17 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (pretty_id): Don't exec if `id' program is not found. + +2003-09-20 Christopher Faylor <cgf@redhat.com> + + * kill.cc (main): Allow negative pids (indicates process groups). + +2003-09-17 Christopher Faylor <cgf@redhat.com> + + * parse_pe.cc (exclusion::sort_and_check): Make error message a little + more explicit and ignore (hopefully) harmless region overlap. + +2003-09-13 Christopher Faylor <cgf@redhat.com> + + * kill.c (forcekill): Wait for process to terminate even if + TerminateProcess says it failed. + +2003-09-11 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Tweak mingw libz.a detection to make it more reliably + detect when libz.a is not available. + +2003-09-11 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * cygcheck.cc (dump_only): New global variable. + (usage): Add "--dump-only" option, fix "--verbose" line. + (longopts, opts): Add "--dump-only" option. + (main): Process the "--dump-only" flag. Add new semantic check. + Pass dump_only information to dump_setup(). + +2003-09-11 Corinna Vinschen <corinna@vinschen.de> + + * getfacl (main): Remove extern declaration of optind. + * setfacl (main): Remove extern declaration of optarg and optind. + +2003-09-10 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (cygcheck.o): Use MINGW_CXX for compilation. + * cygcheck.cc: Just include <getopt.h> rather than cygwin version. + (pretty_id): Avoid compiler warnings. + * cygpath.cc (usage): Ditto. + +2003-09-10 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (MINGW_CXX): Define and use for mingw-compiled files. + * path.cc: Include more path info in cygwin-specific includes since the + cygwin directory is no longer included by default. + +2003-09-10 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: More fixups to adjust for the fact that mingw_getopt.o + is no longer built. + +2003-09-10 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Remove references to getopt since it is now part of + mingwex. + +2003-09-08 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Rename libz.h -> zlib.h. + +2003-09-07 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Detect existence of mingw zlib headers and libraries. + Build cygcheck without package checking if they are unavailable. + * dump_setup.cc: Generate dummy zlib macros when zlib is not available. + (open_package_list): Use zlib functions to uncompress package lists. + Return gzFile rather than FILE *. + (check_package_files): Change fp to gzFile to accommodate zlib + functions. Use gzgets to read from package file. Use gzclose to close + the handle. + (package_list): Ditto. + (package_find): Ditto. + +2003-09-05 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * dump_setup.cc (check_package_files): Don't fail on empty package. + +2003-09-01 AJ Reins <reinsaj@yahoo.com> + + * mount.cc (mount_commands): Ensure user mode is actually user mode and + not the default system mode. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * kill.cc (forcekill): Silence some compiler warnings. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Oops. Put the '...' in the right place. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Display "..." when not-verbose. + + * kill.cc (forcekill): Default to entered pid when pinfo fails. Issue + some warnings when things don't work. + +2003-08-17 David Rothenberger <daveroth@acm.org> + + * dump_setup.cc (check_package_files): Strip leading / and ./ from + package file names. + +2003-08-16 David Rothenberger <daveroth@acm.org> + + * dump_setup.cc (package_find): Don't stop searching on missing + file list. + (package_list): Ditto. + +2003-08-16 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * dump_setup.cc: (package_list): Make output terse unless + verbose requested. Fix formatting. + (package_find): Ditto. + +2003-08-15 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (main): Fix some formatting and help text printing. + +2003-08-15 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * cygcheck.cc (find_package,list_package): New global + variables. + (usage): Add "--find-package" and "--list-package" options, + reformat output. + (longopts, opts): Add "--find-package" and "--list-package" + options. + (main): Process the "--find-package" and "--list-package" + flags. Add new semantic checks. Add calls to find_package() + and list_package(). + * dump_setup.cc: Fix header comment. + (match_argv): Change return type to int to distinguish + between real matches and default ones. + (open_package_list): New static function. + (check_package_files): Factor out opening the package list + file into open_package_list(). + (get_packages): New static function. + (dump_setup): Factor out getting a list of packages into + get_packages(). + (package_list, package_find): New global functions. + +2003-08-15 Corinna Vinschen <corinna@vinschen.de> + + * regtool.cc (usage): Add missing linefeed. Move example to --help + text. Fix forward slash description. + +2003-08-13 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * dump_setup.cc (check_package_files): Fix extra '/' in filename. + Resize command buffer. Fix buffer overflow bug. + +2003-08-09 Igor Pechtchanski <pechtcha@cs.nyu.edu> + Christopher Faylor <cgf@redhat.com> + + * dump_setup.cc (dump_setup): Check for the existence of the package + list file. Rework slightly to use static buffer for popen commands. + +2003-08-07 Igor Pechtchanski <pechtcha@cs.nyu.edu> + Christopher Faylor <cgf@redhat.com> + + * dump_setup.cc (version_len): New static variable. + (could_not_access,directory_exists): New static function. + (file_exists): Ditto. + (check_package_files): Ditto. + (dump_setup): Check the contents of each package if check_files is true + and output the result in the "Status" column. Flush output after each + package. + +2003-08-07 Igor Pechtchanski <pechtcha@cs.nyu.edu> + Christopher Faylor <cgf@redhat.com> + + * dump_setup.cc (dump_setup): Remove redundant null check. Add + informative message if package info not found. + +2003-07-26 Christopher Faylor <cgf@redhat.com> + + * mount.cc (do_mount): Issue warning when using managed mount option on + non-empty directory. + +2003-07-25 Christopher Faylor <cgf@redhat.com> + + * configure.in: Use 'install-sh -c'. + * configure: Regenerate. + +2003-07-25 Christopher Faylor <cgf@redhat.com> + + * configure.in: Always use install-sh. + * configure: Regenerate. + +2003-07-03 Christopher Faylor <cgf@redhat.com> + + * mount.cc (struct opt): Remove posix option. + +2003-07-03 Christopher Faylor <cgf@redhat.com> + + * mount.cc (longopts): Add long "options" keyword. + (opts): Add -o option. + (usage): Display -o option. + (struct opt): New. + (concat3): New function. + (main): Handle -o flag. + +2003-06-12 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (pretty_id): Rework slightly to not rely on spaces. + +2003-06-12 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc: Throughout, always return error when GetShortPathName + returns 0. + +2003-04-26 Joshua Daniel Franklin <joshuadfranklin at yahoo dot com> + + * mkpasswd.c (usage) Remove extra newline from description output. + +2003-04-26 Joshua Daniel Franklin <joshuadfranklin at yahoo dot com> + + * cygcheck.cc (usage) Add description output. + * cygpath.cc (usage) Add description output. + * dumper.cc (usage) Add newline to description output. + * kill.cc (usage) Add description output. + * mkgroup.c (usage) Grammatical change to description output. + * mkpasswd.c (usage) Grammatical change to description output. + * mount.cc (usage) Add description output. + * passwd.c (usage) Add description output. + * ps.cc (usage) Add description output. + * regtool.cc (usage) Add description output. + * setfacl.c (usage) Remove extra newline from description output. + * ssp.c (usage) Add description output. + * strace.cc (usage) Add description output. + * umount.cc (usage) Add description output. + +2003-04-10 Pierre Humblet <pierre.humblet@ieee.org> + + * mkpasswd.c (current_user): print uid and gid as unsigned. + (enum_users): Ditto. Do not free servername. + (usage): Update to allow several domains and improve -p. + (main): On Win9x limit uids to 1000. Only print specials + when -l is specified. Add a loop to allow several domains + and free servername in the loop. + * mkgroup.c (enum_groups): Do not free servername. + (usage): Update to allow several domains. Change uid to gid. + (main): Only print specials when -l is specified. Add a + loop to allow several domains and free servername in the loop. + +2003-03-24 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (dump_sysinfo): Ensure that CYGWIN environment variable + is correctly set. + +2003-03-09 Corinna Vinschen <corinna@vinschen.de> + + * getfacl.c (username): Fix ambiguous printf calls. + (groupname): Ditto. + (main): Ditto. + +2003-03-04 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (strace.exe): Include mingw_getopt.o in link line. + * cygcheck.cc (print_version): Update copyright. + * strace.cc (print_version): Ditto. + (main): Allow cygwin paths for -o option. + (create_child): Allow cygwin path as argv[0]. + * path.cc (path.cc): Allow UNC paths. + +2003-03-01 Pierre Humblet <pierre.humblet@ieee.org> + + * mkpasswd.cc (main): On Win95, output both a default line and a + line for the current user (if known) with a pseudorandom uid. If + the -u switch is given, produce a line for the specified user. + * mkgroup.cc (main): On Win95 change the group name from "unknown" to + "all". + +2003-02-28 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (cygcheck.o): Fix so that actual mingw include files are + used. + * cygcheck.cc (find_on_path): Translate from cygwin path when qualified + path name found. + (pretty_id): New function. Dump output of id command. + (dump_sysinfo): Call pretty_id for CYGWIN=ntsec and CYGWIN=nontsec cases. + +2003-02-12 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc (doit): Allocate more space for non-path filename. + +2003-02-07 Christopher Faylor <cgf@redhat.com> + + * dump_setup.cc (compar): Use case insensitive comparisons. + (dump_setup): Calculate package_len based on already used "len". Don't + bother with version_len. + +2003-02-07 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * dump_setup.cc (dump_setup): Compute the longest package name + and align columns properly. + +2003-02-06 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (common_apps): Add some more apps. + * path.cc (get_cygdrive): Correctly set system flag. + +2003-01-22 Corinna Vinschen <corinna@vinschen.de> + + * Makefile.in (ALL_LDFLAGS): Remove newlib/libm from tools paths. + (MINGW_LDFLAGS): Remove in favor of definition from Makefile.common. + +2003-01-15 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c: Fix copyright date. + * mkpasswd.c: Ditto. + * setfacl.c: Ditto. + +2003-01-09 Pierre Humblet <pierre.humblet@ieee.org> + + * setfacl.c (usage): Remove double ":" for mask and other. + +2003-01-07 Pierre Humblet <pierre.humblet@ieee.org> + + * mkpasswd.c (current_user): Create. + (usage): Reorganize to support Win95/98/ME. + (main): Add option for -c. Reorganize to parse options for + Win95/98/ME and to call current_user. Add username in gecos field + on Win95/98/ME. + * mkgroup.c (enum_groups): Print gid with %u. + (print_win_error): Create from passwd.cc. + (current_group): Create. + (usage): Reorganize to support Win95/98/ME. + (main): Add option for -c. Reorganize to parse options for + Win95/98/ME and to call current_group. + +2002-12-14 Pierre Humblet <pierre.humblet@ieee.org> + + * setfacl.c (main): Place a single : after other and mask. + * getfacl.c (getaclentry): Allow both : and :: for other and mask. + (main): Remove extraneous break. + +2002-11-25 Corinna Vinschen <corinna@vinschen.de> + + * mkpasswd.c (main): Set pw_passwd field to '*' on 9x/Me. + +2002-11-24 Corinna Vinschen <corinna@vinschen.de> + + * setfacl.c (getperm): Set only `other' permission bits. + (getaclentry): Set a_id to -1 by default. + +2002-11-24 Corinna Vinschen <corinna@vinschen.de> + + * getfacl.c (permstr): Use `other' permission bits for requesting + ace permissions. + +2002-11-08 Corinna Vinschen <corinna@vinschen.de> + + * setfacl.c (usage): Add missing LF. + +2002-10-30 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc (get_long_path_name_w32impl): Define similarly to + GetLongPathName. + (get_long_name): Correctly prototype GetLongPathName. + (get_long_paths): Implement using get_long_name to cut down on code + duplication. + (doit): Do various things to make path output work predictably. + +2002-10-19 Christopher Faylor <cgf@redhat.com> + + * mount.cc (usage): Correctly report default mode. + * strace.cc (main): Use strtoul as appropriate. + +2002-09-29 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc (close_arg): Remove unused static. + * mkgroup.c (enum_local_users): Avoid compiler warning. + (enum_users): Ditto. + * mkpasswd.c: Ditto throughout. + * ssp.c: Ditto throughout. + +2002-09-18 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc: Include sys/time.h rather than time.h to avoid a + compiler error. + +2002-09-17 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc: Reorder includes to avoid compiler error. + +2002-09-15 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * cygcheck.cc (print_version) Add final newline. + * getfacl.c (print_version) Add final newline. + * kill.cc (print_version) Add final newline. + * mkgroup.c (print_version) Add final newline. + * mkpasswd.c (print_version) Add final newline. + * mount.cc (print_version) Add final newline. + * passwd.c (print_version) Add final newline. + * ps.cc (print_version) Add final newline. + * regtool.cc (print_version) Add final newline. + * setfacl.c (print_version) Add final newline. + * ssp.c (print_version) Add final newline. + * strace.cc (print_version) Add final newline. + * umount.cc (print_version) Add final newline. + +2002-09-12 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * cygpath.cc (options) New global variable. + (main) Make short options global for easier change. + (print_version) Add a missing newline. + +2002-08-07 Igor Pechtchanski <pechtcha@cs.nyu.edu> + + * regtool.cc (find_key): Add support for custom key separator. + (usage): Document it. + +2002-08-02 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c (main): Don't print an entry for "Everyone". + * mkpasswd.c (print_special): Set pw_gid to 544 for SYSTEM user. + (main): Don't print an entry for "Everyone". + +2002-07-06 Christopher Faylor <cgf@redhat.com> + + * configure.in: Check for libiconv.a. + * configure: Regenerate. + * Makefile.in: Add libiconv.a to libraries required for dumper.exe. + +2002-07-06 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Use MINGW stuff from Makefile.common. + +2002-07-01 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c: Add function pointers for functions from advapi32.dll. + (load_netapi): Load used functions from advapi32.dll. + (main): Use function pointers instead of statically linked functions + from advapi32.dll. + +2002-07-01 Christopher Faylor <cgf@redhat.com> + + * mount.cc (main): Ensure that mount_already_exists() also gets default + flag that is passed to do_mount. + +2002-06-22 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * cygpath.cc (long_options): Add "dos" and "mixed", correct "close", + "file" and "type" to use NULL flag. + (usage): Clean up usage output (more), accomodate new options. + (main): Add --dos and --mixed options; accomodate all output forms in + --type. Make UNIXy output default. + +2002-06-18 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * cygpath.cc (usage): Clean up usage output. + (dowin): Correct output of -t mixed for -ADHPSW options. + +2002-06-14 Corinna Vinschen <corinna@vinschen.de> + + * passwd.c: Rearrange includes to avoid unnecessary warnings. + (GetPW): Add parameter to (dis)allow printing of Windows username. + Use defines instead of numerical constants where possible. + Try avoiding impersonation problem. Rearrange to print Windows + username only if it's different from Cygwin username. + (ChangePW): Use defines instead of numerical constants where possible. + (main): Call GetPW with additional parameter. Change error text. + +2002-06-14 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * passwd.c (GetPW): Handle case of user-edited /etc/passwd + with cygwin_internal (CW_EXTRACT_DOMAIN_AND_USER, ...). + +2002-06-09 Christopher Faylor <cgf@redhat.com> + + * path.cc (cygpath): Change MOUNT_AUTO to MOUNT_CYGDRIVE. + (getmntent): Ditto. + +2002-06-09 Christopher Faylor <cgf@redhat.com> + + * mount.cc (main): Use default system/user flag for cygdrive stuff, + too. + (change_cygdrive_prefix): Change MOUNT_AUTO to MOUNT_CYGDRIVE. + * umount.cc (remove_cygdrive_prefix): Ditto. + (main): Use default system/user flag for cygdrive stuff, too. + +2002-06-08 Christopher Faylor <cgf@redhat.com> + + * mount.cc (opts): Remove '-i' option. + (usage): Ditto. + (main): Ditto. + (longopts): Remove --import-old-mounts option. + +2002-06-07 David Peterson <chief@mail.idrive.com> + Christopher Faylor <cgf@redhat.com> + + * cygpath.cc: Add option to output windows paths in different formats: + "dos" and "mixed". + (main): Process options. + (doit): Check new options flags. + +2002-06-06 Egor Duda <deo@logos-m.ru> + + * regtool.cc (Fail): Be more verbose. + (find_key): Add support for remote registry access. + (usage): Document it. + * utils.sgml: Document it. + +2002-06-06 Christopher Faylor <cgf@redhat.com> + + * strace.cc (main): Make toggle a local variable. + +2002-06-07 Conrad Scott <conrad.scott@dsl.pipex.com> + + * strace.cc (toggle): New global variable. + (error): Use exit instead of ExitProcess so that stdio buffers get + flushed. + (create_child): Remove command line error checking. + (dostrace): Ditto. + (dotoggle): New function. + (usage): Add entry for new option -T|--toggle. Alphabetize. + (longopts): Add new option -T|--toggle. + (opts): Ditto. + (main): Handle new -T|--toggle option. Move all command line checking + here from other functions. + * utils.sgml: Update section for strace. + +2002-06-05 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * strace.cc (version): New global variable. + (usage): Accommodate stdout output. + (print_version): New function. + +2002-06-03 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * ssp.c (version): New global variable. + (longopts): Ditto. + (opts): Ditto. + (run_program): Correct cmd_line typos to cmdline. + (usage): New function. Standardize usage output. Add ssp.txt to + --help output. + (print_version): New function. + (main): Accommodate getopt. + +2002-06-03 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * umount.cc (version): New global variable. + (longopts): Accommodate new --version option. + (opts): Ditto. + (usage): Standardize usage output. + (print_version): New function. + (main): Accommodate --help, --version options. + +2002-06-02 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * regtool.cc (prog_name): New global variable. + (longopts): Ditto. + (opts): Ditto. + (usage): Standardize usage output. Rearrange/add descriptions. + (print_version): New function. + (main): Accommodate longopts and new --help, --version options. Add + check for (_argv[optind+1] == NULL). + +2002-06-02 Christopher Faylor <cgf@redhat.com> + + * strace.cc (forkdebug): Make true by default. + (attach_process): Use window pid if cygwin pid isn't available (yet). + (create_child): Use either DEBUG_ONLY_THIS_PROCESS or DEBUG_PROCESS, + exclusively. (Suggested by Conrad.Scott@dsl.pipex.com) + +2002-05-30 Christopher Faylor <cgf@redhat.com> + + * mkpasswd.cc (main): Don't reset to binmode if stdout is a terminal. + +2002-05-29 Christopher Faylor <cgf@redhat.com> + + * mount.cc (main): Make -b the default. + +2002-05-29 Corinna Vinschen <corinna@vinschen.de> + + * ps.cc (main): Use uid or uid32 member of struct external_pinfo + dependent of the value of the struct's version member. + +2002-05-29 Corinna Vinschen <corinna@vinschen.de> + + * ps.cc (main): Change print format for uid to unsigned. Use uid32 + member of struct external_pinfo instead of uid. + +2002-05-28 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * passwd.c (prog_name): New global variable. + (longopts): Ditto. + (opts): Ditto. + (usage): Standardize output. Accommodate new options. + (print_version): New function. + (main): Accommodate longopts and new --help, --version options. + +2002-05-28 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * ps.cc (usage): Fix typo. + +2002-05-27 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * ps.cc (prog_name): New global variable. + (longopts): Ditto. + (opts): Ditto. + (usage): New function. + (print_version): New function. + (main): Accommodate longopts and new --help, --version options. + +2002-05-26 Christopher Faylor <cgf@redhat.com> + + * strace.cc (attach_process): Don't tell process to start stracing + here. + (proc_child): Do it here, instead, after first debug event. This + should work around inexplicable races with DebugActiveProcess. + (dostrace): Pass any pid argument to proc_child. + +2002-05-23 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * getfacl.c (usage): Standardize usage output. Change return type to + static void. + (print_version): New function. + (longopts): Added longopts for all options. + (main): Accommodate new help and version options. + +2002-05-22 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * mount.cc (version): New global variable. + (usage): Standardize usage output. Accommodate new version option. + (print_version): New function. + (longopts): Accommodate new version option. + (opts): Ditto. + (main): Ditto. + +2002-05-22 Corinna Vinschen <corinna@vinschen.de> + + * cygpath.cc (usage): Change usage output slightly. + +2002-05-20 Joerg Schaible <joerg.schaible@gmx.de> + + * cygpath.cc (main): Add option l to support conversion to + Windows long file names. Refactured code for capital options. + Support of options from file for capital options. + (dowin): New function. Refactured from main. + (doit): Call long path conversion. + (get_long_name): New function. + (get_long_paths): New function. + (get_long_path_name_w32impl): New function. Reimplementation + of Windows API function GetLongPathName (only 98/Me/2000/XP or + higher). + (get_short_name): Call GetShortPathName only once. + (get_short_paths): Fix calculating buffer size. + * utils.sgml: Update cygpath section for l option. + +2002-05-18 Christopher Faylor <cgf@redhat.com> + + * strace.cc (add_child): Use calloc since new requires working + libstdc++.a. + (remove_child): Ditto for delete/free. + +2002-05-15 Mark Blackburn <marklist@fangorn.ca> + + * cygpath.cc (get_short_paths): Fix more error checking. + (get_short_name): Ditto. + +2002-05-14 Joerg Schaible <joerg.schaible@gmx.de> + + * cygpath.cc (main): Add option H to show the Windows' profiles + directory. Support short names for options DPSW. Clean up + copied code lines. + * utils.sgml: Update cygpath section for H option and s support. + +2002-05-14 Mark Blackburn <marklist@fangorn.ca> + + * cygpath.cc (get_short_paths): Fix error checking. + +2002-05-13 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * kill.cc (prog_name): New global variable. + (usage): Standardize usage output. Add descriptions. + (print_version): New function. + (longopts): Accommodate new version option. + (opts): Ditto. + (main): Ditto. + +2002-05-13 Christopher Faylor <cgf@redhat.com> + + * kill.cc (listsig): Display numeric id when given symbolic input. + +2002-05-13 Christopher Faylor <cgf@redhat.com> + + * kill.cc (usage): Show new options. Allow specification of output + stream. + (main): Implement 'h' option. + +2002-05-13 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (kill.exe): Add as a specific target. + * kill.cc (longopts): New. + (opts): Ditto. + (get_sig): Accept const char * parameter. Return -1 on unknown signal. + (test_for_unknown_sig): New function. + (listsig): New function. + (main): Use getopt_long for option parsing. Implement -l, and -s + options. Use test_for_unknown_sig() to test for signal validity. + + * mount.cc (longopts): Make static. + (opts): Ditto. + +2002-05-12 Christopher Faylor <cgf@redhat.com> + + * mount.cc (do_mount): Default to non-exec option for remote drives. + Report warnings before attempting a mount. + (longopts): Add no-executable option. + (mount_commands): Ditto. + (opts): Ditto. + (usage): Ditto. Indicate that system mount is now the default. + (main): Accommodate no-executable option. Make system mount the + default. + * umount.cc (usage): Indicate that system umount is now the default. + (main): Make system umount the default. + +2002-05-07 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * dumper.cc (usage): Standardize usage output. Generalize to allow use + for help. + (longopts): New struct. Added longopts for all options. + (print_version): New function. + (main): Change getopt to getopt_long. Accommodate new help and version + options. + +2002-03-29 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c (main): Change call to exit() to a return statement. + * mkpasswd.c (main): Ditto. + +2002-03-27 Joshua Daniel Franklin joshuadfranklin@yahoo.com + + * mkpasswd.c (usage): Simplify usage output. Generalize to allow use + for help. Correct '?' typo to 'h'. + (longopts): Add version option. + (opts): Add 'v' version option. + (print_version): New function. + (main): Accommodate new version option. Accommodate usage parameter + changes. + +2002-03-19 Christopher Faylor <cgf@redhat.com> + + * mkgroup.c (usage): Use one just fprintf + string concatenation for + output message. + (print_version): Add void to function type and update copyright. + +2002-03-16 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * mkgroup.c (usage): Simplify usage output. Generalize to allow use + for help. Correct '?' typo to 'h'. + (longopts): Add version option. + (opts): Add 'v' version option. + (print_version): New function. + (main): Accommodate new version option. Accommodate usage parameter + changes. Use usage to output help message. + +2002-03-15 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c (main): When generating group 513, check for computer's + primary domain instead of current user's domain. + +2002-03-14 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c (main): When generating group 513, check for domain SID + if computer name isn't mapped to a SID. + +2001-03-11 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * kill.cc (usage): Move to top of file. + (getsig): Ditto. + (forcekill): Ditto. + +2002-03-06 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc: Reformat. + +2002-03-06 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (longopts): Use correct short option for --version. + +2002-03-06 Christopher Faylor <cgf@redhat.com> + + * cygpath.cc: Reformat. + +2002-02-27 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * cygpath.cc (print_version): New function. + (main): Accommodate new version function. Initialize 'o' to prevent + warning. + +2002-02-27 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (main): Eliminate non-GNU formatting glitch. + +2002-02-27 Christopher Faylor <cgf@redhat.com> + + * ssp.c (help_desk): Fix compiler warning. + +2002-02-27 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + Christopher Faylor <cgf@redhat.com> + + Change appropriate globals to statics throughout. + * cygcheck.cc (usage): Simplify usage output. Generalize to allow use + for help. + (longopts): Add version option. + (opts): Add 'V" version option. + (print_version): New function. + (main): Accommodate new version option. Accommodate usage parameter + changes. Use usage to output help message. + +002-02-19 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (CXXFLAGS): Add override so that HAVE_DECL_GETOPT is + defined. + (libbfd): Use -B rather than -L so that bfd from build directory is + correctly found. + (libintl): Ditto. + +2002-02-15 Christopher Faylor <cgf@redhat.com> + + * mount.cc (usage): Fix typo in output. + +2002-02-14 Christopher Faylor <cgf@redhat.com> + + * strace.cc (attach_process): Change CW_STRACE_ON to CW_STRACE_TOGGLE. + +2002-01-31 Corinna Vinschen <corinna@vinschen.de> + + * mkpasswd.c (main): Set default home path for 9x/Me if --path-to-home + isn't given. + +2002-01-30 Corinna Vinschen <corinna@vinschen.de> + + * passwd.cc (ChangePW): Add parameter to differ between called for + checking old password and called for changing password. If called + for checking old password, return non-zero only if password is surely + incorrect. + (main): Call ChangePW() with additional parameter. + +2002-01-29 Christopher Faylor <cgf@redhat.com> + + * dump_setup.cc (parse_filename): Don't consider '_' part of the + version. + +2002-01-28 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc: Fix typo. Remove uid_t kludge. Rely on kludge in + sys/cygwin.h instead. + * getfacl.c: Add include to remove warning. + * mkgroup.c (main): Assign variables outside of parameter passing. + This seems to eliminate some compiler warnings. + +2002-01-28 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc: Use specific cygwin includes, as required. + +2002-01-28 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (MINGW_INCLUDES): Change cygwin include. + * strace.cc: Use specific cygwin includes, as required. + (load_cygwin): New function. Loads cygwin DLL, if necessary. + (attach_process): Attaches strace to a running process. + (dostrace): Accept pid argument. Attach to pid if supplied. + (usage): Describe -p option. Correct system description. + (longopts): Alphabetize. + (opts): Ditto. + (main): Ditto. Handle -p option. + +2002-01-21 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (CXXFLAGS): Ensure that important settings are preserved + even when overriden by command line. + +2002-01-21 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Build intermediate cygcheck.o to force use of MINGW_CXXFLAGS. + * cygcheck.cc (cygwin_info): Intitialize variable to quiet g++ warning. + (dump_sysinfo): Make variables unsigned to quiet g++ warnings. + * strace.cc (version_string): Rename from SCCSid. + (add_child): Remove unused variable. + (version): Use version_string. Avoid use of fprintf. + +2002-01-21 DJ Delorie <dj@redhat.com> + + * Makefile.in: Use CXX instead of CC to compile C++ sources. + +2002-01-17 DJ Delorie <dj@redhat.com> + + * cygcheck.cc (already_did): Avoid default constructors, which may + require exception support. + +2001-01-16 Joshua Daniel Franklin <joshuadfranklin@yahoo.com> + + * cygpath.cc (main): Add options to show Desktop and Start + Menu's Programs directory for current user or all users. + Move bulk of DPWS options outside the getopt case statement. + * utils.sgml: Update cygpath section for ADPWS options. + +2002-01-15 Joerg Schaible <joerg.schaible@gmx.de> + + * cygpath.cc (doit): Empty file ignored using option -i. + +2002-01-15 Mark Bradshaw <bradshaw@crosswalk.com> + + * mkpasswd.c (print_win_error): Add a new function. + (enum_users): Use print_win_error. + (enum_local_groups): Ditto. + (main): Ditto. + +2001-12-26 Jonathan Kamens <jik@curl.com> + + * cygpath.cc (doit): Detect and warn about an empty path. Detect and + warn about errors converting a path. + (main): Set prog_name correctly -- don't leave an extra slash or + backslash at the beginning of it. + +Fri Dec 14 14:04:37 2001 Jason Tishler <jason@tishler.net> + + * mkpasswd.c (enum_users): Change to unconditionally use + the --path-to-home option, if supplied by the user. Use default + --path-to-home option value, if appropriate. + (usage): Change usage statement to reflect new semantics. + (main): Remove defaulting of the --path-to-home option. + +Fri Dec 14 12:10:39 2001 Jason Tishler <jason@tishler.net> + + * mkpasswd.c (opts): Add indication that '-p' option requires an + argument. + +2001-12-11 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Add define to CXXFLAGS. + +2001-12-03 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (usage): Add -c description. + * cygpath.cc (usage): Alphabetize options. + * strace.cc (usage): Ditto. + +2001-11-21 Christopher Faylor <cgf@redhat.com> + + * path.cc (cygpath): Don't consider cygdrive stuff when trying to derive + native paths. + +2001-11-20 Mark Bradshaw <bradshaw@staff.crosswalk.com> + + * mkpasswd.c: include lmerr.h + (main): New -u option to allow specifying a + specific user. If specified, groups aren't displayed and + output is limited to only the specified user. + (enum_users): If specific user is specified, via -u option, + display only that user's record. With -u use NetUserGetInfo + instead of NetUserEnum. + (load_netapi): Added netusergetinfo. + +2001-11-15 Gary R. Van Sickle <g.r.vansickle@worldnet.att.net> + + * strace.cc (main): Change getopt() to getopt_long(). + Add support for help and version info. + Use new parse_mask() function for -m/--mask option. + (longopts): Add long options structure. + (opts): Move options string from getopts call to static var. + (usage): Print usage information. + (SCCSid): Version info. + (version): New function for displaying version info. + (parse_mask): New function supporting parsing of mnemonics, + hex, and basic expressions in masks. + (mnemonic2ul): New mnemonic parsing function. + (tag_mask_mnemonic): New type. + (mnemonic_table): New table of mnemonics for mnemonic2ul() to + search through. + +2001-11-12 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Redefine output format slightly. + Print correct build number on 9x/ME systems. + +2001-11-12 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (main): Slight formatting tweak. + +2001-11-12 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Add some more details. + +2001-11-12 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Print more detailed OS information string + using OSVERSIONINFOEX information. + +2001-11-11 Christopher Faylor <cgf@redhat.com> + + * path.h: New file. + +2001-11-11 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (scan_registry): Open registry with read-only access. + (main): Reflect argument change for dump_setup. + * dump_setup.cc (dump_setup): Add preliminary extra argument for future + use. + * path.cc (read_mounts): Open registry with read-only access. + +2001-11-11 Christopher Faylor <cgf@redhat.com> + + * cygcheck.cc (main): Display package info when '-s' is specified. + * dump_setup.cc (dump_setup): Change header. Remove typo. Always sort + packages output. + +2001-11-11 Christopher Faylor <cgf@redhat.com> + + * dump_setup.cc: New file. + * Makefile.in: Add info for dump_setup.o. + * cygcheck.cc (main): Recognize '-c' option for checking setup + installed base. + * path.cc (cygpath): Read mount table if not loaded. + +2001-11-10 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (cygcheck.exe): Compile as -mno-cygwin executable. + * path.cc: New file. + * cygcheck.cc (init_paths): Use MS-DOS path syntax. + (cygwin_info): Properly display cygwin version numbers. Prettify some + output. + (dump_sysinfo): Calculate max names of posix and ms-dos paths for + prettier output. + +2001-11-09 Corinna Vinschen <corinna@vinschen.de> + + * cygcheck.cc (dump_sysinfo): Print more detailed OS information string. + +2001-11-04 Corinna Vinschen <corinna@vinschen.de> + + * getfacl.c (username): New function. + (groupname): Ditto. + (usage): Ditto. Add more user friendly help text. + (main): Add -n and --help option. Print names instead of IDs + unless -n option is given. + * setfacl.c (getperm): Tolerate whitespace and comment chars in input. + (getaclentry): Fix several input string misdetections. + (getaclentries): Allow - as input file name to read from stdin. + Tolerate whitespace and comment chars in input. + (usage): Add more user friendly help text. + (main): Add --help option. + +2001-11-04 Egor Duda <deo@logos-m.ru> + + * strace.cc (main): New option '-w'. Start traced process in separate + window. New option '-S x'. Flush buffered output every x seconds. + (create_child): Start child process in new window, when requested. + When requested, periodically flush debugging output. + +2001-10-24 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Remove EXEEXT consideration. We always need .exe + extensions. + +2001-10-20 Corinna Vinschen <corinna@vinschen.de> + + * mkgroup.c: Avoid compiler warnings. + (print_special): New function. + (main): Print special accounts by calling print_special(). + * mkpasswd.c: Avoid compiler warnings. + (enum_users): Print additional U-domain\username info in gecos + field when SIDs are printed. + (print_special): New function. + (main): Print special accounts by calling print_special(). + +2001-10-15 Christopher Faylor <cgf@redhat.com> + + * mkpasswd.cc (enum_users): Shorten "unused" passwd field. + +2001-10-13 Christopher Faylor <cgf@redhat.com> + + * Makefile.in: Allow stdinc searching for dumper. + +Tue Oct 9 21:59:00 2001 Christopher Faylor <cgf@cygnus.com> + + * Makefile.in (MINGW_INCLUDES): Accommodate changes in Makefile.common. + +Sun Oct 7 23:06:39 2001 Christopher Faylor <cgf@cygnus.com> + + * kill.cc (main): Set exit value to 1 on invalid pid. Perform minor + cleanup. + +Fri Sep 21 20:40:30 2001 Christopher Faylor <cgf@cygnus.com> + + * Makefile.in (mingw_getopt.o): Ensure that newlib include directories + are not searched. + +Thu Sep 20 21:00:20 2001 Christopher Faylor <cgf@cygnus.com> + + * Makefile.in (MINGW_INCLUDES): Still need cygwin/include directory. + +Wed Sep 19 12:22:08 2001 Christopher Faylor <cgf@cygnus.com> + + * Makefile.in: Filter newlib includes from mingw compilation. + +2001-09-14 Egor Duda <deo@logos-m.ru> + + * dumper.cc (dumper::dumper): Print error code in case of error. + (dumper::add_thread): Ditto. + (dumper::collect_memory_sections): Ditto. + (dumper::dump_memory_region): Ditto. + (dumper::collect_process_information): Ditto. + (print_section_name): Fix formatting. + +2001-09-13 Egor Duda <deo@logos-m.ru> + + * dumper.cc (main): Properly recognize negative pids (used by w9x). + +2001-09-09 Egor Duda <deo@logos-m.ru> + + * dumper.cc (main): Change command-line arguments format to be + similar to gdb. Allow adding error_start=x:\path\to\dumper.exe to + CYGWIN environment variable to perform core dumping in case of + program crash. + (usage): Ditto. + +Wed Sep 5 22:37:21 2001 Christopher Faylor <cgf@cygnus.com> + + * Makefile.in (dumper): Change logic for libbfd and libintl so that + they will be found either in the build tree or the installed directory. + * mkpasswd.c (enum_users): Add a comment as a password for NT. + +2001-09-03 Michael A Chase <mchase@ix.netcom.com> + + * mount.cc (mount_commands): Don't write /cygdrive/u mount points. + +2001-09-03 Michael A Chase <mchase@ix.netcom.com> + + * mount.cc (longopts): Add mount-commands to options list. + (usage): Add mount-commands option. + (mount_commands): New function. Write commands to recreate current + mounts. + +Sat Sep 1 15:58:11 2001 Christopher Faylor <cgf@cygnus.com> + + * mount.cc (main): Eliminate excess arguments in printf. + +2001-08-30 Egor Duda <deo@logos-m.ru> + + * dumper.h: Update copyright notice. + * dumper.cc: Ditto. + * dumper.cc: (dumper::print_core_section_list): New function. + * dumper.h: (dumper::print_core_section_list): Declare it. + * dumper.cc (print_section_name): New function. + (dumper::collect_process_information): Augment debugging output. + Stop debugee processing if it posts double exception. + (usage): Fix typo in option name. + +Tue Aug 28 14:45:02 2001 Christopher Faylor <cgf@cygnus.com> + + * mount.cc (main): Issue correct warning for 'not enough arguments'. + +2001-08-14 Joerg Schaible <joerg.schaible@gmx.de> + + * cygpath.cc (main): Support -w for Windows (System) directories and + return physical correct orthography for the Windows System dir. + +2001-07-14 Chris Genly <chgenly@alum.mit.edu> + + * regtool.cc (find_key): Handle keys with only one subkey. + +Wed Jun 27 22:46:10 2001 Christopher Faylor <cgf@cygnus.com> + + * regtool.cc (find_key): Revert previous change. + +Wed Jun 27 13:37:41 2001 Keith Starsmeare" <keith_starsmeare@yahoo.co.uk> + + * regtool.cc (find_key): Allow '/' as a synonym for '\\'. + +Fri Jun 15 00:49:21 2001 Christopher Faylor <cgf@cygnus.com> + + * mkpasswd.c (main): Output passwd file in binary mode so that there + are no CRs in the file. + * umount.cc (remove_all_user_mounts): Don't try to remove /cygdrive + mounts. + +Tue May 1 10:50:48 2001 Christopher Faylor <cgf@cygnus.com> + + * mkpasswd.c (enum_users): Use /bin/bash as the default shell. + (main): Ditto. + +Sat Apr 28 22:32:01 2001 Christopher Faylor <cgf@cygnus.com> + + * passwd.cc (ttymnam): New function. + (main): Use ttynam() to report name of tty. + +2001-04-19 Egor Duda <deo@logos-m.ru> + + * dumper.cc (dumper::init_core_dump): Set architecture field in dump + header. + +Mon Apr 16 15:08:00 2001 Corinna Vinschen <vinschen@redhat.com> + + * mkgroup.c: Add function pointers `netlocalgroupgetmembers' and + `netgroupgetusers'. + (load_netapi): Load function pointers `netlocalgroupgetmembers' + and `netgroupgetusers'. + (enum_local_users): New function. + (enum_local_groups): Call `enum_local_users' for each group if + `print_users' is set. + (enum_users): New function. + (enum_groups): Call `enum_users' for each group if `print_users' is set. + (usage): Add usage text for `-u/--users'. + (longopts): Add option `--users'. + (opts): Add option character `u'. + (main): Set `print_users' according to option `-u/--users'. + Call `enum_local_groups' and `enum_groups' with additional parameter + `print_users'. + +2001-04-15 Michael A Chase <mchase@ix.netcom.com> + + * mount.cc (longopts): Add help to options list. + (opts): Add 'h' to options string. + * umount.cc (longopts): Add help to options list. + (opts): Add 'h' to options string and change 'R' to 'A'. + +2001-04-13 Kazuhiro Fujieda <fujieda@jaist.ac.jp> + + * mkgroup.c (enum_groups): Use RID + offset specified an additional + argument as ID. + (usage): Add description of -o option. + (longopts, opts): Add specifications of -o/--id-offset option. + (main): Add -o option. Invoke enum_groups with specified offset. + * mkpasswd.c (enum_users): Just like mkgroup.c. + (usage, longopts, opts): Ditto. + (main): Add -o option. Invoke enum_users with specified offset only + against domain accounts. + +2001-04-11 Egor Duda <deo@logos-m.ru> + + * mkgroup.c (uni2ansi): Use native method to convert from Unicode + to multi-byte strings. + * mkpasswd.c (uni2ansi): Ditto. + (enum_users): Pass buffer size when converting string. + (enum_local_groups): Ditto. + * mkgroup.c (enum_groups): Ditto. + (enum_local_groups): Ditto. + +Mon Apr 2 22:41:33 2001 Christopher Faylor <cgf@cygnus.com> + + * mount.cc (main): Use getopt_long for parsing arguments. + (usage): Reformat, show long and short options. + * umount.cc (main): Ditto, all of the above. + +Mon Apr 2 10:58:26 2001 Christopher Faylor <cgf@cygnus.com> + + * mount.cc (show_mounts): Change format string to more closely resemble + UNIX when displaying mount table. + +Sat Mar 17 21:46:06 2001 Christopher Faylor <cgf@cygnus.com> + + * kill.cc (forcekill): Use dwProcessId when opening a process, not the + Cygwin pid. + +Mon Mar 5 18:50:00 2001 Corinna Vinschen <vinschen@redhat.com> + + * getfacl.c: Add copyright hint. + * setfacl.c: Ditto. + * strace.cc: Ditto. + +Tue Jan 09 10:26:23 2001 Tony Sideris <tonys1110@home.com> + + * regtool.cc (cmd_list): Add new registry display options. + (cmd_list): Add code to implement -p, -k, and -l options. + + * regtool.cc (Fail): Add call to LocalFree to free memory allocated by + FormatMessage. + +Wed Jan 10 09:16:57 2001 Christopher Faylor <cgf@cygnus.com> + + * regtool.cc (translate): Ensure that 'c' is initialized. + (cmd_set): Ditto for rv. diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in new file mode 100644 index 00000000000..5c8a2da37be --- /dev/null +++ b/winsup/utils/Makefile.in @@ -0,0 +1,225 @@ +# Makefile for Cygwin utilities +# Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2007 Red Hat, Inc. + +# This file is part of Cygwin. + +# This software is a copyrighted work licensed under the terms of the +# Cygwin license. Please consult the file "CYGWIN_LICENSE" for +# details. + +SHELL:=@SHELL@ + +srcdir:=@srcdir@ +VPATH:=@srcdir@ +prefix:=@prefix@ +exec_prefix:=@exec_prefix@ + +bindir:=@bindir@ +program_transform_name:=@program_transform_name@ + +override INSTALL:=@INSTALL@ +override INSTALL_PROGRAM:=@INSTALL_PROGRAM@ +override INSTALL_DATA:=@INSTALL_DATA@ + +EXEEXT:=@EXEEXT@ +EXEEXT_FOR_BUILD:=@EXEEXT_FOR_BUILD@ + +CC:=@CC@ +CC_FOR_TARGET:=$(CC) +CXX:=@CXX@ +CXX_FOR_TARGET:=$(CXX) + +CFLAGS:=@CFLAGS@ +CXXFLAGS:=@CXXFLAGS@ +override CXXFLAGS+=-fno-exceptions -fno-rtti -DHAVE_DECL_GETOPT=0 + +include $(srcdir)/../Makefile.common + +LIBICONV:=@libiconv@ +libbfd:=${shell $(CC) -B$(bupdir2)/bfd/ --print-file-name=libbfd.a} +libintl:=${shell $(CC) -B$(bupdir2)/intl/ --print-file-name=libintl.a} +build_dumper:=${shell test -r $(libbfd) -a -r $(libintl) -a -n "$(LIBICONV)" && echo 1} + +libz:=${shell x=$$($(CC) -mno-cygwin --print-file-name=libz.a); cd $$(dirname $$x); dir=$$(pwd); case "$$dir" in *mingw*) echo $$dir/libz.a ;; esac} +zlib_h:=-include ${patsubst %/lib/mingw/libz.a,%/include/zlib.h,${patsubst %/lib/libz.a,%/include/zlib.h,$(libz)}} +zconf_h:=${patsubst %/zlib.h,%/zconf.h,$(zlib_h)} +ifeq "${libz}" "" +zlib_h:= +zconf_h:= +libz:= +endif + +DUMPER_INCLUDES:=-I$(bupdir2)/bfd -I$(updir1)/include + +libcygwin:=$(cygwin_build)/libcygwin.a +libuser32:=$(w32api_lib)/libuser32.a +libkernel32:=$(w32api_lib)/libkernel32.a +ALL_DEP_LDLIBS:=$(libcygwin) $(w32api_lib)/libnetapi32.a \ + $(w32api_lib)/libadvapi32.a $(w32api_lib)/libkernel32.a \ + $(w32api_lib)/libuser32.a + +ALL_LDLIBS:=${patsubst $(w32api_lib)/lib%.a,-l%,\ + ${filter-out $(libuser32),\ + ${filter-out $(libkernel32),\ + ${filter-out $(libcygwin), $(ALL_DEP_LDLIBS)}}}} + +MINGW_LIB:=$(mingw_build)/libmingw32.a +DUMPER_LIB:=${libbfd} ${libintl} -L$(bupdir1)/libiberty $(LIBICONV) -liberty +MINGW_LDLIBS:=${filter-out $(libcygwin),$(ALL_LDLIBS) $(MINGW_LIB)} +MINGW_DEP_LDLIBS:=${ALL_DEP_LDLIBS} ${MINGW_LIB} +ALL_LDFLAGS:=-B$(newlib_build)/libc -B$(w32api_lib) $(LDFLAGS) $(ALL_LDLIBS) +DUMPER_LDFLAGS:=$(ALL_LDFLAGS) $(DUMPER_LIB) +MINGW_CXX:=${patsubst %/cygwin/include,%/mingw/include,${filter-out -I$(newlib_source)/%,$(COMPILE_CXX)}} -I$(updir) + +PROGS:= cygcheck.exe cygpath.exe getfacl.exe kill.exe mkgroup.exe \ + mkpasswd.exe mount.exe passwd.exe ps.exe regtool.exe setfacl.exe \ + setmetamode.exe ssp.exe strace.exe umount.exe ipcrm.exe ipcs.exe + +CLEAN_PROGS:=$(PROGS) +ifndef build_dumper +PROGS:=warn_dumper $(PROGS) +else +PROGS+=dumper$(EXEEXT) +CLEAN_PROGS+=dumper.exe +endif + +.SUFFIXES: +.NOEXPORT: + +.PHONY: all install clean realclean warn_dumper + +all: Makefile $(PROGS) + +strace.exe: strace.o path.o $(MINGW_DEP_LDLIBS) +ifdef VERBOSE + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,2,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) +else + @echo $(CXX) -o $@ ${wordlist 1,2,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)};\ + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,2,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) +endif + +cygcheck.exe: cygcheck.o bloda.o path.o dump_setup.o $(MINGW_DEP_LDLIBS) +ifeq "$(libz)" "" + @echo '*** Building cygcheck without package content checking due to missing mingw libz.a.' +endif +ifdef VERBOSE + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,4,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) $(libz) -lntdll +else + @echo $(CXX) -o $@ ${wordlist 1,4,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)} $(libz) -lntdll;\ + $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,4,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) $(libz) -lntdll +endif + +dumper.o: dumper.cc dumper.h +ifdef VERBOSE + ${filter-out -nostdinc,$(COMPILE_CXX)} $c -o $@ $(DUMPER_INCLUDES) ${firstword $^} +else + @echo $(CXX) $c $(CFLAGS) $(DUMPER_INCLUDES) ... $(basename $@).cc;\ + ${filter-out -nostdinc,$(COMPILE_CXX)} $c -o $(@D)/$(basename $@)$o $(DUMPER_INCLUDES) $< +endif + +module_info.o: module_info.cc +ifdef VERBOSE + ${filter-out -nostdinc,$(COMPILE_CXX)} $c -o $@ $(DUMPER_INCLUDES) ${firstword $^} +else + @echo $(CXX) $c $(CFLAGS) $(DUMPER_INCLUDES) ... $(basename $@).cc;\ + ${filter-out -nostdinc,$(COMPILE_CXX)} $c -o $(@D)/$(basename $@)$o $(DUMPER_INCLUDES) $< +endif + +parse_pe.o: parse_pe.cc dumper.h +ifdef VERBOSE + ${filter-out -nostdinc,$(COMPILE_CXX)} $c -o $@ $(DUMPER_INCLUDES) ${firstword $^} +else + @echo $(CXX) $c $(CFLAGS) $(DUMPER_INCLUDES) ... $(basename $@).cc;\ + ${filter-out -nostdinc,$(COMPILE_CXX)} $c -o $(@D)/$(basename $@)$o $(DUMPER_INCLUDES) $< +endif + +path.o: path.cc +ifdef VERBOSE + $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< +else + @echo $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) ... $^;\ + ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< +endif + +dump_setup.o: dump_setup.cc +ifdef VERBOSE + $(MINGW_CXX) $(zconf_h) $(zlib_h) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< +else + @echo $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) ... $^;\ + $(MINGW_CXX) $(zconf_h) $(zlib_h) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) $< +endif + +bloda.o: bloda.cc +ifdef VERBOSE + ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< +else + @echo $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) ... $^;\ + ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< +endif + +cygcheck.o: cygcheck.cc +ifdef VERBOSE + ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< +else + @echo $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) ... $^;\ + ${MINGW_CXX} $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< +endif + +strace.o: strace.cc +ifdef VERBOSE + $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< +else + @echo $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) ... $^;\ + $(MINGW_CXX) $c -o $(@D)/$(basename $@)$o $(MINGW_CXXFLAGS) -I$(updir) $< +endif + +kill.exe: kill.o $(bupdir1)/libiberty/strsignal.o +ifdef VERBOSE + $(CXX) -o $@ $^ -B$(cygwin_build)/ $(ALL_LDFLAGS) $(KILL_LIB) +else + @echo $(CXX) -o $@ $^ ${filter-out -B%, $(ALL_LDFLAGS)};\ + $(CXX) -o $@ $^ -B$(cygwin_build)/ $(ALL_LDFLAGS) $(KILL_LIB) +endif + +clean: + rm -f *.o $(CLEAN_PROGS) + +realclean: clean + rm -f Makefile config.cache + +install: all + $(SHELL) $(updir1)/mkinstalldirs $(bindir) + for i in $(PROGS) ; do \ + n=`echo $$i | sed '$(program_transform_name)'`; \ + $(INSTALL_PROGRAM) $$i $(bindir)/$$n; \ + done + +$(cygwin_build)/libcygwin.a: $(cygwin_build)/Makefile + @$(MAKE) -C $(@D) $(@F) + +$(mingw_build)/libmingw32.a: $(mingw_build)/Makefile + @$(MAKE) -C $(@D) $(@F) + +warn_dumper: + @echo '*** Not building dumper.exe since some required libraries are' + @echo '*** missing: libbfd.a and libintl.a.' + @echo '*** If you need this program, check out the naked-bfd and naked-intl' + @echo '*** sources from sources.redhat.com. Then, configure and build these' + @echo '*** libraries. Otherwise, you can safely ignore this warning.' + +dumper.exe: module_info.o parse_pe.o dumper.o $(ALL_DEP_LDLIBS) +ifdef VERBOSE + $(CXX) -o $@ ${wordlist 1,3,$^} -B$(cygwin_build)/ $(DUMPER_LDFLAGS) +else + @echo $(CXX) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(DUMPER_LDFLAGS)};\ + $(CXX) -o $@ ${wordlist 1,3,$^} -B$(cygwin_build)/ $(DUMPER_LDFLAGS) +endif + +%.exe: %.o $(ALL_DEP_LDLIBS) +ifdef VERBOSE + $(CXX) -o $@ ${firstword $^} -B$(cygwin_build)/ $(ALL_LDFLAGS) +else + @echo $(CXX) -o $@ ${firstword $^} ... ${filter-out -B%, $(ALL_LDFLAGS)};\ + $(CXX) -o $@ ${firstword $^} -B$(cygwin_build)/ $(ALL_LDFLAGS) +endif diff --git a/winsup/utils/bloda.cc b/winsup/utils/bloda.cc new file mode 100644 index 00000000000..52aa67c941f --- /dev/null +++ b/winsup/utils/bloda.cc @@ -0,0 +1,410 @@ +/* bloda.cc + + Copyright 2007 Red Hat, Inc. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#define cygwin_internal cygwin_internal_dontuse +#include <stdio.h> +#include <assert.h> +#include <windows.h> +#include <ntdef.h> +#include <ddk/ntstatus.h> +#include <ddk/ntapi.h> +#undef cygwin_internal + +#undef DEBUGGING +#ifdef DEBUGGING +#define dbg_printf(ARGS) printf ARGS ; fflush (NULL) +#else /* !DEBUGGING */ +#define dbg_printf(ARGS) do { } while (0) +#endif /* ?DEBUGGING */ + +/* This module detects applications from the Big List of Dodgy Apps, + a list of applications that have at some given time been shown to + interfere with the operation of cygwin. It detects the presence of + applications on the system by looking for any of four traces an + installation might leave: 1) registry keys, 2) files on disk + 3) running executables 4) loaded dlls or drivers. + + At the time of writing, the BLODA amounts to:- + + Sonic Solutions burning software containing DLA component + Norton/MacAffee/Symantec antivirus or antispyware + Logitech webcam software with "Logitech process monitor" service + Kerio, Agnitum or ZoneAlarm Personal Firewall + Iolo System Mechanic/AntiVirus/Firewall + LanDesk + Windows Defender + Embassy Trust Suite fingerprint reader software containing wxvault.dll +*/ + +enum bad_app +{ + SONIC, NORTON, MACAFFEE, SYMANTEC, + LOGITECH, KERIO, AGNITUM, ZONEALARM, + IOLO, LANDESK, WINDEFENDER, EMBASSYTS +}; + +struct bad_app_info +{ + enum bad_app app_id; + const char *details; + char found_it; +}; + +enum bad_app_det_method +{ + HKLMKEY, HKCUKEY, FILENAME, PROCESSNAME, HOOKDLLNAME +}; + +struct bad_app_det +{ + enum bad_app_det_method type; + const char *param; + enum bad_app app; +}; + +static const struct bad_app_det dodgy_app_detects[] = +{ + { PROCESSNAME, "dlactrlw.exe", SONIC }, + { HOOKDLLNAME, "wxvault.dll", EMBASSYTS }, + { HKLMKEY, "SYSTEM\\CurrentControlSet\\Services\\vsdatant", ZONEALARM }, + { FILENAME, "%windir%\\System32\\vsdatant.sys", ZONEALARM }, + { HKLMKEY, "SYSTEM\\CurrentControlSet\\Services\\lvprcsrv", LOGITECH }, + { PROCESSNAME, "LVPrcSrv.exe", LOGITECH }, + { FILENAME, "%programfiles%\\common files\\logitech\\lvmvfm\\LVPrcSrv.exe", LOGITECH }, +}; + +static const size_t num_of_detects = sizeof (dodgy_app_detects) / sizeof (dodgy_app_detects[0]); + +static struct bad_app_info big_list_of_dodgy_apps[] = +{ + { SONIC, "Sonic Solutions burning software containing DLA component" }, + { NORTON, "Norton antivirus or antispyware software" }, + { MACAFFEE, "Macaffee antivirus or antispyware software" }, + { SYMANTEC, "Symantec antivirus or antispyware software" }, + { LOGITECH, "Logitech Process Monitor service" }, + { KERIO, "Kerio Personal Firewall" }, + { AGNITUM, "Agnitum Personal Firewall" }, + { ZONEALARM, "ZoneAlarm Personal Firewall" }, + { IOLO, "Iolo System Mechanic/AntiVirus/Firewall software" }, + { LANDESK, "Landesk" }, + { WINDEFENDER, "Windows Defender" }, + { EMBASSYTS, "Embassy Trust Suite fingerprint reader software containing wxvault.dll" }, +}; + +static const size_t num_of_dodgy_apps = sizeof (big_list_of_dodgy_apps) / sizeof (big_list_of_dodgy_apps[0]); + +/* This function is not in the ntdll export lib, so it has + to be looked up at runtime and called through a pointer. */ +VOID NTAPI (*pRtlFreeUnicodeString)(PUNICODE_STRING) = NULL; + +static PSYSTEM_PROCESSES +get_process_list (void) +{ + int n_procs = 0x100; + PSYSTEM_PROCESSES pslist = (PSYSTEM_PROCESSES) malloc (n_procs * sizeof *pslist); + + while (NtQuerySystemInformation (SystemProcessesAndThreadsInformation, + pslist, n_procs * sizeof *pslist, 0) == STATUS_INFO_LENGTH_MISMATCH) + { + n_procs *= 2; + free (pslist); + pslist = (PSYSTEM_PROCESSES) malloc (n_procs * sizeof *pslist); + } + return pslist; +} + +static PSYSTEM_MODULE_INFORMATION +get_module_list (void) +{ + int modsize = 0x1000; + PSYSTEM_MODULE_INFORMATION modlist = (PSYSTEM_MODULE_INFORMATION) malloc (modsize); + + while (NtQuerySystemInformation (SystemModuleInformation, + modlist, modsize, NULL) == STATUS_INFO_LENGTH_MISMATCH) + { + modsize *= 2; + free (modlist); + modlist = (PSYSTEM_MODULE_INFORMATION) malloc (modsize); + } + return modlist; +} + +static bool +find_process_in_list (PSYSTEM_PROCESSES pslist, PUNICODE_STRING psname) +{ + while (1) + { + if (pslist->ProcessName.Length && pslist->ProcessName.Buffer) + { + dbg_printf (("%S\n", pslist->ProcessName.Buffer)); + if (!_wcsicmp (pslist->ProcessName.Buffer, psname->Buffer)) + return true; + } + if (!pslist->NextEntryDelta) + break; + pslist = (PSYSTEM_PROCESSES)(pslist->NextEntryDelta + (char *)pslist); + }; + return false; +} + +static bool +find_module_in_list (PSYSTEM_MODULE_INFORMATION modlist, const char * const modname) +{ + PSYSTEM_MODULE_INFORMATION_ENTRY modptr = &modlist->Module[0]; + DWORD count = modlist->Count; + while (count--) + { + dbg_printf (("name '%s' offset %d ", &modptr->ImageName[0], modptr->PathLength)); + dbg_printf (("= '%s'\n", &modptr->ImageName[modptr->PathLength])); + if (!_stricmp (&modptr->ImageName[modptr->PathLength], modname)) + return true; + modptr++; + } + return false; +} + +static bool +expand_path (const char *path, char *outbuf) +{ + char *dst = outbuf; + const char *end, *envval; + char envvar[MAX_PATH]; + size_t len; + + while ((dst - outbuf) < MAX_PATH) + { + if (*path != '%') + { + if ((*dst++ = *path++) != 0) + continue; + break; + } + /* Expand an environ var. */ + end = path + 1; + while (*end != '%') + { + /* Watch out for unterminated % */ + if (*end++ == 0) + { + end = NULL; + break; + } + } + /* If we didn't find the end, can't expand it. */ + if ((end == NULL) || (end == (path + 1))) + { + /* Unterminated % so copy verbatim. */ + *dst++ = *path++; + continue; + } + /* Expand the environment var into the new path. */ + if ((end - (path + 1)) >= MAX_PATH) + return -1; + memcpy (envvar, path + 1, end - (path + 1)); + envvar[end - (path + 1)] = 0; + envval = getenv (envvar); + /* If not found, copy env var name verbatim. */ + if (envval == NULL) + { + *dst++ = *path++; + continue; + } + /* Check enough room before copying. */ + len = strlen (envval); + if ((dst + len - outbuf) >= MAX_PATH) + return false; + memcpy (dst, envval, len); + dst += len; + /* And carry on past the end of env var name. */ + path = end + 1; + } + return (dst - outbuf) < MAX_PATH; +} + +static bool +detect_dodgy_app (const struct bad_app_det *det, PSYSTEM_PROCESSES pslist, PSYSTEM_MODULE_INFORMATION modlist) +{ + HANDLE fh; + HKEY hk; + UNICODE_STRING unicodename; + ANSI_STRING ansiname; + NTSTATUS rv; + bool found; + char expandedname[MAX_PATH]; + + switch (det->type) + { + case HKLMKEY: + dbg_printf (("Detect reg key hklm '%s'... ", det->param)); + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, det->param, 0, STANDARD_RIGHTS_READ, &hk) == ERROR_SUCCESS) + { + RegCloseKey (hk); + dbg_printf (("found!\n")); + return true; + } + break; + + case HKCUKEY: + dbg_printf (("Detect reg key hkcu '%s'... ", det->param)); + if (RegOpenKeyEx (HKEY_CURRENT_USER, det->param, 0, STANDARD_RIGHTS_READ, &hk) == ERROR_SUCCESS) + { + RegCloseKey (hk); + dbg_printf (("found!\n")); + return true; + } + break; + + case FILENAME: + dbg_printf (("Detect filename '%s'... ", det->param)); + if (!expand_path (det->param, expandedname)) + { + printf ("Expansion failure!\n"); + break; + } + dbg_printf (("('%s' after expansion)... ", expandedname)); + fh = CreateFile (expandedname, 0, FILE_SHARE_READ | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); + if (fh != INVALID_HANDLE_VALUE) + { + CloseHandle (fh); + dbg_printf (("found!\n")); + return true; + } + break; + + case PROCESSNAME: + dbg_printf (("Detect proc name '%s'... ", det->param)); + /* Equivalent of RtlInitAnsiString. */ + ansiname.Length = ansiname.MaximumLength = strlen (det->param); + ansiname.Buffer = (CHAR *) det->param; + rv = RtlAnsiStringToUnicodeString (&unicodename, &ansiname, TRUE); + if (rv != STATUS_SUCCESS) + { + printf ("Ansi to unicode conversion failure $%08x\n", (unsigned int) rv); + break; + } + found = find_process_in_list (pslist, &unicodename); + if (!pRtlFreeUnicodeString) + pRtlFreeUnicodeString = (VOID NTAPI (*)(PUNICODE_STRING)) GetProcAddress (LoadLibrary ("ntdll.dll"), "RtlFreeUnicodeString"); + if (pRtlFreeUnicodeString) + pRtlFreeUnicodeString (&unicodename); + else + printf ("leaking mem...oops\n"); + if (found) + { + dbg_printf (("found!\n")); + return true; + } + break; + + case HOOKDLLNAME: + dbg_printf (("Detect hookdll '%s'... ", det->param)); + if (find_module_in_list (modlist, det->param)) + { + dbg_printf (("found!\n")); + return true; + } + break; + + } + dbg_printf (("not found.\n")); + return false; +} + +static struct bad_app_info * +find_dodgy_app_info (enum bad_app which_app) +{ + size_t i; + for (i = 0; i < num_of_dodgy_apps; i++) + { + if (big_list_of_dodgy_apps[i].app_id == which_app) + return &big_list_of_dodgy_apps[i]; + } + return NULL; +} + +/* External entrypoint called from cygcheck.cc/dump_sysinfo. */ +void +dump_dodgy_apps (int verbose) +{ + size_t i, n_det = 0; + PSYSTEM_PROCESSES pslist; + PSYSTEM_MODULE_INFORMATION modlist; + + /* Read system info for detect testing. */ + pslist = get_process_list (); + modlist = get_module_list (); + + /* Go with builtin list for now; later may enhance to + read dodgy apps from a file or download from an URL. */ + for (i = 0; i < num_of_dodgy_apps; i++) + { + big_list_of_dodgy_apps[i].found_it = false; + } + + for (i = 0; i < num_of_detects; i++) + { + const struct bad_app_det *det = &dodgy_app_detects[i]; + struct bad_app_info *found = find_dodgy_app_info (det->app); + bool detected = detect_dodgy_app (det, pslist, modlist); + + /* Not found would mean we coded the lists bad. */ + assert (found); + if (detected) + { + ++n_det; + found->found_it |= (1 << det->type); + } + } + if (n_det) + { + printf ("\nPotential app conflicts:\n\n"); + for (i = 0; i < num_of_dodgy_apps; i++) + { + if (big_list_of_dodgy_apps[i].found_it) + { + printf ("%s%s", big_list_of_dodgy_apps[i].details, + verbose ? "\nDetected: " : ".\n"); + if (!verbose) + continue; + const char *sep = ""; + if (big_list_of_dodgy_apps[i].found_it & (1 << HKLMKEY)) + { + printf ("HKLM Registry Key"); + sep = ", "; + } + if (big_list_of_dodgy_apps[i].found_it & (1 << HKCUKEY)) + { + printf ("%sHKCU Registry Key", sep); + sep = ", "; + } + if (big_list_of_dodgy_apps[i].found_it & (1 << FILENAME)) + { + printf ("%sNamed file", sep); + sep = ", "; + } + if (big_list_of_dodgy_apps[i].found_it & (1 << PROCESSNAME)) + { + printf ("%sNamed process", sep); + sep = ", "; + } + if (big_list_of_dodgy_apps[i].found_it & (1 << HOOKDLLNAME)) + { + printf ("%sLoaded hook DLL", sep); + } + printf (".\n\n"); + } + } + } + /* Tidy up allocations. */ + free (pslist); + free (modlist); +} + diff --git a/winsup/utils/cygcheck.cc b/winsup/utils/cygcheck.cc new file mode 100644 index 00000000000..3c63913f7b1 --- /dev/null +++ b/winsup/utils/cygcheck.cc @@ -0,0 +1,1958 @@ +/* cygcheck.cc + + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2006 Red Hat, Inc. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#define cygwin_internal cygwin_internal_dontuse +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/time.h> +#include <ctype.h> +#include <io.h> +#include <windows.h> +#include <wininet.h> +#include "path.h" +#include <getopt.h> +#include "cygwin/include/sys/cygwin.h" +#include "cygwin/include/mntent.h" +#undef cygwin_internal + +#define alloca __builtin_alloca + +int verbose = 0; +int registry = 0; +int sysinfo = 0; +int givehelp = 0; +int keycheck = 0; +int check_setup = 0; +int dump_only = 0; +int find_package = 0; +int list_package = 0; +int grep_packages = 0; + +/* This is global because it's used in both internet_display_error as well + as package_grep. */ +BOOL (WINAPI *pInternetCloseHandle) (HINTERNET); + +#ifdef __GNUC__ +typedef long long longlong; +#else +typedef __int64 longlong; +#endif + +/* In dump_setup.cc */ +void dump_setup (int, char **, bool); +void package_find (int, char **); +void package_list (int, char **); +/* In bloda.cc */ +void dump_dodgy_apps (int verbose); + + +static const char version[] = "$Revision$"; + +static const char *known_env_vars[] = { + "c_include_path", + "compiler_path", + "cxx_include_path", + "cygwin", + "cygwin32", + "dejagnu", + "expect", + "gcc_default_options", + "gcc_exec_prefix", + "home", + "ld_library_path", + "library_path", + "login", + "lpath", + "make_mode", + "makeflags", + "path", + "pwd", + "strace", + "tcl_library", + "user", + 0 +}; + +struct +{ + const char *name; + int missing_is_good; +} +static common_apps[] = { + {"awk", 0}, + {"bash", 0}, + {"cat", 0}, + {"cp", 0}, + {"cpp", 1}, + {"crontab", 0}, + {"find", 0}, + {"gcc", 0}, + {"gdb", 0}, + {"grep", 0}, + {"kill", 0}, + {"ld", 0}, + {"ls", 0}, + {"make", 0}, + {"mv", 0}, + {"patch", 0}, + {"perl", 0}, + {"rm", 0}, + {"sed", 0}, + {"ssh", 0}, + {"sh", 0}, + {"tar", 0}, + {"test", 0}, + {"vi", 0}, + {"vim", 0}, + {0, 0} +}; + +static int num_paths, max_paths; +static char **paths; +int first_nonsys_path; + +void +eprintf (const char *format, ...) +{ + va_list ap; + va_start (ap, format); + vfprintf (stderr, format, ap); + va_end (ap); +} + +/* + * display_error() is used to report failure modes + */ +static int +display_error (const char *name, bool show_error = true, bool print_failed = true) +{ + if (show_error) + fprintf (stderr, "cygcheck: %s%s: %lu\n", name, + print_failed ? " failed" : "", GetLastError ()); + else + fprintf (stderr, "cygcheck: %s%s\n", name, + print_failed ? " failed" : ""); + return 1; +} + +/* Display a WinInet error message, and close a variable number of handles. + (Passed a list of handles terminated by NULL.) */ +static int +display_internet_error (const char *message, ...) +{ + DWORD err = GetLastError (); + TCHAR err_buf[256]; + va_list hptr; + HINTERNET h; + + /* in the case of a successful connection but 404 response, there is no + win32 error message, but we still get passed a message to display. */ + if (err) + { + if (FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, + GetModuleHandle ("wininet.dll"), err, 0, err_buf, + sizeof (err_buf), NULL) == 0) + strcpy (err_buf, "(Unknown error)"); + + fprintf (stderr, "cygcheck: %s: %s (win32 error %d)\n", message, + err_buf, err); + } + else + fprintf (stderr, "cygcheck: %s\n", message); + + va_start (hptr, message); + while ((h = va_arg (hptr, HINTERNET)) != 0) + pInternetCloseHandle (h); + va_end (hptr); + + return 1; +} + +static void +add_path (char *s, int maxlen) +{ + if (num_paths >= max_paths) + { + max_paths += 10; + if (paths) + paths = (char **) realloc (paths, max_paths * sizeof (char *)); + else + paths = (char **) malloc (max_paths * sizeof (char *)); + } + paths[num_paths] = (char *) malloc (maxlen + 1); + if (paths[num_paths] == NULL) + { + display_error ("add_path: malloc()"); + return; + } + memcpy (paths[num_paths], s, maxlen); + paths[num_paths][maxlen] = 0; + char *e = paths[num_paths] + strlen (paths[num_paths]); + if (e[-1] == '\\' && e[-2] != ':') + *--e = 0; + for (int i = 1; i < num_paths; i++) + if (strcasecmp (paths[num_paths], paths[i]) == 0) + { + free (paths[num_paths]); + return; + } + num_paths++; +} + +static void +init_paths () +{ + char tmp[4000], *sl; + add_path ((char *) ".", 1); /* to be replaced later */ + + if (GetCurrentDirectory (4000, tmp)) + add_path (tmp, strlen (tmp)); + else + display_error ("init_paths: GetCurrentDirectory()"); + + if (GetSystemDirectory (tmp, 4000)) + add_path (tmp, strlen (tmp)); + else + display_error ("init_paths: GetSystemDirectory()"); + sl = strrchr (tmp, '\\'); + if (sl) + { + strcpy (sl, "\\SYSTEM"); + add_path (tmp, strlen (tmp)); + } + GetWindowsDirectory (tmp, 4000); + add_path (tmp, strlen (tmp)); + first_nonsys_path = num_paths; + + char *wpath = getenv ("PATH"); + if (!wpath) + fprintf (stderr, "WARNING: PATH is not set at all!\n"); + else + { + char *b, *e; + b = wpath; + while (1) + { + for (e = b; *e && *e != ';'; e++); + if (strncmp(b, ".", 1) && strncmp(b, ".\\", 2)) + add_path (b, e - b); + if (!*e) + break; + b = e + 1; + } + } +} + +static char * +find_on_path (char *file, char *default_extension, + int showall = 0, int search_sysdirs = 0) +{ + static char rv[4000]; + char tmp[4000], *ptr = rv; + + if (!file) + { + display_error ("find_on_path: NULL pointer for file", false, false); + return 0; + } + + if (default_extension == NULL) + { + display_error ("find_on_path: NULL pointer for default_extension", false, false); + return 0; + } + + if (strchr (file, ':') || strchr (file, '\\') || strchr (file, '/')) + { + char *fn = cygpath (file, NULL); + if (access (fn, F_OK) == 0) + return fn; + strcpy (rv, fn); + strcat (rv, default_extension); + return access (rv, F_OK) == 0 ? strdup (rv) : fn; + } + + if (strchr (file, '.')) + default_extension = (char *) ""; + + for (int i = search_sysdirs ? 0 : first_nonsys_path; i < num_paths; i++) + { + if (i == 0 || !search_sysdirs || strcasecmp (paths[i], paths[0])) + { + sprintf (ptr, "%s\\%s%s", paths[i], file, default_extension); + if (GetFileAttributes (ptr) != (DWORD) - 1) + { + if (showall) + printf ("Found: %s\n", ptr); + if (ptr == tmp && verbose) + printf ("Warning: %s hides %s\n", rv, ptr); + ptr = tmp; + } + } + } + + if (ptr == tmp) + return rv; + + return 0; +} + +#define DID_NEW 1 +#define DID_ACTIVE 2 +#define DID_INACTIVE 3 + +struct Did +{ + Did *next; + char *file; + int state; +}; +static Did *did = 0; + +static Did * +already_did (char *file) +{ + Did *d; + for (d = did; d; d = d->next) + if (strcasecmp (d->file, file) == 0) + return d; + d = (Did *) malloc (sizeof (Did)); + d->file = strdup (file); + d->next = did; + d->state = DID_NEW; + did = d; + return d; +} + +static int +get_word (HANDLE fh, int offset) +{ + short rv; + unsigned r; + + if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + display_error ("get_word: SetFilePointer()"); + + if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0)) + display_error ("get_word: Readfile()"); + + return rv; +} + +static int +get_dword (HANDLE fh, int offset) +{ + int rv; + unsigned r; + + if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + display_error ("get_dword: SetFilePointer()"); + + if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0)) + display_error ("get_dword: Readfile()"); + + return rv; +} + +struct Section +{ + char name[8]; + int virtual_size; + int virtual_address; + int size_of_raw_data; + int pointer_to_raw_data; +}; + +static int +rva_to_offset (int rva, char *sections, int nsections, int *sz) +{ + int i; + + if (sections == NULL) + { + display_error ("rva_to_offset: NULL passed for sections", true, false); + return 0; + } + + for (i = 0; i < nsections; i++) + { + Section *s = (Section *) (sections + i * 40); +#if 0 + printf ("%08x < %08x < %08x ? %08x\n", + s->virtual_address, rva, + s->virtual_address + s->virtual_size, s->pointer_to_raw_data); +#endif + if (rva >= s->virtual_address + && rva < s->virtual_address + s->virtual_size) + { + if (sz) + *sz = s->virtual_address + s->virtual_size - rva; + return rva - s->virtual_address + s->pointer_to_raw_data; + } + } + return 0; /* punt */ +} + +struct ExpDirectory +{ + int flags; + int timestamp; + short major_ver; + short minor_ver; + int name_rva; +}; + +struct ImpDirectory +{ + unsigned characteristics; + unsigned timestamp; + unsigned forwarder_chain; + unsigned name_rva; + unsigned iat_rva; +}; + + +static bool track_down (char *file, char *suffix, int lvl); + +#define CYGPREFIX (sizeof ("%%% Cygwin ") - 1) +static void +cygwin_info (HANDLE h) +{ + char *buf, *bufend, *buf_start = NULL; + const char *hello = " Cygwin DLL version info:\n"; + DWORD size = GetFileSize (h, NULL); + DWORD n; + + if (size == 0xffffffff) + return; + + buf_start = buf = (char *) calloc (1, size + 1); + if (buf == NULL) + { + display_error ("cygwin_info: calloc()"); + return; + } + + (void) SetFilePointer (h, 0, NULL, FILE_BEGIN); + if (!ReadFile (h, buf, size, &n, NULL)) + { + free (buf_start); + return; + } + + static char dummy[] = "\0\0\0\0\0\0\0"; + char *dll_major = dummy; + bufend = buf + size; + while (buf < bufend) + if ((buf = (char *) memchr (buf, '%', bufend - buf)) == NULL) + break; + else if (strncmp ("%%% Cygwin ", buf, CYGPREFIX) != 0) + buf++; + else + { + char *p = strchr (buf += CYGPREFIX, '\n'); + if (!p) + break; + if (strncasecmp (buf, "dll major:", 10) == 0) + { + dll_major = buf + 11; + continue; + } + char *s, pbuf[80]; + int len; + len = 1 + p - buf; + if (strncasecmp (buf, "dll minor:", 10) != 0) + s = buf; + else + { + char c = dll_major[1]; + dll_major[1] = '\0'; + int maj = atoi (dll_major); + dll_major[1] = c; + int min = atoi (dll_major + 1); + sprintf (pbuf, "DLL version: %d.%d.%.*s", maj, min, len - 11, + buf + 11); + len = strlen (s = pbuf); + } + if (strncmp (s, "dll", 3) == 0) + memcpy (s, "DLL", 3); + else if (strncmp (s, "api", 3) == 0) + memcpy (s, "API", 3); + else if (islower (*s)) + *s = toupper (*s); + fprintf (stdout, "%s %.*s", hello, len, s); + hello = ""; + } + + if (!*hello) + puts (""); + + free (buf_start); + return; +} + +static void +dll_info (const char *path, HANDLE fh, int lvl, int recurse) +{ + DWORD junk; + int i; + int pe_header_offset = get_dword (fh, 0x3c); + int opthdr_ofs = pe_header_offset + 4 + 20; + unsigned short v[6]; + + if (path == NULL) + { + display_error ("dll_info: NULL passed for path", true, false); + return; + } + + if (SetFilePointer (fh, opthdr_ofs + 40, 0, FILE_BEGIN) == + INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) + display_error ("dll_info: SetFilePointer()"); + + if (!ReadFile (fh, &v, sizeof (v), &junk, 0)) + display_error ("dll_info: Readfile()"); + + if (verbose) + printf (" - os=%d.%d img=%d.%d sys=%d.%d\n", + v[0], v[1], v[2], v[3], v[4], v[5]); + else + printf ("\n"); + + int num_entries = get_dword (fh, opthdr_ofs + 92); + int export_rva = get_dword (fh, opthdr_ofs + 96); + int export_size = get_dword (fh, opthdr_ofs + 100); + int import_rva = get_dword (fh, opthdr_ofs + 104); + int import_size = get_dword (fh, opthdr_ofs + 108); + + int nsections = get_word (fh, pe_header_offset + 4 + 2); + char *sections = (char *) malloc (nsections * 40); + + if (SetFilePointer (fh, pe_header_offset + 4 + 20 + + get_word (fh, pe_header_offset + 4 + 16), 0, + FILE_BEGIN) == INVALID_SET_FILE_POINTER + && GetLastError () != NO_ERROR) + display_error ("dll_info: SetFilePointer()"); + + if (!ReadFile (fh, sections, nsections * 40, &junk, 0)) + display_error ("dll_info: Readfile()"); + + if (verbose && num_entries >= 1 && export_size > 0) + { + int expsz; + int expbase = rva_to_offset (export_rva, sections, nsections, &expsz); + + if (expbase) + { + if (SetFilePointer (fh, expbase, 0, FILE_BEGIN) == + INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) + display_error ("dll_info: SetFilePointer()"); + + unsigned char *exp = (unsigned char *) malloc (expsz); + + if (!ReadFile (fh, exp, expsz, &junk, 0)) + display_error ("dll_info: Readfile()"); + + ExpDirectory *ed = (ExpDirectory *) exp; + int ofs = ed->name_rva - export_rva; + struct tm *tm = localtime ((const time_t *) &(ed->timestamp)); + if (tm->tm_year < 60) + tm->tm_year += 2000; + if (tm->tm_year < 200) + tm->tm_year += 1900; + printf ("%*c", lvl + 2, ' '); + printf ("\"%s\" v%d.%d ts=", exp + ofs, + ed->major_ver, ed->minor_ver); + printf ("%d/%d/%d %d:%02d\n", + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min); + } + } + + if (num_entries >= 2 && import_size > 0 && recurse) + { + int impsz; + int impbase = rva_to_offset (import_rva, sections, nsections, &impsz); + if (impbase) + { + if (SetFilePointer (fh, impbase, 0, FILE_BEGIN) == + INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) + display_error ("dll_info: SetFilePointer()"); + + unsigned char *imp = (unsigned char *) malloc (impsz); + if (imp == NULL) + { + display_error ("dll_info: malloc()"); + return; + } + + if (!ReadFile (fh, imp, impsz, &junk, 0)) + display_error ("dll_info: Readfile()"); + + ImpDirectory *id = (ImpDirectory *) imp; + for (i = 0; id[i].name_rva; i++) + { + /* int ofs = id[i].name_rva - import_rva; */ + track_down ((char *) imp + id[i].name_rva - import_rva, + (char *) ".dll", lvl + 2); + } + } + } + if (strstr (path, "\\cygwin1.dll")) + cygwin_info (fh); +} + +// Return true on success, false if error printed +static bool +track_down (char *file, char *suffix, int lvl) +{ + if (file == NULL) + { + display_error ("track_down: NULL passed for file", true, false); + return false; + } + + if (suffix == NULL) + { + display_error ("track_down: NULL passed for suffix", false, false); + return false; + } + + char *path = find_on_path (file, suffix, 0, 1); + if (!path) + { + printf ("Error: could not find %s\n", file); + return false; + } + + Did *d = already_did (file); + switch (d->state) + { + case DID_NEW: + break; + case DID_ACTIVE: + if (verbose) + { + if (lvl) + printf ("%*c", lvl, ' '); + printf ("%s", path); + printf (" (recursive)\n"); + } + return true; + case DID_INACTIVE: + if (verbose) + { + if (lvl) + printf ("%*c", lvl, ' '); + printf ("%s", path); + printf (" (already done)\n"); + } + return true; + default: + break; + } + + if (lvl) + printf ("%*c", lvl, ' '); + + if (!path) + { + printf ("%s not found\n", file); + return false; + } + + printf ("%s", path); + + HANDLE fh = + CreateFile (path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fh == INVALID_HANDLE_VALUE) + { + printf (" - Cannot open\n"); + return false; + } + + d->state = DID_ACTIVE; + + dll_info (path, fh, lvl, 1); + d->state = DID_INACTIVE; + if (!CloseHandle (fh)) + display_error ("track_down: CloseHandle()"); + return true; +} + +static void +ls (char *f) +{ + HANDLE h = CreateFile (f, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + BY_HANDLE_FILE_INFORMATION info; + + if (!GetFileInformationByHandle (h, &info)) + display_error ("ls: GetFileInformationByHandle()"); + + SYSTEMTIME systime; + + if (!FileTimeToSystemTime (&info.ftLastWriteTime, &systime)) + display_error ("ls: FileTimeToSystemTime()"); + printf ("%5dk %04d/%02d/%02d %s", + (((int) info.nFileSizeLow) + 512) / 1024, + systime.wYear, systime.wMonth, systime.wDay, f); + dll_info (f, h, 16, 0); + if (!CloseHandle (h)) + display_error ("ls: CloseHandle()"); +} + +// Return true on success, false if error printed +static bool +cygcheck (char *app) +{ + char *papp = find_on_path (app, (char *) ".exe", 1, 0); + if (!papp) + { + printf ("Error: could not find %s\n", app); + return false; + } + char *s = strdup (papp); + char *sl = 0, *t; + for (t = s; *t; t++) + if (*t == '/' || *t == '\\' || *t == ':') + sl = t; + if (sl == 0) + paths[0] = (char *) "."; + else + { + *sl = 0; + paths[0] = s; + } + did = 0; + return track_down (papp, (char *) ".exe", 0); +} + + +extern char **environ; + +struct RegInfo +{ + RegInfo *prev; + char *name; + HKEY key; +}; + +static void +show_reg (RegInfo * ri, int nest) +{ + if (!ri) + return; + show_reg (ri->prev, 1); + if (nest) + printf ("%s\\", ri->name); + else + printf ("%s\n", ri->name); +} + +static void +scan_registry (RegInfo * prev, HKEY hKey, char *name, int cygnus) +{ + RegInfo ri; + ri.prev = prev; + ri.name = name; + ri.key = hKey; + + char *cp; + for (cp = name; *cp; cp++) + if (strncasecmp (cp, "cygnus", 6) == 0) + cygnus = 1; + + DWORD num_subkeys, max_subkey_len, num_values; + DWORD max_value_len, max_valdata_len, i; + if (RegQueryInfoKey (hKey, 0, 0, 0, &num_subkeys, &max_subkey_len, 0, + &num_values, &max_value_len, &max_valdata_len, 0, 0) + != ERROR_SUCCESS) + { +#if 0 + char tmp[400]; + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), tmp, 400, 0); + printf ("RegQueryInfoKey: %s\n", tmp); +#endif + return; + } + + if (cygnus) + { + show_reg (&ri, 0); + + char *value_name = (char *) malloc (max_value_len + 1); + if (value_name == NULL) + { + display_error ("scan_registry: malloc()"); + return; + } + + char *value_data = (char *) malloc (max_valdata_len + 1); + if (value_data == NULL) + { + display_error ("scan_registry: malloc()"); + return; + } + + for (i = 0; i < num_values; i++) + { + DWORD dlen = max_valdata_len + 1; + DWORD nlen = max_value_len + 1; + DWORD type; + RegEnumValue (hKey, i, value_name, &nlen, 0, + &type, (BYTE *) value_data, &dlen); + { + printf (" %s = ", i ? value_name : "(default)"); + switch (type) + { + case REG_DWORD: + printf ("0x%08x\n", *(unsigned *) value_data); + break; + case REG_EXPAND_SZ: + case REG_SZ: + printf ("'%s'\n", value_data); + break; + default: + printf ("(unsupported type)\n"); + break; + } + } + } + free (value_name); + free (value_data); + } + + char *subkey_name = (char *) malloc (max_subkey_len + 1); + for (i = 0; i < num_subkeys; i++) + { + if (RegEnumKey (hKey, i, subkey_name, max_subkey_len + 1) == + ERROR_SUCCESS) + { + HKEY sKey; + if (RegOpenKeyEx (hKey, subkey_name, 0, KEY_READ, &sKey) + == ERROR_SUCCESS) + { + scan_registry (&ri, sKey, subkey_name, cygnus); + if (RegCloseKey (sKey) != ERROR_SUCCESS) + display_error ("scan_registry: RegCloseKey()"); + } + } + } + free (subkey_name); +} + +void +pretty_id (const char *s, char *cygwin, size_t cyglen) +{ + char *groups[16384]; + + strcpy (cygwin + cyglen++, " "); + strcpy (cygwin + cyglen, s); + putenv (cygwin); + + char *id = cygpath ("/bin/id.exe", NULL); + for (char *p = id; (p = strchr (p, '/')); p++) + *p = '\\'; + + if (access (id, X_OK)) + { + fprintf (stderr, "'id' program not found\n"); + return; + } + + FILE *f = popen (id, "rt"); + + char buf[16384]; + buf[0] = '\0'; + fgets (buf, sizeof (buf), f); + pclose (f); + char *uid = strtok (buf, ")"); + if (uid) + uid += strlen ("uid="); + else + { + fprintf (stderr, "garbled output from 'id' command - no uid= found\n"); + return; + } + char *gid = strtok (NULL, ")"); + if (gid) + gid += strlen ("gid=") + 1; + else + { + fprintf (stderr, "garbled output from 'id' command - no gid= found\n"); + return; + } + + char **ng = groups - 1; + size_t len_uid = strlen ("UID: )") + strlen (uid); + size_t len_gid = strlen ("GID: )") + strlen (gid); + *++ng = groups[0] = (char *) alloca (len_uid + 1); + *++ng = groups[1] = (char *) alloca (len_gid + 1); + sprintf (groups[0], "UID: %s)", uid); + sprintf (groups[1], "GID: %s)", gid); + size_t sz = max (len_uid, len_gid); + while ((*++ng = strtok (NULL, ","))) + { + char *p = strchr (*ng, '\n'); + if (p) + *p = '\0'; + if (ng == groups + 2) + *ng += strlen (" groups="); + size_t len = strlen (*ng); + if (sz < len) + sz = len; + } + ng--; + + printf ("\nOutput from %s (%s)\n", id, s); + int n = 80 / (int) ++sz; + int i = n > 2 ? n - 2 : 0; + sz = -sz; + for (char **g = groups; g <= ng; g++) + if ((g != ng) && (++i < n)) + printf ("%*s", sz, *g); + else + { + puts (*g); + i = 0; + } +} + +/* This dumps information about each installed cygwin service, if cygrunsrv + is available. */ +void +dump_sysinfo_services () +{ + char buf[1024]; + char buf2[1024]; + FILE *f; + bool no_services = false; + + if (givehelp) + printf ("\nChecking for any Cygwin services... %s\n\n", + verbose ? "" : "(use -v for more detail)"); + else + fputc ('\n', stdout); + + /* find the location of cygrunsrv.exe */ + char *cygrunsrv = cygpath ("/bin/cygrunsrv.exe", NULL); + for (char *p = cygrunsrv; (p = strchr (p, '/')); p++) + *p = '\\'; + + if (access (cygrunsrv, X_OK)) + { + puts ("Can't find the cygrunsrv utility, skipping services check.\n"); + return; + } + + /* check for a recent cygrunsrv */ + snprintf (buf, sizeof (buf), "%s --version", cygrunsrv); + if ((f = popen (buf, "rt")) == NULL) + { + printf ("Failed to execute '%s', skipping services check.\n", buf); + return; + } + int maj, min; + int ret = fscanf (f, "cygrunsrv V%u.%u", &maj, &min); + if (ferror (f) || feof (f) || ret == EOF || maj < 1 || min < 10) + { + puts ("The version of cygrunsrv installed is too old to dump service info.\n"); + return; + } + fclose (f); + + /* For verbose mode, just run cygrunsrv --list --verbose and copy output + verbatim; otherwise run cygrunsrv --list and then cygrunsrv --query for + each service. */ + snprintf (buf, sizeof (buf), (verbose ? "%s --list --verbose" : "%s --list"), + cygrunsrv); + if ((f = popen (buf, "rt")) == NULL) + { + printf ("Failed to execute '%s', skipping services check.\n", buf); + return; + } + + if (verbose) + { + /* copy output to stdout */ + size_t nchars = 0; + while (!feof (f) && !ferror (f)) + nchars += fwrite ((void *) buf, 1, + fread ((void *) buf, 1, sizeof (buf), f), stdout); + + /* cygrunsrv outputs nothing if there are no cygwin services found */ + if (nchars < 1) + no_services = true; + pclose (f); + } + else + { + /* read the output of --list, and then run --query for each service */ + size_t nchars = fread ((void *) buf, 1, sizeof (buf) - 1, f); + buf[nchars] = 0; + pclose (f); + + if (nchars > 0) + for (char *srv = strtok (buf, "\n"); srv; srv = strtok (NULL, "\n")) + { + snprintf (buf2, sizeof (buf2), "%s --query %s", cygrunsrv, srv); + if ((f = popen (buf2, "rt")) == NULL) + { + printf ("Failed to execute '%s', skipping services check.\n", buf2); + return; + } + + /* copy output to stdout */ + while (!feof (f) && !ferror (f)) + fwrite ((void *) buf2, 1, + fread ((void *) buf2, 1, sizeof (buf2), f), stdout); + pclose (f); + } + else + no_services = true; + } + + /* inform the user if nothing found */ + if (no_services) + puts ("No Cygwin services found.\n"); +} + +static void +dump_sysinfo () +{ + int i, j; + char tmp[4000]; + time_t now; + char *found_cygwin_dll; + bool is_nt = false; + + printf ("\nCygwin Configuration Diagnostics\n"); + time (&now); + printf ("Current System Time: %s\n", ctime (&now)); + + OSVERSIONINFO osversion; + osversion.dwOSVersionInfoSize = sizeof (osversion); + if (!GetVersionEx (&osversion)) + display_error ("dump_sysinfo: GetVersionEx()"); + const char *osname = "unknown OS"; + switch (osversion.dwPlatformId) + { + case VER_PLATFORM_WIN32s: + osname = "32s"; + break; + case VER_PLATFORM_WIN32_WINDOWS: + switch (osversion.dwMinorVersion) + { + case 0: + if (strchr (osversion.szCSDVersion, 'C')) + osname = "95 OSR2"; + else + osname = "95"; + break; + case 10: + if (strchr (osversion.szCSDVersion, 'A')) + osname = "98 SE"; + else + osname = "98"; + break; + case 90: + osname = "ME"; + break; + default: + osname = "9X"; + break; + } + break; + case VER_PLATFORM_WIN32_NT: + is_nt = true; + if (osversion.dwMajorVersion == 6) + osname = "Longhorn/Vista (not yet supported!)"; + else if (osversion.dwMajorVersion == 5) + { + BOOL more_info = FALSE; + OSVERSIONINFOEX osversionex; + osversionex.dwOSVersionInfoSize = sizeof (osversionex); + if (GetVersionEx ((OSVERSIONINFO *) &osversionex)) + more_info = TRUE; + if (osversion.dwMinorVersion == 0) + { + if (!more_info) + osname = "2000"; + else if (osversionex.wProductType == VER_NT_SERVER + || osversionex.wProductType == VER_NT_DOMAIN_CONTROLLER) + { + if (osversionex.wSuiteMask & VER_SUITE_DATACENTER) + osname = "2000 Datacenter Server"; + else if (osversionex.wSuiteMask & VER_SUITE_ENTERPRISE) + osname = "2000 Advanced Server"; + else + osname = "2000 Server"; + } + else + osname = "2000 Professional"; + } + else if (osversion.dwMinorVersion == 1) + { + if (GetSystemMetrics (SM_MEDIACENTER)) + osname = "XP Media Center Edition"; + else if (GetSystemMetrics (SM_TABLETPC)) + osname = "XP Tablet PC Edition"; + else if (!more_info) + osname = "XP"; + else if (osversionex.wSuiteMask & VER_SUITE_PERSONAL) + osname = "XP Home Edition"; + else + osname = "XP Professional"; + } + else if (osversion.dwMinorVersion == 2) + { + if (!more_info) + osname = "2003 Server"; + else if (osversionex.wSuiteMask & VER_SUITE_BLADE) + osname = "2003 Web Server"; + else if (osversionex.wSuiteMask & VER_SUITE_DATACENTER) + osname = "2003 Datacenter Server"; + else if (osversionex.wSuiteMask & VER_SUITE_ENTERPRISE) + osname = "2003 Enterprise Server"; + else + osname = "2003 Server"; + } + } + else + osname = "NT"; + break; + default: + osname = "??"; + break; + } + printf ("Windows %s Ver %lu.%lu Build %lu %s\n", osname, + osversion.dwMajorVersion, osversion.dwMinorVersion, + osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ? + osversion.dwBuildNumber : (osversion.dwBuildNumber & 0xffff), + osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ? + osversion.szCSDVersion : ""); + + HMODULE k32 = LoadLibrary ("kernel32.dll"); + + BOOL (WINAPI *wow64_func) (HANDLE, PBOOL) = (BOOL (WINAPI *) (HANDLE, PBOOL)) + GetProcAddress (k32, "IsWow64Process"); + BOOL is_wow64 = FALSE; + if (wow64_func && wow64_func (GetCurrentProcess (), &is_wow64) && is_wow64) + { + void (WINAPI *nativinfo) (LPSYSTEM_INFO) = (void (WINAPI *) + (LPSYSTEM_INFO)) GetProcAddress (k32, "GetNativeSystemInfo"); + SYSTEM_INFO natinfo; + nativinfo (&natinfo); + fputs ("\nRunning under WOW64 on ", stdout); + switch (natinfo.wProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_IA64: + puts ("IA64"); + break; + case PROCESSOR_ARCHITECTURE_AMD64: + puts ("AMD64"); + break; + default: + puts("??"); + break; + } + } + + if (GetSystemMetrics (SM_REMOTESESSION)) + printf ("\nRunning in Terminal Service session\n"); + + printf ("\nPath:"); + char *s = getenv ("PATH"), *e; + if (!s) + puts (""); + else + { + char sep = strchr (s, ';') ? ';' : ':'; + int count_path_items = 0; + while (1) + { + for (e = s; *e && *e != sep; e++); + if (e-s) + printf ("\t%.*s\n", e - s, s); + else + puts ("\t."); + count_path_items++; + if (!*e) + break; + s = e + 1; + } + } + + fflush (stdout); + + char *cygwin = getenv ("CYGWIN"); + if (cygwin) + cygwin -= strlen ("CYGWIN="); + else + cygwin = const_cast <char *> ("CYGWIN="); + size_t cyglen = strlen (cygwin); + cygwin = strcpy ((char *) malloc (cyglen + sizeof (" nontsec")), cygwin); + pretty_id ("nontsec", cygwin, cyglen); + pretty_id ("ntsec", cygwin, cyglen); + cygwin[cyglen] = 0; + putenv (cygwin); + + if (!GetSystemDirectory (tmp, 4000)) + display_error ("dump_sysinfo: GetSystemDirectory()"); + printf ("\nSysDir: %s\n", tmp); + + GetWindowsDirectory (tmp, 4000); + printf ("WinDir: %s\n\n", tmp); + + + if (givehelp) + printf ("Here's some environment variables that may affect cygwin:\n"); + for (i = 0; environ[i]; i++) + { + char *eq = strchr (environ[i], '='); + if (!eq) + continue; + /* int len = eq - environ[i]; */ + for (j = 0; known_env_vars[j]; j++) + { + *eq = 0; + if (strcmp (environ[i], "PATH") == 0) + continue; /* we handle this one specially */ + if (strcasecmp (environ[i], known_env_vars[j]) == 0) + printf ("%s = '%s'\n", environ[i], eq + 1); + *eq = '='; + } + } + printf ("\n"); + + if (verbose) + { + if (givehelp) + printf ("Here's the rest of your environment variables:\n"); + for (i = 0; environ[i]; i++) + { + int found = 0; + char *eq = strchr (environ[i], '='); + if (!eq) + continue; + /* int len = eq - environ[i]; */ + for (j = 0; known_env_vars[j]; j++) + { + *eq = 0; + if (strcasecmp (environ[i], known_env_vars[j]) == 0) + found = 1; + *eq = '='; + } + if (!found) + { + *eq = 0; + printf ("%s = '%s'\n", environ[i], eq + 1); + *eq = '='; + } + } + printf ("\n"); + } + + if (registry) + { + if (givehelp) + printf ("Scanning registry for keys with 'Cygnus' in them...\n"); +#if 0 + /* big and not generally useful */ + scan_registry (0, HKEY_CLASSES_ROOT, (char *) "HKEY_CLASSES_ROOT", 0); +#endif + scan_registry (0, HKEY_CURRENT_CONFIG, + (char *) "HKEY_CURRENT_CONFIG", 0); + scan_registry (0, HKEY_CURRENT_USER, (char *) "HKEY_CURRENT_USER", 0); + scan_registry (0, HKEY_LOCAL_MACHINE, (char *) "HKEY_LOCAL_MACHINE", 0); +#if 0 + /* the parts we need are duplicated in HKEY_CURRENT_USER anyway */ + scan_registry (0, HKEY_USERS, (char *) "HKEY_USERS", 0); +#endif + printf ("\n"); + } + else + printf ("Use '-r' to scan registry\n\n"); + + if (givehelp) + { + printf ("Listing available drives...\n"); + printf ("Drv Type Size Used Flags Name\n"); + } + int prev_mode = + SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + int drivemask = GetLogicalDrives (); + + BOOL (WINAPI * gdfse) (LPCSTR, long long *, long long *, long long *) = + (BOOL (WINAPI *) (LPCSTR, long long *, long long *, long long *)) + GetProcAddress (k32, "GetDiskFreeSpaceExA"); + + for (i = 0; i < 26; i++) + { + if (!(drivemask & (1 << i))) + continue; + char drive[4], name[200], fsname[200]; + DWORD serno = 0, maxnamelen = 0, flags = 0; + name[0] = name[0] = fsname[0] = 0; + sprintf (drive, "%c:\\", i + 'a'); + /* Report all errors, except if the Volume is ERROR_NOT_READY. + ERROR_NOT_READY is returned when removeable media drives are empty + (CD, floppy, etc.) */ + if (!GetVolumeInformation (drive, name, sizeof (name), &serno, + &maxnamelen, &flags, fsname, + sizeof (fsname)) + && GetLastError () != ERROR_NOT_READY) + { +# define FMT "dump_sysinfo: GetVolumeInformation() for drive %c:" + char buf[sizeof (FMT)]; + sprintf (buf, FMT, 'A' + i); + display_error (buf); +# undef FMT + } + + int dtype = GetDriveType (drive); + char drive_type[4] = "unk"; + switch (dtype) + { + case DRIVE_REMOVABLE: + strcpy (drive_type, "fd "); + break; + case DRIVE_FIXED: + strcpy (drive_type, "hd "); + break; + case DRIVE_REMOTE: + strcpy (drive_type, "net"); + break; + case DRIVE_CDROM: + strcpy (drive_type, "cd "); + break; + case DRIVE_RAMDISK: + strcpy (drive_type, "ram"); + break; + default: + strcpy (drive_type, "unk"); + } + + long capacity_mb = -1; + int percent_full = -1; + + long long free_me = 0ULL, free_bytes = 0ULL, total_bytes = 1ULL; + if (gdfse != NULL && gdfse (drive, &free_me, &total_bytes, &free_bytes)) + { + capacity_mb = total_bytes / (1024L * 1024L); + percent_full = 100 - (int) ((100.0 * free_me) / total_bytes); + } + else + { + DWORD spc = 0, bps = 0, fc = 0, tc = 1; + if (GetDiskFreeSpace (drive, &spc, &bps, &fc, &tc)) + { + capacity_mb = (spc * bps * tc) / (1024 * 1024); + percent_full = 100 - (int) ((100.0 * fc) / tc); + } + } + + printf ("%.2s %s %-6s ", drive, drive_type, fsname); + if (capacity_mb >= 0) + printf ("%7dMb %3d%% ", (int) capacity_mb, (int) percent_full); + else + printf (" N/A N/A "); + printf ("%s %s %s %s %s %s %s\n", + flags & FS_CASE_IS_PRESERVED ? "CP" : " ", + flags & FS_CASE_SENSITIVE ? "CS" : " ", + flags & FS_UNICODE_STORED_ON_DISK ? "UN" : " ", + flags & FS_PERSISTENT_ACLS ? "PA" : " ", + flags & FS_FILE_COMPRESSION ? "FC" : " ", + flags & FS_VOL_IS_COMPRESSED ? "VC" : " ", +#if 0 + flags & FILE_SUPPORTS_ENCRYPTION ? "EN" : " ", + flags & FILE_SUPPORTS_OBJECT_IDS ? "OI" : " ", + flags & FILE_SUPPORTS_REPARSE_POINTS ? "RP" : " ", + flags & FILE_SUPPORTS_SPARSE_FILES ? "SP" : " ", + flags & FILE_VOLUME_QUOTAS ? "QU" : " ", +#endif + name); + } + + if (!FreeLibrary (k32)) + display_error ("dump_sysinfo: FreeLibrary()"); + SetErrorMode (prev_mode); + if (givehelp) + { + puts ("\n" + "fd = floppy, hd = hard drive, cd = CD-ROM\n" + "net= Network Share, ram= RAM drive, unk= Unknown\n" + "CP = Case Preserving, CS = Case Sensitive, UN = Unicode\n" + "PA = Persistent ACLS, FC = File Compression, VC = Volume Compression"); + } + printf ("\n"); + + unsigned ml_fsname = 4, ml_dir = 7, ml_type = 6; + bool ml_trailing = false; + + struct mntent *mnt; + setmntent (0, 0); + while ((mnt = getmntent (0))) + { + unsigned n = (int) strlen (mnt->mnt_fsname); + ml_trailing |= (n > 1 && strchr ("\\/", mnt->mnt_fsname[n - 1])); + if (ml_fsname < n) + ml_fsname = n; + n = (int) strlen (mnt->mnt_dir); + ml_trailing |= (n > 1 && strchr ("\\/", mnt->mnt_dir[n - 1])); + if (ml_dir < n) + ml_dir = n; + } + + if (ml_trailing) + puts ("Warning: Mount entries should not have a trailing (back)slash\n"); + + if (givehelp) + { + printf + ("Mount entries: these map POSIX directories to your NT drives.\n"); + printf ("%-*s %-*s %-*s %s\n", ml_fsname, "-NT-", ml_dir, "-POSIX-", + ml_type, "-Type-", "-Flags-"); + } + + setmntent (0, 0); + while ((mnt = getmntent (0))) + { + printf ("%-*s %-*s %-*s %s\n", + ml_fsname, mnt->mnt_fsname, + ml_dir, mnt->mnt_dir, ml_type, mnt->mnt_type, mnt->mnt_opts); + } + printf ("\n"); + + add_path ((char *) "\\bin", 4); /* just in case */ + + if (givehelp) + printf + ("Looking to see where common programs can be found, if at all...\n"); + for (i = 0; common_apps[i].name; i++) + if (!find_on_path ((char *) common_apps[i].name, (char *) ".exe", 1, 0)) + { + if (common_apps[i].missing_is_good) + printf ("Not Found: %s (good!)\n", common_apps[i].name); + else + printf ("Not Found: %s\n", common_apps[i].name); + } + printf ("\n"); + + if (givehelp) + printf ("Looking for various Cygwin DLLs... (-v gives version info)\n"); + int cygwin_dll_count = 0; + for (i = 1; i < num_paths; i++) + { + WIN32_FIND_DATA ffinfo; + sprintf (tmp, "%s/*.*", paths[i]); + HANDLE ff = FindFirstFile (tmp, &ffinfo); + int found = (ff != INVALID_HANDLE_VALUE); + found_cygwin_dll = NULL; + while (found) + { + char *f = ffinfo.cFileName; + if (strcasecmp (f + strlen (f) - 4, ".dll") == 0) + { + if (strncasecmp (f, "cyg", 3) == 0) + { + sprintf (tmp, "%s\\%s", paths[i], f); + if (strcasecmp (f, "cygwin1.dll") == 0) + { + cygwin_dll_count++; + found_cygwin_dll = strdup (tmp); + } + else + ls (tmp); + } + } + found = FindNextFile (ff, &ffinfo); + } + if (found_cygwin_dll) + { + ls (found_cygwin_dll); + free (found_cygwin_dll); + } + + FindClose (ff); + } + if (cygwin_dll_count > 1) + puts ("Warning: There are multiple cygwin1.dlls on your path"); + if (!cygwin_dll_count) + puts ("Warning: cygwin1.dll not found on your path"); + + dump_dodgy_apps (verbose); + + if (is_nt) + dump_sysinfo_services (); +} + +static int +check_keys () +{ + HANDLE h = CreateFileA ("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE || h == NULL) + return (display_error ("check_keys: Opening CONIN$")); + + DWORD mode; + + if (!GetConsoleMode (h, &mode)) + display_error ("check_keys: GetConsoleMode()"); + else + { + mode &= ~ENABLE_PROCESSED_INPUT; + if (!SetConsoleMode (h, mode)) + display_error ("check_keys: SetConsoleMode()"); + } + + fputs ("\nThis key check works only in a console window,", stderr); + fputs (" _NOT_ in a terminal session!\n", stderr); + fputs ("Abort with Ctrl+C if in a terminal session.\n\n", stderr); + fputs ("Press 'q' to exit.\n", stderr); + + INPUT_RECORD in, prev_in; + + // Drop first <RETURN> key + ReadConsoleInput (h, &in, 1, &mode); + + memset (&in, 0, sizeof in); + + do + { + prev_in = in; + if (!ReadConsoleInput (h, &in, 1, &mode)) + display_error ("check_keys: ReadConsoleInput()"); + + if (!memcmp (&in, &prev_in, sizeof in)) + continue; + + switch (in.EventType) + { + case KEY_EVENT: + printf ("%s %ux VK: 0x%02x VS: 0x%02x A: 0x%02x CTRL: ", + in.Event.KeyEvent.bKeyDown ? "Pressed " : "Released", + in.Event.KeyEvent.wRepeatCount, + in.Event.KeyEvent.wVirtualKeyCode, + in.Event.KeyEvent.wVirtualScanCode, + (unsigned char) in.Event.KeyEvent.uChar.AsciiChar); + fputs (in.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON ? + "CL " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY ? + "EK " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED ? + "LA " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED ? + "LC " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & NUMLOCK_ON ? + "NL " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & RIGHT_ALT_PRESSED ? + "RA " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & RIGHT_CTRL_PRESSED ? + "RC " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & SCROLLLOCK_ON ? + "SL " : "-- ", stdout); + fputs (in.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED ? + "SH " : "-- ", stdout); + fputc ('\n', stdout); + break; + + default: + break; + } + } + while (in.EventType != KEY_EVENT || + in.Event.KeyEvent.bKeyDown != FALSE || + in.Event.KeyEvent.uChar.AsciiChar != 'q'); + + CloseHandle (h); + return 0; +} + +/* RFC1738 says that these do not need to be escaped. */ +static const char safe_chars[] = "$-_.+!*'(),"; + +/* the URL to query. */ +static const char base_url[] = + "http://cygwin.com/cgi-bin2/package-grep.cgi?text=1&grep="; + +/* Queries Cygwin web site for packages containing files matching a regexp. + Return value is 1 if there was a problem, otherwise 0. */ +static int +package_grep (char *search) +{ + char buf[1024]; + + /* Attempt to dynamically load the necessary WinInet API functions so that + cygcheck can still function on older systems without IE. */ + HMODULE hWinInet; + if (!(hWinInet = LoadLibrary ("wininet.dll"))) + { + fputs ("Unable to locate WININET.DLL. This feature requires Microsoft " + "Internet Explorer v3 or later to function.\n", stderr); + return 1; + } + + /* InternetCloseHandle is used outside this function so it is declared + global. The rest of these functions are only used here, so declare them + and call GetProcAddress for each of them with the following macro. */ + + pInternetCloseHandle = (BOOL (WINAPI *) (HINTERNET)) + GetProcAddress (hWinInet, "InternetCloseHandle"); +#define make_func_pointer(name, ret, args) ret (WINAPI * p##name) args = \ + (ret (WINAPI *) args) GetProcAddress (hWinInet, #name); + make_func_pointer (InternetAttemptConnect, DWORD, (DWORD)); + make_func_pointer (InternetOpenA, HINTERNET, (LPCSTR, DWORD, LPCSTR, LPCSTR, + DWORD)); + make_func_pointer (InternetOpenUrlA, HINTERNET, (HINTERNET, LPCSTR, LPCSTR, + DWORD, DWORD, DWORD)); + make_func_pointer (InternetReadFile, BOOL, (HINTERNET, PVOID, DWORD, PDWORD)); + make_func_pointer (HttpQueryInfoA, BOOL, (HINTERNET, DWORD, PVOID, PDWORD, + PDWORD)); +#undef make_func_pointer + + if(!pInternetCloseHandle || !pInternetAttemptConnect || !pInternetOpenA + || !pInternetOpenUrlA || !pInternetReadFile || !pHttpQueryInfoA) + { + fputs ("Unable to load one or more functions from WININET.DLL. This " + "feature requires Microsoft Internet Explorer v3 or later to " + "function.\n", stderr); + return 1; + } + + /* construct the actual URL by escaping */ + char *url = (char *) alloca (sizeof (base_url) + strlen (search) * 3); + strcpy (url, base_url); + + char *dest; + for (dest = &url[sizeof (base_url) - 1]; *search; search++) + { + if (isalnum (*search) + || memchr (safe_chars, *search, sizeof (safe_chars) - 1)) + { + *dest++ = *search; + } + else + { + *dest++ = '%'; + sprintf (dest, "%02x", (unsigned char) *search); + dest += 2; + } + } + *dest = 0; + + /* Connect to the net and open the URL. */ + if (pInternetAttemptConnect (0) != ERROR_SUCCESS) + { + fputs ("An internet connection is required for this function.\n", stderr); + return 1; + } + + /* Initialize WinInet and attempt to fetch our URL. */ + HINTERNET hi = NULL, hurl = NULL; + if (!(hi = pInternetOpenA ("cygcheck", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0))) + return display_internet_error ("InternetOpen() failed", NULL); + + if (!(hurl = pInternetOpenUrlA (hi, url, NULL, 0, 0, 0))) + return display_internet_error ("unable to contact cygwin.com site, " + "InternetOpenUrl() failed", hi, NULL); + + /* Check the HTTP response code. */ + DWORD rc = 0, rc_s = sizeof (DWORD); + if (!pHttpQueryInfoA (hurl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, + (void *) &rc, &rc_s, NULL)) + return display_internet_error ("HttpQueryInfo() failed", hurl, hi, NULL); + + if (rc != HTTP_STATUS_OK) + { + sprintf (buf, "error retrieving results from cygwin.com site, " + "HTTP status code %lu", rc); + return display_internet_error (buf, hurl, hi, NULL); + } + + /* Fetch result and print to stdout. */ + DWORD numread; + do + { + if (!pInternetReadFile (hurl, (void *) buf, sizeof (buf), &numread)) + return display_internet_error ("InternetReadFile failed", hurl, hi, NULL); + if (numread) + fwrite ((void *) buf, (size_t) numread, 1, stdout); + } + while (numread); + + pInternetCloseHandle (hurl); + pInternetCloseHandle (hi); + return 0; +} + +static void +usage (FILE * stream, int status) +{ + fprintf (stream, "\ +Usage: cygcheck PROGRAM [ -v ] [ -h ]\n\ + cygcheck -c [ PACKAGE ] [ -d ]\n\ + cygcheck -s [ -r ] [ -v ] [ -h ]\n\ + cygcheck -k\n\ + cygcheck -f FILE [ FILE ... ]\n\ + cygcheck -l [ PACKAGE ] [ PACKAGE ... ]\n\ + cygcheck -p REGEXP\n\ +List system information, check installed packages, or query package database.\n\ +\n\ +At least one command option or a PROGRAM is required, as shown above.\n\ +\n\ + PROGRAM list library (DLL) dependencies of PROGRAM\n\ + -c, --check-setup show installed version of PACKAGE and verify integrity\n\ + (or for all installed packages if none specified)\n\ + -d, --dump-only just list packages, do not verify (with -c)\n\ + -s, --sysinfo produce diagnostic system information (implies -c -d)\n\ + -r, --registry also scan registry for Cygwin settings (with -s)\n\ + -k, --keycheck perform a keyboard check session (must be run from a\n\ + plain console only, not from a pty/rxvt/xterm)\n\ + -f, --find-package find the package that FILE belongs to\n\ + -l, --list-package list contents of PACKAGE (or all packages if none given)\n\ + -p, --package-query search for REGEXP in the entire cygwin.com package\n\ + repository (requies internet connectivity)\n\ + -v, --verbose produce more verbose output\n\ + -h, --help annotate output with explanatory comments when given\n\ + with another command, otherwise print this help\n\ + -V, --version print the version of cygcheck and exit\n\ +\n\ +Note: -c, -f, and -l only report on packages that are currently installed. To\n\ + search all official Cygwin packages use -p instead. The -p REGEXP matches\n\ + package names, descriptions, and names of files/paths within all packages.\n\ +\n"); + exit (status); +} + +struct option longopts[] = { + {"check-setup", no_argument, NULL, 'c'}, + {"dump-only", no_argument, NULL, 'd'}, + {"sysinfo", no_argument, NULL, 's'}, + {"registry", no_argument, NULL, 'r'}, + {"verbose", no_argument, NULL, 'v'}, + {"keycheck", no_argument, NULL, 'k'}, + {"find-package", no_argument, NULL, 'f'}, + {"list-package", no_argument, NULL, 'l'}, + {"package-query", no_argument, NULL, 'p'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, 0, 'V'}, + {0, no_argument, NULL, 0} +}; + +static char opts[] = "cdsrvkflphV"; + +static void +print_version () +{ + const char *v = strchr (version, ':'); + int len; + if (!v) + { + v = "?"; + len = 1; + } + else + { + v += 2; + len = strchr (v, ' ') - v; + } + printf ("\ +cygcheck version %.*s\n\ +System Checker for Cygwin\n\ +Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.\n\ +Compiled on %s\n\ +", len, v, __DATE__); +} + +void +nuke (char *ev) +{ + int n = 1 + strchr (ev, '=') - ev; + char *s = (char *) alloca (n + 1); + memcpy (s, ev, n); + s[n] = '\0'; + putenv (s); +} + +extern "C" { +unsigned long (*cygwin_internal) (int, ...); +}; + +static void +load_cygwin (int& argc, char **&argv) +{ + HMODULE h; + + if (!(h = LoadLibrary ("cygwin1.dll"))) + return; + if (!(cygwin_internal = (DWORD (*) (int, ...)) GetProcAddress (h, "cygwin_internal"))) + return; + + char **av = (char **) cygwin_internal (CW_ARGV); + if (av && ((DWORD) av != (DWORD) -1)) + for (argc = 0, argv = av; *av; av++) + argc++; + + char **envp = (char **) cygwin_internal (CW_ENVP); + if (envp && ((DWORD) envp != (DWORD) -1)) + { + /* Store path and revert to this value, otherwise path gets overwritten + by the POSIXy Cygwin variation, which breaks cygcheck. + Another approach would be to use the Cygwin PATH and convert it to + Win32 again. */ + char *path = NULL; + char **env; + while (*(env = _environ)) + { + if (strncmp (*env, "PATH=", 5) == 0) + path = strdup (*env); + nuke (*env); + } + for (char **ev = envp; *ev; ev++) + if (strncmp (*ev, "PATH=", 5) != 0) + putenv (*ev); + if (path) + putenv (path); + } +} + +int +main (int argc, char **argv) +{ + int i; + bool ok = true; + load_cygwin (argc, argv); + + /* Need POSIX sorting while parsing args, but don't forget the + user's original environment. */ + char *posixly = getenv ("POSIXLY_CORRECT"); + if (posixly == NULL) + (void) putenv("POSIXLY_CORRECT=1"); + while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) + switch (i) + { + case 's': + sysinfo = 1; + break; + case 'c': + check_setup = 1; + break; + case 'd': + dump_only = 1; + break; + case 'r': + registry = 1; + break; + case 'v': + verbose = 1; + break; + case 'k': + keycheck = 1; + break; + case 'f': + find_package = 1; + break; + case 'l': + list_package = 1; + break; + case 'p': + grep_packages = 1; + break; + case 'h': + givehelp = 1; + break; + case 'V': + print_version (); + exit (0); + default: + usage (stderr, 1); + /*NOTREACHED*/} + argc -= optind; + argv += optind; + if (posixly == NULL) + putenv ("POSIXLY_CORRECT="); + + if (argc == 0 && !sysinfo && !keycheck && !check_setup && !list_package) + if (givehelp) + usage (stdout, 0); + else + usage (stderr, 1); + + if ((check_setup || sysinfo || find_package || list_package || grep_packages) + && keycheck) + usage (stderr, 1); + + if ((find_package || list_package || grep_packages) && check_setup) + usage (stderr, 1); + + if (dump_only && !check_setup) + usage (stderr, 1); + + if (find_package + list_package + grep_packages > 1) + usage (stderr, 1); + + if (keycheck) + return check_keys (); + if (grep_packages) + return package_grep (*argv); + + init_paths (); + + /* FIXME: Add help for check_setup and {list,find}_package */ + if (argc >= 1 && givehelp && !check_setup && !find_package && !list_package) + { + printf("Here is where the OS will find your program%s, and which dlls\n", + argc > 1 ? "s" : ""); + printf ("will be used for it. Use -v to see DLL version info\n"); + + if (!sysinfo) + printf ("\n"); + } + + if (check_setup) + dump_setup (verbose, argv, !dump_only); + else if (find_package) + package_find (verbose, argv); + else if (list_package) + package_list (verbose, argv); + else + for (i = 0; i < argc; i++) + { + if (i) + puts (""); + ok &= cygcheck (argv[i]); + } + + if (sysinfo) + { + dump_sysinfo (); + if (!check_setup) + { + puts (""); + dump_setup (verbose, NULL, false); + } + + if (!givehelp) + puts ("Use -h to see help about each section"); + } + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/winsup/utils/cygpath.cc b/winsup/utils/cygpath.cc new file mode 100644 index 00000000000..33262fed7c8 --- /dev/null +++ b/winsup/utils/cygpath.cc @@ -0,0 +1,831 @@ +/* cygpath.cc -- convert pathnames between Windows and Unix format + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2006, 2007 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#define NOCOMATTRIBUTE + +#include <shlobj.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <getopt.h> +#include <windows.h> +#include <io.h> +#include <sys/fcntl.h> +#include <sys/cygwin.h> +#include <ctype.h> +#include <errno.h> + +static const char version[] = "$Revision$"; + +static char *prog_name; +static char *file_arg; +static int path_flag, unix_flag, windows_flag, absolute_flag; +static int shortname_flag, longname_flag; +static int ignore_flag, allusers_flag, output_flag; +static int mixed_flag; +static const char *format_type_arg; + +static struct option long_options[] = { + {(char *) "absolute", no_argument, NULL, 'a'}, + {(char *) "close", required_argument, NULL, 'c'}, + {(char *) "dos", no_argument, NULL, 'd'}, + {(char *) "file", required_argument, NULL, 'f'}, + {(char *) "help", no_argument, NULL, 'h'}, + {(char *) "ignore", no_argument, NULL, 'i'}, + {(char *) "long-name", no_argument, NULL, 'l'}, + {(char *) "mixed", no_argument, NULL, 'm'}, + {(char *) "mode", no_argument, NULL, 'M'}, + {(char *) "option", no_argument, NULL, 'o'}, + {(char *) "path", no_argument, NULL, 'p'}, + {(char *) "short-name", no_argument, NULL, 's'}, + {(char *) "type", required_argument, NULL, 't'}, + {(char *) "unix", no_argument, NULL, 'u'}, + {(char *) "version", no_argument, NULL, 'v'}, + {(char *) "windows", no_argument, NULL, 'w'}, + {(char *) "allusers", no_argument, NULL, 'A'}, + {(char *) "desktop", no_argument, NULL, 'D'}, + {(char *) "homeroot", no_argument, NULL, 'H'}, + {(char *) "smprograms", no_argument, NULL, 'P'}, + {(char *) "sysdir", no_argument, NULL, 'S'}, + {(char *) "windir", no_argument, NULL, 'W'}, + {0, no_argument, 0, 0} +}; + +static char options[] = "ac:df:hilmMopst:uvwADHPSW"; + +static void +usage (FILE * stream, int status) +{ + if (!ignore_flag || !status) + fprintf (stream, "\ +Usage: %s (-d|-m|-u|-w|-t TYPE) [-f FILE] [OPTION]... NAME...\n\ + %s [-c HANDLE] \n\ + %s [-ADHPSW] \n\ +Convert Unix and Windows format paths, or output system path information\n\ +\n\ +Output type options:\n\ + -d, --dos print DOS (short) form of NAMEs (C:\\PROGRA~1\\)\n\ + -m, --mixed like --windows, but with regular slashes (C:/WINNT)\n\ + -M, --mode report on mode of file (binmode or textmode)\n\ + -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)\n\ + -w, --windows print Windows form of NAMEs (C:\\WINNT)\n\ + -t, --type TYPE print TYPE form: 'dos', 'mixed', 'unix', or 'windows'\n\ +Path conversion options:\n\ + -a, --absolute output absolute path\n\ + -l, --long-name print Windows long form of NAMEs (with -w, -m only)\n\ + -p, --path NAME is a PATH list (i.e., '/bin:/usr/bin')\n\ + -s, --short-name print DOS (short) form of NAMEs (with -w, -m only)\n\ +System information:\n\ + -A, --allusers use `All Users' instead of current user for -D, -P\n\ + -D, --desktop output `Desktop' directory and exit\n\ + -H, --homeroot output `Profiles' directory (home root) and exit\n\ + -P, --smprograms output Start Menu `Programs' directory and exit\n\ + -S, --sysdir output system directory and exit\n\ + -W, --windir output `Windows' directory and exit\n\ +", prog_name, prog_name, prog_name); + if (ignore_flag) + /* nothing to do */; + else if (stream != stdout) + fprintf(stream, "Try `%s --help' for more information.\n", prog_name); + else + { + fprintf (stream, "\ +Other options:\n\ + -f, --file FILE read FILE for input; use - to read from STDIN\n\ + -o, --option read options from FILE as well (for use with --file)\n\ + -c, --close HANDLE close HANDLE (for use in captured process)\n\ + -i, --ignore ignore missing argument\n\ + -h, --help output usage information and exit\n\ + -v, --version output version information and exit\n\ +"); + } + exit (ignore_flag ? 0 : status); +} + +static char * +get_short_paths (char *path) +{ + char *sbuf; + char *sptr; + char *next; + char *ptr = path; + char *end = strrchr (path, 0); + DWORD acc = 0; + DWORD len; + + while (ptr != NULL) + { + next = ptr; + ptr = strchr (ptr, ';'); + if (ptr) + *ptr++ = 0; + len = GetShortPathName (next, NULL, 0); + if (!len) + { + fprintf (stderr, "%s: cannot create short name of %s\n", prog_name, + next); + exit (2); + } + acc += len + 1; + } + sptr = sbuf = (char *) malloc (acc + 1); + if (sbuf == NULL) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + ptr = path; + for (;;) + { + len = GetShortPathName (ptr, sptr, acc); + if (!len) + { + fprintf (stderr, "%s: cannot create short name of %s\n", prog_name, + ptr); + exit (2); + } + + ptr = strrchr (ptr, 0); + sptr = strrchr (sptr, 0); + if (ptr == end) + break; + *sptr = ';'; + ++ptr, ++sptr; + acc -= len + 1; + } + return sbuf; +} + +static char * +get_short_name (const char *filename) +{ + char *sbuf, buf[MAX_PATH]; + DWORD len = GetShortPathName (filename, buf, MAX_PATH); + if (!len) + { + fprintf (stderr, "%s: cannot create short name of %s\n", prog_name, + filename); + exit (2); + } + sbuf = (char *) malloc (++len); + if (sbuf == NULL) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + return strcpy (sbuf, buf); +} + +static DWORD WINAPI +get_long_path_name_w32impl (LPCSTR src, LPSTR sbuf, DWORD) +{ + char buf1[MAX_PATH], buf2[MAX_PATH], *ptr; + const char *pelem, *next; + WIN32_FIND_DATA w32_fd; + int len; + + strcpy (buf1, src); + *buf2 = 0; + pelem = src; + ptr = buf2; + while (pelem) + { + next = pelem; + if (*next == '\\') + { + strcat (ptr++, "\\"); + pelem++; + if (!*pelem) + break; + continue; + } + pelem = strchr (next, '\\'); + len = pelem ? (pelem++ - next) : strlen (next); + strncpy (ptr, next, len); + ptr[len] = 0; + if (next[1] != ':' && strcmp(next, ".") && strcmp(next, "..")) + { + HANDLE h; + h = FindFirstFile (buf2, &w32_fd); + if (h != INVALID_HANDLE_VALUE) + { + strcpy (ptr, w32_fd.cFileName); + FindClose (h); + } + } + ptr += strlen (ptr); + if (pelem) + { + *ptr++ = '\\'; + *ptr = 0; + } + } + if (sbuf) + strcpy (sbuf, buf2); + SetLastError (0); + return strlen (buf2) + (sbuf ? 0 : 1); +} + +static char * +get_long_name (const char *filename, DWORD& len) +{ + char *sbuf, buf[MAX_PATH]; + static HINSTANCE k32 = LoadLibrary ("kernel32.dll"); + static DWORD (WINAPI *GetLongPathName) (LPCSTR, LPSTR, DWORD) = + (DWORD (WINAPI *) (LPCSTR, LPSTR, DWORD)) GetProcAddress (k32, "GetLongPathNameA"); + if (!GetLongPathName) + GetLongPathName = get_long_path_name_w32impl; + + len = GetLongPathName (filename, buf, MAX_PATH); + if (len == 0) + { + DWORD err = GetLastError (); + + if (err == ERROR_INVALID_PARAMETER) + { + fprintf (stderr, "%s: cannot create long name of %s\n", prog_name, + filename); + exit (2); + } + else if (err == ERROR_FILE_NOT_FOUND) + len = get_long_path_name_w32impl (filename, buf, MAX_PATH); + else + { + buf[0] = '\0'; + strncat (buf, filename, MAX_PATH - 1); + len = strlen (buf); + } + } + sbuf = (char *) malloc (len + 1); + if (!sbuf) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + return strcpy (sbuf, buf); +} + +static char * +get_long_paths (char *path) +{ + char *sbuf; + char *ptr; + int n = 1; + + ptr = path; + while ((ptr = strchr (ptr, ';'))) + { + ptr++; + n++; + } + + char *paths[n]; + DWORD acc = 0; + int i; + if (!n) + return strdup (""); + + for (i = 0, ptr = path; ptr; i++) + { + DWORD len; + char *next = ptr; + ptr = strchr (ptr, ';'); + if (ptr) + *ptr++ = 0; + paths[i] = get_long_name (next, len); + acc += len + 1; + } + + sbuf = (char *) malloc (acc + 1); + if (sbuf == NULL) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + + sbuf[0] = '\0'; + for (i = 0; i < n; i++) + { + strcat (strcat (sbuf, paths[i]), ";"); + free (paths[i]); + } + + strchr (sbuf, '\0')[-1] = '\0'; + return sbuf; +} + +static void +convert_slashes (char* name) +{ + while ((name = strchr (name, '\\')) != NULL) + { + if (*name == '\\') + *name = '/'; + name++; + } +} + +static char * +get_mixed_name (const char* filename) +{ + char* mixed_buf = strdup (filename); + + if (mixed_buf == NULL) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + + convert_slashes (mixed_buf); + + return mixed_buf; +} + +static void +dowin (char option) +{ + char *buf, buf1[MAX_PATH], buf2[MAX_PATH]; + DWORD len = MAX_PATH; + WIN32_FIND_DATA w32_fd; + LPITEMIDLIST id; + HINSTANCE k32; + BOOL (*GetProfilesDirectoryAPtr) (LPSTR, LPDWORD) = 0; + + buf = buf1; + switch (option) + { + case 'D': + SHGetSpecialFolderLocation (NULL, allusers_flag ? + CSIDL_COMMON_DESKTOPDIRECTORY : CSIDL_DESKTOPDIRECTORY, &id); + SHGetPathFromIDList (id, buf); + /* This if clause is a Fix for Win95 without any "All Users" */ + if (strlen (buf) == 0) + { + SHGetSpecialFolderLocation (NULL, CSIDL_DESKTOPDIRECTORY, &id); + SHGetPathFromIDList (id, buf); + } + break; + + case 'P': + SHGetSpecialFolderLocation (NULL, allusers_flag ? + CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS, &id); + SHGetPathFromIDList (id, buf); + /* This if clause is a Fix for Win95 without any "All Users" */ + if (strlen (buf) == 0) + { + SHGetSpecialFolderLocation (NULL, CSIDL_PROGRAMS, &id); + SHGetPathFromIDList (id, buf); + } + break; + + case 'H': + k32 = LoadLibrary ("userenv"); + if (k32) + GetProfilesDirectoryAPtr = (BOOL (*) (LPSTR, LPDWORD)) + GetProcAddress (k32, "GetProfilesDirectoryA"); + if (GetProfilesDirectoryAPtr) + (*GetProfilesDirectoryAPtr) (buf, &len); + else + { + GetWindowsDirectory (buf, MAX_PATH); + strcat (buf, "\\Profiles"); + } + break; + + case 'S': + GetSystemDirectory (buf, MAX_PATH); + FindFirstFile (buf, &w32_fd); + strcpy (strrchr (buf, '\\') + 1, w32_fd.cFileName); + break; + + case 'W': + GetWindowsDirectory (buf, MAX_PATH); + break; + + default: + usage (stderr, 1); + } + + if (!windows_flag) + { + if (cygwin_conv_to_posix_path (buf, buf2)) + fprintf (stderr, "%s: error converting \"%s\" - %s\n", + prog_name, buf, strerror (errno)); + else + buf = buf2; + } + else + { + if (shortname_flag) + buf = get_short_name (buf); + if (mixed_flag) + buf = get_mixed_name (buf); + } + printf ("%s\n", buf); + exit (0); +} + +static void +report_mode (char *filename) +{ + switch (cygwin_internal (CW_GET_BINMODE, filename)) + { + case O_BINARY: + printf ("%s: binary\n", filename); + break; + case O_TEXT: + printf ("%s: text\n", filename); + break; + default: + fprintf (stderr, "%s: file '%s' - %s\n", prog_name, filename, + strerror (errno)); + break; + } +} + +static void +doit (char *filename) +{ + char *buf; + DWORD len; + int err; + int (*conv_func) (const char *, char *); + + if (!path_flag) + { + len = strlen (filename); + if (len) + len += MAX_PATH + 1001; + else if (ignore_flag) + exit (0); + else + { + fprintf (stderr, "%s: can't convert empty path\n", prog_name); + exit (1); + } + } + else if (unix_flag) + len = cygwin_win32_to_posix_path_list_buf_size (filename); + else + len = cygwin_posix_to_win32_path_list_buf_size (filename); + + buf = (char *) malloc (len); + if (buf == NULL) + { + fprintf (stderr, "%s: out of memory\n", prog_name); + exit (1); + } + + if (path_flag) + { + if (unix_flag) + err = cygwin_win32_to_posix_path_list (filename, buf); + else + { + err = cygwin_posix_to_win32_path_list (filename, buf); + if (err) + /* oops */; + if (shortname_flag) + buf = get_short_paths (buf); + if (longname_flag) + buf = get_long_paths (buf); + if (mixed_flag) + buf = get_mixed_name (buf); + } + if (err) + { + fprintf (stderr, "%s: error converting \"%s\" - %s\n", + prog_name, filename, strerror (errno)); + exit (1); + } + } + else + { + if (unix_flag) + conv_func = (absolute_flag ? cygwin_conv_to_full_posix_path : + cygwin_conv_to_posix_path); + else + conv_func = (absolute_flag ? cygwin_conv_to_full_win32_path : + cygwin_conv_to_win32_path); + err = conv_func (filename, buf); + if (err) + { + fprintf (stderr, "%s: error converting \"%s\" - %s\n", + prog_name, filename, strerror (errno)); + exit (1); + } + if (!unix_flag) + { + if (shortname_flag) + buf = get_short_name (buf); + if (longname_flag) + buf = get_long_name (buf, len); + if (mixed_flag) + buf = get_mixed_name (buf); + } + } + + puts (buf); +} + +static void +print_version () +{ + const char *v = strchr (version, ':'); + int len; + if (!v) + { + v = "?"; + len = 1; + } + else + { + v += 2; + len = strchr (v, ' ') - v; + } + printf ("\ +cygpath (cygwin) %.*s\n\ +Path Conversion Utility\n\ +Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n\ +Compiled on %s\n\ +", len, v, __DATE__); +} + +int +main (int argc, char **argv) +{ + int c, o = 0; + int options_from_file_flag; + int mode_flag; + + prog_name = strrchr (argv[0], '/'); + if (prog_name == NULL) + prog_name = strrchr (argv[0], '\\'); + if (prog_name == NULL) + prog_name = argv[0]; + else + prog_name++; + + path_flag = 0; + unix_flag = 1; + windows_flag = 0; + shortname_flag = 0; + longname_flag = 0; + mixed_flag = 0; + ignore_flag = 0; + options_from_file_flag = 0; + allusers_flag = 0; + output_flag = 0; + mode_flag = 0; + while ((c = getopt_long (argc, argv, options, + long_options, (int *) NULL)) != EOF) + { + switch (c) + { + case 'a': + absolute_flag = 1; + break; + + case 'c': + CloseHandle ((HANDLE) strtoul (optarg, NULL, 16)); + break; + + case 'd': + if (windows_flag) + usage (stderr, 1); + unix_flag = 0; + windows_flag = 1; + shortname_flag = 1; + break; + + case 'f': + file_arg = optarg; + break; + + case 'M': + mode_flag = 1; + break; + + case 'o': + options_from_file_flag = 1; + break; + + case 'p': + path_flag = 1; + break; + + case 'u': + if (windows_flag || mixed_flag) + usage (stderr, 1); + unix_flag = 1; + break; + + case 'w': + if (windows_flag || mixed_flag) + usage (stderr, 1); + unix_flag = 0; + windows_flag = 1; + break; + + case 'm': + unix_flag = 0; + windows_flag = 1; + mixed_flag = 1; + break; + + case 'l': + longname_flag = 1; + break; + + case 's': + shortname_flag = 1; + break; + + case 't': + if (optarg == NULL) + usage (stderr, 1); + + format_type_arg = (*optarg == '=') ? (optarg + 1) : (optarg); + if (strcasecmp (format_type_arg, "dos") == 0) + { + if (windows_flag || longname_flag) + usage (stderr, 1); + unix_flag = 0; + windows_flag = 1; + shortname_flag = 1; + } + else if (strcasecmp (format_type_arg, "mixed") == 0) + { + unix_flag = 0; + mixed_flag = 1; + } + else if (strcasecmp (format_type_arg, "unix") == 0) + { + if (windows_flag) + usage (stderr, 1); + unix_flag = 1; + } + else if (strcasecmp (format_type_arg, "windows") == 0) + { + if (mixed_flag) + usage (stderr, 1); + unix_flag = 0; + windows_flag = 1; + } + else + usage (stderr, 1); + break; + + case 'A': + allusers_flag = 1; + break; + + case 'D': + case 'H': + case 'P': + case 'S': + case 'W': + if (output_flag) + usage (stderr, 1); + output_flag = 1; + o = c; + break; + + case 'i': + ignore_flag = 1; + break; + + case 'h': + usage (stdout, 0); + break; + + case 'v': + print_version (); + exit (0); + + default: + usage (stderr, 1); + break; + } + } + + if (options_from_file_flag && !file_arg) + usage (stderr, 1); + + if (longname_flag && !windows_flag) + usage (stderr, 1); + + if (shortname_flag && !windows_flag) + usage (stderr, 1); + + if (!unix_flag && !windows_flag && !mixed_flag && !options_from_file_flag) + usage (stderr, 1); + + if (!file_arg) + { + if (output_flag) + dowin (o); + + if (optind > argc - 1) + usage (stderr, 1); + + for (int i = optind; argv[i]; i++) + if (mode_flag) + report_mode (argv[i]); + else + doit (argv[i]); + } + else + { + FILE *fp; + char buf[PATH_MAX * 2 + 1]; + + if (argv[optind]) + usage (stderr, 1); + + if (strcmp (file_arg, "-") != 0) + fp = fopen (file_arg, "rt"); + else + { + fp = stdin; + setmode (0, O_TEXT); + } + if (fp == NULL) + { + perror ("cygpath"); + exit (1); + } + + setbuf (stdout, NULL); + while (fgets (buf, sizeof (buf), fp) != NULL) + { + char *s = buf; + char *p = strchr (s, '\n'); + if (p) + *p = '\0'; + if (options_from_file_flag && *s == '-') + { + char c; + for (c = *++s; c && !isspace (c); c = *++s) + switch (c) + { + case 'a': + absolute_flag = 1; + break; + case 'i': + ignore_flag = 1; + break; + case 's': + shortname_flag = 1; + longname_flag = 0; + break; + case 'l': + shortname_flag = 0; + longname_flag = 1; + break; + case 'm': + unix_flag = 0; + windows_flag = 1; + mixed_flag = 1; + case 'w': + unix_flag = 0; + windows_flag = 1; + break; + case 'u': + windows_flag = 0; + unix_flag = 1; + break; + case 'p': + path_flag = 1; + break; + case 'D': + case 'H': + case 'P': + case 'S': + case 'W': + output_flag = 1; + o = c; + break; + } + if (*s) + do + s++; + while (*s && isspace (*s)); + } + if (*s && !output_flag) + doit (s); + if (!*s && output_flag) + dowin (o); + } + } + + exit (0); +} diff --git a/winsup/utils/dumper.cc b/winsup/utils/dumper.cc new file mode 100644 index 00000000000..9d5de81098f --- /dev/null +++ b/winsup/utils/dumper.cc @@ -0,0 +1,947 @@ +/* dumper.cc + + Copyright 1999, 2001, 2002, 2004, 2006, 2007 Hat Inc. + + Written by Egor Duda <deo@logos-m.ru> + + This file is part of Cygwin. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (file COPYING.dumper) for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <bfd.h> +#include <ansidecl.h> +#include <elf/common.h> +#include <elf/external.h> +#include <sys/procfs.h> +#include <sys/cygwin.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <windows.h> + +#include "dumper.h" +#define NOTE_NAME_SIZE 16 + +typedef struct _note_header + { + Elf_External_Note elf_note_header; + char name[NOTE_NAME_SIZE - 1]; /* external note contains first byte of data */ + } +#ifdef __GNUC__ +__attribute__ ((packed)) +#endif + note_header; + +static const char version[] = "$Revision$"; + +BOOL verbose = FALSE; + +int deb_printf (const char *format,...) +{ + if (!verbose) + return 0; + va_list va; + va_start (va, format); + int ret_val = vprintf (format, va); + va_end (va); + return ret_val; +} + +dumper::dumper (DWORD pid, DWORD tid, const char *file_name) +{ + this->file_name = strdup (file_name); + + this->pid = pid; + this->tid = tid; + core_bfd = NULL; + excl_list = new exclusion (20); + + list = last = NULL; + + status_section = NULL; + + memory_num = module_num = thread_num = 0; + + hProcess = OpenProcess (PROCESS_ALL_ACCESS, + FALSE, /* no inheritance */ + pid); + if (!hProcess) + { + fprintf (stderr, "Failed to open process #%lu, error %ld\n", pid, GetLastError ()); + return; + } + + init_core_dump (); + + if (!sane ()) + dumper_abort (); +} + +dumper::~dumper () +{ + close (); + free (file_name); +} + +void +dumper::dumper_abort () +{ + close (); + unlink (file_name); +} + +void +dumper::close () +{ + if (core_bfd) + bfd_close (core_bfd); + if (excl_list) + delete excl_list; + if (hProcess) + CloseHandle (hProcess); + core_bfd = NULL; + hProcess = NULL; + excl_list = NULL; +} + +int +dumper::sane () +{ + if (hProcess == NULL || core_bfd == NULL || excl_list == NULL) + return 0; + return 1; +} + +void +print_section_name (bfd* abfd, asection* sect, PTR obj) +{ + deb_printf (" %s", bfd_get_section_name (abfd, sect)); +} + +void +dumper::print_core_section_list () +{ + deb_printf ("current sections:"); + bfd_map_over_sections (core_bfd, &print_section_name, NULL); + deb_printf ("\n"); +} + +process_entity * +dumper::add_process_entity_to_list (process_entity_type type) +{ + if (!sane ()) + return NULL; + + process_entity *new_entity = (process_entity *) malloc (sizeof (process_entity)); + if (new_entity == NULL) + return NULL; + new_entity->next = NULL; + new_entity->section = NULL; + if (last == NULL) + list = new_entity; + else + last->next = new_entity; + last = new_entity; + return new_entity; +} + +int +dumper::add_thread (DWORD tid, HANDLE hThread) +{ + if (!sane ()) + return 0; + + CONTEXT *pcontext; + + process_entity *new_entity = add_process_entity_to_list (pr_ent_thread); + if (new_entity == NULL) + return 0; + new_entity->type = pr_ent_thread; + thread_num++; + + new_entity->u.thread.tid = tid; + new_entity->u.thread.hThread = hThread; + + pcontext = &(new_entity->u.thread.context); + pcontext->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT; + if (!GetThreadContext (hThread, pcontext)) + { + deb_printf ("Failed to read thread context (tid=%x), error %ld\n", tid, GetLastError ()); + return 0; + } + + deb_printf ("added thread %u\n", tid); + return 1; +} + +int +dumper::add_mem_region (LPBYTE base, DWORD size) +{ + if (!sane ()) + return 0; + + if (base == NULL || size == 0) + return 1; // just ignore empty regions + + process_entity *new_entity = add_process_entity_to_list (pr_ent_memory); + if (new_entity == NULL) + return 0; + new_entity->type = pr_ent_memory; + memory_num++; + + new_entity->u.memory.base = base; + new_entity->u.memory.size = size; + + deb_printf ("added memory region %08x-%08x\n", (DWORD) base, (DWORD) base + size); + return 1; +} + +/* split_add_mem_region scans list of regions to be excluded from dumping process + (excl_list) and removes all "excluded" parts from given region. */ +int +dumper::split_add_mem_region (LPBYTE base, DWORD size) +{ + if (!sane ()) + return 0; + + if (base == NULL || size == 0) + return 1; // just ignore empty regions + + LPBYTE last_base = base; + + for (process_mem_region * p = excl_list->region; + p < excl_list->region + excl_list->last; + p++) + { + if (p->base >= base + size || p->base + p->size <= base) + continue; + + if (p->base <= base) + { + last_base = p->base + p->size; + continue; + } + + add_mem_region (last_base, p->base - last_base); + last_base = p->base + p->size; + } + + if (last_base < base + size) + add_mem_region (last_base, base + size - last_base); + + return 1; +} + +int +dumper::add_module (LPVOID base_address) +{ + if (!sane ()) + return 0; + + char *module_name = psapi_get_module_name (hProcess, (DWORD) base_address); + if (module_name == NULL) + return 1; + + process_entity *new_entity = add_process_entity_to_list (pr_ent_module); + if (new_entity == NULL) + return 0; + new_entity->type = pr_ent_module; + module_num++; + + new_entity->u.module.base_address = base_address; + new_entity->u.module.name = module_name; + + parse_pe (module_name, excl_list); + + deb_printf ("added module %08x %s\n", base_address, module_name); + return 1; +} + +#define PAGE_BUFFER_SIZE 4096 + +int +dumper::collect_memory_sections () +{ + if (!sane ()) + return 0; + + LPBYTE current_page_address; + LPBYTE last_base = (LPBYTE) 0xFFFFFFFF; + DWORD last_size = 0; + DWORD done; + + char mem_buf[PAGE_BUFFER_SIZE]; + + MEMORY_BASIC_INFORMATION mbi; + + if (hProcess == NULL) + return 0; + + for (current_page_address = 0; current_page_address < (LPBYTE) 0xFFFF0000;) + { + if (!VirtualQueryEx (hProcess, current_page_address, &mbi, sizeof (mbi))) + break; + + int skip_region_p = 0; + + if (mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD) || + mbi.State != MEM_COMMIT) + skip_region_p = 1; + + if (!skip_region_p) + { + /* just to make sure that later we'll be able to read it. + According to MS docs either region is all-readable or + all-nonreadable */ + if (!ReadProcessMemory (hProcess, current_page_address, mem_buf, sizeof (mem_buf), &done)) + { + DWORD err = GetLastError (); + const char *pt[10]; + pt[0] = (mbi.Protect & PAGE_READONLY) ? "RO " : ""; + pt[1] = (mbi.Protect & PAGE_READWRITE) ? "RW " : ""; + pt[2] = (mbi.Protect & PAGE_WRITECOPY) ? "WC " : ""; + pt[3] = (mbi.Protect & PAGE_EXECUTE) ? "EX " : ""; + pt[4] = (mbi.Protect & PAGE_EXECUTE_READ) ? "EXRO " : ""; + pt[5] = (mbi.Protect & PAGE_EXECUTE_READWRITE) ? "EXRW " : ""; + pt[6] = (mbi.Protect & PAGE_EXECUTE_WRITECOPY) ? "EXWC " : ""; + pt[7] = (mbi.Protect & PAGE_GUARD) ? "GRD " : ""; + pt[8] = (mbi.Protect & PAGE_NOACCESS) ? "NA " : ""; + pt[9] = (mbi.Protect & PAGE_NOCACHE) ? "NC " : ""; + char buf[10 * 6]; + buf[0] = '\0'; + for (int i = 0; i < 10; i++) + strcat (buf, pt[i]); + + deb_printf ("warning: failed to read memory at %08x-%08x (protect = %s), error %ld.\n", + (DWORD) current_page_address, + (DWORD) current_page_address + mbi.RegionSize, + buf, err); + skip_region_p = 1; + } + } + + if (!skip_region_p) + { + if (last_base + last_size == current_page_address) + last_size += mbi.RegionSize; + else + { + split_add_mem_region (last_base, last_size); + last_base = (LPBYTE) mbi.BaseAddress; + last_size = mbi.RegionSize; + } + } + else + { + split_add_mem_region (last_base, last_size); + last_base = NULL; + last_size = 0; + } + + current_page_address += mbi.RegionSize; + } + + /* dump last sections, if any */ + split_add_mem_region (last_base, last_size); + return 1; +}; + +int +dumper::dump_memory_region (asection * to, process_mem_region * memory) +{ + if (!sane ()) + return 0; + + DWORD size = memory->size; + DWORD todo; + DWORD done; + LPBYTE pos = memory->base; + DWORD sect_pos = 0; + + if (to == NULL || memory == NULL) + return 0; + + char mem_buf[PAGE_BUFFER_SIZE]; + + while (size > 0) + { + todo = min (size, PAGE_BUFFER_SIZE); + if (!ReadProcessMemory (hProcess, pos, mem_buf, todo, &done)) + { + deb_printf ("Failed to read process memory at %x(%x), error %ld\n", pos, todo, GetLastError ()); + return 0; + } + size -= done; + pos += done; + if (!bfd_set_section_contents (core_bfd, to, mem_buf, sect_pos, done)) + { + bfd_perror ("writing memory region to bfd"); + dumper_abort (); + return 0; + }; + sect_pos += done; + } + return 1; +} + +int +dumper::dump_thread (asection * to, process_thread * thread) +{ + if (!sane ()) + return 0; + + if (to == NULL || thread == NULL) + return 0; + + win32_pstatus thread_pstatus; + + note_header header; + bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz); + bfd_putl32 (sizeof (thread_pstatus), header.elf_note_header.descsz); + bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type); + strncpy ((char *) &header.elf_note_header.name, "win32thread", NOTE_NAME_SIZE); + + thread_pstatus.data_type = NOTE_INFO_THREAD; + thread_pstatus.data.thread_info.tid = thread->tid; + + if (tid == 0) + { + /* this is a special case. we don't know, which thread + was active when exception occured, so let's blame + the first one */ + thread_pstatus.data.thread_info.is_active_thread = TRUE; + tid = (DWORD) - 1; + } + else if (tid > 0 && thread->tid == tid) + thread_pstatus.data.thread_info.is_active_thread = TRUE; + else + thread_pstatus.data.thread_info.is_active_thread = FALSE; + + memcpy (&(thread_pstatus.data.thread_info.thread_context), + &(thread->context), + sizeof (thread->context)); + + if (!bfd_set_section_contents (core_bfd, to, &header, + 0, + sizeof (header)) || + !bfd_set_section_contents (core_bfd, to, &thread_pstatus, + sizeof (header), + sizeof (thread_pstatus))) + { + bfd_perror ("writing thread info to bfd"); + dumper_abort (); + return 0; + }; + return 1; +} + +int +dumper::dump_module (asection * to, process_module * module) +{ + if (!sane ()) + return 0; + + if (to == NULL || module == NULL) + return 0; + + struct win32_pstatus *module_pstatus_ptr; + + int note_length = sizeof (struct win32_pstatus) + strlen (module->name); + + char *buf = (char *) malloc (note_length); + + if (!buf) + { + fprintf (stderr, "Error alloating memory. Dumping aborted.\n"); + goto out; + }; + + module_pstatus_ptr = (struct win32_pstatus *) buf; + + note_header header; + bfd_putl32 (NOTE_NAME_SIZE, header.elf_note_header.namesz); + bfd_putl32 (note_length, header.elf_note_header.descsz); + bfd_putl32 (NT_WIN32PSTATUS, header.elf_note_header.type); + strncpy ((char *) &header.elf_note_header.name, "win32module", NOTE_NAME_SIZE); + + module_pstatus_ptr->data_type = NOTE_INFO_MODULE; + module_pstatus_ptr->data.module_info.base_address = module->base_address; + module_pstatus_ptr->data.module_info.module_name_size = strlen (module->name) + 1; + strcpy (module_pstatus_ptr->data.module_info.module_name, module->name); + + if (!bfd_set_section_contents (core_bfd, to, &header, + 0, + sizeof (header)) || + !bfd_set_section_contents (core_bfd, to, module_pstatus_ptr, + sizeof (header), + note_length)) + { + bfd_perror ("writing module info to bfd"); + goto out; + }; + return 1; + +out: + if (buf) + free (buf); + dumper_abort (); + return 0; + +} + +int +dumper::collect_process_information () +{ + int exception_level = 0; + + if (!sane ()) + return 0; + + if (!DebugActiveProcess (pid)) + { + fprintf (stderr, "Cannot attach to process #%lu, error %ld", pid, GetLastError ()); + return 0; + } + + char event_name[sizeof ("cygwin_error_start_event") + 20]; + sprintf (event_name, "cygwin_error_start_event%16lx", pid); + HANDLE sync_with_debugee = OpenEvent (EVENT_MODIFY_STATE, FALSE, event_name); + + DEBUG_EVENT current_event; + + while (1) + { + if (!WaitForDebugEvent (¤t_event, 20000)) + return 0; + + deb_printf ("got debug event %d\n", current_event.dwDebugEventCode); + + switch (current_event.dwDebugEventCode) + { + case CREATE_THREAD_DEBUG_EVENT: + + if (!add_thread (current_event.dwThreadId, + current_event.u.CreateThread.hThread)) + goto failed; + + break; + + case CREATE_PROCESS_DEBUG_EVENT: + + if (!add_module (current_event.u.CreateProcessInfo.lpBaseOfImage) || + !add_thread (current_event.dwThreadId, + current_event.u.CreateProcessInfo.hThread)) + goto failed; + + break; + + case EXIT_PROCESS_DEBUG_EVENT: + + deb_printf ("debugee quits"); + ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, + DBG_CONTINUE); + + return 1; + + break; + + case LOAD_DLL_DEBUG_EVENT: + + if (!add_module (current_event.u.LoadDll.lpBaseOfDll)) + goto failed; + + break; + + case EXCEPTION_DEBUG_EVENT: + + exception_level++; + if (exception_level == 2) + break; + else if (exception_level > 2) + return 0; + + collect_memory_sections (); + + /* got all info. time to dump */ + + if (!prepare_core_dump ()) + { + fprintf (stderr, "Failed to prepare core dump\n"); + goto failed; + }; + + if (!write_core_dump ()) + { + fprintf (stderr, "Failed to write core dump\n"); + goto failed; + }; + + /* signal a debugee that we've finished */ + if (sync_with_debugee) + SetEvent (sync_with_debugee); + + break; + + default: + + break; + + } + + ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, + DBG_CONTINUE); + } +failed: + /* set debugee free */ + if (sync_with_debugee) + SetEvent (sync_with_debugee); + + return 0; +} + +int +dumper::init_core_dump () +{ + bfd_init (); + + core_bfd = bfd_openw (file_name, "elf32-i386"); + if (core_bfd == NULL) + { + bfd_perror ("opening bfd"); + goto failed; + } + + if (!bfd_set_format (core_bfd, bfd_core)) + { + bfd_perror ("setting bfd format"); + goto failed; + } + + if (!bfd_set_arch_mach (core_bfd, bfd_arch_i386, 0)) + { + bfd_perror ("setting bfd architecture"); + goto failed; + } + + return 1; + +failed: + dumper_abort (); + return 0; + +} + +int +dumper::prepare_core_dump () +{ + if (!sane ()) + return 0; + + int sect_no = 0; + char sect_name[50]; + + flagword sect_flags; + DWORD sect_size; + bfd_vma sect_vma; + + asection *new_section; + + for (process_entity * p = list; p != NULL; p = p->next) + { + sect_no++; + + unsigned long phdr_type = PT_LOAD; + + switch (p->type) + { + case pr_ent_memory: + sprintf (sect_name, ".mem/%u", sect_no); + sect_flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD; + sect_size = p->u.memory.size; + sect_vma = (bfd_vma) (p->u.memory.base); + phdr_type = PT_LOAD; + break; + + case pr_ent_thread: + sprintf (sect_name, ".note/%u", sect_no); + sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; + sect_size = sizeof (note_header) + sizeof (struct win32_pstatus); + sect_vma = 0; + phdr_type = PT_NOTE; + break; + + case pr_ent_module: + sprintf (sect_name, ".note/%u", sect_no); + sect_flags = SEC_HAS_CONTENTS | SEC_LOAD; + sect_size = sizeof (note_header) + sizeof (struct win32_pstatus) + + (bfd_size_type) (strlen (p->u.module.name)); + sect_vma = 0; + phdr_type = PT_NOTE; + break; + + default: + continue; + } + + if (p->type == pr_ent_module && status_section != NULL) + { + if (!bfd_set_section_size (core_bfd, + status_section, + (bfd_get_section_size (status_section) + + sect_size))) + { + bfd_perror ("resizing status section"); + goto failed; + }; + continue; + } + + deb_printf ("creating section (type%u) %s(%u), flags=%08x\n", + p->type, sect_name, sect_size, sect_flags); + + bfd_set_error (bfd_error_no_error); + char *buf = strdup (sect_name); + new_section = bfd_make_section (core_bfd, buf); + if (new_section == NULL) + { + if (bfd_get_error () == bfd_error_no_error) + fprintf (stderr, "error creating new section (%s), section already exists.\n", buf); + else + bfd_perror ("creating section"); + goto failed; + } + + if (!bfd_set_section_flags (core_bfd, new_section, sect_flags) || + !bfd_set_section_size (core_bfd, new_section, sect_size)) + { + bfd_perror ("setting section attributes"); + goto failed; + }; + + new_section->vma = sect_vma; + new_section->lma = 0; + new_section->output_section = new_section; + new_section->output_offset = 0; + p->section = new_section; + int section_count = 1; + + bfd_boolean filehdr = 0; + bfd_boolean phdrs = 0; + + bfd_vma at = 0; + bfd_boolean valid_at = 0; + + flagword flags = 0; + bfd_boolean valid_flags = 1; + + if (p->type == pr_ent_memory) + { + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQueryEx (hProcess, (LPVOID)sect_vma, &mbi, sizeof (mbi))) + { + bfd_perror ("getting mem region flags"); + goto failed; + } + + static const struct + { + DWORD protect; + flagword flags; + } mappings[] = + { + { PAGE_READONLY, PF_R }, + { PAGE_READWRITE, PF_R | PF_W }, + { PAGE_WRITECOPY, PF_W }, + { PAGE_EXECUTE, PF_X }, + { PAGE_EXECUTE_READ, PF_X | PF_R }, + { PAGE_EXECUTE_READWRITE, PF_X | PF_R | PF_W }, + { PAGE_EXECUTE_WRITECOPY, PF_X | PF_W } + }; + + for (size_t i = 0; + i < sizeof (mappings) / sizeof (mappings[0]); + i++) + if ((mbi.Protect & mappings[i].protect) != 0) + flags |= mappings[i].flags; + } + + if (!bfd_record_phdr (core_bfd, phdr_type, + valid_flags, flags, + valid_at, at, + filehdr, phdrs, + section_count, &new_section)) + { + bfd_perror ("recording program headers"); + goto failed; + } + } + return 1; + +failed: + dumper_abort (); + return 0; +} + +int +dumper::write_core_dump () +{ + if (!sane ()) + return 0; + + for (process_entity * p = list; p != NULL; p = p->next) + { + if (p->section == NULL) + continue; + + deb_printf ("writing section type=%u base=%08x size=%08x flags=%08x\n", + p->type, + p->section->vma, + bfd_get_section_size (p->section), + p->section->flags); + + switch (p->type) + { + case pr_ent_memory: + dump_memory_region (p->section, &(p->u.memory)); + break; + + case pr_ent_thread: + dump_thread (p->section, &(p->u.thread)); + break; + + case pr_ent_module: + dump_module (p->section, &(p->u.module)); + break; + + default: + continue; + + } + } + return 1; +} + +static void +usage (FILE *stream, int status) +{ + fprintf (stream, "\ +Usage: dumper [OPTION] FILENAME WIN32PID\n\ +Dump core from WIN32PID to FILENAME.core\n\ +\n\ + -d, --verbose be verbose while dumping\n\ + -h, --help output help information and exit\n\ + -q, --quiet be quiet while dumping (default)\n\ + -v, --version output version information and exit\n\ +"); + exit (status); +} + +struct option longopts[] = { + {"verbose", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"quiet", no_argument, NULL, 'q'}, + {"version", no_argument, 0, 'v'}, + {0, no_argument, NULL, 0} +}; + +static void +print_version () +{ + const char *v = strchr (version, ':'); + int len; + if (!v) + { + v = "?"; + len = 1; + } + else + { + v += 2; + len = strchr (v, ' ') - v; + } + printf ("\ +dumper (cygwin) %.*s\n\ +Core Dumper for Cygwin\n\ +Copyright 1999, 2001, 2002 Red Hat, Inc.\n", len, v); +} + +int +main (int argc, char **argv) +{ + int opt; + const char *p = ""; + DWORD pid; + char win32_name [MAX_PATH]; + + while ((opt = getopt_long (argc, argv, "dqhv", longopts, NULL) ) != EOF) + switch (opt) + { + case 'd': + verbose = TRUE; + break; + case 'q': + verbose = FALSE; + break; + case 'h': + usage (stdout, 0); + case 'v': + print_version (); + exit (0); + default: + usage (stderr, 1); + break; + } + + if (argv && *(argv + optind) && *(argv + optind +1)) + { + *win32_name = '\0'; + cygwin_conv_to_win32_path (*(argv + optind), win32_name); + if ((p = strrchr (win32_name, '\\'))) + p++; + else + p = win32_name; + pid = strtoul (*(argv + optind + 1), NULL, 10); + } + else + { + usage (stderr, 1); + return -1; + } + + char *core_file = (char *) malloc (strlen (p) + sizeof (".core")); + if (!core_file) + { + fprintf (stderr, "error allocating memory\n"); + return -1; + } + sprintf (core_file, "%s.core", p); + + DWORD tid = 0; + + if (verbose) + printf ("dumping process #%lu to %s\n", pid, core_file); + + dumper d (pid, tid, core_file); + if (!d.sane ()) + return -1; + d.collect_process_information (); + free (core_file); + + return 0; +}; diff --git a/winsup/utils/dumper.h b/winsup/utils/dumper.h new file mode 100644 index 00000000000..4b8d2bdf728 --- /dev/null +++ b/winsup/utils/dumper.h @@ -0,0 +1,142 @@ +/* dumper.h + + Copyright 1999,2001 Red Hat Inc. + + Written by Egor Duda <deo@logos-m.ru> + + This file is part of Cygwin. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (file COPYING.dumper) for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _DUMPER_H_ +#define _DUMPER_H_ + +#include <windows.h> + +typedef struct +{ + LPBYTE base; + DWORD size; +} process_mem_region; + +typedef struct +{ + DWORD tid; + HANDLE hThread; + CONTEXT context; +} process_thread; + +typedef struct +{ + LPVOID base_address; + char* name; +} process_module; + +enum process_entity_type +{ + pr_ent_memory, + pr_ent_thread, + pr_ent_module +}; + +typedef struct _process_entity +{ + process_entity_type type; + union + { + process_thread thread; + process_mem_region memory; + process_module module; + } u; + asection* section; + struct _process_entity* next; +} process_entity; + +class exclusion +{ +public: + int last; + int size; + int step; + process_mem_region* region; + + exclusion ( int step ) { last = size = 0; + this->step = step; + region = NULL; } + ~exclusion () { free ( region ); } + int add ( LPBYTE mem_base, DWORD mem_size ); + int sort_and_check (); +}; + +#define PAGE_BUFFER_SIZE 4096 + +class dumper +{ + DWORD pid; + DWORD tid; /* thread id of active thread */ + HANDLE hProcess; + process_entity* list; + process_entity* last; + exclusion* excl_list; + + char* file_name; + bfd* core_bfd; + + asection* status_section; + + int memory_num; + int module_num; + int thread_num; + + void close (); + void dumper_abort (); + + process_entity* add_process_entity_to_list ( process_entity_type type ); + int add_thread ( DWORD tid, HANDLE hThread ); + int add_mem_region ( LPBYTE base, DWORD size ); + + /* break mem_region by excl_list and add add all subregions */ + int split_add_mem_region ( LPBYTE base, DWORD size ); + + int add_module ( LPVOID base_address ); + + int collect_memory_sections (); + int dump_memory_region ( asection* to, process_mem_region* memory ); + int dump_thread ( asection* to, process_thread* thread ); + int dump_module ( asection* to, process_module* module ); + +public: + int sane (); + + int collect_process_information (); + void print_core_section_list (); + + dumper ( DWORD pid, DWORD tid, const char* name ); + ~dumper (); + + int init_core_dump (); + int prepare_core_dump (); + int write_core_dump (); +}; + +extern int deb_printf ( const char* format, ... ); + +extern char* psapi_get_module_name ( HANDLE hProcess, DWORD BaseAddress ); + +extern int parse_pe ( const char* file_name, exclusion* excl_list ); + +extern BOOL verbose; + +#endif diff --git a/winsup/utils/mkgroup.c b/winsup/utils/mkgroup.c new file mode 100644 index 00000000000..1926d915668 --- /dev/null +++ b/winsup/utils/mkgroup.c @@ -0,0 +1,797 @@ +/* mkgroup.c: + + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#include <ctype.h> +#include <stdlib.h> +#include <wchar.h> +#include <stdio.h> +#include <windows.h> +#include <sys/cygwin.h> +#include <getopt.h> +#include <lmaccess.h> +#include <lmapibuf.h> +#include <ntsecapi.h> +#include <ntdef.h> + +#define print_win_error(x) _print_win_error(x, __LINE__) + +static const char version[] = "$Revision$"; + +typedef struct { + LPWSTR DomainControllerName; + LPWSTR DomainControllerAddress; + ULONG DomainControllerAddressType; + GUID DomainGuid; + LPWSTR DomainName; + LPWSTR DnsForestName; + ULONG Flags; + LPWSTR DcSiteName; + LPWSTR ClientSiteName; +} *PDOMAIN_CONTROLLER_INFOW; + +SID_IDENTIFIER_AUTHORITY sid_world_auth = {SECURITY_WORLD_SID_AUTHORITY}; +SID_IDENTIFIER_AUTHORITY sid_nt_auth = {SECURITY_NT_AUTHORITY}; + +NET_API_STATUS WINAPI (*netapibufferallocate)(DWORD,PVOID*); +NET_API_STATUS WINAPI (*netapibufferfree)(PVOID); +NET_API_STATUS WINAPI (*netgroupenum)(LPWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,PDWORD); +NET_API_STATUS WINAPI (*netgroupgetinfo)(LPWSTR,LPWSTR,DWORD,PBYTE*); +NET_API_STATUS WINAPI (*netlocalgroupenum)(LPWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,PDWORD); +NET_API_STATUS WINAPI (*netlocalgroupgetmembers)(LPWSTR,LPWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,PDWORD); +NET_API_STATUS WINAPI (*netgetdcname)(LPWSTR,LPWSTR,PBYTE*); +NET_API_STATUS WINAPI (*netgroupgetusers)(LPWSTR,LPWSTR,DWORD,PBYTE*,DWORD,PDWORD,PDWORD,PDWORD); + +NTSTATUS NTAPI (*lsaclose)(LSA_HANDLE); +NTSTATUS NTAPI (*lsaopenpolicy)(PLSA_UNICODE_STRING,PLSA_OBJECT_ATTRIBUTES,ACCESS_MASK,PLSA_HANDLE); +NTSTATUS NTAPI (*lsaqueryinformationpolicy)(LSA_HANDLE,POLICY_INFORMATION_CLASS,PVOID*); +NTSTATUS NTAPI (*lsafreememory)(PVOID); + +NET_API_STATUS WINAPI (*dsgetdcname)(LPWSTR,LPWSTR,GUID*,LPWSTR,ULONG,PDOMAIN_CONTROLLER_INFOW*); + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +BOOL +load_netapi () +{ + HANDLE h = LoadLibrary ("netapi32.dll"); + + if (!h) + return FALSE; + + if (!(netapibufferallocate = (void *) GetProcAddress (h, "NetApiBufferAllocate"))) + return FALSE; + if (!(netapibufferfree = (void *) GetProcAddress (h, "NetApiBufferFree"))) + return FALSE; + if (!(netgroupenum = (void *) GetProcAddress (h, "NetGroupEnum"))) + return FALSE; + if (!(netgroupgetinfo = (void *) GetProcAddress (h, "NetGroupGetInfo"))) + return FALSE; + if (!(netgroupgetusers = (void *) GetProcAddress (h, "NetGroupGetUsers"))) + return FALSE; + if (!(netlocalgroupenum = (void *) GetProcAddress (h, "NetLocalGroupEnum"))) + return FALSE; + if (!(netlocalgroupgetmembers = (void *) GetProcAddress (h, "NetLocalGroupGetMembers"))) + return FALSE; + if (!(netgetdcname = (void *) GetProcAddress (h, "NetGetDCName"))) + return FALSE; + + dsgetdcname = (void *) GetProcAddress (h, "DsGetDcNameW"); + + if (!(h = LoadLibrary ("advapi32.dll"))) + return FALSE; + + if (!(lsaclose = (void *) GetProcAddress (h, "LsaClose"))) + return FALSE; + if (!(lsaopenpolicy = (void *) GetProcAddress (h, "LsaOpenPolicy"))) + return FALSE; + if (!(lsaqueryinformationpolicy = (void *) GetProcAddress (h, "LsaQueryInformationPolicy"))) + return FALSE; + if (!(lsafreememory = (void *) GetProcAddress (h, "LsaFreeMemory"))) + return FALSE; + + return TRUE; +} + +char * +put_sid (PSID sid) +{ + static char s[512]; + char t[32]; + DWORD i; + + strcpy (s, "S-1-"); + sprintf(t, "%u", GetSidIdentifierAuthority (sid)->Value[5]); + strcat (s, t); + for (i = 0; i < *GetSidSubAuthorityCount (sid); ++i) + { + sprintf(t, "-%lu", *GetSidSubAuthority (sid, i)); + strcat (s, t); + } + return s; +} + +void +psx_dir (char *in, char *out) +{ + if (isalpha (in[0]) && in[1] == ':') + { + sprintf (out, "/cygdrive/%c", in[0]); + in += 2; + out += strlen (out); + } + + while (*in) + { + if (*in == '\\') + *out = '/'; + else + *out = *in; + in++; + out++; + } + + *out = '\0'; +} + +void +uni2ansi (LPWSTR wcs, char *mbs, int size) +{ + if (wcs) + WideCharToMultiByte (CP_ACP, 0, wcs, -1, mbs, size, NULL, NULL); + else + *mbs = '\0'; +} + +void +_print_win_error(DWORD code, int line) +{ + char buf[4096]; + + if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + code, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) buf, sizeof (buf), NULL)) + fprintf (stderr, "mkgroup (%d): [%lu] %s", line, code, buf); + else + fprintf (stderr, "mkgroup (%d): error %lu", line, code); +} + +void +enum_local_users (LPWSTR groupname) +{ + LOCALGROUP_MEMBERS_INFO_1 *buf1; + DWORD entries = 0; + DWORD total = 0; + DWORD reshdl = 0; + + if (!netlocalgroupgetmembers (NULL, groupname, + 1, (void *) &buf1, + MAX_PREFERRED_LENGTH, + &entries, &total, &reshdl)) + { + unsigned i, first = 1; + + for (i = 0; i < entries; ++i) + if (buf1[i].lgrmi1_sidusage == SidTypeUser) + { + char user[256]; + + if (!first) + printf (","); + first = 0; + uni2ansi (buf1[i].lgrmi1_name, user, sizeof (user)); + printf ("%s", user); + } + netapibufferfree (buf1); + } +} + +int +enum_local_groups (int print_sids, int print_users, char *disp_groupname) +{ + LOCALGROUP_INFO_0 *buffer; + DWORD entriesread = 0; + DWORD totalentries = 0; + DWORD resume_handle = 0; + WCHAR uni_name[512]; + DWORD rc; + + do + { + DWORD i; + + if (disp_groupname != NULL) + { + MultiByteToWideChar (CP_ACP, 0, disp_groupname, -1, uni_name, 512 ); + rc = netapibufferallocate(sizeof(LOCALGROUP_INFO_0), (void *) &buffer ); + buffer[0].lgrpi0_name = (LPWSTR) & uni_name; + entriesread=1; + } + else + rc = netlocalgroupenum (NULL, 0, (void *) &buffer, 1024, + &entriesread, &totalentries, &resume_handle); + switch (rc) + { + case ERROR_ACCESS_DENIED: + print_win_error(rc); + exit (1); + + case ERROR_MORE_DATA: + case ERROR_SUCCESS: + break; + + default: + print_win_error(rc); + exit (1); + } + + for (i = 0; i < entriesread; i++) + { + char localgroup_name[100]; + char domain_name[100]; + DWORD domname_len = 100; + char psid_buffer[1024]; + PSID psid = (PSID) psid_buffer; + DWORD sid_length = 1024; + DWORD gid; + SID_NAME_USE acc_type; + uni2ansi (buffer[i].lgrpi0_name, localgroup_name, sizeof (localgroup_name)); + + if (!LookupAccountName (NULL, localgroup_name, psid, + &sid_length, domain_name, &domname_len, + &acc_type)) + { + print_win_error(rc); + fprintf(stderr, " (%s)\n", localgroup_name); + continue; + } + else if (acc_type == SidTypeDomain) + { + char domname[356]; + + strcpy (domname, domain_name); + strcat (domname, "\\"); + strcat (domname, localgroup_name); + sid_length = 1024; + domname_len = 100; + if (!LookupAccountName (NULL, domname, + psid, &sid_length, + domain_name, &domname_len, + &acc_type)) + { + print_win_error(rc); + fprintf(stderr, " (%s)\n", domname); + continue; + } + } + + gid = *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1); + + printf ("%s:%s:%ld:", localgroup_name, + print_sids ? put_sid (psid) : "", + gid); + if (print_users) + enum_local_users (buffer[i].lgrpi0_name); + printf ("\n"); + } + + netapibufferfree (buffer); + + } + while (rc == ERROR_MORE_DATA); + + return 0; +} + +void +enum_users (LPWSTR servername, LPWSTR groupname) +{ + GROUP_USERS_INFO_0 *buf1; + DWORD entries = 0; + DWORD total = 0; + DWORD reshdl = 0; + + if (!netgroupgetusers (servername, groupname, + 0, (void *) &buf1, + MAX_PREFERRED_LENGTH, + &entries, &total, &reshdl)) + { + unsigned i, first = 1; + + for (i = 0; i < entries; ++i) + { + char user[256]; + + if (!first) + printf (","); + first = 0; + uni2ansi (buf1[i].grui0_name, user, sizeof (user)); + printf ("%s", user); + } + netapibufferfree (buf1); + } +} + +void +enum_groups (LPWSTR servername, int print_sids, int print_users, int id_offset, + char *disp_groupname) +{ + GROUP_INFO_2 *buffer; + DWORD entriesread = 0; + DWORD totalentries = 0; + DWORD resume_handle = 0; + WCHAR uni_name[512]; + DWORD rc; + char ansi_srvname[256]; + + if (servername) + uni2ansi (servername, ansi_srvname, sizeof (ansi_srvname)); + + do + { + DWORD i; + + if (disp_groupname != NULL) + { + MultiByteToWideChar (CP_ACP, 0, disp_groupname, -1, uni_name, 512 ); + rc = netgroupgetinfo(servername, (LPWSTR) & uni_name, 2, + (void *) &buffer ); + entriesread=1; + } + else + rc = netgroupenum (servername, 2, (void *) & buffer, MAX_PREFERRED_LENGTH, + &entriesread, &totalentries, &resume_handle); + switch (rc) + { + case ERROR_ACCESS_DENIED: + print_win_error(rc); + exit (1); + + case ERROR_MORE_DATA: + case ERROR_SUCCESS: + break; + + default: + print_win_error(rc); + exit (1); + } + + for (i = 0; i < entriesread; i++) + { + char groupname[100]; + char domain_name[100]; + DWORD domname_len = 100; + char psid_buffer[1024]; + PSID psid = (PSID) psid_buffer; + DWORD sid_length = 1024; + SID_NAME_USE acc_type; + + int gid = buffer[i].grpi2_group_id; + uni2ansi (buffer[i].grpi2_name, groupname, sizeof (groupname)); + if (print_sids) + { + if (!LookupAccountName (servername ? ansi_srvname : NULL, + groupname, + psid, &sid_length, + domain_name, &domname_len, + &acc_type)) + { + print_win_error(rc); + fprintf(stderr, " (%s)\n", groupname); + continue; + } + else if (acc_type == SidTypeDomain) + { + char domname[356]; + + strcpy (domname, domain_name); + strcat (domname, "\\"); + strcat (domname, groupname); + sid_length = 1024; + domname_len = 100; + if (!LookupAccountName (servername ? ansi_srvname : NULL, + domname, + psid, &sid_length, + domain_name, &domname_len, + &acc_type)) + { + print_win_error(rc); + fprintf(stderr, " (%s)\n", domname); + continue; + } + } + } + printf ("%s:%s:%u:", groupname, + print_sids ? put_sid (psid) : "", + gid + id_offset); + if (print_users) + enum_users (servername, buffer[i].grpi2_name); + printf ("\n"); + } + + netapibufferfree (buffer); + + } + while (rc == ERROR_MORE_DATA); +} + +void +print_special (int print_sids, + PSID_IDENTIFIER_AUTHORITY auth, BYTE cnt, + DWORD sub1, DWORD sub2, DWORD sub3, DWORD sub4, + DWORD sub5, DWORD sub6, DWORD sub7, DWORD sub8) +{ + char name[256], dom[256]; + DWORD len, len2, rid; + PSID sid; + SID_NAME_USE use; + + if (AllocateAndInitializeSid (auth, cnt, sub1, sub2, sub3, sub4, + sub5, sub6, sub7, sub8, &sid)) + { + if (LookupAccountSid (NULL, sid, + name, (len = 256, &len), + dom, (len2 = 256, &len), + &use)) + { + if (sub8) + rid = sub8; + else if (sub7) + rid = sub7; + else if (sub6) + rid = sub6; + else if (sub5) + rid = sub5; + else if (sub4) + rid = sub4; + else if (sub3) + rid = sub3; + else if (sub2) + rid = sub2; + else + rid = sub1; + printf ("%s:%s:%lu:\n", name, + print_sids ? put_sid (sid) : "", + rid); + } + FreeSid (sid); + } +} + +void +current_group (int print_sids, int print_users, int id_offset) +{ + char name[UNLEN + 1], *envname, *envdomain; + DWORD len; + HANDLE ptok; + int errpos = 0; + struct { + PSID psid; + int buffer[10]; + } tg; + + + if ((!GetUserName (name, (len = sizeof (name), &len)) && (errpos = __LINE__)) + || !name[0] + || !(envname = getenv("USERNAME")) + || strcasecmp (envname, name) + || (!GetComputerName (name, (len = sizeof (name), &len)) + && (errpos = __LINE__)) + || !(envdomain = getenv("USERDOMAIN")) + || !envdomain[0] + || !strcasecmp (envdomain, name) + || (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok) + && (errpos = __LINE__)) + || (!GetTokenInformation (ptok, TokenPrimaryGroup, &tg, sizeof tg, &len) + && (errpos = __LINE__)) + || (!CloseHandle (ptok) && (errpos = __LINE__))) + { + if (errpos) + { + print_win_error (GetLastError ()); + } + return; + } + + int gid = *GetSidSubAuthority (tg.psid, *GetSidSubAuthorityCount(tg.psid) - 1); + + printf ("mkgroup_l_d:%s:%u:", print_sids ? put_sid (tg.psid) : "", + gid + id_offset); + if (print_users) + printf("%s", envname); + printf ("\n"); +} + +int +usage (FILE * stream, int isNT) +{ + fprintf (stream, "Usage: mkgroup [OPTION]... [domain]...\n" + "Print /etc/group file to stdout\n\n" + "Options:\n"); + if (isNT) + fprintf (stream, " -l,--local print local group information\n" + " -c,--current print current group, if a domain account\n" + " -d,--domain print global group information (from current\n" + " domain if no domains specified)\n" + " -o,--id-offset offset change the default offset (10000) added to gids\n" + " in domain accounts.\n" + " -s,--no-sids don't print SIDs in pwd field\n" + " (this affects ntsec)\n" + " -u,--users print user list in gr_mem field\n" + " -g,--group groupname only return information for the specified group\n"); + fprintf (stream, " -h,--help print this message\n" + " -v,--version print version information and exit\n\n"); + if (isNT) + fprintf (stream, "One of '-l' or '-d' must be given.\n"); + + return 1; +} + +struct option longopts[] = { + {"local", no_argument, NULL, 'l'}, + {"current", no_argument, NULL, 'c'}, + {"domain", no_argument, NULL, 'd'}, + {"id-offset", required_argument, NULL, 'o'}, + {"no-sids", no_argument, NULL, 's'}, + {"users", no_argument, NULL, 'u'}, + {"group", required_argument, NULL, 'g'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {0, no_argument, NULL, 0} +}; + +char opts[] = "lcdo:sug:hv"; + +void +print_version () +{ + const char *v = strchr (version, ':'); + int len; + if (!v) + { + v = "?"; + len = 1; + } + else + { + v += 2; + len = strchr (v, ' ') - v; + } + printf ("\ +mkgroup (cygwin) %.*s\n\ +group File Generator\n\ +Copyright 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.\n\ +Compiled on %s\n\ +", len, v, __DATE__); +} + +int +main (int argc, char **argv) +{ + LPWSTR servername; + DWORD rc = ERROR_SUCCESS; + WCHAR domain_name[100]; + int print_local = 0; + int print_current = 0; + int print_domain = 0; + int print_sids = 1; + int print_users = 0; + int domain_specified = 0; + int id_offset = 10000; + char *disp_groupname = NULL; + int isRoot = 0; + int isNT; + int i; + + char name[256], dom[256]; + DWORD len, len2; + char buf[1024]; + PSID psid = NULL; + SID_NAME_USE use; + + LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 }; + LSA_HANDLE lsa = INVALID_HANDLE_VALUE; + NTSTATUS ret; + PPOLICY_PRIMARY_DOMAIN_INFO pdi; + + isNT = (GetVersion () < 0x80000000); + + if (isNT && argc == 1) + return usage(stderr, isNT); + else + { + while ((i = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) + switch (i) + { + case 'l': + print_local = 1; + break; + case 'c': + print_current = 1; + break; + case 'd': + print_domain = 1; + break; + case 'o': + id_offset = strtol (optarg, NULL, 10); + break; + case 's': + print_sids = 0; + break; + case 'u': + print_users = 1; + break; + case 'g': + disp_groupname = optarg; + isRoot = !strcmp(disp_groupname, "root"); + break; + case 'h': + usage (stdout, isNT); + return 0; + case 'v': + print_version (); + return 0; + default: + fprintf (stderr, "Try '%s --help' for more information.\n", argv[0]); + return 1; + } + } + + /* This takes Windows 9x/ME into account. */ + if (!isNT) + { + printf ("all::%ld:\n", DOMAIN_ALIAS_RID_ADMINS); + return 0; + } + + if (!print_local && !print_domain) + { + fprintf (stderr, "%s: Specify one of '-l' or '-d'\n", argv[0]); + return 1; + } + if (optind < argc) + { + if (!print_domain) + { + fprintf (stderr, "%s: A domain name is only accepted " + "when '-d' is given.\n", argv[0]); + return 1; + } + domain_specified = 1; + } + if (!load_netapi ()) + { + print_win_error(GetLastError ()); + return 1; + } + + if (print_local) + { + if (isRoot) + { + /* + * Very special feature for the oncoming future: + * Create a "root" group account, being actually the local + * Administrators group. Since user name, sid and gid are + * fixed, there's no need to call print_special() for this. + */ + printf ("root:S-1-5-32-544:0:\n"); + } + + if (disp_groupname == NULL) + { + /* + * Get 'system' group + */ + print_special (print_sids, &sid_nt_auth, 1, SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0); + /* + * Get 'None' group + */ + len = 256; + GetComputerName (name, &len); + len = 1024; + len2 = 256; + if (LookupAccountName (NULL, name, (PSID) buf, &len, dom, &len, &use)) + psid = (PSID) buf; + else + { + ret = lsaopenpolicy (NULL, &oa, POLICY_VIEW_LOCAL_INFORMATION, &lsa); + if (ret == STATUS_SUCCESS && lsa != INVALID_HANDLE_VALUE) + { + ret = lsaqueryinformationpolicy (lsa, + PolicyPrimaryDomainInformation, + (void *) &pdi); + if (ret == STATUS_SUCCESS) + { + if (pdi->Sid) + { + CopySid (1024, (PSID) buf, pdi->Sid); + psid = (PSID) buf; + } + lsafreememory (pdi); + } + lsaclose (lsa); + } + } + if (!psid) + fprintf (stderr, + "WARNING: Group 513 couldn't get retrieved. Try mkgroup -d\n"); + else + print_special (print_sids, GetSidIdentifierAuthority (psid), 5, + *GetSidSubAuthority (psid, 0), + *GetSidSubAuthority (psid, 1), + *GetSidSubAuthority (psid, 2), + *GetSidSubAuthority (psid, 3), + 513, + 0, + 0, + 0); + } + + if (!isRoot) + { + enum_local_groups (print_sids, print_users, disp_groupname); + } + } + + i = 1; + if (print_domain) + do + { + PDOMAIN_CONTROLLER_INFOW pdci = NULL; + + if (dsgetdcname) + { + if (domain_specified) + { + mbstowcs (domain_name, argv[optind], strlen (argv[optind]) + 1); + rc = dsgetdcname (NULL, domain_name, NULL, NULL, 0, &pdci); + } + else + rc = dsgetdcname (NULL, NULL, NULL, NULL, 0, &pdci); + if (rc != ERROR_SUCCESS) + { + print_win_error(rc); + return 1; + } + servername = pdci->DomainControllerName; + } + else + { + rc = netgetdcname (NULL, NULL, (void *) &servername); + if (rc == ERROR_SUCCESS && domain_specified) + { + LPWSTR server = servername; + mbstowcs (domain_name, argv[optind], strlen (argv[optind]) + 1); + rc = netgetdcname (NULL, domain_name, (void *) &servername); + netapibufferfree (server); + } + if (rc != ERROR_SUCCESS) + { + print_win_error(rc); + return 1; + } + } + enum_groups (servername, print_sids, print_users, id_offset * i++, + disp_groupname); + netapibufferfree (pdci ? (PVOID) pdci : (PVOID) servername); + } + while (++optind < argc); + + if (print_current && !print_domain) + current_group (print_sids, print_users, id_offset); + + return 0; +} diff --git a/winsup/utils/parse_pe.cc b/winsup/utils/parse_pe.cc new file mode 100644 index 00000000000..44b4068a466 --- /dev/null +++ b/winsup/utils/parse_pe.cc @@ -0,0 +1,104 @@ +/* parse_pe.cc + + Copyright 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + + Written by Egor Duda <deo@logos-m.ru> + + This file is part of Cygwin. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (file COPYING.dumper) for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <bfd.h> +#include <stdio.h> +#include <stdlib.h> + +#include "dumper.h" + +int +exclusion::add (LPBYTE mem_base, DWORD mem_size) +{ + while (last >= size) + size += step; + region = (process_mem_region *) realloc (region, size * sizeof (process_mem_region)); + if (region == NULL) + return 0; + region[last].base = mem_base; + region[last].size = mem_size; + last++; + return 1; +}; + +int +cmp_regions (const void *r1, const void *r2) +{ + if (((process_mem_region *) r1)->base < ((process_mem_region *) r2)->base) + return -1; + if (((process_mem_region *) r1)->base > ((process_mem_region *) r2)->base) + return 1; + return 0; +} + +int +exclusion::sort_and_check () +{ + qsort (region, last, sizeof (process_mem_region), &cmp_regions); + for (process_mem_region * p = region; p < region + last - 1; p++) + { + process_mem_region *q = p + 1; + if (q == p + 1) + continue; + if (p->base + size > q->base) + { + fprintf (stderr, "region error @ (%8p + %d) > %8p\n", p->base, size, q->base); + return 0; + } + } + return 1; +} + +static void +select_data_section (bfd * abfd, asection * sect, PTR obj) +{ + exclusion *excl_list = (exclusion *) obj; + + if ((sect->flags & (SEC_CODE | SEC_DEBUGGING)) && + sect->vma && bfd_get_section_size (sect)) + { + excl_list->add ((LPBYTE) sect->vma, (DWORD) bfd_get_section_size (sect)); + deb_printf ("excluding section: %20s %08lx\n", sect->name, + bfd_get_section_size (sect)); + } +} + +int +parse_pe (const char *file_name, exclusion * excl_list) +{ + if (file_name == NULL || excl_list == NULL) + return 0; + + bfd *abfd = bfd_openr (file_name, "pei-i386"); + if (abfd == NULL) + { + bfd_perror ("failed to open file"); + return 0; + } + + bfd_check_format (abfd, bfd_object); + bfd_map_over_sections (abfd, &select_data_section, (PTR) excl_list); + excl_list->sort_and_check (); + + bfd_close (abfd); + return 1; +} diff --git a/winsup/utils/path.cc b/winsup/utils/path.cc new file mode 100644 index 00000000000..57bd3a4ff65 --- /dev/null +++ b/winsup/utils/path.cc @@ -0,0 +1,353 @@ +/* path.cc + + Copyright 2001, 2002, 2003, 2005 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +/* The purpose of this file is to hide all the details about accessing + Cygwin's mount table. If the format or location of the mount table + changes, this is the file to change to match it. */ + +#define str(a) #a +#define scat(a,b) str(a##b) +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include "cygwin/include/cygwin/version.h" +#include "cygwin/include/sys/mount.h" +#include "cygwin/include/mntent.h" + +/* Used when treating / and \ as equivalent. */ +#define SLASH_P(ch) \ + ({ \ + char __c = (ch); \ + ((__c) == '/' || (__c) == '\\'); \ + }) + + +static struct mnt + { + const char *native; + char *posix; + unsigned flags; + int issys; + } mount_table[255]; + +struct mnt *root_here = NULL; + +static char * +find2 (HKEY rkey, unsigned *flags, char *what) +{ + char *retval = 0; + DWORD retvallen = 0; + DWORD type; + HKEY key; + + if (RegOpenKeyEx (rkey, what, 0, KEY_READ, &key) != ERROR_SUCCESS) + return 0; + + if (RegQueryValueEx (key, "native", 0, &type, 0, &retvallen) + == ERROR_SUCCESS) + { + retval = (char *) malloc (MAX_PATH + 1); + if (RegQueryValueEx (key, "native", 0, &type, (BYTE *) retval, &retvallen) + != ERROR_SUCCESS) + { + free (retval); + retval = 0; + } + } + + retvallen = sizeof (flags); + RegQueryValueEx (key, "flags", 0, &type, (BYTE *)flags, &retvallen); + + RegCloseKey (key); + + return retval; +} + +static LONG +get_cygdrive0 (HKEY key, const char *what, void *val, DWORD len) +{ + LONG status = RegQueryValueEx (key, what, 0, 0, (BYTE *)val, &len); + return status; +} + +static mnt * +get_cygdrive (HKEY key, mnt *m, int issystem) +{ + if (get_cygdrive0 (key, CYGWIN_INFO_CYGDRIVE_FLAGS, &m->flags, + sizeof (m->flags)) != ERROR_SUCCESS) { + free (m->posix); + return m; + } + get_cygdrive0 (key, CYGWIN_INFO_CYGDRIVE_PREFIX, m->posix, MAX_PATH); + m->native = strdup ("."); + m->issys = issystem; + return m + 1; +} + +static void +read_mounts () +{ + DWORD posix_path_size; + int res; + struct mnt *m = mount_table; + DWORD disposition; + char buf[10000]; + + root_here = NULL; + for (mnt *m1 = mount_table; m1->posix; m1++) + { + free (m1->posix); + if (m1->native) + free ((char *) m1->native); + m1->posix = NULL; + } + + /* Loop through subkeys */ + /* FIXME: we would like to not check MAX_MOUNTS but the heap in the + shared area is currently statically allocated so we can't have an + arbitrarily large number of mounts. */ + for (int issystem = 0; issystem <= 1; issystem++) + { + sprintf (buf, "Software\\%s\\%s\\%s", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, + CYGWIN_INFO_CYGWIN_REGISTRY_NAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME); + + HKEY key = issystem ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + if (RegCreateKeyEx (key, buf, 0, (LPTSTR) "Cygwin", 0, KEY_READ, + 0, &key, &disposition) != ERROR_SUCCESS) + break; + for (int i = 0; ;i++, m++) + { + m->posix = (char *) malloc (MAX_PATH + 1); + posix_path_size = MAX_PATH; + /* FIXME: if maximum posix_path_size is 256, we're going to + run into problems if we ever try to store a mount point that's + over 256 but is under MAX_PATH. */ + res = RegEnumKeyEx (key, i, m->posix, &posix_path_size, NULL, + NULL, NULL, NULL); + + if (res == ERROR_NO_MORE_ITEMS) + { + m = get_cygdrive (key, m, issystem); + m->posix = NULL; + break; + } + + if (!*m->posix) + goto no_go; + else if (res != ERROR_SUCCESS) + break; + else + { + m->native = find2 (key, &m->flags, m->posix); + m->issys = issystem; + if (!m->native) + goto no_go; + } + continue; + no_go: + free (m->posix); + m->posix = NULL; + m--; + } + RegCloseKey (key); + } +} + +/* Return non-zero if PATH1 is a prefix of PATH2. + Both are assumed to be of the same path style and / vs \ usage. + Neither may be "". + LEN1 = strlen (PATH1). It's passed because often it's already known. + + Examples: + /foo/ is a prefix of /foo <-- may seem odd, but desired + /foo is a prefix of /foo/ + / is a prefix of /foo/bar + / is not a prefix of foo/bar + foo/ is a prefix foo/bar + /foo is not a prefix of /foobar +*/ + +static int +path_prefix_p (const char *path1, const char *path2, int len1) +{ + /* Handle case where PATH1 has trailing '/' and when it doesn't. */ + if (len1 > 0 && SLASH_P (path1[len1 - 1])) + len1--; + + if (len1 == 0) + return SLASH_P (path2[0]) && !SLASH_P (path2[1]); + + if (strncasecmp (path1, path2, len1) != 0) + return 0; + + return SLASH_P (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':'; +} + +static char * +vconcat (const char *s, va_list v) +{ + int len; + char *rv, *arg; + va_list save_v = v; + int unc; + + if (!s) + return 0; + + len = strlen (s); + + unc = SLASH_P (*s) && SLASH_P (s[1]); + + while (1) + { + arg = va_arg (v, char *); + if (arg == 0) + break; + len += strlen (arg); + } + va_end (v); + + rv = (char *) malloc (len + 1); + strcpy (rv, s); + v = save_v; + while (1) + { + arg = va_arg (v, char *); + if (arg == 0) + break; + strcat (rv, arg); + } + va_end (v); + + char *d, *p; + + /* concat is only used for urls and files, so we can safely + canonicalize the results */ + for (p = d = rv; *p; p++) + { + *d++ = *p; + /* special case for URLs */ + if (*p == ':' && p[1] == '/' && p[2] == '/' && p > rv + 1) + { + *d++ = *++p; + *d++ = *++p; + } + else if (*p == '/' || *p == '\\') + { + if (p == rv && unc) + *d++ = *p++; + while (p[1] == '/') + p++; + } + } + *d = 0; + + return rv; +} + +static char * +concat (const char *s, ...) +{ + va_list v; + + va_start (v, s); + + return vconcat (s, v); +} + +char * +cygpath (const char *s, ...) +{ + va_list v; + int max_len = -1; + struct mnt *m, *match = NULL; + + if (!mount_table[0].posix) + read_mounts (); + va_start (v, s); + char *path = vconcat (s, v); + if (strncmp (path, "./", 2) == 0) + memmove (path, path + 2, strlen (path + 2) + 1); + if (strncmp (path, "/./", 3) == 0) + memmove (path + 1, path + 3, strlen (path + 3) + 1); + + for (m = mount_table; m->posix ; m++) + { + if (m->flags & MOUNT_CYGDRIVE) + continue; + + int n = strlen (m->posix); + if (n < max_len || !path_prefix_p (m->posix, path, n)) + continue; + max_len = n; + match = m; + } + + char *native; + if (match == NULL) + native = strdup (path); + else if (max_len == (int) strlen (path)) + native = strdup (match->native); + else + native = concat (match->native, "\\", path + max_len, NULL); + free (path); + + return native; +} + +static mnt *m = NULL; + +extern "C" FILE * +setmntent (const char *, const char *) +{ + m = mount_table; + if (!m->posix) + read_mounts (); + return NULL; +} + +extern "C" struct mntent * +getmntent (FILE *) +{ + static mntent mnt; + if (!m->posix) + return NULL; + + mnt.mnt_fsname = (char *) m->native; + mnt.mnt_dir = (char *) m->posix; + if (!mnt.mnt_type) + mnt.mnt_type = (char *) malloc (1024); + if (!mnt.mnt_opts) + mnt.mnt_opts = (char *) malloc (1024); + if (!m->issys) + strcpy (mnt.mnt_type, (char *) "user"); + else + strcpy (mnt.mnt_type, (char *) "system"); + if (!(m->flags & MOUNT_BINARY)) + strcpy (mnt.mnt_opts, (char *) "textmode"); + else + strcpy (mnt.mnt_opts, (char *) "binmode"); + if (m->flags & MOUNT_CYGWIN_EXEC) + strcat (mnt.mnt_opts, (char *) ",cygexec"); + else if (m->flags & MOUNT_EXEC) + strcat (mnt.mnt_opts, (char *) ",exec"); + else if (m->flags & MOUNT_NOTEXEC) + strcat (mnt.mnt_opts, (char *) ",noexec"); + if (m->flags & MOUNT_ENC) + strcat (mnt.mnt_opts, ",managed"); + if ((m->flags & MOUNT_CYGDRIVE)) /* cygdrive */ + strcat (mnt.mnt_opts, (char *) ",cygdrive"); + mnt.mnt_freq = 1; + mnt.mnt_passno = 1; + m++; + return &mnt; +} diff --git a/winsup/utils/strace.cc b/winsup/utils/strace.cc new file mode 100644 index 00000000000..38332641632 --- /dev/null +++ b/winsup/utils/strace.cc @@ -0,0 +1,1061 @@ +/* strace.cc + + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat Inc. + + Written by Chris Faylor <cgf@redhat.com> + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#define cygwin_internal cygwin_internal_dontuse +#include <stdio.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <windows.h> +#include <signal.h> +#include <errno.h> +#include "cygwin/include/sys/strace.h" +#include "cygwin/include/sys/cygwin.h" +#include "path.h" +#undef cygwin_internal + +/* we *know* we're being built with GCC */ +#define alloca __builtin_alloca + +// Version string. +static const char version[] = "$Revision$"; + +static const char *pgm; +static int forkdebug = 1; +static int numerror = 1; +static int show_usecs = 1; +static int delta = 1; +static int hhmmss; +static int bufsize; +static int new_window; +static long flush_period; +static int include_hex; +static int quiet = -1; + +static unsigned char strace_active = 1; +static int processes; + +static BOOL close_handle (HANDLE h, DWORD ok); + +#define CloseHandle(h) close_handle(h, 0) + +struct child_list +{ + DWORD id; + HANDLE hproc; + int saw_stars; + char nfields; + long long start_time; + DWORD last_usecs; + struct child_list *next; + child_list ():id (0), hproc (NULL), saw_stars (0), nfields (0), + start_time (0), last_usecs (0), next (NULL) + { + } +}; + +child_list children; + +static void +warn (int geterrno, const char *fmt, ...) +{ + va_list args; + char buf[4096]; + + va_start (args, fmt); + sprintf (buf, "%s: ", pgm); + vsprintf (strchr (buf, '\0'), fmt, args); + if (geterrno) + perror (buf); + else + { + fputs (buf, stderr); + fputs ("\n", stderr); + } +} + +static void __attribute__ ((noreturn)) +error (int geterrno, const char *fmt, ...) +{ + va_list args; + char buf[4096]; + + va_start (args, fmt); + sprintf (buf, "%s: ", pgm); + vsprintf (strchr (buf, '\0'), fmt, args); + if (geterrno) + perror (buf); + else + { + fputs (buf, stderr); + fputs ("\n", stderr); + } + exit (1); +} + +DWORD lastid = 0; +HANDLE lasth; + +static child_list * +get_child (DWORD id) +{ + child_list *c; + for (c = &children; (c = c->next) != NULL;) + if (c->id == id) + return c; + + return NULL; +} + +static void +add_child (DWORD id, HANDLE hproc) +{ + if (!get_child (id)) + { + child_list *c = children.next; + children.next = (child_list *) calloc (1, sizeof (child_list)); + children.next->next = c; + lastid = children.next->id = id; + lasth = children.next->hproc = hproc; + processes++; + if (!quiet) + fprintf (stderr, "Windows process %d attached\n", id); + } +} + +static void +remove_child (DWORD id) +{ + child_list *c; + if (id == lastid) + lastid = 0; + for (c = &children; c->next != NULL; c = c->next) + if (c->next->id == id) + { + child_list *c1 = c->next; + c->next = c1->next; + free (c1); + if (!quiet) + fprintf (stderr, "Windows process %d detached\n", id); + processes--; + return; + } + + error (0, "no process id %d found", id); +} + +#define LINE_BUF_CHUNK 128 + +class linebuf +{ + size_t alloc; +public: + size_t ix; + char *buf; + linebuf () + { + ix = 0; + alloc = 0; + buf = NULL; + } + ~linebuf () + { + if (buf) + free (buf); + } + void add (const char *what, int len); + void add (const char *what) + { + add (what, strlen (what)); + } + void prepend (const char *what, int len); +}; + +void +linebuf::add (const char *what, int len) +{ + size_t newix; + if ((newix = ix + len) >= alloc) + { + alloc += LINE_BUF_CHUNK + len; + buf = (char *) realloc (buf, alloc + 1); + } + memcpy (buf + ix, what, len); + ix = newix; + buf[ix] = '\0'; +} + +void +linebuf::prepend (const char *what, int len) +{ + int buflen; + size_t newix; + if ((newix = ix + len) >= alloc) + { + alloc += LINE_BUF_CHUNK + len; + buf = (char *) realloc (buf, alloc + 1); + buf[ix] = '\0'; + } + if ((buflen = strlen (buf))) + memmove (buf + len, buf, buflen + 1); + else + buf[newix] = '\0'; + memcpy (buf, what, len); + ix = newix; +} + +static void +make_command_line (linebuf & one_line, char **argv) +{ + for (; *argv; argv++) + { + char *p = NULL; + const char *a = *argv; + + int len = strlen (a); + if (len != 0 && !(p = strpbrk (a, " \t\n\r\""))) + one_line.add (a, len); + else + { + one_line.add ("\"", 1); + for (; p; a = p, p = strchr (p, '"')) + { + one_line.add (a, ++p - a); + if (p[-1] == '"') + one_line.add ("\"", 1); + } + if (*a) + one_line.add (a); + one_line.add ("\"", 1); + } + one_line.add (" ", 1); + } + + if (one_line.ix) + one_line.buf[one_line.ix - 1] = '\0'; + else + one_line.add ("", 1); +} + +static DWORD child_pid; + +static BOOL WINAPI +ctrl_c (DWORD) +{ + static int tic = 1; + if ((tic ^= 1) && !GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0)) + error (0, "couldn't send CTRL-C to child, win32 error %d\n", + GetLastError ()); + return TRUE; +} + +extern "C" { +unsigned long (*cygwin_internal) (int, ...); +}; + +static int +load_cygwin () +{ + static HMODULE h; + + if (cygwin_internal) + return 1; + + if (h) + return 0; + + if (!(h = LoadLibrary ("cygwin1.dll"))) + { + errno = ENOENT; + return 0; + } + if (!(cygwin_internal = (DWORD (*) (int, ...)) GetProcAddress (h, "cygwin_internal"))) + { + errno = ENOSYS; + return 0; + } + return 1; +} + +static void +attach_process (pid_t pid) +{ + child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid); + if (!child_pid) + child_pid = pid; + + if (!DebugActiveProcess (child_pid)) + error (0, "couldn't attach to pid %d for debugging", child_pid); + + return; +} + + +static void +create_child (char **argv) +{ + linebuf one_line; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ret; + DWORD flags; + + if (strchr (*argv, '/')) + *argv = cygpath (*argv, NULL); + memset (&si, 0, sizeof (si)); + si.cb = sizeof (si); + + flags = CREATE_DEFAULT_ERROR_MODE + | (forkdebug ? DEBUG_PROCESS : DEBUG_ONLY_THIS_PROCESS); + if (new_window) + flags |= CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP; + + make_command_line (one_line, argv); + + SetConsoleCtrlHandler (NULL, 0); + ret = CreateProcess (0, one_line.buf, /* command line */ + NULL, /* Security */ + NULL, /* thread */ + TRUE, /* inherit handles */ + flags, /* start flags */ + NULL, NULL, /* current directory */ + &si, &pi); + if (!ret) + error (0, "error creating process %s, (error %d)", *argv, + GetLastError ()); + + CloseHandle (pi.hThread); + CloseHandle (pi.hProcess); + child_pid = pi.dwProcessId; + SetConsoleCtrlHandler (ctrl_c, 1); +} + +static int +output_winerror (FILE *ofile, char *s) +{ + char *winerr = strstr (s, "Win32 error "); + if (!winerr) + return 0; + + DWORD errnum = atoi (winerr + sizeof ("Win32 error ") - 1); + if (!errnum) + return 0; + + /* + * NOTE: Currently there is no policy for how long the + * the buffers are, and looks like 256 is a smallest one + * (dlfcn.cc). Other than error 1395 (length 213) and + * error 1015 (length 249), the rest are all under 188 + * characters, and so I'll use 189 as the buffer length. + * For those longer error messages, FormatMessage will + * return FALSE, and we'll get the old behaviour such as + * ``Win32 error 1395'' etc. + */ + char buf[4096]; + if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errnum, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) buf, sizeof (buf), NULL)) + return 0; + + /* Get rid the trailing CR/NL pair. */ + char *p = strchr (buf, '\0'); + p[-2] = '\n'; + p[-1] = '\0'; + + *winerr = '\0'; + fputs (s, ofile); + fputs (buf, ofile); + return 1; +} + +static SYSTEMTIME * +syst (long long t) +{ + FILETIME n; + static SYSTEMTIME st; + long long now = t /*+ ((long long) usecs * 10)*/; + n.dwHighDateTime = now >> 32; + n.dwLowDateTime = now & 0xffffffff; + FileTimeToSystemTime (&n, &st); + return &st; +} + +static void __stdcall +handle_output_debug_string (DWORD id, LPVOID p, unsigned mask, FILE *ofile) +{ + int len; + int special; + char alen[3 + 8 + 1]; + DWORD nbytes; + child_list *child = get_child (id); + if (!child) + error (0, "no process id %d found", id); + HANDLE hchild = child->hproc; +#define INTROLEN (sizeof (alen) - 1) + + if (id == lastid && hchild != lasth) + warn (0, "%p != %p", hchild, lasth); + + alen[INTROLEN] = '\0'; + if (!ReadProcessMemory (hchild, p, alen, INTROLEN, &nbytes)) +#ifndef DEBUGGING + return; +#else + error (0, + "couldn't get message length from subprocess %d<%p>, windows error %d", + id, hchild, GetLastError ()); +#endif + + if (strncmp (alen, "cYg", 3)) + return; + len = (int) strtoul (alen + 3, NULL, 16); + if (!len) + return; + + if (len > 0) + special = 0; + else + { + special = len; + if (special == _STRACE_INTERFACE_ACTIVATE_ADDR || special == _STRACE_CHILD_PID) + len = 17; + } + + char *buf; + buf = (char *) alloca (len + 85) + 20; + + if (!ReadProcessMemory (hchild, ((char *) p) + INTROLEN, buf, len, &nbytes)) + error (0, "couldn't get message from subprocess, windows error %d", + GetLastError ()); + + buf[len] = '\0'; + char *s = strtok (buf, " "); + + unsigned long n = strtoul (s, NULL, 16); + + s = strchr (s, '\0') + 1; + + if (special == _STRACE_CHILD_PID) + { + if (!DebugActiveProcess (n)) + error (0, "couldn't attach to subprocess %d for debugging, " + "windows error %d", n, GetLastError ()); + return; + } + + if (special == _STRACE_INTERFACE_ACTIVATE_ADDR) + { + if (!WriteProcessMemory (hchild, (LPVOID) n, &strace_active, + sizeof (strace_active), &nbytes)) + error (0, "couldn't write strace flag to subprocess at %p, " + "windows error %d", n, GetLastError ()); + return; + } + + char *origs = s; + + if (mask & n) + /* got it */ ; + else if (!(mask & _STRACE_ALL) || (n & _STRACE_NOTALL)) + return; /* This should not be included in "all" output */ + + DWORD dusecs, usecs; + char *ptusec, *ptrest; + + dusecs = strtoul (s, &ptusec, 10); + char *q = ptusec; + while (*q == ' ') + q++; + if (*q != '[') + { + usecs = strtoul (q, &ptrest, 10); + while (*ptrest == ' ') + ptrest++; + } + else + { + ptrest = q; + ptusec = show_usecs ? s : ptrest; + usecs = dusecs; + } + + if (child->saw_stars == 0) + { + FILETIME st; + char *news; + + GetSystemTimeAsFileTime (&st); + FileTimeToLocalFileTime (&st, &st); + child->start_time = st.dwHighDateTime; + child->start_time <<= 32; + child->start_time |= st.dwLowDateTime; + if (*(news = ptrest) != '[') + child->saw_stars = 2; + else + { + child->saw_stars++; + while ((news = strchr (news, ' ')) != NULL && *++news != '*') + child->nfields++; + if (news == NULL) + child->saw_stars++; + else + { + s = news; + child->nfields++; + } + } + } + else if (child->saw_stars < 2) + { + int i; + char *news; + if (*(news = ptrest) != '[') + child->saw_stars = 2; + else + { + for (i = 0; i < child->nfields; i++) + if ((news = strchr (news, ' ')) == NULL) + break; // Should never happen + else + news++; + + if (news == NULL) + child->saw_stars = 2; + else + { + s = news; + if (*s == '*') + { + SYSTEMTIME *st = syst (child->start_time); + fprintf (ofile, + "Date/Time: %d-%02d-%02d %02d:%02d:%02d\n", + st->wYear, st->wMonth, st->wDay, st->wHour, + st->wMinute, st->wSecond); + child->saw_stars++; + } + } + } + } + + long long d = usecs - child->last_usecs; + char intbuf[40]; + + if (child->saw_stars < 2 || s != origs) + /* Nothing */ ; + else if (hhmmss) + { + s = ptrest - 9; + SYSTEMTIME *st = syst (child->start_time + (long long) usecs * 10); + sprintf (s, "%02d:%02d:%02d", st->wHour, st->wMinute, st->wSecond); + *strchr (s, '\0') = ' '; + } + else if (!delta) + s = ptusec; + else + { + s = ptusec; + sprintf (intbuf, "%5d ", (int) d); + int len = strlen (intbuf); + + memcpy ((s -= len), intbuf, len); + } + + if (include_hex) + { + s -= 8; + sprintf (s, "%p", (void *) n); + strchr (s, '\0')[0] = ' '; + } + child->last_usecs = usecs; + if (numerror || !output_winerror (ofile, s)) + fputs (s, ofile); + if (!bufsize) + fflush (ofile); +} + +static DWORD +proc_child (unsigned mask, FILE *ofile, pid_t pid) +{ + DWORD res = 0; + DEBUG_EVENT ev; + time_t cur_time, last_time; + + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); + last_time = time (NULL); + while (1) + { + BOOL debug_event = WaitForDebugEvent (&ev, 1000); + DWORD status = DBG_CONTINUE; + + if (bufsize && flush_period > 0 && + (cur_time = time (NULL)) >= last_time + flush_period) + { + last_time = cur_time; + fflush (ofile); + } + + if (!debug_event) + continue; + + if (pid) + { + (void) cygwin_internal (CW_STRACE_TOGGLE, pid); + pid = 0; + } + + switch (ev.dwDebugEventCode) + { + case CREATE_PROCESS_DEBUG_EVENT: + if (ev.u.CreateProcessInfo.hFile) + CloseHandle (ev.u.CreateProcessInfo.hFile); + add_child (ev.dwProcessId, ev.u.CreateProcessInfo.hProcess); + break; + + case CREATE_THREAD_DEBUG_EVENT: + break; + + case LOAD_DLL_DEBUG_EVENT: + if (ev.u.LoadDll.hFile) + CloseHandle (ev.u.LoadDll.hFile); + break; + + case OUTPUT_DEBUG_STRING_EVENT: + handle_output_debug_string (ev.dwProcessId, + ev.u.DebugString.lpDebugStringData, + mask, ofile); + break; + + case EXIT_PROCESS_DEBUG_EVENT: + res = ev.u.ExitProcess.dwExitCode >> 8; + remove_child (ev.dwProcessId); + break; + case EXCEPTION_DEBUG_EVENT: + if (ev.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT) + { + status = DBG_EXCEPTION_NOT_HANDLED; + if (ev.u.Exception.dwFirstChance) + fprintf (ofile, "--- Process %u, exception %p at %p\n", ev.dwProcessId, + ev.u.Exception.ExceptionRecord.ExceptionCode, + ev.u.Exception.ExceptionRecord.ExceptionAddress); + } + break; + } + if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, status)) + error (0, "couldn't continue debug event, windows error %d", + GetLastError ()); + if (!processes) + break; + } + + return res; +} + +static void +dotoggle (pid_t pid) +{ + child_pid = (DWORD) cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid); + if (!child_pid) + { + warn (0, "no such cygwin pid - %d", pid); + child_pid = pid; + } + if (cygwin_internal (CW_STRACE_TOGGLE, child_pid)) + error (0, "failed to toggle tracing for process %d<%d>", pid, child_pid); + + return; +} + +static DWORD +dostrace (unsigned mask, FILE *ofile, pid_t pid, char **argv) +{ + if (!pid) + create_child (argv); + else + attach_process (pid); + + return proc_child (mask, ofile, pid); +} + +typedef struct tag_mask_mnemonic +{ + unsigned long val; + const char *text; +} +mask_mnemonic; + +static const mask_mnemonic mnemonic_table[] = { + {_STRACE_ALL, "all"}, + {_STRACE_FLUSH, "flush"}, + {_STRACE_INHERIT, "inherit"}, + {_STRACE_UHOH, "uhoh"}, + {_STRACE_SYSCALL, "syscall"}, + {_STRACE_STARTUP, "startup"}, + {_STRACE_DEBUG, "debug"}, + {_STRACE_PARANOID, "paranoid"}, + {_STRACE_TERMIOS, "termios"}, + {_STRACE_SELECT, "select"}, + {_STRACE_WM, "wm"}, + {_STRACE_SIGP, "sigp"}, + {_STRACE_MINIMAL, "minimal"}, + {_STRACE_EXITDUMP, "exitdump"}, + {_STRACE_SYSTEM, "system"}, + {_STRACE_NOMUTEX, "nomutex"}, + {_STRACE_MALLOC, "malloc"}, + {_STRACE_THREAD, "thread"}, + {0, NULL} +}; + +static unsigned long +mnemonic2ul (const char *nptr, char **endptr) +{ + // Look up mnemonic in table, return value. + // *endptr = ptr to char that breaks match. + const mask_mnemonic *mnp = mnemonic_table; + + while (mnp->text != NULL) + { + if (strcmp (mnp->text, nptr) == 0) + { + // Found a match. + if (endptr != NULL) + { + *endptr = ((char *) nptr) + strlen (mnp->text); + } + return mnp->val; + } + mnp++; + } + + // Didn't find it. + if (endptr != NULL) + { + *endptr = (char *) nptr; + } + return 0; +} + +static unsigned long +parse_mask (const char *ms, char **endptr) +{ + const char *p = ms; + char *newp; + unsigned long retval = 0, thisval; + const size_t bufsize = 16; + char buffer[bufsize]; + size_t len; + + while (*p != '\0') + { + // First extract the term, terminate it, and lowercase it. + strncpy (buffer, p, bufsize); + buffer[bufsize - 1] = '\0'; + len = strcspn (buffer, "+,\0"); + buffer[len] = '\0'; + strlwr (buffer); + + // Check if this is a mnemonic. We have to do this first or strtoul() + // will false-trigger on anything starting with "a" through "f". + thisval = mnemonic2ul (buffer, &newp); + if (buffer == newp) + { + // This term isn't mnemonic, check if it's hex. + thisval = strtoul (buffer, &newp, 16); + if (newp != buffer + len) + { + // Not hex either, syntax error. + *endptr = (char *) p; + return 0; + } + } + + p += len; + retval += thisval; + + // Handle operators + if (*p == '\0') + break; + if ((*p == '+') || (*p == ',')) + { + // For now these both equate to addition/ORing. Until we get + // fancy and add things like "all-<something>", all we need do is + // continue the looping. + p++; + continue; + } + else + { + // Syntax error + *endptr = (char *) p; + return 0; + } + } + + *endptr = (char *) p; + return retval; +} + +static void +usage (FILE *where = stderr) +{ + fprintf (where, "\ +Usage: %s [OPTIONS] <command-line>\n\ +Usage: %s [OPTIONS] -p <pid>\n\ +Trace system calls and signals\n\ +\n\ + -b, --buffer-size=SIZE set size of output file buffer\n\ + -d, --no-delta don't display the delta-t microsecond timestamp\n\ + -f, --trace-children trace child processes (toggle - default true)\n\ + -h, --help output usage information and exit\n\ + -m, --mask=MASK set message filter mask\n\ + -n, --crack-error-numbers output descriptive text instead of error\n\ + numbers for Windows errors\n\ + -o, --output=FILENAME set output file to FILENAME\n\ + -p, --pid=n attach to executing program with cygwin pid n\n\ + -q, --quiet suppress messages about attaching, detaching, etc.\n\ + -S, --flush-period=PERIOD flush buffered strace output every PERIOD secs\n\ + -t, --timestamp use an absolute hh:mm:ss timestamp insted of \n\ + the default microsecond timestamp. Implies -d\n\ + -T, --toggle toggle tracing in a process already being\n\ + traced. Requires -p <pid>\n\ + -u, --usecs toggle printing of microseconds timestamp\n\ + -v, --version output version information and exit\n\ + -w, --new-window spawn program under test in a new window\n\ +\n", pgm, pgm); + if ( where == stdout) + fprintf (stdout, "\ + MASK can be any combination of the following mnemonics and/or hex values\n\ + (0x is optional). Combine masks with '+' or ',' like so:\n\ +\n\ + --mask=wm+system,malloc+0x00800\n\ +\n\ + Mnemonic Hex Corresponding Def Description\n\ + =========================================================================\n\ + all 0x00001 (_STRACE_ALL) All strace messages.\n\ + flush 0x00002 (_STRACE_FLUSH) Flush output buffer after each message.\n\ + inherit 0x00004 (_STRACE_INHERIT) Children inherit mask from parent.\n\ + uhoh 0x00008 (_STRACE_UHOH) Unusual or weird phenomenon.\n\ + syscall 0x00010 (_STRACE_SYSCALL) System calls.\n\ + startup 0x00020 (_STRACE_STARTUP) argc/envp printout at startup.\n\ + debug 0x00040 (_STRACE_DEBUG) Info to help debugging. \n\ + paranoid 0x00080 (_STRACE_PARANOID) Paranoid info.\n\ + termios 0x00100 (_STRACE_TERMIOS) Info for debugging termios stuff.\n\ + select 0x00200 (_STRACE_SELECT) Info on ugly select internals.\n\ + wm 0x00400 (_STRACE_WM) Trace Windows msgs (enable _strace_wm).\n\ + sigp 0x00800 (_STRACE_SIGP) Trace signal and process handling.\n\ + minimal 0x01000 (_STRACE_MINIMAL) Very minimal strace output.\n\ + exitdump 0x04000 (_STRACE_EXITDUMP) Dump strace cache on exit.\n\ + system 0x08000 (_STRACE_SYSTEM) Serious error; goes to console and log.\n\ + nomutex 0x10000 (_STRACE_NOMUTEX) Don't use mutex for synchronization.\n\ + malloc 0x20000 (_STRACE_MALLOC) Trace malloc calls.\n\ + thread 0x40000 (_STRACE_THREAD) Thread-locking calls.\n\ +"); + if (where == stderr) + fprintf (stderr, "Try '%s --help' for more information.\n", pgm); + exit (where == stderr ? 1 : 0 ); +} + +struct option longopts[] = { + {"buffer-size", required_argument, NULL, 'b'}, + {"help", no_argument, NULL, 'h'}, + {"flush-period", required_argument, NULL, 'S'}, + {"hex", no_argument, NULL, 'H'}, + {"mask", required_argument, NULL, 'm'}, + {"new-window", no_argument, NULL, 'w'}, + {"output", required_argument, NULL, 'o'}, + {"no-delta", no_argument, NULL, 'd'}, + {"pid", required_argument, NULL, 'p'}, + {"quiet", no_argument, NULL, 'q'}, + {"timestamp", no_argument, NULL, 't'}, + {"toggle", no_argument, NULL, 'T'}, + {"trace-children", no_argument, NULL, 'f'}, + {"translate-error-numbers", no_argument, NULL, 'n'}, + {"usecs", no_argument, NULL, 'u'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +static const char *const opts = "+b:dhHfm:no:p:qS:tTuvw"; + +static void +print_version () +{ + const char *v = strchr (version, ':'); + int len; + if (!v) + { + v = "?"; + len = 1; + } + else + { + v += 2; + len = strchr (v, ' ') - v; + } + printf ("\ +%s (cygwin) %.*s\n\ +System Trace\n\ +Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n\ +Compiled on %s\n\ +", pgm, len, v, __DATE__); +} + +int +main (int argc, char **argv) +{ + unsigned mask = 0; + FILE *ofile = NULL; + pid_t pid = 0; + int opt; + int toggle = 0; + int sawquiet = -1; + + if (load_cygwin ()) + { + char **av = (char **) cygwin_internal (CW_ARGV); + if (av && (DWORD) av != (DWORD) -1) + for (argc = 0, argv = av; *av; av++) + argc++; + } + + if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/'))) + pgm = *argv; + else + pgm++; + + while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) + switch (opt) + { + case 'b': + bufsize = atoi (optarg); + break; + case 'd': + delta ^= 1; + break; + case 'f': + forkdebug ^= 1; + break; + case 'h': + // Print help and exit + usage (stdout); + break; + case 'H': + include_hex ^= 1; + break; + case 'm': + { + char *endptr; + mask = parse_mask (optarg, &endptr); + if (*endptr != '\0') + { + // Bad mask expression. + error (0, "syntax error in mask expression \"%s\" near \ +character #%d.\n", optarg, (int) (endptr - optarg), endptr); + } + break; + } + case 'n': + numerror ^= 1; + break; + case 'o': + if ((ofile = fopen (cygpath (optarg, NULL), "w")) == NULL) + error (1, "can't open %s", optarg); +#ifdef F_SETFD + (void) fcntl (fileno (ofile), F_SETFD, 0); +#endif + break; + case 'p': + pid = strtoul (optarg, NULL, 10); + strace_active |= 2; + break; + case 'q': + if (sawquiet < 0) + sawquiet = 1; + else + sawquiet ^= 1; + break; + case 'S': + flush_period = strtoul (optarg, NULL, 10); + break; + case 't': + hhmmss ^= 1; + break; + case 'T': + toggle ^= 1; + break; + case 'u': + // FIXME: currently unimplemented + show_usecs ^= 1; + delta ^= 1; + break; + case 'v': + // Print version info and exit + print_version (); + return 0; + case 'w': + new_window ^= 1; + break; + case '?': + fprintf (stderr, "Try '%s --help' for more information.\n", pgm); + exit (1); + } + + if (pid && argv[optind]) + error (0, "cannot provide both a command line and a process id"); + + if (!pid && !argv[optind]) + error (0, "must provide either a command line or a process id"); + + if (toggle && !pid) + error (0, "must provide a process id to toggle tracing"); + + if (!pid) + quiet = sawquiet < 0 || !sawquiet; + else if (sawquiet < 0) + quiet = 0; + else + quiet = sawquiet; + + if (!mask) + mask = _STRACE_ALL; + + if (bufsize) + setvbuf (ofile, (char *) alloca (bufsize), _IOFBF, bufsize); + + if (!ofile) + ofile = stdout; + + DWORD res = 0; + if (toggle) + dotoggle (pid); + else + res = dostrace (mask, ofile, pid, argv + optind); + return res; +} + +#undef CloseHandle + +static BOOL +close_handle (HANDLE h, DWORD ok) +{ + child_list *c; + for (c = &children; (c = c->next) != NULL;) + if (c->hproc == h && c->id != ok) + error (0, "Closing child handle %p", h); + return CloseHandle (h); +} |