Normally, posix programs should call abort(), exit(), _exit(), kill() -- or various pthread functions -- to terminate operation (either their own, or that of some other processes/thread). However, there are two cases where the win32 ExitProcess and TerminateProcess functions might justifiably be called: 1) inside cygwin's own process startup/shutdown implementation 2) "Native" programs that use the w32api throughout, but are compiled using the cygwin compiler (e.g. without -mno-cygwin). [*]
However, the ExitProcess and TerminateProcess functions, when called directly, do not allow for the 'exit status' maintained by cygwin to be set. This can be a problem when such cygwin applications are exec'ed by other cygwin apps: cygwin's code for exec'ing children doesn't ever check the value of GetExitCodeProcess as set by these win32 functions, if the child application is also a cygwin app. The attached patch address this problem, by providing two wrappers: cygwin_terminate_process <--> TerminateProcess cygwin_exit_process <--> ExitProcess which simply set the cygwin exit code using the specified status value, and then delegate to the underlying win32 call. This way, the parent process -- if it is a cygwin process -- can access the exit code as expected. Note that TerminateProcess/ExitProcess -- and the new wrappers -- bypass most of the cygwin shutdown code, so it is not recommended that ANY of these functions be used in normal cygwin applications; they should be used only when necessary -- e.g. case #1 above. [*] In case #2, even with this patch, the client code must be modified to call the wrappers. We can, in a follow-up patch, add objects to libcygwin.a that explicitly force using the wrappers when client code calls ExitProcess/TerminateProcess, but I'm not sure that's a good idea, so I deferred that decision. Furthermore, if you're going to modify the source code of the client, you might as well modify it to use the recommended posix functions instead...so, in actuality, it's just case #1 that really needs this support. So, how should cygwin's process startup/shutdown code use these new functions? Well, this can serve as the beginnings of a generic mechanism for deferring error messages on process startup, using custom NTSTATUS values (which have bit 29 set: eg. 0x[ea62]0000000). At present, cygwin maps any NTSTATUS values greater than 0xc00000000 to a posix exit value of 127, because when bits 30 and 31 are set, this means "STATUS_SEVERITY_ERROR", and cygwin assumes that these NTSTATUS values will only occur during process startup -- thus, a posix 127 is appropriate for "errors that occur during process startup". We could revisit this assumption later. One example of intended use is the v2 runtime pseudo-reloc stuff, posted simultaneously on the cygwin-devel list. But, here's a short example: ============================================ #include <stdio.h> #include <windows.h> #include <ntdef.h> #include <sys/cygwin.h> #define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xc0000269) #define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xc0000135) /* custom NTSTATUS value */ #define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269) int main(int argc, char* argv[]) { //cygwin_terminate_process (GetCurrentProcess(), STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION); cygwin_exit_process (STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION); exit (1); } ============================================ $ gcc -o foo foo.c $ ./foo $ echo $? 127 2009-10-04 Charles Wilson <...> Add cygwin wrappers for ExitProcess and TerminateProcess. * include/sys/cygwin.h: Declare new functions cygwin_exit_process and cygwin_terminate_process. * cygwin.din: Add new functions cygwin_exit_process and cygwin_terminate_process. * dcrt0.cc (cygwin_exit_process): New function. (cygwin_terminate_process): New function. * include/cygwin/version.h: Bump version. * pinfo.h (pinfo::set_exit_code): New method. * pinfo.cc (pinfo::set_exit_code): New, refactored from... (pinfo::maybe_set_exit_code_from_windows): here. Call it. -- Chuck
Index: cygwin.din =================================================================== RCS file: /cvs/src/src/winsup/cygwin/cygwin.din,v retrieving revision 1.216 diff -u -p -r1.216 cygwin.din --- cygwin.din 26 Sep 2009 21:01:10 -0000 1.216 +++ cygwin.din 5 Oct 2009 16:58:48 -0000 @@ -250,6 +250,7 @@ cygwin_conv_to_win32_path SIGFE cygwin_create_path SIGFE cygwin_detach_dll SIGFE_MAYBE cygwin_dll_init NOSIGFE +cygwin_exit_process NOSIGFE cygwin_internal NOSIGFE cygwin_logon_user SIGFE cygwin_posix_path_list_p NOSIGFE @@ -258,6 +259,7 @@ cygwin_posix_to_win32_path_list_buf_size cygwin_set_impersonation_token SIGFE cygwin_split_path NOSIGFE cygwin_stackdump SIGFE +cygwin_terminate_process NOSIGFE cygwin_umount SIGFE cygwin_win32_to_posix_path_list SIGFE cygwin_win32_to_posix_path_list_buf_size SIGFE Index: dcrt0.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/dcrt0.cc,v retrieving revision 1.365 diff -u -p -r1.365 dcrt0.cc --- dcrt0.cc 2 Oct 2009 14:58:10 -0000 1.365 +++ dcrt0.cc 5 Oct 2009 17:40:17 -0000 @@ -41,6 +41,8 @@ details. */ extern "C" void cygwin_exit (int) __attribute__ ((noreturn)); +extern "C" BOOL cygwin_terminate_process (HANDLE, UINT); +extern "C" void cygwin_exit_process (UINT) __attribute__ ((noreturn)); extern "C" void __sinit (_reent *); static int NO_COPY envc; @@ -1136,6 +1138,67 @@ _exit (int n) do_exit (((DWORD) n & 0xff) << 8); } +/* DOCTOOL-START + <title>cygwin_terminate_process</title> + + <funcsynopsis><funcprototype> + <funcdef>extern "C" BOOL + <function>cygwin_terminate_process</function> + </funcdef> + <paramdef>HANDLE <parameter>process</parameter></paramdef> + <paramdef>UINT <parameter>status</parameter></paramdef> + </funcprototype></funcsynopsis> + + <para>Cygwin-specific wrapper for win32 TerminateProcess. It +ensures that if used to terminate the current process, then the +correct exit code will be made available to this process's parent +(if that parent is also a cygwin process). Otherwise, it simply +delegates to the win32 TerminateProcess.</para> + + <para>This function should be used in cygwin programs instead +of TerminateProcess. Ordinarily, however, the ANSI abort() or the +POSIX _exit() function should be preferred over either +TerminateProcess or cygwin_terminate_process when used to terminate +the current process. Similarly, the POSIX kill() function should +be used to terminate cygwin processes other than the current one. +</sect1> + + DOCTOOL-END */ +extern "C" BOOL +cygwin_terminate_process (HANDLE process, UINT status) +{ + if (process == GetCurrentProcess()) + myself.set_exit_code ((DWORD)status); + + return TerminateProcess (process, status); +} + +/* DOCTOOL-START + <title>cygwin_exit_process</title> + + <funcsynopsis><funcprototype> + <funcdef>extern "C" void + <function>cygwin_exit_process</function> + </funcdef> + <paramdef>UINT <parameter>status</parameter></paramdef> + </funcprototype></funcsynopsis> + + <para>Cygwin-specific wrapper for win32 ExitProcess, which +ensures that parent cygwin process receives the specified status +as an exit code, before calling ExitProcess. This function should +be used in cygwin programs instead of ExitProcess. Ordinarily, +however, the ANSI exit() function should be preferred over either +ExitProcess or cygwin_exit_process.</para> +</sect1> + + DOCTOOL-END */ +extern "C" void +cygwin_exit_process (UINT status) +{ + myself.set_exit_code ((DWORD)status); + ExitProcess (status); +} + extern "C" void __api_fatal (const char *fmt, ...) { Index: pinfo.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/pinfo.cc,v retrieving revision 1.253 diff -u -p -r1.253 pinfo.cc --- pinfo.cc 12 Jul 2009 21:15:47 -0000 1.253 +++ pinfo.cc 5 Oct 2009 16:58:49 -0000 @@ -136,11 +136,19 @@ status_exit (DWORD x) # define self (*this) void +pinfo::set_exit_code (DWORD x) +{ + extern int sigExeced; + if (x >= 0xc0000000UL) + x = status_exit (x); + self->exitcode = EXITCODE_SET | (sigExeced ?: (x & 0xff) << 8); +} + +void pinfo::maybe_set_exit_code_from_windows () { DWORD x = 0xdeadbeef; DWORD oexitcode = self->exitcode; - extern int sigExeced; if (hProcess && !(self->exitcode & EXITCODE_SET)) { @@ -148,9 +160,7 @@ pinfo::maybe_set_exit_code_from_windows process hasn't quite exited after closing pipe */ GetExitCodeProcess (hProcess, &x); - if (x >= 0xc0000000UL) - x = status_exit (x); - self->exitcode = EXITCODE_SET | (sigExeced ?: (x & 0xff) << 8); + set_exit_code (x); } sigproc_printf ("pid %d, exit value - old %p, windows %p, cygwin %p", self->pid, oexitcode, x, self->exitcode); Index: pinfo.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/pinfo.h,v retrieving revision 1.108 diff -u -p -r1.108 pinfo.h --- pinfo.h 20 Dec 2008 17:32:31 -0000 1.108 +++ pinfo.h 5 Oct 2009 16:58:49 -0000 @@ -155,6 +155,7 @@ public: } void exit (DWORD n) __attribute__ ((noreturn, regparm(2))); void maybe_set_exit_code_from_windows () __attribute__ ((regparm(1))); + void set_exit_code (DWORD n) __attribute__ ((regparm(2))); _pinfo *operator -> () const {return procinfo;} int operator == (pinfo *x) const {return x->procinfo == procinfo;} int operator == (pinfo &x) const {return x.procinfo == procinfo;} Index: include/cygwin/version.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/include/cygwin/version.h,v retrieving revision 1.299 diff -u -p -r1.299 version.h --- include/cygwin/version.h 26 Sep 2009 21:01:10 -0000 1.299 +++ include/cygwin/version.h 5 Oct 2009 16:58:49 -0000 @@ -368,12 +368,13 @@ details. */ 212: Add and export libstdc++ malloc wrappers. 213: Export canonicalize_file_name, eaccess, euidaccess. 214: Export execvpe, fexecve. + 215: Export cygwin_terminate_process, cygwin_exit_process. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 214 +#define CYGWIN_VERSION_API_MINOR 215 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible Index: include/sys/cygwin.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/include/sys/cygwin.h,v retrieving revision 1.80 diff -u -p -r1.80 cygwin.h --- include/sys/cygwin.h 7 Jul 2009 20:12:44 -0000 1.80 +++ include/sys/cygwin.h 5 Oct 2009 16:58:49 -0000 @@ -94,6 +94,8 @@ extern void *cygwin_create_path (cygwin_ extern pid_t cygwin_winpid_to_pid (int); extern int cygwin_posix_path_list_p (const char *); extern void cygwin_split_path (const char *, char *, char *); +extern void cygwin_exit_process (unsigned int status) __attribute__((noreturn)); +extern BOOL cygwin_terminate_process (HANDLE process, unsigned int status); struct __cygwin_perfile {