summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <cgf@redhat.com>2004-01-11 18:13:51 +0000
committerChristopher Faylor <cgf@redhat.com>2004-01-11 18:13:51 +0000
commit46f9078b6f5ef23c2fcebc7318aed97b3f60482c (patch)
tree9581d766b006dc62da49033215581ec32a173f9b
parent69a7b5f79888513741e65a54216d7756474b76c2 (diff)
downloadgdb-46f9078b6f5ef23c2fcebc7318aed97b3f60482c.tar.gz
* Makefile.in: Add still more -fomit-frame-pointer functions.
* dtable.h (dtable::lock): New function. (dtable::unlock): New function. (dtable::init_lock): New function. * cygheap.h (HEAP_TLS): Declare new enum value. (init_cygheap::threadlist): Declare new array. (init_cygheap::sthreads): Declare new variable. (cygheap_fdmanip::~cygheap_fdmanip): Use new dtable lock/unlock functions. (cygheap_fdnew::cygheap_fdnew): Ditto. (cygheap_fdget::cygheap_fdget): Ditto. * dtable.cc (dtable_init): Initialize fdtab critical section. (dtable::fixup_after_fork): Ditto. (dtable::fixup_after_exec): Ditto. (dtable::dup2): Use lock/unlock calls to protect access to fdtab. (dtable::find_fifo): Ditto. (dtable::fixup_before_fork): Ditto. (dtable::fixup_before_exec): Ditto. (dtable::set_file_pointers_for_exec): Ditto. (dtable::vfork_child_dup): Ditto. (dtable::vfork_parent_restore): Ditto. * syscalls.cc (close_all_files): Ditto. * sync.h (muto::acquired): Declare new function. (new_muto1): Declare new macro used to specify name of muto storage. * sync.cc (muto::acquired): Define new function. * cygthread.cc (cygthread::stub): Remove signal chain removal call since it is handled during initialization now. * cygthread.cc (cygthread::simplestub): Remove signal chain removal call since it is handled during initialization now. * cygtls.cc (sentry): New class used for locking. Use throughout. (_threadinfo::reset_exception): Don't pop stack. (_threadinfo::find_tls): Move from exceptions.cc. (_threadinfo::init_thread): Initialize array of threads rather than linked list. Take second argument indicating thread function for this thread. (_threadinfo::remove): Search thread array rather than linked list. Use sentry to lock. Only unlock if we got the lock. (_threadinfo::find_tls): Ditto for first two. (handle_threadlist_exception): Handle exceptions when manipulating the thread list in case of premature thread termination. (_threadinfo::init_threadlist_exceptions): Ditto. * cygtls.h (TLS_STACK_SIZE): Decrease size. (_threadinfo::padding): Add element to avoid overwriting lower part of stack. (_threadinfo::remove): Add a "wait" argument to control how long we wait for a lock before removing. * exceptions.cc (init_exception_handler): Make global. Take argument to control exception handler being set. (ctrl_c_handler): Wait forever when removing self from signal chain. (_threadinfo::find_tls): Move to cygtls.cc. (sig_handle): Reorganize detection for thread-specific signals. * heap.cc (heap_init): Rework slightly. Make fatal error more verbose. Remove malloc initialization since it can't happen during dll attach. * init.cc (search_for): Move address to search for on stack here. (threadfunc_ix): Ditto for stack offset. Make shared so that stack walk potentially only has to be done once when cygwin processes are running. (threadfunc_fe): Use standard tls to store thread function (may change back later). (calibration_thread): New function. Potentially called to find threadfunc_ix. (munge_threadfunc): Search for "search_for" value on stack. Output warning when thread func not found on stack. Use standard tls to store thread function. (prime_threads): New function. Called to prime thread front end. (dll_entry): Call dll_crt0_0 here when DLL_PROCESS_ATTACH. Call prime_threads here. Try to remove thread from signal list here. * sigproc.cc (wait_sig): Initialize threadlist exception stuff here. * thread.cc (pthread::exit): Pass argument to signal list remove function. * thread.h: Remove obsolete *ResourceLock defines. * tlsoffsets.h: Regenerate. * winsup.h (spf): Define temporary debug macro to be deleted later. * dcrt0.cc (dll_crt0_0): New function, called during DLL initialization. Mainly consists of code pulled from dll_crt0_1. (dll_crt0_1): See above. (_dll_crt0): Wait for initial calibration thread to complete, if appropriate. Move some stuff to dll_crt0_0. (initialize_main_tls): Accommodate argument change to _thread_info::init_thread. * fork.cc (fork_child): Ditto. (sync_with_child): Fix debug message. * external.cc (cygwin_internal): Remove special considerations for uninitialized dll since initialization happens during dll attach now. * dlfcn.cc (dlopen): Remove obsolete *ResourceLock calls. (dlclose): Ditto. * cygheap.h (init_cygheap::close_ctty): Declare new function. * cygheap.cc (init_cygheap::close_ctty): Define new function. * syscalls.cc (close_all_files): Use close_ctty. (setsid): Ditto. * cygthread.cc (cygthread::stub): Remove exception initialization. * cygthread.cc (cygthread::stub): Remove exception initialization. (cygthread::simplestub): Ditto. * thread.cc (pthread::thread_init_wrapper): Ditto. * cygtls.cc (_last_thread): Make static. (_threadinfo::call2): Initialize exception handler here. (_threadinfo::find_tls): Move here. * exceptions.cc (_threadinfo::find_tls): Move. * dcrt0.cc (__api_fatal): Add prefix info to message here rather than including it in every call to function. * winsup.h (api_fatal): Accommodate above change. * debug.cc (add_handle): Don't do anything if cygheap not around. (mark_closed): Ditto. * dll_init.cc (dll_list::detach): Fix debug output. * fork.cc (sync_with_child): Ditto. (vfork): Improve debug output. * heap.cc (heap_init): Ditto. * exceptions.cc (try_to_debug): Clarify message when debugger attaches.
-rw-r--r--winsup/cygwin/ChangeLog145
-rw-r--r--winsup/cygwin/Makefile.in450
-rw-r--r--winsup/cygwin/cygheap.cc462
-rw-r--r--winsup/cygwin/cygheap.h375
-rw-r--r--winsup/cygwin/cygthread.cc324
-rw-r--r--winsup/cygwin/cygthread.h46
-rw-r--r--winsup/cygwin/cygtls.cc214
-rw-r--r--winsup/cygwin/cygtls.h146
-rw-r--r--winsup/cygwin/dcrt0.cc1172
-rw-r--r--winsup/cygwin/debug.cc231
-rw-r--r--winsup/cygwin/dlfcn.cc147
-rw-r--r--winsup/cygwin/dll_init.cc430
-rw-r--r--winsup/cygwin/dtable.cc893
-rw-r--r--winsup/cygwin/dtable.h97
-rw-r--r--winsup/cygwin/exceptions.cc1133
-rw-r--r--winsup/cygwin/external.cc295
-rw-r--r--winsup/cygwin/fork.cc763
-rw-r--r--winsup/cygwin/heap.cc164
-rw-r--r--winsup/cygwin/init.cc111
-rw-r--r--winsup/cygwin/malloc_wrapper.cc334
-rw-r--r--winsup/cygwin/sigproc.cc1263
-rw-r--r--winsup/cygwin/sync.cc155
-rw-r--r--winsup/cygwin/sync.h57
-rw-r--r--winsup/cygwin/syscalls.cc2994
-rw-r--r--winsup/cygwin/thread.cc3223
-rw-r--r--winsup/cygwin/thread.h691
-rw-r--r--winsup/cygwin/tlsoffsets.h46
-rw-r--r--winsup/cygwin/winsup.h359
28 files changed, 16720 insertions, 0 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
new file mode 100644
index 00000000000..88606d16d38
--- /dev/null
+++ b/winsup/cygwin/ChangeLog
@@ -0,0 +1,145 @@
+2004-01-10 Christopher Faylor <cgf@redhat.com>
+
+ * Makefile.in: Add still more -fomit-frame-pointer functions.
+ * dtable.h (dtable::lock): New function.
+ (dtable::unlock): New function.
+ (dtable::init_lock): New function.
+ * cygheap.h (HEAP_TLS): Declare new enum value.
+ (init_cygheap::threadlist): Declare new array.
+ (init_cygheap::sthreads): Declare new variable.
+ (cygheap_fdmanip::~cygheap_fdmanip): Use new dtable lock/unlock
+ functions.
+ (cygheap_fdnew::cygheap_fdnew): Ditto.
+ (cygheap_fdget::cygheap_fdget): Ditto.
+ * dtable.cc (dtable_init): Initialize fdtab critical section.
+ (dtable::fixup_after_fork): Ditto.
+ (dtable::fixup_after_exec): Ditto.
+ (dtable::dup2): Use lock/unlock calls to protect access to fdtab.
+ (dtable::find_fifo): Ditto.
+ (dtable::fixup_before_fork): Ditto.
+ (dtable::fixup_before_exec): Ditto.
+ (dtable::set_file_pointers_for_exec): Ditto.
+ (dtable::vfork_child_dup): Ditto.
+ (dtable::vfork_parent_restore): Ditto.
+ * syscalls.cc (close_all_files): Ditto.
+ * sync.h (muto::acquired): Declare new function.
+ (new_muto1): Declare new macro used to specify name of muto storage.
+ * sync.cc (muto::acquired): Define new function.
+
+ * cygthread.cc (cygthread::stub): Remove signal chain removal call
+ since it is handled during initialization now.
+ * cygthread.cc (cygthread::simplestub): Remove signal chain removal
+ call since it is handled during initialization now.
+ * cygtls.cc (sentry): New class used for locking. Use throughout.
+ (_threadinfo::reset_exception): Don't pop stack.
+ (_threadinfo::find_tls): Move from exceptions.cc.
+ (_threadinfo::init_thread): Initialize array of threads rather than
+ linked list. Take second argument indicating thread function for this
+ thread.
+ (_threadinfo::remove): Search thread array rather than linked list.
+ Use sentry to lock. Only unlock if we got the lock.
+ (_threadinfo::find_tls): Ditto for first two.
+ (handle_threadlist_exception): Handle exceptions when manipulating the
+ thread list in case of premature thread termination.
+ (_threadinfo::init_threadlist_exceptions): Ditto.
+ * cygtls.h (TLS_STACK_SIZE): Decrease size.
+ (_threadinfo::padding): Add element to avoid overwriting lower part of
+ stack.
+ (_threadinfo::remove): Add a "wait" argument to control how long we
+ wait for a lock before removing.
+ * exceptions.cc (init_exception_handler): Make global. Take argument
+ to control exception handler being set.
+ (ctrl_c_handler): Wait forever when removing self from signal chain.
+ (_threadinfo::find_tls): Move to cygtls.cc.
+ (sig_handle): Reorganize detection for thread-specific signals.
+ * heap.cc (heap_init): Rework slightly. Make fatal error more verbose.
+ Remove malloc initialization since it can't happen during dll attach.
+ * init.cc (search_for): Move address to search for on stack here.
+ (threadfunc_ix): Ditto for stack offset. Make shared so that stack
+ walk potentially only has to be done once when cygwin processes are
+ running.
+ (threadfunc_fe): Use standard tls to store thread function (may change
+ back later).
+ (calibration_thread): New function. Potentially called to find
+ threadfunc_ix.
+ (munge_threadfunc): Search for "search_for" value on stack. Output
+ warning when thread func not found on stack. Use standard tls to store
+ thread function.
+ (prime_threads): New function. Called to prime thread front end.
+ (dll_entry): Call dll_crt0_0 here when DLL_PROCESS_ATTACH. Call
+ prime_threads here. Try to remove thread from signal list here.
+ * sigproc.cc (wait_sig): Initialize threadlist exception stuff here.
+ * thread.cc (pthread::exit): Pass argument to signal list remove
+ function.
+ * thread.h: Remove obsolete *ResourceLock defines.
+
+ * tlsoffsets.h: Regenerate.
+
+ * winsup.h (spf): Define temporary debug macro to be deleted later.
+
+ * dcrt0.cc (dll_crt0_0): New function, called during DLL
+ initialization. Mainly consists of code pulled from dll_crt0_1.
+ (dll_crt0_1): See above.
+ (_dll_crt0): Wait for initial calibration thread to complete, if
+ appropriate. Move some stuff to dll_crt0_0.
+ (initialize_main_tls): Accommodate argument change to
+ _thread_info::init_thread.
+ * fork.cc (fork_child): Ditto.
+ (sync_with_child): Fix debug message.
+ * external.cc (cygwin_internal): Remove special considerations for
+ uninitialized dll since initialization happens during dll attach now.
+
+ * dlfcn.cc (dlopen): Remove obsolete *ResourceLock calls.
+ (dlclose): Ditto.
+
+2004-01-05 Christopher Faylor <cgf@redhat.com>
+
+ * cygheap.h (init_cygheap::close_ctty): Declare new function.
+ * cygheap.cc (init_cygheap::close_ctty): Define new function.
+ * syscalls.cc (close_all_files): Use close_ctty.
+ (setsid): Ditto.
+
+ * cygthread.cc (cygthread::stub): Remove exception initialization.
+ * cygthread.cc (cygthread::stub): Remove exception initialization.
+ (cygthread::simplestub): Ditto.
+ * thread.cc (pthread::thread_init_wrapper): Ditto.
+ * cygtls.cc (_last_thread): Make static.
+ (_threadinfo::call2): Initialize exception handler here.
+ (_threadinfo::find_tls): Move here.
+ * exceptions.cc (_threadinfo::find_tls): Move.
+
+ * dcrt0.cc (__api_fatal): Add prefix info to message here rather than
+ including it in every call to function.
+ * winsup.h (api_fatal): Accommodate above change.
+ * debug.cc (add_handle): Don't do anything if cygheap not around.
+ (mark_closed): Ditto.
+
+ * dll_init.cc (dll_list::detach): Fix debug output.
+ * fork.cc (sync_with_child): Ditto.
+ (vfork): Improve debug output.
+ * heap.cc (heap_init): Ditto.
+
+ * exceptions.cc (try_to_debug): Clarify message when debugger attaches.
+
+2004-01-03 Christopher Faylor <cgf@redhat.com>
+
+ * exceptions.cc (_threadinfo::interrupt_now): Avoid double call to
+ sigdelayed.
+ * pinfo.cc (_pinfo::commune_send): Avoid inexplicable test which caused
+ most pids to be shown as "<defunct>" on Win9x.
+
+2004-01-02 Christopher Faylor <cgf@redhat.com>
+
+ * cygheap.h (init_cygheap): Play more vfork shell games and move
+ ctty_on_hold and open_fhs_on_hold (back) here.
+ * dcrt0.cc (_dll_crt0): Just set impure_ptr_ptr here and let later
+ initialization deal with tls.
+ * dtable.cc (dtable::vfork_child_fixup): Move ctty considerations here.
+ (dtable:vfork_parent_restore): And here.
+ * fork.cc (vfork): Reflect change to ctty handling.
+ * perthread.h (vfork_save::fhctty): Eliminate.
+
+ * cygwin.din: Make more exports NOSIGFE that will never be interrupted
+ by a signal.
+
+ * init.cc (dll_entry): Set stackptr to NULL to catch problems earlier.
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
new file mode 100644
index 00000000000..c3336bcf72c
--- /dev/null
+++ b/winsup/cygwin/Makefile.in
@@ -0,0 +1,450 @@
+# Makefile.in for Cygwin.
+# Copyright 1995, 1996, 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.
+
+# This makefile requires GNU make.
+
+SHELL:=@SHELL@
+srcdir:=@srcdir@
+objdir:=.
+
+CONFIG_DIR:=$(srcdir)/config/@CONFIG_DIR@
+VPATH:=$(srcdir):$(CONFIG_DIR):$(srcdir)/regex:$(srcdir)/regexp:$(srcdir)/lib
+
+target_alias:=@target_alias@
+build_alias:=@build_alias@
+host_alias:=@host_alias@
+prefix:=@prefix@
+
+program_transform_name:=@program_transform_name@
+exec_prefix:=@exec_prefix@
+bindir:=@bindir@
+libdir:=@libdir@
+ifeq ($(target_alias),$(host_alias))
+ifeq ($(build_alias),$(host_alias))
+tooldir:=$(exec_prefix)
+else
+tooldir:=$(exec_prefix)/$(target_alias)
+endif
+else
+tooldir:=$(exec_prefix)/$(target_alias)
+endif
+datadir:=@datadir@
+infodir:=@infodir@
+includedir:=@includedir@
+
+override INSTALL:=@INSTALL@
+override INSTALL_PROGRAM:=@INSTALL_PROGRAM@
+override INSTALL_DATA:=@INSTALL_DATA@
+
+#
+# --enable options from configure
+#
+MT_SAFE:=@MT_SAFE@
+DEFS:=@DEFS@
+
+CC:=@CC@
+# FIXME: Which is it, CC or CC_FOR_TARGET?
+CC_FOR_TARGET:=$(CC)
+CFLAGS=@CFLAGS@
+override CFLAGS+=-MMD ${$(*F)_CFLAGS} -fmerge-constants -ftracer
+CXX=@CXX@
+CXXFLAGS=@CXXFLAGS@
+
+AR:=@AR@
+AR_FLAGS:=qv
+RANLIB:=@RANLIB@
+LD:=@LD@
+DLLTOOL:=@DLLTOOL@
+WINDRES:=@WINDRES@
+AS:=@AS@
+NM:=@NM@
+LDSCRIPT:=cygwin.sc
+
+#
+# Include common definitions for winsup directory
+#
+include $(srcdir)/../Makefile.common
+
+@SET_MAKE@
+
+# Setup the testing framework, if you have one
+EXPECT = `if [ -f $${rootme}/../../expect/expect$(EXEEXT) ] ; then \
+ echo $${rootme}/../../expect/expect$(EXEEXT) ; \
+ else echo expect ; fi`
+
+RUNTEST = `if [ -f $${srcdir}/../dejagnu/runtest ] ; then \
+ echo $${srcdir}/../dejagnu/runtest ; \
+ else echo runtest; fi`
+RUNTESTFLAGS =
+
+# Parameters used in building the cygwin.dll.
+# We build as cygwin0.dll and rename at install time to overcome
+# native rebuilding issues (we don't want the build tools to see a partially
+# built cygwin.dll and attempt to use it instead of the old one).
+
+DLL_NAME:=cygwin1.dll
+TEST_DLL_NAME:=cygwin0.dll
+TEST_LIB_NAME:=libcygwin0.a
+DEF_FILE:=cygwin.def
+DLL_ENTRY:=@DLL_ENTRY@
+
+LIBGMON_A:=libgmon.a
+CYGWIN_START:=crt0.o
+GMON_START:=gcrt0.o
+
+# Some things want these from libc, but they have their own static
+# data which apps can get to, which is a pain in the dll, so we
+# include them directly into the library.
+
+LIBCOS:=${sort ${addsuffix .o,${basename ${notdir ${wildcard $(srcdir)/lib/*.c}}}} \
+ ${addsuffix .o,${basename ${notdir ${wildcard $(srcdir)/lib/*.cc}}}}}
+
+# Build all source files in the config directory
+
+EXTRA_DLL_OFILES:=${addsuffix .o,${basename ${notdir ${wildcard $(CONFIG_DIR)/*.c}}}}
+
+EXTRA_OFILES=$(bupdir1)/libiberty/random.o $(bupdir1)/libiberty/strsignal.o
+
+MALLOC_OFILES=@MALLOC_OFILES@
+
+DLL_IMPORTS:=$(w32api_lib)/libkernel32.a
+
+MT_SAFE_OBJECTS:=
+# Please maintain this list in sorted order, with maximum files per 80 col line
+#
+DLL_OFILES:=assert.o autoload.o bsdlib.o cxx.o cygheap.o cygthread.o cygtls.o \
+ dcrt0.o debug.o delqueue.o devices.o dir.o dlfcn.o dll_init.o dtable.o \
+ environ.o errno.o exceptions.o exec.o external.o fcntl.o fhandler.o \
+ fhandler_clipboard.o fhandler_console.o fhandler_disk_file.o \
+ fhandler_dsp.o fhandler_fifo.o fhandler_floppy.o fhandler_mem.o \
+ fhandler_nodevice.o fhandler_proc.o fhandler_process.o \
+ fhandler_random.o fhandler_raw.o fhandler_registry.o fhandler_serial.o \
+ fhandler_socket.o fhandler_tape.o fhandler_termios.o \
+ fhandler_tty.o fhandler_virtual.o fhandler_windows.o \
+ fhandler_zero.o flock.o fnmatch.o fork.o getopt.o glob.o grp.o heap.o \
+ init.o ioctl.o ipc.o iruserok.o localtime.o malloc_wrapper.o miscfuncs.o \
+ mmap.o msg.o net.o netdb.o ntea.o passwd.o path.o pinfo.o pipe.o \
+ poll.o pthread.o regcomp.o regerror.o regexec.o regfree.o registry.o \
+ resource.o scandir.o sched.o sec_acl.o sec_helper.o security.o \
+ select.o sem.o shared.o shm.o sigfe.o signal.o sigproc.o smallprint.o \
+ spawn.o strace.o strsep.o sync.o syscalls.o sysconf.o syslog.o \
+ termios.o thread.o times.o tty.o uinfo.o uname.o v8_regexp.o \
+ v8_regerror.o v8_regsub.o wait.o wincap.o window.o \
+ $(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS)
+
+GMON_OFILES:=gmon.o mcount.o profil.o
+
+OBSOLETE_FUNCTIONS:=regcomp regerror regexec regfree regsub \
+ open acl aclcheck aclfrommode aclfrompbits \
+ aclfromtext aclsort acltomode acltopbits \
+ acltotext chown facl fchown fcntl fdopen fgetpos fopen \
+ freopen fseeko fsetpos fstat ftello ftruncate \
+ getegid geteuid getgid getgrent getgrgid getgrnam \
+ getgroups getpwuid getpwuid_r getuid initgroups \
+ lchown lseek lstat mknod mmap seekdir setegid seteuid \
+ setgid setgroups setregid setreuid setuid stat \
+ telldir tmpfile truncate
+
+NEW_FUNCTIONS:=regcomp posix_regcomp \
+ regerror posix_regerror \
+ regexec posix_regexec \
+ regfree posix_regfree \
+ open _open64 \
+ acl _acl32 \
+ aclcheck _aclcheck32 \
+ aclfrommode _aclfrommode32 \
+ aclfrompbits _aclfrompbits32 \
+ aclfromtext _aclfromtext32 \
+ aclsort _aclsort32 \
+ acltomode _acltomode32 \
+ acltopbits _acltopbits32 \
+ acltotext _acltotext32 \
+ chown _chown32 \
+ facl _facl32 \
+ fchown _fchown32 \
+ fcntl _fcntl64 \
+ fdopen _fdopen64 \
+ fgetpos _fgetpos64 \
+ fopen _fopen64 \
+ freopen _freopen64 \
+ fseeko _fseeko64 \
+ fsetpos _fsetpos64 \
+ fstat _fstat64 \
+ ftello _ftello64 \
+ ftruncate _ftruncate64 \
+ getegid _getegid32 \
+ geteuid _geteuid32 \
+ getgid _getgid32 \
+ getgrent _getgrent32 \
+ getgrgid _getgrgid32 \
+ getgrnam _getgrnam32 \
+ getgroups _getgroups32 \
+ getpwuid _getpwuid32 \
+ getpwuid_r _getpwuid_r32 \
+ getuid _getuid32 \
+ initgroups _initgroups32 \
+ lchown _lchown32 \
+ lseek _lseek64 \
+ lstat _lstat64 \
+ mknod _mknod32 \
+ mmap _mmap64 \
+ seekdir _seekdir64 \
+ setegid _setegid32 \
+ seteuid _seteuid32 \
+ setgid _setgid32 \
+ setgroups _setgroups32 \
+ setregid _setregid32 \
+ setreuid _setreuid32 \
+ setuid _setuid32 \
+ stat _stat64 \
+ telldir _telldir64 \
+ tmpfile _tmpfile64 \
+ truncate _truncate64
+
+API_VER:=$(srcdir)/include/cygwin/version.h
+
+PWD:=${shell pwd}
+LIB_NAME:=$(PWD)/libcygwin.a
+LIBSERVER:=@LIBSERVER@
+SUBLIBS:=$(PWD)/libpthread.a $(PWD)/libm.a $(PWD)/libc.a
+EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a
+INSTOBJS:=automode.o binmode.o textmode.o
+TARGET_LIBS:=$(LIB_NAME) $(CYGWIN_START) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS)
+
+ifneq "${filter -O%,$(CFLAGS)}" ""
+cygheap_CFLAGS:=-fomit-frame-pointer
+cygthread_CFLAGS:=-fomit-frame-pointer
+cygtls_CFLAGS:=-fomit-frame-pointer
+devices_CFLAGS:=-fomit-frame-pointer -Os
+dir_CFLAGS:=-fomit-frame-pointer
+fcntl_CFLAGS:=-fomit-frame-pointer
+fhandler_CFLAGS:=-fomit-frame-pointer
+fhandler_clipboard_CFLAGS:=-fomit-frame-pointer
+fhandler_console_CFLAGS:=-fomit-frame-pointer
+fhandler_disk_file_CFLAGS:=-fomit-frame-pointer
+fhandler_dsp_CFLAGS:=-fomit-frame-pointer
+fhandler_floppy_CFLAGS:=-fomit-frame-pointer
+fhandler_mem_CFLAGS:=-fomit-frame-pointer
+fhandler_proc_CFLAGS:=-fomit-frame-pointer
+fhandler_process_CFLAGS:=-fomit-frame-pointer
+fhandler_random_CFLAGS:=-fomit-frame-pointer
+fhandler_raw_CFLAGS:=-fomit-frame-pointer
+fhandler_registry_CFLAGS:=-fomit-frame-pointer
+fhandler_serial_CFLAGS:=-fomit-frame-pointer
+fhandler_socket_CFLAGS:=-fomit-frame-pointer
+fhandler_syslog_CFLAGS:=-fomit-frame-pointer
+fhandler_tape_CFLAGS:=-fomit-frame-pointer
+fhandler_termios_CFLAGS:=-fomit-frame-pointer
+fhandler_tty_CFLAGS:=-fomit-frame-pointer
+fhandler_virtual_CFLAGS:=-fomit-frame-pointer
+fhandler_windows_CFLAGS:=-fomit-frame-pointer
+fhandler_zero_CFLAGS:=-fomit-frame-pointer
+malloc_CFLAGS:=-fomit-frame-pointer
+malloc_wrapper_CFLAGS:=-fomit-frame-pointer
+miscfuncs_CFLAGS:=-fomit-frame-pointer
+regcomp_CFLAGS=-fomit-frame-pointer
+regerror_CFLAGS=-fomit-frame-pointer
+regexec_CFLAGS=-fomit-frame-pointer
+regfree_CFLAGS=-fomit-frame-pointer
+shared_CFLAGS:=-fomit-frame-pointer
+smallprint_CFLAGS:=-fomit-frame-pointer
+sysconf_CFLAGS:=-fomit-frame-pointer
+uinfo_CFLAGS:=-fomit-frame-pointer
+endif
+
+.PHONY: all force dll_ofiles install all_target install_target all_host install_host \
+ install install-libs install-headers -lgcc
+
+.SUFFIXES:
+.SUFFIXES: .c .cc .def .a .o .d .s
+
+all_host=@all_host@
+install_host=@install_host@
+
+all: all_target $(all_host)
+
+all_target: $(TARGET_LIBS)
+
+all_host: $(TEST_LIB_NAME)
+
+force:
+
+install: install-libs install-headers install-man install_target \
+ $(install_host) $(install_target)
+
+uninstall: uninstall-libs uninstall-headers uninstall-man
+
+install-libs: $(TARGET_LIBS)
+ $(INSTALL_PROGRAM) $(TEST_DLL_NAME) $(bindir)/$(DLL_NAME); \
+ for i in $^; do \
+ $(INSTALL_DATA) $$i $(tooldir)/lib/`basename $$i` ; \
+ done
+
+install-headers:
+ cd $(srcdir); \
+ for sub in `find include -name '[a-z]*' -type d -print | sort`; do \
+ for i in $$sub/*.h ; do \
+ $(INSTALL_DATA) $$i $(tooldir)/$$sub/`basename $$i` ; \
+ done ; \
+ done ; \
+ $(INSTALL_DATA) regex/regex.h $(tooldir)/include/regex.h
+
+install-man:
+ cd $(srcdir); \
+ for i in `find . -type f -name '*.2'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man2/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.3'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man3/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.5'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man5/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.7'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man7/`basename $$i` ; \
+ done
+
+install_target:
+
+install_host:
+
+uninstall-libs: $(TARGET_LIBS)
+ rm -f $(bindir)/$(DLL_NAME); \
+ for i in $^; do \
+ rm -f $(tooldir)/lib/$$i ; \
+ done
+
+uninstall-headers:
+ cd $(srcdir); \
+ for sub in `find include -name '[a-z]*' -type d -print | sort`; do \
+ for i in $$sub/*.h ; do \
+ rm -f $(tooldir)/$$sub/`basename $$i` ; \
+ done ; \
+ done ; \
+ rm -f $(tooldir)/include/regex.h
+
+uninstall-man:
+ cd $(srcdir); \
+ for i in `find . -type f -name '*.2'`; do \
+ rm -f $(tooldir)/man/man2/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.3'`; do \
+ rm -f $(tooldir)/man/man3/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.5'`; do \
+ rm -f $(tooldir)/man/man5/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.7'`; do \
+ rm -f $(tooldir)/man/man7/`basename $$i` ; \
+ done
+
+clean:
+ -rm -f *.o *.dll *.a *.exp junk *.base version.cc regexp/*.o winver_stamp *.exe *.d *stamp* *_magic.h
+
+maintainer-clean realclean: clean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -fr configure
+
+
+# Rule to build cygwin.dll
+$(TEST_DLL_NAME): $(LDSCRIPT) $(DLL_OFILES) $(DLL_IMPORTS) $(LIBSERVER) $(LIBC) $(LIBM) $(API_VER) Makefile winver_stamp
+ $(CXX) $(CXXFLAGS) $(nostdlib) -Wl,-T$(firstword $^) -Wl,--out-implib,cygdll.a -shared -o $@ \
+ -e $(DLL_ENTRY) $(DEF_FILE) $(DLL_OFILES) version.o winver.o \
+ $(MALLOC_OBJ) $(LIBSERVER) $(LIBM) $(LIBC) \
+ -lgcc $(DLL_IMPORTS)
+ @ln -f $@ new-$(DLL_NAME)
+
+# Rule to build libcygwin.a
+$(LIB_NAME): rmsym newsym $(TEST_DLL_NAME) $(LIBCOS)
+ /bin/sh ${word 1,$^} ./cygdll.a "$(NM)" "$(AR)" $(OBSOLETE_FUNCTIONS) || exit 0
+ /bin/sh ${word 2,$^} ./cygdll.a "$(AS)" "$(AR)" $(NEW_FUNCTIONS) || exit 0
+ (echo create $(LIB_NAME); echo addmod $(LIBCOS); echo addlib cygdll.a; echo save) | $(AR) -M
+ $(RANLIB) $@
+
+# Rule to make stub library used by testsuite
+# dependency set to $(LIB_NAME) to accommodate make -j2.
+# Otherwise dlltool gets confused. cgf (11-16-2000)
+$(TEST_LIB_NAME): $(LIB_NAME)
+ perl -p -e 'BEGIN{binmode(STDIN); binmode(STDOUT);}; s/cygwin1/cygwin0/g' < $? > $@
+
+$(LIBSERVER): $(bupdir)/cygserver/Makefile
+ $(MAKE) -C $(bupdir)/cygserver libcygserver.a
+
+dll_ofiles: $(DLL_OFILES)
+
+$(LIBGMON_A): $(GMON_OFILES) $(GMON_START)
+ $(AR) rcv $(LIBGMON_A) $(GMON_OFILES)
+
+$(API_VER): $(srcdir)/cygwin.din
+ @echo Error: Version info is older than DLL API!;\
+ false
+
+version.cc winver.o: winver_stamp
+ @ :
+
+shared_info_magic.h: cygmagic shared_info.h
+ /bin/sh ${word 1,$^} $@ "$(CC) -x c" ${word 2,$^} USER_MAGIC 'class user_info' SHARED_MAGIC 'class shared_info'
+
+child_info_magic.h: cygmagic child_info.h
+ /bin/sh ${word 1,$^} $@ "$(CC) -x c" ${word 2,$^} CHILD_INFO_MAGIC 'class child_info'
+
+dcrt0.o sigproc.o: child_info_magic.h
+
+shared.o: shared_info_magic.h
+
+$(srcdir)/qevices.cc: cygwin-gperf devices.gperf devices.h
+ $^ > $@
+
+$(srcdir)/devices.cc: gendevices devices.in devices.h
+ ${wordlist 1,2,$^} $@
+
+$(PWD)/libpthread.a: speclib $(LIB_NAME) pthread.o thread.o
+ /bin/sh ${word 1, $^} $@ "${NM}" "$(AR)" ${wordlist 2, 99, $^}
+
+$(PWD)/libm.a: speclib $(LIB_NAME) $(LIBM)
+ /bin/sh ${word 1, $^} $@ "${NM}" "$(AR)" ${wordlist 2, 99, $^}
+
+$(PWD)/libc.a: speclib $(LIB_NAME) $(PWD)/libm.a libpthread.a
+ /bin/sh ${word 1, $^} -v $@ "${NM}" "$(AR)" ${wordlist 2, 99, $^}
+
+lib%.a: %.o
+ $(AR) cru $@ $?
+
+winver_stamp: mkvers.sh include/cygwin/version.h winver.rc $(DLL_OFILES)
+ @echo "Making version.o and winver.o";\
+ $(SHELL) ${word 1,$^} ${word 2,$^} ${word 3,$^} $(WINDRES) && \
+ $(COMPILE_CXX) -o version.o version.cc && \
+ touch $@
+
+-lgcc:
+ :
+
+#
+
+Makefile: cygwin.din
+
+$(DEF_FILE): gendef cygwin.din $(srcdir)/tlsoffsets.h
+ $^ $@ sigfe.s
+
+$(srcdir)/tlsoffsets.h: gentls_offsets cygtls.h
+ $^ $@ $(COMPILE_CXX)
+
+sigfe.s: $(DEF_FILE)
+ @touch $@
+
+sigfe.o: sigfe.s
+ $(CC) -c -o $@ $?
+
+winsup.h: config.h
+
+deps:=${wildcard *.d}
+ifneq (,$(deps))
+include $(deps)
+endif
diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc
new file mode 100644
index 00000000000..b6dbe265109
--- /dev/null
+++ b/winsup/cygwin/cygheap.cc
@@ -0,0 +1,462 @@
+/* cygheap.cc: Cygwin heap manager.
+
+ Copyright 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 "winsup.h"
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "cygheap.h"
+#include "child_info.h"
+#include "heap.h"
+#include "sync.h"
+#include "shared_info.h"
+#include "sigproc.h"
+
+init_cygheap NO_COPY *cygheap;
+void NO_COPY *cygheap_max;
+
+static NO_COPY muto *cygheap_protect;
+
+struct cygheap_entry
+ {
+ int type;
+ struct cygheap_entry *next;
+ char data[0];
+ };
+
+#define NBUCKETS (sizeof (cygheap->buckets) / sizeof (cygheap->buckets[0]))
+#define N0 ((_cmalloc_entry *) NULL)
+#define to_cmalloc(s) ((_cmalloc_entry *) (((char *) (s)) - (unsigned) (N0->data)))
+
+#define CFMAP_OPTIONS (SEC_RESERVE | PAGE_READWRITE)
+#define MVMAP_OPTIONS (FILE_MAP_WRITE)
+
+extern "C" {
+static void __stdcall _cfree (void *ptr) __attribute__((regparm(1)));
+}
+
+static void
+init_cheap ()
+{
+ cygheap = (init_cygheap *) VirtualAlloc ((void *) &_cygheap_start, CYGHEAPSIZE, MEM_RESERVE, PAGE_NOACCESS);
+ if (!cygheap)
+ {
+ MEMORY_BASIC_INFORMATION m;
+ if (!VirtualQuery ((LPCVOID) &_cygheap_start, &m, sizeof m))
+ system_printf ("couldn't get memory info, %E");
+ system_printf ("Couldn't reserve space for cygwin's heap, %E");
+ api_fatal ("AllocationBase %p, BaseAddress %p, RegionSize %p, State %p\n",
+ m.AllocationBase, m.BaseAddress, m.RegionSize, m.State);
+ }
+ cygheap_max = cygheap;
+}
+
+static void dup_now (void *, child_info *, unsigned) __attribute__ ((regparm(3)));
+static void
+dup_now (void *newcygheap, child_info *ci, unsigned n)
+{
+ if (!VirtualAlloc (newcygheap, n, MEM_COMMIT, PAGE_READWRITE))
+ api_fatal ("couldn't allocate new cygwin heap %p, %d for child, %E",
+ newcygheap, n);
+ memcpy (newcygheap, cygheap, n);
+}
+
+void *__stdcall
+cygheap_setup_for_child (child_info *ci, bool dup_later)
+{
+ void *newcygheap;
+ cygheap_protect->acquire ();
+ unsigned n = (char *) cygheap_max - (char *) cygheap;
+ unsigned size = CYGHEAPSIZE;
+ if (size < n)
+ size = n + (128 * 1024);
+ ci->cygheap_h = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_none,
+ CFMAP_OPTIONS, 0, size, NULL);
+ if (!ci->cygheap_h)
+ api_fatal ("Couldn't create heap for child, size %d, %E", CYGHEAPSIZE);
+ newcygheap = MapViewOfFileEx (ci->cygheap_h, MVMAP_OPTIONS, 0, 0, 0, NULL);
+ ProtectHandle1INH (ci->cygheap_h, passed_cygheap_h);
+ if (!dup_later)
+ dup_now (newcygheap, ci, n);
+ cygheap_protect->release ();
+ ci->cygheap = cygheap;
+ ci->cygheap_max = cygheap_max;
+ return newcygheap;
+}
+
+void __stdcall
+cygheap_setup_for_child_cleanup (void *newcygheap, child_info *ci,
+ bool dup_it_now)
+{
+ if (dup_it_now)
+ {
+ /* NOTE: There is an assumption here that cygheap_max has not changed
+ between the time that cygheap_setup_for_child was called and now.
+ Make sure that this is a correct assumption. */
+ cygheap_protect->acquire ();
+ dup_now (newcygheap, ci, (char *) cygheap_max - (char *) cygheap);
+ cygheap_protect->release ();
+ }
+ UnmapViewOfFile (newcygheap);
+ ForceCloseHandle1 (ci->cygheap_h, passed_cygheap_h);
+}
+
+/* Called by fork or spawn to reallocate cygwin heap */
+void __stdcall
+cygheap_fixup_in_child (bool execed)
+{
+ cygheap = child_proc_info->cygheap;
+ cygheap_max = child_proc_info->cygheap_max;
+ void *addr = !wincap.map_view_of_file_ex_sucks () ? cygheap : NULL;
+ void *newaddr;
+
+ newaddr = MapViewOfFileEx (child_proc_info->cygheap_h, MVMAP_OPTIONS, 0, 0, 0, addr);
+ if (newaddr != cygheap)
+ {
+ if (!newaddr)
+ newaddr = MapViewOfFileEx (child_proc_info->cygheap_h, MVMAP_OPTIONS, 0, 0, 0, NULL);
+ DWORD n = (DWORD) cygheap_max - (DWORD) cygheap;
+ /* Reserve cygwin heap in same spot as parent */
+ if (!VirtualAlloc (cygheap, CYGHEAPSIZE, MEM_RESERVE, PAGE_NOACCESS))
+ {
+ MEMORY_BASIC_INFORMATION m;
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) cygheap, &m, sizeof m))
+ system_printf ("couldn't get memory info, %E");
+
+ system_printf ("Couldn't reserve space for cygwin's heap (%p <%p>) in child, %E", cygheap, newaddr);
+ api_fatal ("m.AllocationBase %p, m.BaseAddress %p, m.RegionSize %p, m.State %p\n",
+ m.AllocationBase, m.BaseAddress, m.RegionSize, m.State);
+ }
+
+ /* Allocate same amount of memory as parent */
+ if (!VirtualAlloc (cygheap, n, MEM_COMMIT, PAGE_READWRITE))
+ api_fatal ("Couldn't allocate space for child's heap %p, size %d, %E",
+ cygheap, n);
+ memcpy (cygheap, newaddr, n);
+ UnmapViewOfFile (newaddr);
+ }
+
+ ForceCloseHandle1 (child_proc_info->cygheap_h, passed_cygheap_h);
+
+ cygheap_init ();
+ debug_fixup_after_fork_exec ();
+
+ if (execed)
+ {
+ cygheap->user_heap.base = NULL; /* We can allocate the heap anywhere */
+ /* Walk the allocated memory chain looking for orphaned memory from
+ previous execs */
+ for (_cmalloc_entry *rvc = cygheap->chain; rvc; rvc = rvc->prev)
+ {
+ cygheap_entry *ce = (cygheap_entry *) rvc->data;
+ if (!rvc->ptr || rvc->b >= NBUCKETS || ce->type <= HEAP_1_START)
+ continue;
+ else if (ce->type < HEAP_1_MAX)
+ ce->type += HEAP_1_MAX; /* Mark for freeing after next exec */
+ else
+ _cfree (ce); /* Marked by parent for freeing in child */
+ }
+ }
+}
+
+void
+init_cygheap::close_ctty ()
+{
+ debug_printf ("closing cygheap->ctty %p", cygheap->ctty);
+ cygheap->ctty->close ();
+ if (cygheap->ctty_on_hold == cygheap->ctty)
+ cygheap->ctty_on_hold = NULL;
+ cygheap->ctty = NULL;
+}
+
+#define pagetrunc(x) ((void *) (((DWORD) (x)) & ~(4096 - 1)))
+
+static void *__stdcall
+_csbrk (int sbs)
+{
+ void *prebrk = cygheap_max;
+ void *prebrka = pagetrunc (prebrk);
+ cygheap_max = (char *) cygheap_max + sbs;
+ if (!sbs || (prebrk != prebrka && prebrka == pagetrunc (cygheap_max)))
+ /* nothing to do */;
+ else if (!VirtualAlloc (prebrk, (DWORD) sbs, MEM_COMMIT, PAGE_READWRITE))
+ {
+ malloc_printf ("couldn't commit memory for cygwin heap, %E");
+ __seterrno ();
+ cygheap_max = (char *) cygheap_max - sbs;
+ return NULL;
+ }
+
+ return prebrk;
+}
+
+extern "C" void __stdcall
+cygheap_init ()
+{
+ new_muto (cygheap_protect);
+ if (!cygheap)
+ {
+ init_cheap ();
+ (void) _csbrk (sizeof (*cygheap));
+ }
+ if (!cygheap->fdtab)
+ cygheap->fdtab.init ();
+ if (!cygheap->sigs)
+ sigalloc ();
+ if (!cygheap->shared_prefix)
+ cygheap->shared_prefix = cstrdup (
+ wincap.has_terminal_services ()
+ && (set_process_privilege (SE_CREATE_GLOBAL_NAME, true) >= 0
+ || GetLastError () == ERROR_NO_SUCH_PRIVILEGE)
+ ? "Global\\" : "");
+}
+
+/* Copyright (C) 1997, 2000 DJ Delorie */
+
+static void *_cmalloc (unsigned size) __attribute ((regparm(1)));
+static void *__stdcall _crealloc (void *ptr, unsigned size) __attribute ((regparm(2)));
+
+static void *__stdcall
+_cmalloc (unsigned size)
+{
+ _cmalloc_entry *rvc;
+ unsigned b, sz;
+
+ /* Calculate "bit bucket" and size as a power of two. */
+ for (b = 3, sz = 8; sz && sz < size; b++, sz <<= 1)
+ continue;
+
+ cygheap_protect->acquire ();
+ if (cygheap->buckets[b])
+ {
+ rvc = (_cmalloc_entry *) cygheap->buckets[b];
+ cygheap->buckets[b] = rvc->ptr;
+ rvc->b = b;
+ }
+ else
+ {
+ rvc = (_cmalloc_entry *) _csbrk (sz + sizeof (_cmalloc_entry));
+ if (!rvc)
+ {
+ cygheap_protect->release ();
+ return NULL;
+ }
+
+ rvc->b = b;
+ rvc->prev = cygheap->chain;
+ cygheap->chain = rvc;
+ }
+ cygheap_protect->release ();
+ return rvc->data;
+}
+
+static void __stdcall
+_cfree (void *ptr)
+{
+ cygheap_protect->acquire ();
+ _cmalloc_entry *rvc = to_cmalloc (ptr);
+ DWORD b = rvc->b;
+ rvc->ptr = cygheap->buckets[b];
+ cygheap->buckets[b] = (char *) rvc;
+ cygheap_protect->release ();
+}
+
+static void *__stdcall
+_crealloc (void *ptr, unsigned size)
+{
+ void *newptr;
+ if (ptr == NULL)
+ newptr = _cmalloc (size);
+ else
+ {
+ unsigned oldsize = 1 << to_cmalloc (ptr)->b;
+ if (size <= oldsize)
+ return ptr;
+ newptr = _cmalloc (size);
+ memcpy (newptr, ptr, oldsize);
+ _cfree (ptr);
+ }
+ return newptr;
+}
+
+/* End Copyright (C) 1997 DJ Delorie */
+
+#define sizeof_cygheap(n) ((n) + sizeof (cygheap_entry))
+
+#define N ((cygheap_entry *) NULL)
+#define tocygheap(s) ((cygheap_entry *) (((char *) (s)) - (int) (N->data)))
+
+inline static void *
+creturn (cygheap_types x, cygheap_entry * c, unsigned len)
+{
+ if (!c)
+ {
+ set_errno (ENOMEM);
+ return NULL;
+ }
+ c->type = x;
+ char *cend = ((char *) c + sizeof (*c) + len);
+ if (cygheap_max < cend)
+ cygheap_max = cend;
+ MALLOC_CHECK;
+ return (void *) c->data;
+}
+
+extern "C" void *__stdcall
+cmalloc (cygheap_types x, DWORD n)
+{
+ cygheap_entry *c;
+ MALLOC_CHECK;
+ c = (cygheap_entry *) _cmalloc (sizeof_cygheap (n));
+ if (!c)
+ system_printf ("cmalloc returned NULL");
+ return creturn (x, c, n);
+}
+
+extern "C" void *__stdcall
+crealloc (void *s, DWORD n)
+{
+ MALLOC_CHECK;
+ if (s == NULL)
+ return cmalloc (HEAP_STR, n); // kludge
+
+ assert (!inheap (s));
+ cygheap_entry *c = tocygheap (s);
+ cygheap_types t = (cygheap_types) c->type;
+ c = (cygheap_entry *) _crealloc (c, sizeof_cygheap (n));
+ if (!c)
+ system_printf ("crealloc returned NULL");
+ return creturn (t, c, n);
+}
+
+extern "C" void __stdcall
+cfree (void *s)
+{
+ assert (!inheap (s));
+ (void) _cfree (tocygheap (s));
+ MALLOC_CHECK;
+}
+
+extern "C" void __stdcall
+cfree_and_set (char *&s, char *what)
+{
+ if (s && s != almost_null)
+ cfree (s);
+ s = what;
+}
+
+extern "C" void *__stdcall
+ccalloc (cygheap_types x, DWORD n, DWORD size)
+{
+ cygheap_entry *c;
+ MALLOC_CHECK;
+ n *= size;
+ c = (cygheap_entry *) _cmalloc (sizeof_cygheap (n));
+ if (c)
+ memset (c->data, 0, n);
+#ifdef DEBUGGING
+ if (!c)
+ system_printf ("ccalloc returned NULL");
+#endif
+ return creturn (x, c, n);
+}
+
+extern "C" char *__stdcall
+cstrdup (const char *s)
+{
+ MALLOC_CHECK;
+ char *p = (char *) cmalloc (HEAP_STR, strlen (s) + 1);
+ if (!p)
+ return NULL;
+ strcpy (p, s);
+ MALLOC_CHECK;
+ return p;
+}
+
+extern "C" char *__stdcall
+cstrdup1 (const char *s)
+{
+ MALLOC_CHECK;
+ char *p = (char *) cmalloc (HEAP_1_STR, strlen (s) + 1);
+ if (!p)
+ return NULL;
+ strcpy (p, s);
+ MALLOC_CHECK;
+ return p;
+}
+
+void
+cygheap_root::set (const char *posix, const char *native)
+{
+ if (*posix == '/' && posix[1] == '\0')
+ {
+ if (m)
+ {
+ cfree (m);
+ m = NULL;
+ }
+ return;
+ }
+ if (!m)
+ m = (struct cygheap_root_mount_info *) ccalloc (HEAP_MOUNT, 1, sizeof (*m));
+ strcpy (m->posix_path, posix);
+ m->posix_pathlen = strlen (posix);
+ if (m->posix_pathlen >= 1 && m->posix_path[m->posix_pathlen - 1] == '/')
+ m->posix_path[--m->posix_pathlen] = '\0';
+
+ strcpy (m->native_path, native);
+ m->native_pathlen = strlen (native);
+ if (m->native_pathlen >= 1 && m->native_path[m->native_pathlen - 1] == '\\')
+ m->native_path[--m->native_pathlen] = '\0';
+}
+
+cygheap_user::~cygheap_user ()
+{
+#if 0
+ if (pname)
+ cfree (pname);
+ if (plogsrv)
+ cfree (plogsrv - 2);
+ if (pdomain)
+ cfree (pdomain);
+ if (psid)
+ cfree (psid);
+#endif
+}
+
+void
+cygheap_user::set_name (const char *new_name)
+{
+ bool allocated = !!pname;
+
+ if (allocated)
+ {
+ if (strcasematch (new_name, pname))
+ return;
+ cfree (pname);
+ }
+
+ pname = cstrdup (new_name ? new_name : "");
+ if (!allocated)
+ return; /* Initializing. Don't bother with other stuff. */
+
+ cfree_and_set (homedrive);
+ cfree_and_set (homepath);
+ cfree_and_set (plogsrv);
+ cfree_and_set (pdomain);
+ cfree_and_set (pwinname);
+}
+
diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h
new file mode 100644
index 00000000000..be515277165
--- /dev/null
+++ b/winsup/cygwin/cygheap.h
@@ -0,0 +1,375 @@
+/* cygheap.h: Cygwin heap manager.
+
+ Copyright 2000, 2001, 2002, 2003, 2004 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. */
+
+#undef cfree
+
+enum cygheap_types
+{
+ HEAP_FHANDLER,
+ HEAP_STR,
+ HEAP_ARGV,
+ HEAP_BUF,
+ HEAP_MOUNT,
+ HEAP_SIGS,
+ HEAP_ARCHETYPES,
+ HEAP_TLS,
+ HEAP_1_START,
+ HEAP_1_STR,
+ HEAP_1_ARGV,
+ HEAP_1_BUF,
+ HEAP_1_EXEC,
+ HEAP_1_MAX = 100,
+ HEAP_MMAP = 200
+};
+
+#define incygheap(s) (cygheap && ((char *) (s) >= (char *) cygheap) && ((char *) (s) <= ((char *) cygheap_max)))
+
+struct _cmalloc_entry
+{
+ union
+ {
+ DWORD b;
+ char *ptr;
+ };
+ struct _cmalloc_entry *prev;
+ char data[0];
+};
+
+struct cygheap_root_mount_info
+{
+ char posix_path[CYG_MAX_PATH];
+ unsigned posix_pathlen;
+ char native_path[CYG_MAX_PATH];
+ unsigned native_pathlen;
+};
+
+/* CGF: FIXME This doesn't belong here */
+
+class cygheap_root
+{
+ /* Root directory information.
+ This is used after a chroot is called. */
+ struct cygheap_root_mount_info *m;
+
+public:
+ bool posix_ok (const char *path)
+ {
+ if (!m)
+ return 1;
+ return path_prefix_p (m->posix_path, path, m->posix_pathlen);
+ }
+ bool ischroot_native (const char *path)
+ {
+ if (!m)
+ return 1;
+ return strncasematch (m->native_path, path, m->native_pathlen)
+ && (path[m->native_pathlen] == '\\' || !path[m->native_pathlen]);
+ }
+ const char *unchroot (const char *path)
+ {
+ if (!m)
+ return path;
+ const char *p = path + m->posix_pathlen;
+ if (!*p)
+ p = "/";
+ return p;
+ }
+ bool exists () {return !!m;}
+ void set (const char *posix, const char *native);
+ size_t posix_length () const { return m->posix_pathlen; }
+ const char *posix_path () const { return m->posix_path; }
+ size_t native_length () const { return m->native_pathlen; }
+ const char *native_path () const { return m->native_path; }
+};
+
+enum homebodies
+{
+ CH_HOMEDRIVE,
+ CH_HOMEPATH,
+ CH_HOME
+};
+
+class cygheap_user
+{
+ /* Extendend user information.
+ The information is derived from the internal_getlogin call
+ when on a NT system. */
+ char *pname; /* user's name */
+ char *plogsrv; /* Logon server, may be FQDN */
+ char *pdomain; /* Logon domain of the user */
+ char *homedrive; /* User's home drive */
+ char *homepath; /* User's home path */
+ char *pwinname; /* User's name as far as Windows knows it */
+ char *puserprof; /* User profile */
+ cygsid effec_cygsid; /* buffer for user's SID */
+ cygsid saved_cygsid; /* Remains intact even after impersonation */
+public:
+ __uid32_t saved_uid; /* Remains intact even after impersonation */
+ __gid32_t saved_gid; /* Ditto */
+ __uid32_t real_uid; /* Remains intact on seteuid, replaced by setuid */
+ __gid32_t real_gid; /* Ditto */
+ user_groups groups; /* Primary and supp SIDs */
+
+ /* token is needed if set(e)uid should be called. It can be set by a call
+ to `set_impersonation_token()'. */
+ HANDLE external_token;
+ HANDLE internal_token;
+ HANDLE current_token;
+
+ /* CGF 2002-06-27. I removed the initializaton from this constructor
+ since this class is always allocated statically. That means that everything
+ is zero anyway so there is no need to initialize it to zero. Since the
+ token initialization is always handled during process startup as well,
+ I've removed the constructor entirely. Please reinstate this if this
+ situation ever changes.
+ cygheap_user () : pname (NULL), plogsrv (NULL), pdomain (NULL),
+ homedrive (NULL), homepath (NULL),
+ token (INVALID_HANDLE_VALUE) {}
+ */
+
+ ~cygheap_user ();
+
+ void init ();
+ void set_name (const char *new_name);
+ const char *name () const { return pname; }
+
+ const char *env_logsrv (const char *, size_t);
+ const char *env_homepath (const char *, size_t);
+ const char *env_homedrive (const char *, size_t);
+ const char *env_userprofile (const char *, size_t);
+ const char *env_domain (const char *, size_t);
+ const char *env_name (const char *, size_t);
+
+ const char *logsrv ()
+ {
+ const char *p = env_logsrv ("LOGONSERVER=", sizeof ("LOGONSERVER=") - 1);
+ return (p == almost_null) ? NULL : p;
+ }
+ const char *winname ()
+ {
+ const char *p = env_name ("USERNAME=", sizeof ("USERNAME=") - 1);
+ return (p == almost_null) ? NULL : p;
+ }
+ const char *domain ()
+ {
+ const char *p = env_domain ("USERDOMAIN=", sizeof ("USERDOMAIN=") - 1);
+ return (p == almost_null) ? NULL : p;
+ }
+ BOOL set_sid (PSID new_sid) {return (BOOL) (effec_cygsid = new_sid);}
+ BOOL set_saved_sid () { return (BOOL) (saved_cygsid = effec_cygsid); }
+ PSID sid () { return effec_cygsid; }
+ PSID saved_sid () { return saved_cygsid; }
+ const char *ontherange (homebodies what, struct passwd * = NULL);
+ bool issetuid () const { return current_token != INVALID_HANDLE_VALUE; }
+ HANDLE token () { return current_token; }
+ void deimpersonate ()
+ {
+ if (issetuid ())
+ RevertToSelf ();
+ }
+ void reimpersonate ()
+ {
+ if (issetuid ()
+ && !ImpersonateLoggedOnUser (token ()))
+ system_printf ("ImpersonateLoggedOnUser: %E");
+ }
+ bool has_impersonation_tokens ()
+ { return external_token != INVALID_HANDLE_VALUE
+ || internal_token != INVALID_HANDLE_VALUE
+ || current_token != INVALID_HANDLE_VALUE; }
+ void close_impersonation_tokens ()
+ {
+ if (current_token != INVALID_HANDLE_VALUE)
+ {
+ if( current_token != external_token && current_token != internal_token)
+ CloseHandle (current_token);
+ current_token = INVALID_HANDLE_VALUE;
+ }
+ if (external_token != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (external_token);
+ external_token = INVALID_HANDLE_VALUE;
+ }
+ if (internal_token != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (internal_token);
+ internal_token = INVALID_HANDLE_VALUE;
+ }
+ }
+ const char *cygheap_user::test_uid (char *&, const char *, size_t)
+ __attribute__ ((regparm (3)));
+};
+
+/* cwd cache stuff. */
+
+class muto;
+
+struct cwdstuff
+{
+ char *posix;
+ char *win32;
+ DWORD hash;
+ muto *cwd_lock;
+ char *get (char *buf, int need_posix = 1, int with_chroot = 0, unsigned ulen = CYG_MAX_PATH);
+ DWORD get_hash ();
+ void init ();
+ void fixup_after_exec (char *win32, char *posix, DWORD hash);
+ bool get_initial ();
+ void set (const char *win32_cwd, const char *posix_cwd = NULL);
+};
+
+#ifdef DEBUGGING
+struct cygheap_debug
+{
+ handle_list starth;
+ handle_list *endh;
+ handle_list freeh[500];
+};
+#endif
+
+struct user_heap_info
+{
+ void *base;
+ void *ptr;
+ void *top;
+ void *max;
+ unsigned chunk;
+};
+
+struct init_cygheap
+{
+ _cmalloc_entry *chain;
+ char *buckets[32];
+ cygheap_root root;
+ cygheap_user user;
+ user_heap_info user_heap;
+ mode_t umask;
+ HANDLE shared_h;
+ HANDLE console_h;
+ char *cygwin_regname;
+ cwdstuff cwd;
+ dtable fdtab;
+ const char *shared_prefix;
+#ifdef DEBUGGING
+ cygheap_debug debug;
+#endif
+ struct sigaction *sigs;
+
+ fhandler_tty_slave *ctty; /* Current tty */
+ fhandler_tty_slave *ctty_on_hold;
+ struct _threadinfo **threadlist;
+ size_t sthreads;
+ int open_fhs;
+ void close_ctty ();
+};
+
+#define CYGHEAPSIZE (sizeof (init_cygheap) + (20000 * sizeof (fhandler_union)) + (5 * 65536))
+
+extern init_cygheap *cygheap;
+extern void *cygheap_max;
+
+class cygheap_fdmanip
+{
+ protected:
+ int fd;
+ fhandler_base **fh;
+ bool locked;
+ public:
+ cygheap_fdmanip (): fh (NULL) {}
+ virtual ~cygheap_fdmanip ()
+ {
+ if (locked)
+ cygheap->fdtab.unlock ();
+ }
+ void release ()
+ {
+ cygheap->fdtab.release (fd);
+ }
+ operator int &() {return fd;}
+ operator fhandler_base* &() {return *fh;}
+ operator fhandler_socket* () const {return reinterpret_cast<fhandler_socket *> (*fh);}
+ void operator = (fhandler_base *fh) {*this->fh = fh;}
+ fhandler_base *operator -> () const {return *fh;}
+ bool isopen () const
+ {
+ if (*fh)
+ return true;
+ set_errno (EBADF);
+ return false;
+ }
+};
+
+class cygheap_fdnew : public cygheap_fdmanip
+{
+ public:
+ cygheap_fdnew (int seed_fd = -1, bool lockit = true)
+ {
+ if (lockit)
+ cygheap->fdtab.lock ();
+ if (seed_fd < 0)
+ fd = cygheap->fdtab.find_unused_handle ();
+ else
+ fd = cygheap->fdtab.find_unused_handle (seed_fd + 1);
+ if (fd >= 0)
+ {
+ locked = lockit;
+ fh = cygheap->fdtab + fd;
+ }
+ else
+ {
+ set_errno (EMFILE);
+ if (lockit)
+ cygheap->fdtab.unlock ();
+ locked = false;
+ }
+ }
+ void operator = (fhandler_base *fh) {*this->fh = fh;}
+};
+
+class cygheap_fdget : public cygheap_fdmanip
+{
+ public:
+ cygheap_fdget (int fd, bool lockit = false, bool do_set_errno = true)
+ {
+ if (lockit)
+ cygheap->fdtab.lock ();
+ if (fd >= 0 && fd < (int) cygheap->fdtab.size
+ && *(fh = cygheap->fdtab + fd) != NULL)
+ {
+ this->fd = fd;
+ locked = lockit;
+ }
+ else
+ {
+ this->fd = -1;
+ if (do_set_errno)
+ set_errno (EBADF);
+ if (lockit)
+ cygheap->fdtab.unlock ();
+ locked = false;
+ }
+ }
+};
+
+class child_info;
+void *__stdcall cygheap_setup_for_child (child_info *ci, bool dup_later) __attribute__ ((regparm(2)));
+void __stdcall cygheap_setup_for_child_cleanup (void *, child_info *, bool) __attribute__ ((regparm(3)));
+void __stdcall cygheap_fixup_in_child (bool);
+extern "C" {
+void __stdcall cfree (void *) __attribute__ ((regparm(1)));
+void *__stdcall cmalloc (cygheap_types, DWORD) __attribute__ ((regparm(2)));
+void *__stdcall crealloc (void *, DWORD) __attribute__ ((regparm(2)));
+void *__stdcall ccalloc (cygheap_types, DWORD, DWORD) __attribute__ ((regparm(3)));
+char *__stdcall cstrdup (const char *) __attribute__ ((regparm(1)));
+char *__stdcall cstrdup1 (const char *) __attribute__ ((regparm(1)));
+void __stdcall cfree_and_set (char *&, char * = NULL) __attribute__ ((regparm(2)));
+void __stdcall cygheap_init ();
+extern DWORD _cygheap_start;
+}
diff --git a/winsup/cygwin/cygthread.cc b/winsup/cygwin/cygthread.cc
new file mode 100644
index 00000000000..eb761ea56ff
--- /dev/null
+++ b/winsup/cygwin/cygthread.cc
@@ -0,0 +1,324 @@
+/* cygthread.cc
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <windows.h>
+#include <stdlib.h>
+#include "exceptions.h"
+#include "security.h"
+#include "cygthread.h"
+#include "sync.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "thread.h"
+#include "cygtls.h"
+
+#undef CloseHandle
+
+static cygthread NO_COPY threads[18];
+#define NTHREADS (sizeof (threads) / sizeof (threads[0]))
+
+DWORD NO_COPY cygthread::main_thread_id;
+bool NO_COPY cygthread::exiting;
+
+/* Initial stub called by cygthread constructor. Performs initial
+ per-thread initialization and loops waiting for new thread functions
+ to execute. */
+DWORD WINAPI
+cygthread::stub (VOID *arg)
+{
+ cygthread *info = (cygthread *) arg;
+ if (info->arg == cygself)
+ {
+ if (info->ev)
+ {
+ CloseHandle (info->ev);
+ CloseHandle (info->thread_sync);
+ }
+ info->ev = info->thread_sync = info->stack_ptr = NULL;
+ }
+ else
+ {
+ info->stack_ptr = &arg;
+ if (!info->ev)
+ {
+ info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ }
+ }
+ while (1)
+ {
+ if (!info->__name)
+ system_printf ("erroneous thread activation");
+ else
+ {
+ if (!info->func || exiting)
+ return 0;
+
+ /* Cygwin threads should not call ExitThread directly */
+ info->func (info->arg == cygself ? info : info->arg);
+ /* ...so the above should always return */
+
+#ifdef DEBUGGING
+ info->func = NULL; // catch erroneous activation
+#endif
+ info->__name = NULL;
+ SetEvent (info->ev);
+ }
+ switch (WaitForSingleObject (info->thread_sync, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ continue;
+ default:
+ api_fatal ("WFSO failed, %E");
+ break;
+ }
+ }
+}
+
+/* Overflow stub called by cygthread constructor. Calls specified function
+ and then exits the thread. */
+DWORD WINAPI
+cygthread::simplestub (VOID *arg)
+{
+ cygthread *info = (cygthread *) arg;
+ info->stack_ptr = &arg;
+ info->ev = info->h;
+ info->func (info->arg == cygself ? info : info->arg);
+ return 0;
+}
+
+/* Start things going. Called from dll_crt0_1. */
+void
+cygthread::init ()
+{
+ main_thread_id = GetCurrentThreadId ();
+}
+
+bool
+cygthread::is ()
+{
+ DWORD tid = GetCurrentThreadId ();
+
+ for (DWORD i = 0; i < NTHREADS; i++)
+ if (threads[i].id == tid)
+ return 1;
+
+ return 0;
+}
+
+cygthread *
+cygthread::freerange ()
+{
+ cygthread *self = (cygthread *) calloc (1, sizeof (*self));
+ self->is_freerange = true;
+ self->inuse = 1;
+ return self;
+}
+
+void * cygthread::operator
+new (size_t)
+{
+ cygthread *info;
+
+ /* Search the threads array for an empty slot to use */
+ for (info = threads; info < threads + NTHREADS; info++)
+ if (!InterlockedExchange (&info->inuse, 1))
+ {
+ /* available */
+#ifdef DEBUGGING
+ if (info->__name)
+ api_fatal ("name not NULL? id %p, i %d", info->id, info - threads);
+#endif
+ goto out;
+ }
+
+#ifdef DEBUGGING
+ char buf[1024];
+ if (!GetEnvironmentVariable ("CYGWIN_FREERANGE_NOCHECK", buf, sizeof (buf)))
+ api_fatal ("Overflowed cygwin thread pool");
+ else
+ thread_printf ("Overflowed cygwin thread pool");
+#endif
+
+ info = freerange (); /* exhausted thread pool */
+
+out:
+ return info;
+}
+
+cygthread::cygthread (LPTHREAD_START_ROUTINE start, LPVOID param,
+ const char *name): __name (name),
+ func (start), arg (param)
+{
+ thread_printf ("name %s, id %p", name, id);
+ if (h)
+ {
+ while (!thread_sync)
+ low_priority_sleep (0);
+ SetEvent (thread_sync);
+ thread_printf ("activated thread_sync %p", thread_sync);
+ }
+ else
+ {
+ stack_ptr = NULL;
+ h = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
+ this, 0, &id);
+ if (!h)
+ api_fatal ("thread handle not set - %p<%p>, %E", h, id);
+ thread_printf ("created thread %p", h);
+ }
+}
+
+/* Return the symbolic name of the current thread for debugging.
+ */
+const char *
+cygthread::name (DWORD tid)
+{
+ const char *res = NULL;
+ if (!tid)
+ tid = GetCurrentThreadId ();
+
+ if (tid == main_thread_id)
+ return "main";
+
+ for (DWORD i = 0; i < NTHREADS; i++)
+ if (threads[i].id == tid)
+ {
+ res = threads[i].__name ?: "exiting thread";
+ break;
+ }
+
+ if (!res)
+ {
+ static char buf[30] NO_COPY = {0};
+ __small_sprintf (buf, "unknown (%p)", tid);
+ res = buf;
+ }
+
+ return res;
+}
+
+cygthread::operator
+HANDLE ()
+{
+ while (!ev)
+ low_priority_sleep (0);
+ return ev;
+}
+
+/* Should only be called when the process is exiting since it
+ leaves an open thread slot. */
+void
+cygthread::exit_thread ()
+{
+ if (!is_freerange)
+ SetEvent (*this);
+ ExitThread (0);
+}
+
+/* Forcibly terminate a thread. */
+void
+cygthread::terminate_thread ()
+{
+ if (!is_freerange)
+ {
+ ResetEvent (*this);
+ ResetEvent (thread_sync);
+ }
+ (void) TerminateThread (h, 0);
+ (void) WaitForSingleObject (h, INFINITE);
+ CloseHandle (h);
+
+ while (!stack_ptr)
+ low_priority_sleep (0);
+
+ MEMORY_BASIC_INFORMATION m;
+ memset (&m, 0, sizeof (m));
+ (void) VirtualQuery (stack_ptr, &m, sizeof m);
+
+ if (!m.RegionSize)
+ system_printf ("m.RegionSize 0? stack_ptr %p", stack_ptr);
+ else if (!VirtualFree (m.AllocationBase, 0, MEM_RELEASE))
+ debug_printf ("VirtualFree of allocation base %p<%p> failed, %E",
+ stack_ptr, m.AllocationBase);
+
+ if (is_freerange)
+ free (this);
+ else
+ {
+ h = NULL;
+ __name = NULL;
+ stack_ptr = NULL;
+ (void) InterlockedExchange (&inuse, 0); /* No longer in use */
+ }
+}
+
+/* Detach the cygthread from the current thread. Note that the
+ theory is that cygthreads are only associated with one thread.
+ So, there should be never be multiple threads doing waits
+ on the same cygthread. */
+bool
+cygthread::detach (HANDLE sigwait)
+{
+ bool signalled = false;
+ if (!inuse)
+ system_printf ("called detach but inuse %d, thread %p?", inuse, id);
+ else
+ {
+ DWORD res;
+
+ if (!sigwait)
+ res = WaitForSingleObject (*this, INFINITE);
+ else
+ {
+ HANDLE w4[2];
+ w4[0] = *this;
+ w4[1] = signal_arrived;
+ res = WaitForSingleObject (sigwait, INFINITE);
+ if (res != WAIT_OBJECT_0)
+ system_printf ("WFSO sigwait %p failed, res %u, %E", sigwait, res);
+ res = WaitForMultipleObjects (2, w4, FALSE, INFINITE);
+ if (res == WAIT_OBJECT_0)
+ /* nothing */;
+ else if (WaitForSingleObject (sigwait, 0) == WAIT_OBJECT_0)
+ res = WaitForSingleObject (*this, INFINITE);
+ else if ((res = WaitForSingleObject (*this, 0)) != WAIT_OBJECT_0)
+ {
+ signalled = true;
+ terminate_thread ();
+ set_sig_errno (EINTR); /* caller should be dealing with return
+ values. */
+ }
+ }
+
+ thread_printf ("%s returns %d, id %p", sigwait ? "WFMO" : "WFSO",
+ res, id);
+
+ if (signalled)
+ /* already handled */;
+ else if (is_freerange)
+ {
+ CloseHandle (h);
+ free (this);
+ }
+ else
+ {
+ ResetEvent (*this);
+ /* Mark the thread as available by setting inuse to zero */
+ (void) InterlockedExchange (&inuse, 0);
+ }
+ }
+ return signalled;
+}
+
+void
+cygthread::terminate ()
+{
+ exiting = 1;
+}
diff --git a/winsup/cygwin/cygthread.h b/winsup/cygwin/cygthread.h
new file mode 100644
index 00000000000..76604b471d2
--- /dev/null
+++ b/winsup/cygwin/cygthread.h
@@ -0,0 +1,46 @@
+/* cygthread.h
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+class cygthread
+{
+ LONG inuse;
+ DWORD id;
+ HANDLE h;
+ HANDLE ev;
+ HANDLE thread_sync;
+ void *stack_ptr;
+ const char *__name;
+ LPTHREAD_START_ROUTINE func;
+ VOID *arg;
+ bool is_freerange;
+ static bool exiting;
+ void terminate_thread ();
+ public:
+ static DWORD WINAPI stub (VOID *);
+ static DWORD WINAPI simplestub (VOID *);
+ static DWORD main_thread_id;
+ static const char * name (DWORD = 0);
+ cygthread (LPTHREAD_START_ROUTINE, LPVOID, const char *);
+ cygthread () {};
+ static void init ();
+ bool detach (HANDLE = NULL);
+ operator HANDLE ();
+ static bool is ();
+ void * operator new (size_t);
+ static cygthread *freerange ();
+ void exit_thread ();
+ static void terminate ();
+ bool SetThreadPriority (int nPriority) {return ::SetThreadPriority (h, nPriority);}
+ void zap_h ()
+ {
+ (void) CloseHandle (h);
+ h = NULL;
+ }
+};
+
+#define cygself NULL
diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc
new file mode 100644
index 00000000000..7fe9af5c2da
--- /dev/null
+++ b/winsup/cygwin/cygtls.cc
@@ -0,0 +1,214 @@
+/* cygtls.cc
+
+ Copyright 2003, 2004 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include "thread.h"
+#include "cygtls.h"
+#include "assert.h"
+#include <syslog.h>
+#include <signal.h>
+#include "exceptions.h"
+#include "sync.h"
+#include "cygerrno.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "cygthread.h"
+
+class sentry
+{
+ static muto *lock;
+ int destroy;
+public:
+ void init ();
+ bool acquired () {return lock->acquired ();}
+ sentry () {destroy = 0;}
+ sentry (DWORD wait) {destroy = lock->acquire (wait);}
+ ~sentry () {if (destroy) lock->release ();}
+ friend void _threadinfo::init ();
+};
+
+muto NO_COPY *sentry::lock;
+
+static size_t NO_COPY nthreads;
+
+#define THREADLIST_CHUNK 256
+
+void
+_threadinfo::init ()
+{
+ if (cygheap->threadlist)
+ memset (cygheap->threadlist, 0, cygheap->sthreads * sizeof (cygheap->threadlist[0]));
+ else
+ {
+ cygheap->sthreads = THREADLIST_CHUNK;
+ cygheap->threadlist = (_threadinfo **) ccalloc (HEAP_TLS, cygheap->sthreads,
+ sizeof (cygheap->threadlist[0]));
+ }
+ new_muto1 (sentry::lock, sentry_lock);
+}
+
+void
+_threadinfo::set_state (bool is_exception)
+{
+ initialized = CYGTLS_INITIALIZED + is_exception;
+}
+
+void
+_threadinfo::reset_exception ()
+{
+ if (initialized == CYGTLS_EXCEPTION)
+ {
+#ifdef DEBUGGING
+ debug_printf ("resetting stack after an exception stack %p, stackptr %p", stack, stackptr);
+#endif
+ set_state (false);
+ }
+}
+
+/* Two calls to get the stack right... */
+void
+_threadinfo::call (DWORD (*func) (void *, void *), void *arg)
+{
+ char buf[CYGTLS_PADSIZE];
+ call2 (func, arg, buf);
+}
+
+void
+_threadinfo::call2 (DWORD (*func) (void *, void *), void *arg, void *buf)
+{
+ exception_list except_entry;
+ /* Initialize this thread's ability to respond to things like
+ SIGSEGV or SIGFPE. */
+ init_exceptions (&except_entry);
+ _my_tls.init_thread (buf, func);
+ DWORD res = func (arg, buf);
+ _my_tls.remove (INFINITE);
+ ExitThread (res);
+}
+
+void
+_threadinfo::init_thread (void *x, DWORD (*func) (void *, void *))
+{
+ if (x)
+ {
+ memset (this, 0, ((char *) padding - (char *) this));
+ stackptr = stack;
+ if (_GLOBAL_REENT)
+ {
+ local_clib._stdin = _GLOBAL_REENT->_stdin;
+ local_clib._stdout = _GLOBAL_REENT->_stdout;
+ local_clib._stderr = _GLOBAL_REENT->_stderr;
+ local_clib.__sdidinit = _GLOBAL_REENT->__sdidinit;
+ local_clib.__cleanup = _GLOBAL_REENT->__cleanup;
+ }
+ local_clib._current_locale = "C";
+ locals.process_logmask = LOG_UPTO (LOG_DEBUG);
+ }
+
+ set_state (false);
+ errno_addr = &(local_clib._errno);
+
+ if ((void *) func == (void *) cygthread::stub
+ || (void *) func == (void *) cygthread::simplestub)
+ return;
+
+ sentry here (INFINITE);
+ if (nthreads < cygheap->sthreads)
+ {
+ cygheap->threadlist = (_threadinfo **)
+ crealloc (cygheap->threadlist, (cygheap->sthreads += THREADLIST_CHUNK)
+ * sizeof (cygheap->threadlist[0]));
+ memset (cygheap->threadlist + nthreads, 0, THREADLIST_CHUNK * sizeof (cygheap->threadlist[0]));
+ }
+
+ cygheap->threadlist[nthreads++] = this;
+}
+
+void
+_threadinfo::remove (DWORD wait)
+{
+ sentry here (wait);
+ if (here.acquired ())
+ {
+ for (size_t i = 0; i < nthreads; i++)
+ if (&_my_tls == cygheap->threadlist[i])
+ {
+ if (i < --nthreads)
+ cygheap->threadlist[i] = cygheap->threadlist[nthreads];
+ break;
+ }
+ }
+}
+
+void
+_threadinfo::push (__stack_t addr, bool exception)
+{
+ *stackptr++ = (__stack_t) addr;
+ set_state (exception);
+}
+
+__stack_t
+_threadinfo::pop ()
+{
+#ifdef DEBUGGING
+ assert (stackptr > stack);
+#endif
+ __stack_t res = *--stackptr;
+#ifdef DEBUGGING
+ *stackptr = 0;
+ debug_printf ("popped %p, stack %p, stackptr %p", res, stack, stackptr);
+#endif
+ return res;
+}
+
+#define BAD_IX ((size_t) -1)
+static size_t NO_COPY threadlist_ix = BAD_IX;
+
+_threadinfo *
+_threadinfo::find_tls (int sig)
+{
+ sentry here (INFINITE);
+ __asm__ volatile (".equ _threadlist_exception_return,.");
+ _threadinfo *res = NULL;
+ for (threadlist_ix = 0; threadlist_ix < nthreads; threadlist_ix++)
+ if (sigismember (&(cygheap->threadlist[threadlist_ix]->sigwait_mask), sig))
+ {
+ res = cygheap->threadlist[threadlist_ix];
+ break;
+ }
+ threadlist_ix = BAD_IX;
+ return res;
+}
+
+extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
+static int
+handle_threadlist_exception (EXCEPTION_RECORD *e, void *frame, CONTEXT *, void *)
+{
+ small_printf ("in handle_threadlist_exception!\n");
+ if (e->ExceptionCode != STATUS_ACCESS_VIOLATION)
+ return 1;
+
+ sentry here;
+ if (threadlist_ix != BAD_IX || !here.acquired ())
+ return 1;
+
+ extern void *threadlist_exception_return;
+ cygheap->threadlist[threadlist_ix]->remove (INFINITE);
+ threadlist_ix = 0;
+ RtlUnwind (frame, threadlist_exception_return, e, 0);
+ return 0;
+}
+
+void
+_threadinfo::init_threadlist_exceptions (exception_list *el)
+{
+ extern void init_exception_handler (exception_list *, exception_handler *);
+ init_exception_handler (el, handle_threadlist_exception);
+}
diff --git a/winsup/cygwin/cygtls.h b/winsup/cygwin/cygtls.h
new file mode 100644
index 00000000000..5dcbbe3657a
--- /dev/null
+++ b/winsup/cygwin/cygtls.h
@@ -0,0 +1,146 @@
+/* cygtls.h
+
+ Copyright 2003 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _CYGTLS_H
+#define _CYGTLS_H
+
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/time.h>
+#define _NOMNTENT_FUNCS
+#include <mntent.h>
+#undef _NOMNTENT_FUNCS
+
+#define CYGTLS_INITIALIZED 0x43227
+#define CYGTLS_EXCEPTION (0x43227 + true)
+
+#ifndef CYG_MAX_PATH
+# define CYG_MAX_PATH 260
+#endif
+
+#ifndef UNLEN
+# define UNLEN 256
+#endif
+
+#define TLS_STACK_SIZE 256
+
+#pragma pack(push,4)
+struct _local_storage
+{
+ /*
+ Needed for the group functions
+ */
+ struct __group16 grp;
+ char *namearray[2];
+ int grp_pos;
+
+ /* console.cc */
+ unsigned rarg;
+
+ /* dlfcn.cc */
+ int dl_error;
+ char dl_buffer[256];
+
+ /* passwd.cc */
+ struct passwd res;
+ char pass[_PASSWORD_LEN];
+ int pw_pos;
+
+ /* path.cc */
+ struct mntent mntbuf;
+ int iteration;
+ unsigned available_drives;
+ char mnt_type[80];
+ char mnt_opts[80];
+ char mnt_fsname[CYG_MAX_PATH];
+ char mnt_dir[CYG_MAX_PATH];
+
+ /* strerror */
+ char strerror_buf[20];
+
+ /* sysloc.cc */
+ char *process_ident;
+ int process_logopt;
+ int process_facility;
+ int process_logmask;
+
+ /* times.cc */
+ char timezone_buf[20];
+ struct tm _localtime_buf;
+
+ /* uinfo.cc */
+ char username[UNLEN + 1];
+
+ /* net.cc */
+ char *ntoa_buf;
+ struct protoent *protoent_buf;
+ struct servent *servent_buf;
+ struct hostent *hostent_buf;
+};
+
+/* Please keep this file simple. Changes to the below structure may require
+ acompanying changes to the very simple parser in the perl script
+ 'gentls_offsets' (<<-- start parsing here). */
+
+typedef __uint32_t __stack_t;
+struct _threadinfo
+{
+ void (*func) /*gentls_offsets*/(int)/*gentls_offsets*/;
+ int saved_errno;
+ int sa_flags;
+ sigset_t oldmask;
+ sigset_t newmask;
+ HANDLE event;
+ int *errno_addr;
+ unsigned initialized;
+ sigset_t sigmask;
+ sigset_t sigwait_mask;
+ siginfo_t *sigwait_info;
+ siginfo_t infodata;
+ struct pthread *tid;
+ struct _reent local_clib;
+ struct _local_storage locals;
+ struct _threadinfo *prev, *next;
+ __stack_t *stackptr;
+ int sig;
+ __stack_t stack[TLS_STACK_SIZE];
+ unsigned padding[256];
+
+ /*gentls_offsets*/
+ static CRITICAL_SECTION protect_linked_list;
+ static void init ();
+ void init_thread (void *, DWORD (*) (void *, void *));
+ static void call (DWORD (*) (void *, void *), void *) __attribute__ ((regparm (3)));
+ static void call2 (DWORD (*) (void *, void *), void *, void *) __attribute__ ((regparm (3)));
+ static struct _threadinfo *find_tls (int sig);
+ void remove (DWORD);
+ void push (__stack_t, bool = false);
+ __stack_t pop ();
+ bool isinitialized () {return initialized == CYGTLS_INITIALIZED || initialized == CYGTLS_EXCEPTION;}
+ void set_state (bool);
+ void reset_exception ();
+ bool interrupt_now (CONTEXT *, int, void *, struct sigaction&)
+ __attribute__((regparm(3)));
+ void __stdcall interrupt_setup (int sig, void *handler, struct sigaction& siga, __stack_t retaddr)
+ __attribute__((regparm(3)));
+ void init_threadlist_exceptions (struct _exception_list *);
+ operator HANDLE () const {return tid->win32_obj_id;}
+ /*gentls_offsets*/
+};
+#pragma pack(pop)
+
+extern char *_tlsbase __asm__ ("%fs:4");
+extern char *_tlstop __asm__ ("%fs:8");
+#define _my_tls (((_threadinfo *) _tlsbase)[-1])
+extern _threadinfo *_main_tls;
+
+#define __getreent() (&_my_tls.local_clib)
+
+#define CYGTLS_PADSIZE (sizeof (_threadinfo))
+#endif /*_CYGTLS_H*/
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
new file mode 100644
index 00000000000..e512f353c86
--- /dev/null
+++ b/winsup/cygwin/dcrt0.cc
@@ -0,0 +1,1172 @@
+/* dcrt0.cc -- essentially the main() for the Cygwin dll
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 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 "winsup.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "glob.h"
+#include "exceptions.h"
+#include <ctype.h>
+#include <limits.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygerrno.h"
+#define NEED_VFORK
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info_magic.h"
+#include "perthread.h"
+#include "shared_info.h"
+#include "cygwin_version.h"
+#include "dll_init.h"
+#include "cygthread.h"
+#include "sync.h"
+#include "heap.h"
+
+#define MAX_AT_FILE_LEVEL 10
+
+#define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0]))
+
+HANDLE NO_COPY hMainProc = (HANDLE) -1;
+HANDLE NO_COPY hMainThread;
+
+per_thread_waitq NO_COPY waitq_storage;
+per_thread_vfork NO_COPY vfork_storage;
+
+per_thread NO_COPY *threadstuff[] = {&waitq_storage,
+ &vfork_storage,
+ NULL};
+
+bool display_title;
+bool strip_title_path;
+bool allow_glob = true;
+codepage_type current_codepage = ansi_cp;
+
+int __argc_safe;
+int _declspec(dllexport) __argc;
+char _declspec(dllexport) **__argv;
+vfork_save NO_COPY *main_vfork;
+
+static int NO_COPY envc;
+char NO_COPY **envp;
+
+extern "C" void __sinit (_reent *);
+
+_threadinfo NO_COPY *_main_tls;
+
+int cygwin_finished_initializing;
+
+/* Used in SIGTOMASK for generating a bit for insertion into a sigset_t.
+ This is subtracted from the signal number prior to shifting the bit.
+ In older versions of cygwin, the signal was used as-is to shift the
+ bit for masking. So, we'll temporarily detect this and set it to zero
+ for programs that are linked using older cygwins. This is just a stopgap
+ measure to allow an orderly transfer to the new, correct sigmask method. */
+unsigned NO_COPY int signal_shift_subtract = 1;
+
+ResourceLocks _reslock NO_COPY;
+MTinterface _mtinterf;
+
+bool NO_COPY _cygwin_testing;
+
+char NO_COPY almost_null[1];
+
+extern "C"
+{
+ /* This is an exported copy of environ which can be used by DLLs
+ which use cygwin.dll. */
+ char **__cygwin_environ;
+ char ***main_environ;
+ /* __progname used in getopt error message */
+ char *__progname;
+ static struct _reent reent_data;
+ struct per_process __cygwin_user_data =
+ {/* initial_sp */ 0, /* magic_biscuit */ 0,
+ /* dll_major */ CYGWIN_VERSION_DLL_MAJOR,
+ /* dll_major */ CYGWIN_VERSION_DLL_MINOR,
+ /* impure_ptr_ptr */ NULL, /* envptr */ NULL,
+ /* malloc */ malloc, /* free */ free,
+ /* realloc */ realloc,
+ /* fmode_ptr */ NULL, /* main */ NULL, /* ctors */ NULL,
+ /* dtors */ NULL, /* data_start */ NULL, /* data_end */ NULL,
+ /* bss_start */ NULL, /* bss_end */ NULL,
+ /* calloc */ calloc,
+ /* premain */ {NULL, NULL, NULL, NULL},
+ /* run_ctors_p */ 0,
+ /* unused */ {0, 0, 0, 0, 0, 0, 0},
+ /* forkee */ 0,
+ /* hmodule */ NULL,
+ /* api_major */ CYGWIN_VERSION_API_MAJOR,
+ /* api_minor */ CYGWIN_VERSION_API_MINOR,
+ /* unused2 */ {0, 0, 0, 0, 0},
+ /* resourcelocks */ &_reslock, /* threadinterface */ &_mtinterf,
+ /* impure_ptr */ &reent_data,
+ };
+ bool ignore_case_with_glob;
+ int __declspec (dllexport) _check_for_executable = true;
+#ifdef DEBUGGING
+ int pinger;
+#endif
+};
+
+char *old_title;
+char title_buf[TITLESIZE + 1];
+
+static void
+do_global_dtors (void)
+{
+ if (user_data->dtors)
+ {
+ void (**pfunc)() = user_data->dtors;
+ while (*++pfunc)
+ (*pfunc) ();
+ }
+}
+
+static void __stdcall
+do_global_ctors (void (**in_pfunc)(), int force)
+{
+ if (!force && user_data->forkee)
+ return; // inherit constructed stuff from parent pid
+
+ /* Run ctors backwards, so skip the first entry and find how many
+ there are, then run them. */
+
+ void (**pfunc) () = in_pfunc;
+
+ while (*++pfunc)
+ ;
+ while (--pfunc > in_pfunc)
+ (*pfunc) ();
+}
+
+/*
+ * Replaces @file in the command line with the contents of the file.
+ * There may be multiple @file's in a single command line
+ * A \@file is replaced with @file so that echo \@foo would print
+ * @foo and not the contents of foo.
+ */
+static bool __stdcall
+insert_file (char *name, char *&cmd)
+{
+ HANDLE f;
+ DWORD size;
+
+ f = CreateFile (name + 1,
+ GENERIC_READ, /* open for reading */
+ FILE_SHARE_READ, /* share for reading */
+ &sec_none_nih, /* no security */
+ OPEN_EXISTING, /* existing file only */
+ FILE_ATTRIBUTE_NORMAL, /* normal file */
+ NULL); /* no attr. template */
+
+ if (f == INVALID_HANDLE_VALUE)
+ {
+ debug_printf ("couldn't open file '%s', %E", name);
+ return false;
+ }
+
+ /* This only supports files up to about 4 billion bytes in
+ size. I am making the bold assumption that this is big
+ enough for this feature */
+ size = GetFileSize (f, NULL);
+ if (size == 0xFFFFFFFF)
+ {
+ debug_printf ("couldn't get file size for '%s', %E", name);
+ return false;
+ }
+
+ int new_size = strlen (cmd) + size + 2;
+ char *tmp = (char *) malloc (new_size);
+ if (!tmp)
+ {
+ debug_printf ("malloc failed, %E");
+ return false;
+ }
+
+ /* realloc passed as it should */
+ DWORD rf_read;
+ BOOL rf_result;
+ rf_result = ReadFile (f, tmp, size, &rf_read, NULL);
+ CloseHandle (f);
+ if (!rf_result || (rf_read != size))
+ {
+ debug_printf ("ReadFile failed, %E");
+ return false;
+ }
+
+ tmp[size++] = ' ';
+ strcpy (tmp + size, cmd);
+ cmd = tmp;
+ return true;
+}
+
+static inline int
+isquote (char c)
+{
+ char ch = c;
+ return ch == '"' || ch == '\'';
+}
+
+/* Step over a run of characters delimited by quotes */
+static /*__inline*/ char *
+quoted (char *cmd, int winshell)
+{
+ char *p;
+ char quote = *cmd;
+
+ if (!winshell)
+ {
+ char *p;
+ strcpy (cmd, cmd + 1);
+ if (*(p = strechr (cmd, quote)))
+ strcpy (p, p + 1);
+ return p;
+ }
+
+ const char *s = quote == '\'' ? "'" : "\\\"";
+ /* This must have been run from a Windows shell, so preserve
+ quotes for globify to play with later. */
+ while (*cmd && *++cmd)
+ if ((p = strpbrk (cmd, s)) == NULL)
+ {
+ cmd = strchr (cmd, '\0'); // no closing quote
+ break;
+ }
+ else if (*p == '\\')
+ cmd = ++p;
+ else if (quote == '"' && p[1] == '"')
+ {
+ *p = '\\';
+ cmd = ++p; // a quoted quote
+ }
+ else
+ {
+ cmd = p + 1; // point to after end
+ break;
+ }
+ return cmd;
+}
+
+/* Perform a glob on word if it contains wildcard characters.
+ Also quote every character between quotes to force glob to
+ treat the characters literally. */
+static int __stdcall
+globify (char *word, char **&argv, int &argc, int &argvlen)
+{
+ if (*word != '~' && strpbrk (word, "?*[\"\'(){}") == NULL)
+ return 0;
+
+ int n = 0;
+ char *p, *s;
+ int dos_spec = isdrive (word);
+ if (!dos_spec && isquote (*word) && word[1] && word[2])
+ dos_spec = isdrive (word + 1);
+
+ /* We'll need more space if there are quoting characters in
+ word. If that is the case, doubling the size of the
+ string should provide more than enough space. */
+ if (strpbrk (word, "'\""))
+ n = strlen (word);
+ char pattern[strlen (word) + ((dos_spec + 1) * n) + 1];
+
+ /* Fill pattern with characters from word, quoting any
+ characters found within quotes. */
+ for (p = pattern, s = word; *s != '\000'; s++, p++)
+ if (!isquote (*s))
+ {
+ if (dos_spec && *s == '\\')
+ *p++ = '\\';
+ *p = *s;
+ }
+ else
+ {
+ char quote = *s;
+ while (*++s && *s != quote)
+ {
+ if (dos_spec || *s != '\\')
+ /* nothing */;
+ else if (s[1] == quote || s[1] == '\\')
+ s++;
+ *p++ = '\\';
+ *p++ = *s;
+ }
+ if (*s == quote)
+ p--;
+ if (*s == '\0')
+ break;
+ }
+
+ *p = '\0';
+
+ glob_t gl;
+ gl.gl_offs = 0;
+
+ /* Attempt to match the argument. Return just word (minus quoting) if no match. */
+ if (glob (pattern, GLOB_TILDE | GLOB_NOCHECK | GLOB_BRACE | GLOB_QUOTE, NULL, &gl) || !gl.gl_pathc)
+ return 0;
+
+ /* Allocate enough space in argv for the matched filenames. */
+ n = argc;
+ if ((argc += gl.gl_pathc) > argvlen)
+ {
+ argvlen = argc + 10;
+ argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0]));
+ }
+
+ /* Copy the matched filenames to argv. */
+ char **gv = gl.gl_pathv;
+ char **av = argv + n;
+ while (*gv)
+ {
+ debug_printf ("argv[%d] = '%s'", n++, *gv);
+ *av++ = *gv++;
+ }
+
+ /* Clean up after glob. */
+ free (gl.gl_pathv);
+ return 1;
+}
+
+/* Build argv, argc from string passed from Windows. */
+
+static void __stdcall
+build_argv (char *cmd, char **&argv, int &argc, int winshell)
+{
+ int argvlen = 0;
+ int nesting = 0; // monitor "nesting" from insert_file
+
+ argc = 0;
+ argvlen = 0;
+ argv = NULL;
+
+ /* Scan command line until there is nothing left. */
+ while (*cmd)
+ {
+ /* Ignore spaces */
+ if (issep (*cmd))
+ {
+ cmd++;
+ continue;
+ }
+
+ /* Found the beginning of an argument. */
+ char *word = cmd;
+ char *sawquote = NULL;
+ while (*cmd)
+ {
+ if (*cmd != '"' && (!winshell || *cmd != '\''))
+ cmd++; // Skip over this character
+ else
+ /* Skip over characters until the closing quote */
+ {
+ sawquote = cmd;
+ cmd = quoted (cmd, winshell && argc > 0);
+ }
+ if (issep (*cmd)) // End of argument if space
+ break;
+ }
+ if (*cmd)
+ *cmd++ = '\0'; // Terminate `word'
+
+ /* Possibly look for @file construction assuming that this isn't
+ the very first argument and the @ wasn't quoted */
+ if (argc && sawquote != word && *word == '@')
+ {
+ if (++nesting > MAX_AT_FILE_LEVEL)
+ api_fatal ("Too many levels of nesting for %s", word);
+ if (insert_file (word, cmd))
+ continue; // There's new stuff in cmd now
+ }
+
+ /* See if we need to allocate more space for argv */
+ if (argc >= argvlen)
+ {
+ argvlen = argc + 10;
+ argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0]));
+ }
+
+ /* Add word to argv file after (optional) wildcard expansion. */
+ if (!winshell || !argc || !globify (word, argv, argc, argvlen))
+ {
+ debug_printf ("argv[%d] = '%s'", argc, word);
+ argv[argc++] = word;
+ }
+ }
+
+ argv[argc] = NULL;
+
+ debug_printf ("argc %d", argc);
+}
+
+/* sanity and sync check */
+void __stdcall
+check_sanity_and_sync (per_process *p)
+{
+ /* Sanity check to make sure developers didn't change the per_process */
+ /* struct without updating SIZEOF_PER_PROCESS [it makes them think twice */
+ /* about changing it]. */
+ if (sizeof (per_process) != SIZEOF_PER_PROCESS)
+ {
+ api_fatal ("per_process sanity check failed");
+ }
+
+ /* Make sure that the app and the dll are in sync. */
+
+ /* Complain if older than last incompatible change */
+ if (p->dll_major < CYGWIN_VERSION_DLL_EPOCH)
+ api_fatal ("cygwin DLL and APP are out of sync -- DLL version mismatch %d < %d",
+ p->dll_major, CYGWIN_VERSION_DLL_EPOCH);
+
+ /* magic_biscuit != 0 if using the old style version numbering scheme. */
+ if (p->magic_biscuit != SIZEOF_PER_PROCESS)
+ api_fatal ("Incompatible cygwin .dll -- incompatible per_process info %d != %d",
+ p->magic_biscuit, SIZEOF_PER_PROCESS);
+
+ /* Complain if incompatible API changes made */
+ if (p->api_major > cygwin_version.api_major)
+ api_fatal ("cygwin DLL and APP are out of sync -- API version mismatch %d > %d",
+ p->api_major, cygwin_version.api_major);
+
+ if (CYGWIN_VERSION_DLL_MAKE_COMBINED (p->dll_major, p->dll_minor) <=
+ CYGWIN_VERSION_DLL_BAD_SIGNAL_MASK)
+ signal_shift_subtract = 0;
+}
+
+child_info NO_COPY *child_proc_info = NULL;
+static MEMORY_BASIC_INFORMATION NO_COPY sm;
+
+#define CYGWIN_GUARD ((wincap.has_page_guard ()) ? \
+ PAGE_EXECUTE_READWRITE|PAGE_GUARD : PAGE_NOACCESS)
+
+// __inline__ void
+extern void
+alloc_stack_hard_way (child_info_fork *ci, volatile char *b)
+{
+ void *new_stack_pointer;
+ MEMORY_BASIC_INFORMATION m;
+ void *newbase;
+ int newlen;
+ LPBYTE curbot = (LPBYTE) sm.BaseAddress + sm.RegionSize;
+ bool noguard;
+
+ if (ci->stacktop > (LPBYTE) sm.AllocationBase && ci->stacktop < curbot)
+ {
+ newbase = curbot;
+ newlen = (LPBYTE) ci->stackbottom - (LPBYTE) curbot;
+ noguard = 1;
+ }
+ else
+ {
+ newbase = ci->stacktop;
+ newlen = (DWORD) ci->stackbottom - (DWORD) ci->stacktop;
+ noguard = 0;
+ }
+ if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS))
+ api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
+ ci->stacktop, ci->stackbottom);
+
+ new_stack_pointer = (void *) ((LPBYTE) ci->stackbottom - ci->stacksize);
+
+ if (!VirtualAlloc (new_stack_pointer, ci->stacksize, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE))
+ api_fatal ("fork: can't commit memory for stack %p(%d), %E",
+ new_stack_pointer, ci->stacksize);
+ if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m))
+ api_fatal ("fork: couldn't get new stack info, %E");
+ if (!noguard)
+ {
+ m.BaseAddress = (LPVOID)((DWORD)m.BaseAddress - 1);
+ if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT,
+ CYGWIN_GUARD))
+ api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
+ m.BaseAddress);
+ }
+ if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m))
+ api_fatal ("fork: couldn't get new stack info, %E");
+ ci->stacktop = m.BaseAddress;
+ *b = 0;
+}
+
+/* extend the stack prior to fork longjmp */
+
+static void
+alloc_stack (child_info_fork *ci)
+{
+ /* FIXME: adding 16384 seems to avoid a stack copy problem during
+ fork on Win95, but I don't know exactly why yet. DJ */
+ volatile char b[ci->stacksize + 16384];
+
+ if (!VirtualQuery ((LPCVOID) &b, &sm, sizeof sm))
+ api_fatal ("fork: couldn't get stack info, %E");
+
+ if (sm.AllocationBase == ci->stacktop)
+ ci->stacksize = 0;
+ else
+ alloc_stack_hard_way (ci, b + sizeof (b) - 1);
+
+ return;
+}
+
+#ifdef DEBUGGING
+void
+break_here ()
+{
+ debug_printf ("break here");
+}
+#endif
+
+static void
+initial_env ()
+{
+ char buf[CYG_MAX_PATH + 1];
+#ifdef DEBUGGING
+ DWORD len;
+ if (GetEnvironmentVariable ("CYGWIN_SLEEP", buf, sizeof (buf) - 1))
+ {
+ DWORD ms = atoi (buf);
+ buf[0] = '\0';
+ len = GetModuleFileName (NULL, buf, CYG_MAX_PATH);
+ console_printf ("Sleeping %d, pid %u %s\n", ms, GetCurrentProcessId (), buf);
+ while (ms--)
+ Sleep (1);
+ }
+ if (GetEnvironmentVariable ("CYGWIN_DEBUG", buf, sizeof (buf) - 1))
+ {
+ char buf1[CYG_MAX_PATH + 1];
+ len = GetModuleFileName (NULL, buf1, CYG_MAX_PATH);
+ strlwr (buf1);
+ strlwr (buf);
+ char *p = strchr (buf, ':');
+ if (!p)
+ p = (char *) "gdb.exe -nw";
+ else
+ *p++ = '\0';
+ if (strstr (buf1, buf))
+ {
+ error_start_init (p);
+ try_to_debug ();
+ break_here ();
+ }
+ }
+#endif
+
+ if (GetEnvironmentVariable ("CYGWIN_TESTING", buf, sizeof (buf) - 1))
+ _cygwin_testing = 1;
+}
+
+void __stdcall
+dll_crt0_0 ()
+{
+ wincap.init ();
+ initial_env ();
+
+ char zeros[sizeof (child_proc_info->zero)] = {0};
+ static NO_COPY STARTUPINFO si;
+ int mypid = 0;
+
+ _my_tls.stackptr = NULL;
+
+ init_console_handler ();
+ init_global_security ();
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (),
+ GetCurrentProcess (), &hMainProc, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ hMainProc = GetCurrentProcess ();
+
+ DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &hMainThread, 0, false, DUPLICATE_SAME_ACCESS);
+
+ GetStartupInfo (&si);
+ child_proc_info = (child_info *) si.lpReserved2;
+ if (si.cbReserved2 < EXEC_MAGIC_SIZE || !child_proc_info
+ || memcmp (child_proc_info->zero, zeros,
+ sizeof (child_proc_info->zero)) != 0)
+ child_proc_info = NULL;
+ else
+ {
+ if ((child_proc_info->intro & OPROC_MAGIC_MASK) == OPROC_MAGIC_GENERIC)
+ multiple_cygwin_problem ("proc", child_proc_info->intro, 0);
+ else if (child_proc_info->intro == PROC_MAGIC_GENERIC
+ && child_proc_info->magic != CHILD_INFO_MAGIC)
+ multiple_cygwin_problem ("proc", child_proc_info->magic,
+ CHILD_INFO_MAGIC);
+ else if (child_proc_info->cygheap != (void *) &_cygheap_start)
+ multiple_cygwin_problem ("cygheap", (DWORD) child_proc_info->cygheap,
+ (DWORD) &_cygheap_start);
+ unsigned should_be_cb = 0;
+ switch (child_proc_info->type)
+ {
+ case _PROC_FORK:
+ user_data->forkee = child_proc_info->cygpid;
+ should_be_cb = sizeof (child_info_fork);
+ /* fall through */;
+ case _PROC_SPAWN:
+ case _PROC_EXEC:
+ if (!should_be_cb)
+ should_be_cb = sizeof (child_info);
+ if (should_be_cb != child_proc_info->cb)
+ multiple_cygwin_problem ("proc size", child_proc_info->cb, should_be_cb);
+ else if (sizeof (fhandler_union) != child_proc_info->fhandler_union_cb)
+ multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union));
+ else
+ {
+ cygwin_user_h = child_proc_info->user_h;
+ mypid = child_proc_info->cygpid;
+ break;
+ }
+ default:
+ system_printf ("unknown exec type %d", child_proc_info->type);
+ /* intentionally fall through */
+ case _PROC_WHOOPS:
+ child_proc_info = NULL;
+ break;
+ }
+ }
+
+ device::init ();
+ winpids::init ();
+ do_global_ctors (&__CTOR_LIST__, 1);
+
+ if (!child_proc_info)
+ memory_init ();
+ else
+ {
+ bool close_ppid_handle = false;
+ bool close_hexec_proc = false;
+ switch (child_proc_info->type)
+ {
+ case _PROC_FORK:
+ alloc_stack (fork_info);
+ cygheap_fixup_in_child (0);
+ memory_init ();
+ set_myself (mypid);
+ close_ppid_handle = !!child_proc_info->pppid_handle;
+ break;
+ case _PROC_SPAWN:
+ /* Have to delay closes until after cygheap is setup */
+ close_hexec_proc = !!spawn_info->hexec_proc;
+ close_ppid_handle = !!child_proc_info->pppid_handle;
+ goto around;
+ case _PROC_EXEC:
+ hexec_proc = spawn_info->hexec_proc;
+ around:
+ HANDLE h;
+ cygheap_fixup_in_child (1);
+ memory_init ();
+ if (!spawn_info->moreinfo->myself_pinfo ||
+ !DuplicateHandle (hMainProc, spawn_info->moreinfo->myself_pinfo,
+ hMainProc, &h, 0, 0,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ h = NULL;
+ set_myself (mypid, h);
+ __argc = spawn_info->moreinfo->argc;
+ __argv = spawn_info->moreinfo->argv;
+ envp = spawn_info->moreinfo->envp;
+ envc = spawn_info->moreinfo->envc;
+ envp = spawn_info->moreinfo->envp;
+ cygheap->fdtab.fixup_after_exec (spawn_info->parent);
+ signal_fixup_after_exec ();
+ CloseHandle (spawn_info->parent);
+ if (spawn_info->moreinfo->old_title)
+ {
+ old_title = strcpy (title_buf, spawn_info->moreinfo->old_title);
+ cfree (spawn_info->moreinfo->old_title);
+ }
+ break;
+ }
+ if (close_hexec_proc)
+ CloseHandle (spawn_info->hexec_proc);
+ if (close_ppid_handle)
+ CloseHandle (child_proc_info->pppid_handle);
+ }
+
+ _threadinfo::init ();
+}
+
+/* Take over from libc's crt0.o and start the application. Note the
+ various special cases when Cygwin DLL is being runtime loaded (as
+ opposed to being link-time loaded by Cygwin apps) from a non
+ cygwin app via LoadLibrary. */
+static void
+dll_crt0_1 (char *)
+{
+ /* According to onno@stack.urc.tue.nl, the exception handler record must
+ be on the stack. */
+ /* FIXME: Verify forked children get their exception handler set up ok. */
+ exception_list cygwin_except_entry;
+
+ /* Initialize SIGSEGV handling, etc. */
+ init_exceptions (&cygwin_except_entry);
+ check_sanity_and_sync (user_data);
+ malloc_init ();
+
+ /* Nasty static stuff needed by newlib -- point to a local copy of
+ the reent stuff.
+ Note: this MUST be done here (before the forkee code) as the
+ fork copy code doesn't copy the data in libccrt0.cc (that's why we
+ pass in the per_process struct into the .dll from libccrt0). */
+
+ user_data->resourcelocks->Init ();
+ user_data->threadinterface->Init ();
+ ProtectHandle (hMainProc);
+ ProtectHandle (hMainThread);
+ cygthread::init ();
+
+ /* Initialize pthread mainthread when not forked and it is safe to call new,
+ otherwise it is reinitalized in fixup_after_fork */
+ if (!user_data->forkee)
+ {
+ __sinit (_impure_ptr);
+ pthread::init_mainthread ();
+ }
+
+#ifdef DEBUGGING
+ strace.microseconds ();
+#endif
+
+ /* Initialize debug muto, if DLL is built with --enable-debugging.
+ Need to do this before any helper threads start. */
+ debug_init ();
+
+ cygheap->fdtab.vfork_child_fixup ();
+
+ (void) SetErrorMode (SEM_FAILCRITICALERRORS);
+
+ /* Initialize events. */
+ events_init ();
+
+ cygheap->cwd.init ();
+ main_vfork = vfork_storage.create ();
+
+ cygbench ("pre-forkee");
+ if (user_data->forkee)
+ {
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ frame pointer is referencing the new stack so that the OS knows what
+ to do when it needs to increase the size of the stack.
+
+ NOTE: Don't do anything that involves the stack until you've completed
+ this step. */
+ if (fork_info->stacksize)
+ {
+ _tlsbase = (char *) fork_info->stackbottom;
+ _tlstop = (char *) fork_info->stacktop;
+ }
+
+ longjmp (fork_info->jmp, fork_info->cygpid);
+ }
+
+#ifdef DEBUGGING
+ {
+ extern void fork_init ();
+ fork_init ();
+ }
+#endif
+
+ /* Init global well known SID objects */
+ cygsid::init ();
+
+ /* Initialize our process table entry. */
+ pinfo_init (envp, envc);
+
+ if (!old_title && GetConsoleTitle (title_buf, TITLESIZE))
+ old_title = title_buf;
+
+ /* Allocate cygheap->fdtab */
+ dtable_init ();
+
+ /* Initialize user info. */
+ uinfo_init ();
+
+ /* Initialize signal/subprocess handling. */
+ sigproc_init ();
+
+ /* Connect to tty. */
+ tty_init ();
+
+ if (!__argc)
+ {
+ char *line = GetCommandLineA ();
+ line = strcpy ((char *) alloca (strlen (line) + 1), line);
+
+ if (current_codepage == oem_cp)
+ CharToOemA (line, line);
+
+ /* Scan the command line and build argv. Expand wildcards if not
+ called from another cygwin process. */
+ build_argv (line, __argv, __argc,
+ NOTSTATE (myself, PID_CYGPARENT) && allow_glob);
+
+ /* Convert argv[0] to posix rules if it's currently blatantly
+ win32 style. */
+ if ((strchr (__argv[0], ':')) || (strchr (__argv[0], '\\')))
+ {
+ char *new_argv0 = (char *) malloc (CYG_MAX_PATH);
+ cygwin_conv_to_posix_path (__argv[0], new_argv0);
+ __argv[0] = (char *) realloc (new_argv0, strlen (new_argv0) + 1);
+ }
+ }
+
+ __argc_safe = __argc;
+ if (user_data->premain[0])
+ for (unsigned int i = 0; i < PREMAIN_LEN / 2; i++)
+ user_data->premain[i] (__argc, __argv, user_data);
+
+ /* Set up standard fds in file descriptor table. */
+ cygheap->fdtab.stdio_init ();
+
+ /* Set up __progname for getopt error call. */
+ if (__argv[0] && (__progname = strrchr (__argv[0], '/')))
+ ++__progname;
+ else
+ __progname = __argv[0];
+ if (__progname)
+ {
+ char *cp = strchr (__progname, '\0') - 4;
+ if (cp > __progname && strcasematch (cp, ".exe"))
+ *cp = '\0';
+ }
+
+ /* Set new console title if appropriate. */
+
+ if (display_title && !dynamically_loaded)
+ {
+ char *cp = __progname;
+ if (strip_title_path)
+ for (char *ptr = cp; *ptr && *ptr != ' '; ptr++)
+ if (isdirsep (*ptr))
+ cp = ptr + 1;
+ set_console_title (cp);
+ }
+
+ cygwin_finished_initializing = 1;
+ /* Call init of loaded dlls. */
+ dlls.init ();
+
+ /* Execute any specified "premain" functions */
+ if (user_data->premain[PREMAIN_LEN / 2])
+ for (unsigned int i = PREMAIN_LEN / 2; i < PREMAIN_LEN; i++)
+ user_data->premain[i] (__argc, __argv, user_data);
+
+ debug_printf ("user_data->main %p", user_data->main);
+
+ if (dynamically_loaded)
+ {
+ set_errno (0);
+ return;
+ }
+
+ /* Disable case-insensitive globbing */
+ ignore_case_with_glob = false;
+
+
+ set_errno (0);
+
+ MALLOC_CHECK;
+ cygbench (__progname);
+
+ /* Flush signals and ensure that signal thread is up and running. Can't
+ do this for noncygwin case since the signal thread is blocked due to
+ LoadLibrary serialization. */
+ wait_for_sigthread ();
+ if (user_data->main)
+ exit (user_data->main (__argc, __argv, *user_data->envptr));
+}
+
+struct _reent *
+initialize_main_tls (char *padding)
+{
+ if (!_main_tls)
+ {
+ _main_tls = &_my_tls;
+ _main_tls->init_thread (padding, NULL);
+ }
+ return &_main_tls->local_clib;
+}
+
+/* Wrap the real one, otherwise gdb gets confused about
+ two symbols with the same name, but different addresses.
+
+ UPTR is a pointer to global data that lives on the libc side of the
+ line [if one distinguishes the application from the dll]. */
+
+extern "C" void __stdcall
+_dll_crt0 ()
+{
+ extern HANDLE sync_startup;
+ if (sync_startup)
+ {
+ (void) WaitForSingleObject (sync_startup, INFINITE);
+ CloseHandle (sync_startup);
+ }
+
+ main_environ = user_data->envptr;
+ *main_environ = NULL;
+
+ if (child_proc_info && child_proc_info->type == _PROC_FORK)
+ user_data->forkee = child_proc_info->cygpid;
+
+ char padding[CYGTLS_PADSIZE];
+ _impure_ptr = &reent_data;
+ _impure_ptr->_stdin = &_impure_ptr->__sf[0];
+ _impure_ptr->_stdout = &_impure_ptr->__sf[1];
+ _impure_ptr->_stderr = &_impure_ptr->__sf[2];
+ _impure_ptr->_current_locale = "C";
+ initialize_main_tls (padding);
+ dll_crt0_1 (padding);
+}
+
+void
+dll_crt0 (per_process *uptr)
+{
+ /* Set the local copy of the pointer into the user space. */
+ if (uptr && uptr != user_data)
+ {
+ memcpy (user_data, uptr, per_process_overwrite);
+ *(user_data->impure_ptr_ptr) = &reent_data;
+ }
+ _dll_crt0 ();
+}
+
+/* This must be called by anyone who uses LoadLibrary to load cygwin1.dll */
+extern "C" void
+cygwin_dll_init ()
+{
+ static char **envp;
+ static int _fmode;
+
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (),
+ GetCurrentProcess (), &hMainProc, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ hMainProc = GetCurrentProcess ();
+
+ DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &hMainThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ user_data->magic_biscuit = sizeof (per_process);
+
+ user_data->envptr = &envp;
+ user_data->fmode_ptr = &_fmode;
+
+ dll_crt0_1 (NULL);
+}
+
+extern "C" void
+__main (void)
+{
+ do_global_ctors (user_data->ctors, false);
+ atexit (do_global_dtors);
+}
+
+exit_states NO_COPY exit_state;
+extern CRITICAL_SECTION exit_lock;
+
+void __stdcall
+do_exit (int status)
+{
+ syscall_printf ("do_exit (%d), exit_state %d", status, exit_state);
+
+ vfork_save *vf = vfork_storage.val ();
+ if (vf != NULL && vf->pid < 0)
+ {
+ exit_state = ES_NOT_EXITING;
+ vf->restore_exit (status);
+ }
+
+ EnterCriticalSection (&exit_lock);
+ if (exit_state < ES_EVENTS_TERMINATE)
+ {
+ exit_state = ES_EVENTS_TERMINATE;
+ events_terminate ();
+ }
+
+ UINT n = (UINT) status;
+ if (exit_state < ES_THREADTERM)
+ {
+ exit_state = ES_THREADTERM;
+ cygthread::terminate ();
+ }
+
+ if (exit_state < ES_SIGNAL)
+ {
+ exit_state = ES_SIGNAL;
+ if (!(n & EXIT_REPARENTING))
+ {
+ signal (SIGCHLD, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGINT, SIG_IGN);
+ signal (SIGQUIT, SIG_IGN);
+ }
+ }
+
+ if (exit_state < ES_CLOSEALL)
+ {
+ exit_state = ES_CLOSEALL;
+ close_all_files ();
+ }
+
+ if (exit_state < ES_SIGPROCTERMINATE)
+ {
+ exit_state = ES_SIGPROCTERMINATE;
+ sigproc_terminate ();
+ }
+
+ myself->stopsig = 0;
+ if (exit_state < ES_TITLE)
+ {
+ exit_state = ES_TITLE;
+ /* restore console title */
+ if (old_title && display_title)
+ set_console_title (old_title);
+ }
+
+ if (exit_state < ES_HUP_PGRP)
+ {
+ exit_state = ES_HUP_PGRP;
+ /* Kill orphaned children on group leader exit */
+ if (myself->has_pgid_children && myself->pid == myself->pgid)
+ {
+ sigproc_printf ("%d == pgrp %d, send SIG{HUP,CONT} to stopped children",
+ myself->pid, myself->pgid);
+ kill_pgrp (myself->pgid, -SIGHUP);
+ }
+ }
+
+ if (exit_state < ES_HUP_SID)
+ {
+ exit_state = ES_HUP_SID;
+ /* Kill the foreground process group on session leader exit */
+ if (getpgrp () > 0 && myself->pid == myself->sid && real_tty_attached (myself))
+ {
+ tty *tp = cygwin_shared->tty[myself->ctty];
+ sigproc_printf ("%d == sid %d, send SIGHUP to children",
+ myself->pid, myself->sid);
+
+ /* CGF FIXME: This can't be right. */
+ if (tp->getsid () == myself->sid)
+ tp->kill_pgrp (SIGHUP);
+ }
+
+ }
+
+ if (exit_state < ES_TTY_TERMINATE)
+ {
+ exit_state = ES_TTY_TERMINATE;
+ tty_terminate ();
+ }
+
+ minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n);
+ myself->exit (n);
+}
+
+static muto *atexit_lock;
+
+extern "C" int
+cygwin_atexit (void (*function)(void))
+{
+ int res;
+ if (!atexit_lock)
+ new_muto (atexit_lock);
+ atexit_lock->acquire ();
+ res = atexit (function);
+ atexit_lock->release ();
+ return res;
+}
+
+extern "C" void
+cygwin_exit (int n)
+{
+ if (atexit_lock)
+ atexit_lock->acquire ();
+ exit (n);
+}
+
+extern "C" void
+_exit (int n)
+{
+ do_exit ((DWORD) n & 0xffff);
+}
+
+extern "C" void
+__api_fatal (const char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+
+ va_start (ap, fmt);
+ int n = __small_sprintf (buf, "%P (%u): *** ", cygwin_pid (GetCurrentProcessId ()));
+ __small_vsprintf (buf + n, fmt, ap);
+ va_end (ap);
+ strcat (buf, "\n");
+ int len = strlen (buf);
+ DWORD done;
+ (void) WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, len, &done, 0);
+
+ /* Make sure that the message shows up on the screen, too, since this is
+ a serious error. */
+ if (GetFileType (GetStdHandle (STD_ERROR_HANDLE)) != FILE_TYPE_CHAR)
+ {
+ HANDLE h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_WRITE,
+ &sec_none, OPEN_EXISTING, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ (void) WriteFile (h, buf, len, &done, 0);
+ }
+
+ /* We are going down without mercy. Make sure we reset
+ our process_state. */
+ sigproc_terminate ();
+#ifdef DEBUGGING
+ (void) try_to_debug ();
+#endif
+ myself->exit (1);
+}
+
+void
+multiple_cygwin_problem (const char *what, unsigned magic_version, unsigned version)
+{
+ if (_cygwin_testing && (strstr (what, "proc") || strstr (what, "cygheap")))
+ {
+ child_proc_info->type = _PROC_WHOOPS;
+ return;
+ }
+
+ char buf[1024];
+ if (GetEnvironmentVariable ("CYGWIN_MISMATCH_OK", buf, sizeof (buf)))
+ return;
+
+ if (CYGWIN_VERSION_MAGIC_VERSION (magic_version) == version)
+ system_printf ("%s magic number mismatch detected - %p/%p", what, magic_version, version);
+ else
+ api_fatal ("%s version mismatch detected - %p/%p.\n\
+You have multiple copies of cygwin1.dll on your system.\n\
+Search for cygwin1.dll using the Windows Start->Find/Search facility\n\
+and delete all but the most recent version. The most recent version *should*\n\
+reside in x:\\cygwin\\bin, where 'x' is the drive on which you have\n\
+installed the cygwin distribution.", what, magic_version, version);
+}
+
+#ifdef DEBUGGING
+void __stdcall
+cygbench (const char *s)
+{
+ char buf[1024];
+ if (GetEnvironmentVariable ("CYGWIN_BENCH", buf, sizeof (buf)))
+ small_printf ("%05d ***** %s : %10d\n", GetCurrentProcessId (), s, strace.microseconds ());
+}
+#endif
diff --git a/winsup/cygwin/debug.cc b/winsup/cygwin/debug.cc
new file mode 100644
index 00000000000..05b53cf332c
--- /dev/null
+++ b/winsup/cygwin/debug.cc
@@ -0,0 +1,231 @@
+/* debug.cc
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include "sync.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "perthread.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygerrno.h"
+#ifdef DEBUGGING
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#endif
+
+#undef CloseHandle
+
+#ifdef DEBUGGING
+/* Here lies extra debugging routines which help track down internal
+ Cygwin problems when compiled with -DDEBUGGING . */
+#include <stdlib.h>
+#define NFREEH (sizeof (cygheap->debug.freeh) / sizeof (cygheap->debug.freeh[0]))
+
+class lock_debug
+{
+ static muto *locker;
+ bool acquired;
+ public:
+ lock_debug () : acquired (0)
+ {
+ if (locker && !exit_state)
+ acquired = !!locker->acquire (INFINITE);
+ }
+ void unlock ()
+ {
+ if (locker && acquired)
+ {
+ locker->release ();
+ acquired = false;
+ }
+ }
+ ~lock_debug () {unlock ();}
+ friend void debug_init ();
+};
+
+muto NO_COPY *lock_debug::locker = NULL;
+
+static bool __stdcall mark_closed (const char *, int, HANDLE, const char *, bool);
+
+void
+debug_init ()
+{
+ muto *debug_lock_muto;
+ lock_debug::locker = new_muto (debug_lock_muto);
+}
+
+/* Find a registered handle in the linked list of handles. */
+static handle_list * __stdcall
+find_handle (HANDLE h)
+{
+ handle_list *hl;
+ for (hl = &cygheap->debug.starth; hl->next != NULL; hl = hl->next)
+ if (hl->next->h == h)
+ goto out;
+ cygheap->debug.endh = hl;
+ hl = NULL;
+
+out:
+ return hl;
+}
+
+#ifdef DEBUGGING_AND_FDS_PROTECTED
+void
+setclexec (HANDLE oh, HANDLE nh, bool not_inheriting)
+{
+ handle_list *hl = find_handle (oh);
+ if (hl)
+ {
+ hl = hl->next;
+ hl->inherited = !not_inheriting;
+ hl->h = nh;
+ }
+}
+#endif
+
+/* Create a new handle record */
+static handle_list * __stdcall
+newh ()
+{
+ handle_list *hl;
+ lock_debug here;
+
+ for (hl = cygheap->debug.freeh; hl < cygheap->debug.freeh + NFREEH; hl++)
+ if (hl->name == NULL)
+ return hl;
+
+ return NULL;
+}
+
+/* Add a handle to the linked list of known handles. */
+void __stdcall
+add_handle (const char *func, int ln, HANDLE h, const char *name, bool inh)
+{
+ handle_list *hl;
+ lock_debug here;
+
+ if (!cygheap)
+ return;
+
+ if ((hl = find_handle (h)))
+ {
+ hl = hl->next;
+ if (hl->name == name && hl->func == func && hl->ln == ln)
+ return;
+ system_printf ("%s:%d - multiple attempts to add handle %s<%p>", func,
+ ln, name, h);
+ system_printf (" previously allocated by %s:%d(%s<%p>) winpid %d",
+ hl->func, hl->ln, hl->name, hl->h, hl->pid);
+ return;
+ }
+
+ if ((hl = newh ()) == NULL)
+ {
+ here.unlock ();
+ debug_printf ("couldn't allocate memory for %s(%d): %s(%p)",
+ func, ln, name, h);
+ return;
+ }
+ hl->h = h;
+ hl->name = name;
+ hl->func = func;
+ hl->ln = ln;
+ hl->next = NULL;
+ hl->inherited = inh;
+ hl->pid = GetCurrentProcessId ();
+ cygheap->debug.endh->next = hl;
+ cygheap->debug.endh = hl;
+ debug_printf ("protecting handle '%s', inherited flag %d", hl->name, hl->inherited);
+
+ return;
+}
+
+static void __stdcall
+delete_handle (handle_list *hl)
+{
+ handle_list *hnuke = hl->next;
+ debug_printf ("nuking handle '%s'", hnuke->name);
+ hl->next = hl->next->next;
+ memset (hnuke, 0, sizeof (*hnuke));
+}
+
+void
+debug_fixup_after_fork_exec ()
+{
+ /* No lock needed at this point */
+ handle_list *hl;
+ for (hl = &cygheap->debug.starth; hl->next != NULL; /* nothing */)
+ if (hl->next->inherited)
+ hl = hl->next;
+ else
+ delete_handle (hl); // removes hl->next
+}
+
+static bool __stdcall
+mark_closed (const char *func, int ln, HANDLE h, const char *name, bool force)
+{
+ handle_list *hl;
+ lock_debug here;
+
+ if (!cygheap)
+ return true;
+
+ if ((hl = find_handle (h)) && !force)
+ {
+ hl = hl->next;
+ here.unlock (); // race here
+ system_printf ("attempt to close protected handle %s:%d(%s<%p>) winpid %d",
+ hl->func, hl->ln, hl->name, hl->h, hl->pid);
+ system_printf (" by %s:%d(%s<%p>)", func, ln, name, h);
+ return false;
+ }
+
+ handle_list *hln;
+ if (hl && (hln = hl->next) && strcmp (name, hln->name))
+ {
+ system_printf ("closing protected handle %s:%d(%s<%p>)",
+ hln->func, hln->ln, hln->name, hln->h);
+ system_printf (" by %s:%d(%s<%p>)", func, ln, name, h);
+ }
+
+ if (hl)
+ delete_handle (hl);
+
+ return true;
+}
+
+/* Close a known handle. Complain if !force and closing a known handle or
+ if the name of the handle being closed does not match the registered name. */
+bool __stdcall
+close_handle (const char *func, int ln, HANDLE h, const char *name, bool force)
+{
+ bool ret;
+ lock_debug here;
+
+ if (!mark_closed (func, ln, h, name, force))
+ return false;
+
+ ret = CloseHandle (h);
+
+#if 0 /* Uncomment to see CloseHandle failures */
+ if (!ret)
+ small_printf ("CloseHandle(%s) failed %s:%d\n", name, func, ln);
+#endif
+ return ret;
+}
+
+int __stdcall
+__set_errno (const char *func, int ln, int val)
+{
+ debug_printf ("%s:%d val %d", func, ln, val);
+ return errno = val;
+}
+#endif /*DEBUGGING*/
diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc
new file mode 100644
index 00000000000..8a25b7d8cc0
--- /dev/null
+++ b/winsup/cygwin/dlfcn.cc
@@ -0,0 +1,147 @@
+/* dlfcn.cc
+
+ Copyright 1998, 2000, 2001 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 "winsup.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "perprocess.h"
+#include "thread.h"
+#include "dlfcn.h"
+#include "dll_init.h"
+#include "cygerrno.h"
+#include "cygtls.h"
+
+static void __stdcall
+set_dl_error (const char *str)
+{
+ __small_sprintf (_my_tls.locals.dl_buffer, "%s: %E", str);
+ _my_tls.locals.dl_error = 1;
+}
+
+/* Look for an executable file given the name and the environment
+ variable to use for searching (eg., PATH); returns the full
+ pathname (static buffer) if found or NULL if not. */
+inline const char * __stdcall
+check_path_access (const char *mywinenv, const char *name, path_conv& buf)
+{
+ return find_exec (name, buf, mywinenv, FE_NNF | FE_NATIVE | FE_CWD);
+}
+
+/* Search LD_LIBRARY_PATH for dll, if it exists.
+ Return Windows version of given path. */
+static const char * __stdcall
+get_full_path_of_dll (const char* str, char *name)
+{
+ int len = strlen (str);
+
+ /* empty string or too long to be legal win32 pathname? */
+ if (len == 0 || len >= CYG_MAX_PATH - 1)
+ return str; /* Yes. Let caller deal with it. */
+
+ const char *ret;
+
+ strcpy (name, str); /* Put it somewhere where we can manipulate it. */
+
+ /* Add extension if necessary */
+ if (str[len - 1] != '.')
+ {
+ /* Add .dll only if no extension provided. */
+ const char *p = strrchr (str, '.');
+ if (!p || strpbrk (p, "\\/"))
+ strcat (name, ".dll");
+ }
+
+ path_conv real_filename;
+
+ if (isabspath (name) ||
+ (ret = check_path_access ("LD_LIBRARY_PATH=", name, real_filename)) == NULL)
+ real_filename.check (name); /* Convert */
+
+ if (!real_filename.error)
+ ret = strcpy (name, real_filename);
+ else
+ {
+ set_errno (real_filename.error);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+void *
+dlopen (const char *name, int)
+{
+ void *ret;
+
+ if (name == NULL)
+ ret = (void *) GetModuleHandle (NULL); /* handle for the current module */
+ else
+ {
+ char buf[CYG_MAX_PATH];
+ /* handle for the named library */
+ const char *fullpath = get_full_path_of_dll (name, buf);
+ if (!fullpath)
+ ret = NULL;
+ else
+ {
+ ret = (void *) LoadLibrary (fullpath);
+ if (ret == NULL)
+ __seterrno ();
+ }
+ }
+
+ if (!ret)
+ set_dl_error ("dlopen");
+ debug_printf ("ret %p", ret);
+
+ return ret;
+}
+
+void *
+dlsym (void *handle, const char *name)
+{
+ void *ret = (void *) GetProcAddress ((HMODULE) handle, name);
+ if (!ret)
+ set_dl_error ("dlsym");
+ debug_printf ("ret %p", ret);
+ return ret;
+}
+
+int
+dlclose (void *handle)
+{
+ int ret = -1;
+ void *temphandle = (void *) GetModuleHandle (NULL);
+ if (temphandle == handle || FreeLibrary ((HMODULE) handle))
+ ret = 0;
+ if (ret)
+ set_dl_error ("dlclose");
+ CloseHandle ((HMODULE) temphandle);
+ return ret;
+}
+
+char *
+dlerror ()
+{
+ char *res;
+ if (!_my_tls.locals.dl_error)
+ res = NULL;
+ else
+ {
+ _my_tls.locals.dl_error = 0;
+ res = _my_tls.locals.dl_buffer;
+ }
+ return res;
+}
diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc
new file mode 100644
index 00000000000..14bfe936a11
--- /dev/null
+++ b/winsup/cygwin/dll_init.cc
@@ -0,0 +1,430 @@
+/* dll_init.cc
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "dll_init.h"
+#include "environ.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+
+extern void __stdcall check_sanity_and_sync (per_process *);
+
+dll_list NO_COPY dlls;
+
+static NO_COPY int in_forkee = 0;
+static int dll_global_dtors_recorded;
+
+/* Run destructors for all DLLs on exit. */
+static void
+dll_global_dtors ()
+{
+ for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ())
+ d->p.run_dtors ();
+}
+
+/* Run all constructors associated with a dll */
+void
+per_module::run_ctors ()
+{
+ void (**pfunc)() = ctors;
+
+ /* Run ctors backwards, so skip the first entry and find how many
+ there are, then run them. */
+
+ if (pfunc)
+ {
+ int i;
+ for (i = 1; pfunc[i]; i++);
+
+ for (int j = i - 1; j > 0; j--)
+ (pfunc[j]) ();
+ }
+}
+
+/* Run all destructors associated with a dll */
+void
+per_module::run_dtors ()
+{
+ void (**pfunc)() = dtors;
+ for (int i = 1; pfunc[i]; i++)
+ (pfunc[i]) ();
+}
+
+/* Initialize an individual DLL */
+int
+dll::init ()
+{
+ int ret = 1;
+
+ /* Why didn't we just import this variable? */
+ *(p.envptr) = __cygwin_environ;
+
+ /* Don't run constructors or the "main" if we've forked. */
+ if (!in_forkee)
+ {
+ /* global contructors */
+ p.run_ctors ();
+
+ /* entry point of dll (use main of per_process with null args...) */
+ if (p.main)
+ ret = (*(p.main)) (0, 0, 0);
+ }
+
+ return ret;
+}
+
+/* Look for a dll based on name */
+dll *
+dll_list::operator[] (const char *name)
+{
+ dll *d = &start;
+ while ((d = d->next) != NULL)
+ if (strcasematch (name, d->name))
+ return d;
+
+ return NULL;
+}
+
+#define RETRIES 1000
+
+/* Allocate space for a dll struct contiguous with the just-loaded dll. */
+dll *
+dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
+{
+ char name[CYG_MAX_PATH + 1];
+ DWORD namelen = GetModuleFileName (h, name, sizeof (name));
+
+ /* Already loaded? */
+ dll *d = dlls[name];
+ if (d)
+ {
+ d->count++; /* Yes. Bump the usage count. */
+ return d; /* Return previously allocated pointer. */
+ }
+
+ SYSTEM_INFO s1;
+ GetSystemInfo (&s1);
+
+ int i;
+ void *s = p->bss_end;
+ DWORD n;
+ MEMORY_BASIC_INFORMATION m;
+ /* Search for space after the DLL */
+ for (i = 0; i <= RETRIES; i++, s = (char *) m.BaseAddress + m.RegionSize)
+ {
+ if (!VirtualQuery (s, &m, sizeof (m)))
+ return NULL; /* Can't do it. */
+ if (m.State == MEM_FREE)
+ {
+ /* Couldn't find any. Uh oh. FIXME: Issue an error? */
+ if (i == RETRIES)
+ return NULL; /* Oh well. Couldn't locate free space. */
+
+ /* Ensure that this is rounded to the nearest page boundary.
+ FIXME: Should this be ensured by VirtualQuery? */
+ n = (DWORD) m.BaseAddress;
+ DWORD r = n % s1.dwAllocationGranularity;
+
+ if (r)
+ n = ((n - r) + s1.dwAllocationGranularity);
+
+ /* First reserve the area of memory, then commit it. */
+ if (VirtualAlloc ((void *) n, sizeof (dll), MEM_RESERVE, PAGE_READWRITE))
+ d = (dll *) VirtualAlloc ((void *) n, sizeof (dll), MEM_COMMIT,
+ PAGE_READWRITE);
+ if (d)
+ break;
+ }
+ }
+
+ /* Did we succeed? */
+ if (d == NULL)
+ { /* Nope. */
+#ifdef DEBUGGING
+ system_printf ("VirtualAlloc failed, %E");
+#endif
+ __seterrno ();
+ return NULL;
+ }
+
+ /* Now we've allocated a block of information. Fill it in with the supplied
+ info about this DLL. */
+ d->count = 1;
+ d->namelen = namelen;
+ strcpy (d->name, name);
+ d->handle = h;
+ d->p = p;
+ d->type = type;
+ if (end == NULL)
+ end = &start; /* Point to "end" of dll chain. */
+ end->next = d; /* Standard linked list stuff. */
+ d->next = NULL;
+ d->prev = end;
+ end = d;
+ tot++;
+ if (type == DLL_LOAD)
+ loaded_dlls++;
+ return d;
+}
+
+/* Detach a DLL from the chain. */
+void
+dll_list::detach (void *retaddr)
+{
+ if (!myself || myself->process_state == PID_EXITED)
+ return;
+ MEMORY_BASIC_INFORMATION m;
+ if (!VirtualQuery (retaddr, &m, sizeof m))
+ return;
+ HMODULE h = (HMODULE) m.AllocationBase;
+
+ dll *d = &start;
+ while ((d = d->next))
+ if (d->handle != h)
+ continue;
+ else if (d->count <= 0)
+ system_printf ("WARNING: trying to detach an already detached dll ...");
+ else if (--d->count == 0)
+ {
+ d->p.run_dtors ();
+ d->prev->next = d->next;
+ if (d->next)
+ d->next->prev = d->prev;
+ if (d->type == DLL_LOAD)
+ loaded_dlls--;
+ if (end == d)
+ end = d->prev;
+ VirtualFree (d, 0, MEM_RELEASE);
+ break;
+ }
+}
+
+/* Initialization for all linked DLLs, called by dll_crt0_1. */
+void
+dll_list::init ()
+{
+ /* Make sure that destructors are called on exit. */
+ if (!dll_global_dtors_recorded)
+ {
+ atexit (dll_global_dtors);
+ dll_global_dtors_recorded = 1;
+ }
+
+ /* Walk the dll chain, initializing each dll */
+ dll *d = &start;
+ while ((d = d->next))
+ d->init ();
+}
+
+#define A64K (64 * 1024)
+
+/* Mark every memory address up to "here" as reserved. This may force
+ Windows NT to load a DLL in the next available, lowest slot. */
+static void
+reserve_upto (const char *name, DWORD here)
+{
+ DWORD size;
+ MEMORY_BASIC_INFORMATION mb;
+ for (DWORD start = 0x10000; start < here; start += size)
+ if (!VirtualQuery ((void *) start, &mb, sizeof (mb)))
+ size = A64K;
+ else
+ {
+ size = A64K * ((mb.RegionSize + A64K - 1) / A64K);
+ start = A64K * (((DWORD) mb.BaseAddress + A64K - 1) / A64K);
+
+ if (start + size > here)
+ size = here - start;
+ if (mb.State == MEM_FREE &&
+ !VirtualAlloc ((void *) start, size, MEM_RESERVE, PAGE_NOACCESS))
+ api_fatal ("couldn't allocate memory %p(%d) for '%s' alignment, %E\n",
+ start, size, name);
+ }
+}
+
+/* Release all of the memory previously allocated by "upto" above.
+ Note that this may also free otherwise reserved memory. If that becomes
+ a problem, we'll have to keep track of the memory that we reserve above. */
+static void
+release_upto (const char *name, DWORD here)
+{
+ DWORD size;
+ MEMORY_BASIC_INFORMATION mb;
+ for (DWORD start = 0x10000; start < here; start += size)
+ if (!VirtualQuery ((void *) start, &mb, sizeof (mb)))
+ size = 64 * 1024;
+ else
+ {
+ size = mb.RegionSize;
+ if (!(mb.State == MEM_RESERVE && mb.AllocationProtect == PAGE_NOACCESS &&
+ (((void *) start < cygheap->user_heap.base
+ || (void *) start > cygheap->user_heap.top) &&
+ ((void *) start < (void *) cygheap
+ | (void *) start > (void *) ((char *) cygheap + CYGHEAPSIZE)))))
+ continue;
+ if (!VirtualFree ((void *) start, 0, MEM_RELEASE))
+ api_fatal ("couldn't release memory %p(%d) for '%s' alignment, %E\n",
+ start, size, name);
+ }
+}
+
+/* Reload DLLs after a fork. Iterates over the list of dynamically loaded DLLs
+ and attempts to load them in the same place as they were loaded in the parent. */
+void
+dll_list::load_after_fork (HANDLE parent, dll *first)
+{
+ in_forkee = 1;
+ int try2 = 0;
+ dll d;
+
+ void *next = first;
+ while (next)
+ {
+ DWORD nb;
+ /* Read the dll structure from the parent. */
+ if (!ReadProcessMemory (parent, next, &d, sizeof (dll), &nb) ||
+ nb != sizeof (dll))
+ return;
+
+ /* We're only interested in dynamically loaded dlls.
+ Hopefully, this function wouldn't even have been called unless
+ the parent had some of those. */
+ if (d.type == DLL_LOAD)
+ {
+ bool unload = true;
+ HMODULE h = LoadLibraryEx (d.name, NULL, DONT_RESOLVE_DLL_REFERENCES);
+
+ if (!h)
+ system_printf ("can't reload %s", d.name);
+ /* See if DLL will load in proper place. If so, free it and reload
+ it the right way.
+ It sort of stinks that we can't invert the order of the FreeLibrary
+ and LoadLibrary since Microsoft documentation seems to imply that that
+ should do what we want. However, since the library was loaded above,
+ the second LoadLibrary does not execute it's startup code unless it
+ is first unloaded. */
+ else if (h == d.handle)
+ {
+ if (unload)
+ {
+ FreeLibrary (h);
+ LoadLibrary (d.name);
+ }
+ }
+ else if (try2)
+ api_fatal ("unable to remap %s to same address as parent(%p) != %p",
+ d.name, d.handle, h);
+ else
+ {
+ /* It loaded in the wrong place. Dunno why this happens but it always
+ seems to happen when there are multiple DLLs attempting to load into
+ the same address space. In the "forked" process, the second DLL always
+ loads into a different location. */
+ FreeLibrary (h);
+ /* Block all of the memory up to the new load address. */
+ reserve_upto (d.name, (DWORD) d.handle);
+ try2 = 1; /* And try */
+ continue; /* again. */
+ }
+ /* If we reached here, and try2 is set, then there is a lot of memory to
+ release. */
+ if (try2)
+ {
+ release_upto (d.name, (DWORD) d.handle);
+ try2 = 0;
+ }
+ }
+ next = d.next; /* Get the address of the next DLL. */
+ }
+ in_forkee = 0;
+}
+
+extern "C" int
+dll_dllcrt0 (HMODULE h, per_process *p)
+{
+ if (p == NULL)
+ p = &__cygwin_user_data;
+ else
+ *(p->impure_ptr_ptr) = __cygwin_user_data.impure_ptr;
+
+ /* Partially initialize Cygwin guts for non-cygwin apps. */
+ if (dynamically_loaded && user_data->magic_biscuit == 0)
+ dll_crt0 (p);
+ else
+ check_sanity_and_sync (p);
+
+ dll_type type;
+
+ /* If this function is called before cygwin has finished
+ initializing, then the DLL must be a cygwin-aware DLL
+ that was explicitly linked into the program rather than
+ a dlopened DLL. */
+ if (!cygwin_finished_initializing)
+ type = DLL_LINK;
+ else
+ {
+ type = DLL_LOAD;
+ dlls.reload_on_fork = 1;
+ }
+
+ /* Allocate and initialize space for the DLL. */
+ dll *d = dlls.alloc (h, p, type);
+
+ /* If d == NULL, then something is broken.
+ Otherwise, if we've finished initializing, it's ok to
+ initialize the DLL. If we haven't finished initializing,
+ it may not be safe to call the dll's "main" since not
+ all of cygwin's internal structures may have been set up. */
+ if (!d || (cygwin_finished_initializing && !d->init ()))
+ return -1;
+
+ return (DWORD) d;
+}
+
+/* OBSOLETE: This function is obsolescent and will go away in the
+ future. Cygwin can now handle being loaded from a noncygwin app
+ using the same entry point. */
+
+extern "C" int
+dll_noncygwin_dllcrt0 (HMODULE h, per_process *p)
+{
+ return dll_dllcrt0 (h, p);
+}
+
+extern "C" void
+cygwin_detach_dll (dll *)
+{
+ dlls.detach (__builtin_return_address (0));
+}
+
+extern "C" void
+dlfork (int val)
+{
+ dlls.reload_on_fork = val;
+}
+
+/* Called from various places to update all of the individual
+ ideas of the environ block. Explain to me again why we didn't
+ just import __cygwin_environ? */
+void __stdcall
+update_envptrs ()
+{
+ extern char ***main_environ;
+ for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ())
+ {
+ *(d->p.envptr) = __cygwin_environ;
+ }
+ *main_environ = __cygwin_environ;
+}
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
new file mode 100644
index 00000000000..46e91e6f895
--- /dev/null
+++ b/winsup/cygwin/dtable.cc
@@ -0,0 +1,893 @@
+/* dtable.cc: file descriptor support.
+
+ Copyright 1996, 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. */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <ntdef.h>
+#include <winnls.h>
+
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "pinfo.h"
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+#include "tty.h"
+
+static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
+ STD_ERROR_HANDLE};
+
+static const char *handle_to_fn (HANDLE, char *);
+
+static const char NO_COPY unknown_file[] = "some disk file";
+
+/* Set aside space for the table of fds */
+void
+dtable_init ()
+{
+ if (!cygheap->fdtab.size)
+ cygheap->fdtab.extend (NOFILE_INCR);
+ cygheap->fdtab.init_lock ();
+
+}
+
+void __stdcall
+set_std_handle (int fd)
+{
+ if (fd == 0)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_handle ());
+ else if (fd <= 2)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_output_handle ());
+}
+
+int
+dtable::extend (int howmuch)
+{
+ int new_size = size + howmuch;
+ fhandler_base **newfds;
+
+ if (howmuch <= 0)
+ return 0;
+
+ if (new_size > (100 * NOFILE_INCR))
+ {
+ set_errno (EMFILE);
+ return 0;
+ }
+
+ /* Try to allocate more space for fd table. We can't call realloc ()
+ here to preserve old table if memory allocation fails */
+
+ if (!(newfds = (fhandler_base **) ccalloc (HEAP_ARGV, new_size, sizeof newfds[0])))
+ {
+ debug_printf ("calloc failed");
+ set_errno (ENOMEM);
+ return 0;
+ }
+ if (fds)
+ {
+ cfree (fds);
+ memcpy (newfds, fds, size * sizeof (fds[0]));
+ }
+
+ size = new_size;
+ fds = newfds;
+ debug_printf ("size %d, fds %p", size, fds);
+ return 1;
+}
+
+void
+dtable::get_debugger_info ()
+{
+ if (being_debugged ())
+ {
+ char std[3][sizeof ("/dev/ttyNNNN")];
+ std[0][0] = std[1][0] = std [2][0] = '\0';
+ char buf[sizeof ("cYgstd %x") + 32];
+ sprintf (buf, "cYgstd %x %x %x", (unsigned) &std, sizeof (std[0]), 3);
+ OutputDebugString (buf);
+ for (int i = 0; i < 3; i++)
+ if (std[i][0])
+ {
+ HANDLE h = GetStdHandle (std_consts[i]);
+ fhandler_base *fh = build_fh_name (std[i]);
+ if (!fh)
+ continue;
+ fds[i] = fh;
+ if (!fh->open ((i ? O_WRONLY : O_RDONLY) | O_BINARY, 0777))
+ release (i);
+ else
+ CloseHandle (h);
+ }
+ }
+}
+
+/* Initialize the file descriptor/handle mapping table.
+ This function should only be called when a cygwin function is invoked
+ by a non-cygwin function, i.e., it should only happen very rarely. */
+
+void
+dtable::stdio_init ()
+{
+ extern void set_console_ctty ();
+ /* Set these before trying to output anything from strace.
+ Also, always set them even if we're to pick up our parent's fds
+ in case they're missed. */
+
+ if (myself->ppid_handle || ISSTATE (myself, PID_CYGPARENT))
+ return;
+
+ HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
+ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
+ HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
+
+ init_std_file_from_handle (0, in);
+
+ /* STD_ERROR_HANDLE has been observed to be the same as
+ STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes
+ to pass data from child to parent). */
+ if (out == err)
+ {
+ /* Since this code is not invoked for forked tasks, we don't have
+ to worry about the close-on-exec flag here. */
+ if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0,
+ 1, DUPLICATE_SAME_ACCESS))
+ {
+ /* If that fails, do this as a fall back. */
+ err = out;
+ system_printf ("couldn't make stderr distinct from stdout");
+ }
+ }
+
+ init_std_file_from_handle (1, out);
+ init_std_file_from_handle (2, err);
+ /* Assign the console as the controlling tty for this process if we actually
+ have a console and no other controlling tty has been assigned. */
+ if (myself->ctty < 0 && GetConsoleCP () > 0)
+ set_console_ctty ();
+}
+
+const int dtable::initial_archetype_size;
+
+fhandler_base *
+dtable::find_archetype (device& dev)
+{
+ for (unsigned i = 0; i < farchetype; i++)
+ if (archetypes[i]->get_device () == (unsigned) dev)
+ return archetypes[i];
+ return NULL;
+}
+
+fhandler_base **
+dtable::add_archetype ()
+{
+ if (farchetype++ >= narchetypes)
+ archetypes = (fhandler_base **) crealloc (archetypes, (narchetypes += initial_archetype_size) * sizeof archetypes[0]);
+ return archetypes + farchetype - 1;
+}
+
+void
+dtable::delete_archetype (fhandler_base *fh)
+{
+ for (unsigned i = 0; i < farchetype; i++)
+ if (fh == archetypes[i])
+ {
+ debug_printf ("deleting element %d for %s", i, fh->get_name ());
+ if (i < --farchetype)
+ archetypes[i] = archetypes[farchetype];
+ break;
+ }
+
+ delete fh;
+}
+
+int
+dtable::find_unused_handle (int start)
+{
+ do
+ {
+ for (size_t i = start; i < size; i++)
+ /* See if open -- no need for overhead of not_open */
+ if (fds[i] == NULL)
+ return i;
+ }
+ while (extend (NOFILE_INCR));
+ return -1;
+}
+
+void
+dtable::release (int fd)
+{
+ if (!not_open (fd))
+ {
+ if (fds[fd]->need_fixup_before ())
+ dec_need_fixup_before ();
+ fhandler_base *arch = fds[fd]->archetype;
+ delete fds[fd];
+ if (arch && !arch->usecount)
+ cygheap->fdtab.delete_archetype (arch);
+ fds[fd] = NULL;
+ }
+}
+
+extern "C" int
+cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin,
+ DWORD myaccess)
+{
+ if (fd == -1)
+ fd = cygheap->fdtab.find_unused_handle ();
+ fhandler_base *fh = build_fh_name (name);
+ cygheap->fdtab[fd] = fh;
+ fh->init (handle, myaccess, bin ?: fh->pc_binmode ());
+ return fd;
+}
+
+void
+dtable::init_std_file_from_handle (int fd, HANDLE handle)
+{
+ const char *name = NULL;
+ CONSOLE_SCREEN_BUFFER_INFO buf;
+ struct sockaddr sa;
+ int sal = sizeof (sa);
+ DCB dcb;
+ unsigned bin = O_BINARY;
+ device dev;
+
+ dev.devn = 0; /* FIXME: device */
+ first_fd_for_open = 0;
+
+ if (!not_open (fd))
+ return;
+
+ SetLastError (0);
+ DWORD ft = GetFileType (handle);
+ if (ft != FILE_TYPE_UNKNOWN || GetLastError () != ERROR_INVALID_HANDLE)
+ {
+ /* See if we can consoleify it */
+ if (GetConsoleScreenBufferInfo (handle, &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (ft == FILE_TYPE_PIPE)
+ {
+ if (fd == 0)
+ dev = *piper_dev;
+ else
+ dev = *pipew_dev;
+ }
+ else if (wsock_started && getpeername ((SOCKET) handle, &sa, &sal) == 0)
+ dev = *tcp_dev;
+ else if (GetCommState (handle, &dcb))
+ dev.parse ("/dev/ttyS0");
+ else
+ {
+ name = handle_to_fn (handle, (char *) alloca (CYG_MAX_PATH + 100));
+ bin = 0;
+ }
+ }
+
+ if (!name && !dev)
+ fds[fd] = NULL;
+ else
+ {
+ fhandler_base *fh;
+
+ if (dev)
+ fh = build_fh_dev (dev);
+ else
+ fh = build_fh_name (name);
+
+ if (fh)
+ cygheap->fdtab[fd] = fh;
+
+ if (!bin)
+ {
+ bin = fh->get_default_fmode (O_RDWR);
+ if (bin)
+ /* nothing */;
+ else if (dev)
+ bin = O_BINARY;
+ else if (name != unknown_file)
+ bin = fh->pc_binmode ();
+ }
+
+ fh->init (handle, GENERIC_READ | GENERIC_WRITE, bin);
+ set_std_handle (fd);
+ paranoid_printf ("fd %d, handle %p", fd, handle);
+ }
+}
+
+#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name
+fhandler_base *
+build_fh_name (const char *name, HANDLE h, unsigned opt, suffix_info *si)
+{
+ path_conv pc (name, opt | PC_NULLEMPTY | PC_FULL | PC_POSIX, si);
+ if (pc.error)
+ {
+ fhandler_base *fh = cnew (fhandler_nodevice) ();
+ fh->set_error (pc.error);
+ set_errno (pc.error);
+ return fh;
+ }
+
+ if (!pc.exists () && h)
+ pc.fillin (h);
+
+ return build_fh_pc (pc);
+}
+
+fhandler_base *
+build_fh_dev (const device& dev, const char *unix_name)
+{
+ path_conv pc (dev);
+ if (unix_name)
+ pc.set_normalized_path (unix_name);
+ else
+ pc.set_normalized_path (dev.name);
+ return build_fh_pc (pc);
+}
+
+fhandler_base *
+build_fh_pc (path_conv& pc)
+{
+ fhandler_base *fh = NULL;
+
+ switch (pc.dev.major)
+ {
+ case DEV_TTYS_MAJOR:
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ case DEV_TTYM_MAJOR:
+ fh = cnew (fhandler_tty_master) ();
+ break;
+ case DEV_CYGDRIVE_MAJOR:
+ fh = cnew (fhandler_cygdrive) ();
+ break;
+ case DEV_FLOPPY_MAJOR:
+ case DEV_CDROM_MAJOR:
+ case DEV_SD_MAJOR:
+ fh = cnew (fhandler_dev_floppy) ();
+ break;
+ case DEV_TAPE_MAJOR:
+ fh = cnew (fhandler_dev_tape) ();
+ break;
+ case DEV_SERIAL_MAJOR:
+ fh = cnew (fhandler_serial) ();
+ break;
+ default:
+ switch (pc.dev)
+ {
+ case FH_CONSOLE:
+ case FH_CONIN:
+ case FH_CONOUT:
+ fh = cnew (fhandler_console) ();
+ break;
+ case FH_PTYM:
+ fh = cnew (fhandler_pty_master) ();
+ break;
+ case FH_WINDOWS:
+ fh = cnew (fhandler_windows) ();
+ break;
+ case FH_FIFO:
+ fh = cnew (fhandler_fifo) ();
+ break;
+ case FH_PIPE:
+ case FH_PIPER:
+ case FH_PIPEW:
+ fh = cnew (fhandler_pipe) ();
+ break;
+ case FH_TCP:
+ case FH_UDP:
+ case FH_ICMP:
+ case FH_UNIX:
+ case FH_STREAM:
+ case FH_DGRAM:
+ fh = cnew (fhandler_socket) ();
+ break;
+ case FH_FS:
+ fh = cnew (fhandler_disk_file) ();
+ break;
+ case FH_NULL:
+ fh = cnew (fhandler_dev_null) ();
+ break;
+ case FH_ZERO:
+ fh = cnew (fhandler_dev_zero) ();
+ break;
+ case FH_RANDOM:
+ case FH_URANDOM:
+ fh = cnew (fhandler_dev_random) ();
+ break;
+ case FH_MEM:
+ case FH_PORT:
+ fh = cnew (fhandler_dev_mem) ();
+ break;
+ case FH_CLIPBOARD:
+ fh = cnew (fhandler_dev_clipboard) ();
+ break;
+ case FH_OSS_DSP:
+ fh = cnew (fhandler_dev_dsp) ();
+ break;
+ case FH_PROC:
+ fh = cnew (fhandler_proc) ();
+ break;
+ case FH_REGISTRY:
+ fh = cnew (fhandler_registry) ();
+ break;
+ case FH_PROCESS:
+ fh = cnew (fhandler_process) ();
+ break;
+ case FH_TTY:
+ {
+ if (myself->ctty == TTY_CONSOLE)
+ fh = cnew (fhandler_console) ();
+ else if (myself->ctty >= 0)
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ }
+ }
+ }
+
+ if (!fh)
+ fh = cnew (fhandler_nodevice) ();
+
+ fh->set_name (pc);
+
+ debug_printf ("fh %p", fh);
+ return fh;
+}
+
+fhandler_base *
+dtable::dup_worker (fhandler_base *oldfh)
+{
+ fhandler_base *newfh = build_fh_pc (oldfh->pc);
+ *newfh = *oldfh;
+ newfh->set_io_handle (NULL);
+ if (oldfh->dup (newfh))
+ {
+ cfree (newfh);
+ newfh = NULL;
+ return NULL;
+ }
+
+ newfh->set_close_on_exec_flag (0);
+ MALLOC_CHECK;
+ debug_printf ("duped '%s' old %p, new %p", oldfh->get_name (), oldfh->get_io_handle (), newfh->get_io_handle ());
+ return newfh;
+}
+
+int
+dtable::dup2 (int oldfd, int newfd)
+{
+ int res = -1;
+ fhandler_base *newfh = NULL; // = NULL to avoid an incorrect warning
+
+ MALLOC_CHECK;
+ debug_printf ("dup2 (%d, %d)", oldfd, newfd);
+ lock ();
+
+ if (not_open (oldfd))
+ {
+ syscall_printf ("fd %d not open", oldfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd < 0)
+ {
+ syscall_printf ("new fd out of bounds: %d", newfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd == oldfd)
+ {
+ res = 0;
+ goto done;
+ }
+
+ if ((newfh = dup_worker (fds[oldfd])) == NULL)
+ {
+ res = -1;
+ goto done;
+ }
+
+ debug_printf ("newfh->io_handle %p, oldfh->io_handle %p",
+ newfh->get_io_handle (), fds[oldfd]->get_io_handle ());
+
+ if (!not_open (newfd))
+ close (newfd);
+ else if ((size_t) newfd < size)
+ /* nothing to do */;
+ else if (find_unused_handle (newfd) < 0)
+ {
+ newfh->close ();
+ res = -1;
+ goto done;
+ }
+
+ fds[newfd] = newfh;
+
+ if ((res = newfd) <= 2)
+ set_std_handle (res);
+
+done:
+ MALLOC_CHECK;
+ unlock ();
+ syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd);
+
+ return res;
+}
+
+fhandler_fifo *
+dtable::find_fifo (ATOM hill)
+{
+ lock ();
+ for (unsigned i = 0; i < size; i++)
+ {
+ fhandler_base *fh = fds[i];
+ if (fh && fh->isfifo () && ((fhandler_fifo *) fh)->get_atom () == hill)
+ return (fhandler_fifo *) fh;
+ }
+ return NULL;
+}
+
+select_record *
+dtable::select_read (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_read (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_write (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_write (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_except (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_except (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+/* Function to walk the fd table after an exec and perform
+ per-fhandler type fixups. */
+void
+dtable::fixup_before_fork (DWORD target_proc_id)
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ unlock ();
+}
+
+void
+dtable::fixup_before_exec (DWORD target_proc_id)
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && !fh->get_close_on_exec ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ unlock ();
+}
+
+void
+dtable::set_file_pointers_for_exec ()
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && fh->get_flags () & O_APPEND)
+ SetFilePointer (fh->get_handle (), 0, 0, FILE_END);
+ unlock ();
+}
+
+void
+dtable::fixup_after_exec (HANDLE parent)
+{
+ first_fd_for_open = 0;
+ fhandler_base *fh;
+ cygheap->fdtab.init_lock ();
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (fh->get_close_on_exec ())
+ {
+ if (fh->archetype)
+ fh->close ();
+ release (i);
+ }
+ else
+ {
+ fh->fixup_after_exec (parent);
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+ }
+}
+
+void
+dtable::fixup_after_fork (HANDLE parent)
+{
+ fhandler_base *fh;
+ cygheap->fdtab.init_lock ();
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ if (fh->get_close_on_exec () || fh->get_need_fork_fixup ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_after_fork (parent);
+ }
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+}
+
+int
+dtable::vfork_child_dup ()
+{
+ fhandler_base **newtable;
+ lock ();
+ newtable = (fhandler_base **) ccalloc (HEAP_ARGV, size, sizeof (fds[0]));
+ int res = 1;
+
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+ if (cygheap->ctty)
+ {
+ cygheap->ctty->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (cygheap->ctty, "vfork dup", "incremented ", "");
+ }
+
+ for (size_t i = 0; i < size; i++)
+ if (not_open (i))
+ continue;
+ else if ((newtable[i] = dup_worker (fds[i])) != NULL)
+ newtable[i]->set_close_on_exec (fds[i]->get_close_on_exec ());
+ else
+ {
+ res = 0;
+ set_errno (EBADF);
+ goto out;
+ }
+
+ fds_on_hold = fds;
+ fds = newtable;
+
+out:
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+
+ unlock ();
+ return 1;
+}
+
+void
+dtable::vfork_parent_restore ()
+{
+ lock ();
+
+ close_all_files ();
+ fhandler_base **deleteme = fds;
+ fds = fds_on_hold;
+ fds_on_hold = NULL;
+ cfree (deleteme);
+ unlock ();
+
+ cygheap->ctty = cygheap->ctty_on_hold; // revert
+ if (cygheap->ctty)
+ cygheap->ctty->close (); // Undo previous bump of this archetype
+ cygheap->ctty_on_hold = NULL;
+
+ return;
+}
+
+void
+dtable::vfork_child_fixup ()
+{
+ if (!fds_on_hold)
+ return;
+ debug_printf ("here");
+ fhandler_base **saveme = fds;
+ fds = fds_on_hold;
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (!fh->archetype && fh->get_close_on_exec ())
+ release (i);
+ else
+ {
+ fh->close ();
+ release (i);
+ }
+ }
+
+ fds = saveme;
+ cfree (fds_on_hold);
+ fds_on_hold = NULL;
+
+ if (cygheap->ctty_on_hold)
+ {
+ cygheap->ctty_on_hold->close ();
+ cygheap->ctty_on_hold = NULL;
+ }
+
+ return;
+}
+
+#define DEVICE_PREFIX "\\device\\"
+#define DEVICE_PREFIX_LEN sizeof (DEVICE_PREFIX) - 1
+#define REMOTE "\\Device\\LanmanRedirector\\"
+#define REMOTE_LEN sizeof (REMOTE) - 1
+
+static const char *
+handle_to_fn (HANDLE h, char *posix_fn)
+{
+ OBJECT_NAME_INFORMATION *ntfn;
+ char fnbuf[32768];
+
+ memset (fnbuf, 0, sizeof (fnbuf));
+ ntfn = (OBJECT_NAME_INFORMATION *) fnbuf;
+ ntfn->Name.MaximumLength = sizeof (fnbuf) - sizeof (*ntfn);
+ ntfn->Name.Buffer = (WCHAR *) (ntfn + 1);
+
+ DWORD res = NtQueryObject (h, ObjectNameInformation, ntfn, sizeof (fnbuf), NULL);
+
+ if (res)
+ {
+ strcpy (posix_fn, unknown_file);
+ debug_printf ("NtQueryObject failed");
+ return unknown_file;
+ }
+
+ // NT seems to do this on an unopened file
+ if (!ntfn->Name.Buffer)
+ {
+ debug_printf ("nt->Name.Buffer == NULL");
+ return NULL;
+ }
+
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = 0;
+
+ char win32_fn[CYG_MAX_PATH + 100];
+ sys_wcstombs (win32_fn, ntfn->Name.Buffer, ntfn->Name.Length);
+ debug_printf ("nt name '%s'", win32_fn);
+ if (!strncasematch (win32_fn, DEVICE_PREFIX, DEVICE_PREFIX_LEN)
+ || !QueryDosDevice (NULL, fnbuf, sizeof (fnbuf)))
+ return strcpy (posix_fn, win32_fn);
+
+ char *p = strechr (win32_fn + DEVICE_PREFIX_LEN, '\\');
+
+ int n = p - win32_fn;
+ int maxmatchlen = 0;
+ char *maxmatchdos = NULL;
+ for (char *s = fnbuf; *s; s = strchr (s, '\0') + 1)
+ {
+ char device[CYG_MAX_PATH + 10];
+ device[CYG_MAX_PATH + 9] = '\0';
+ if (strchr (s, ':') == NULL)
+ continue;
+ if (!QueryDosDevice (s, device, sizeof (device) - 1))
+ continue;
+ char *q = strrchr (device, ';');
+ if (q)
+ {
+ char *r = strchr (q, '\\');
+ if (r)
+ strcpy (q, r + 1);
+ }
+ int devlen = strlen (device);
+ if (device[devlen - 1] == '\\')
+ device[--devlen] = '\0';
+ if (devlen < maxmatchlen)
+ continue;
+ if (!strncasematch (device, win32_fn, devlen) ||
+ (win32_fn[devlen] != '\0' && win32_fn[devlen] != '\\'))
+ continue;
+ maxmatchlen = devlen;
+ maxmatchdos = s;
+ debug_printf ("current match '%s'", device);
+ }
+
+ char *w32 = win32_fn;
+ if (maxmatchlen)
+ {
+ n = strlen (maxmatchdos);
+ if (maxmatchdos[n - 1] == '\\')
+ n--;
+ w32 += maxmatchlen - n;
+ memcpy (w32, maxmatchdos, n);
+ w32[n] = '\\';
+ }
+ else if (strncasematch (w32, REMOTE, REMOTE_LEN))
+ {
+ w32 += REMOTE_LEN - 2;
+ *w32 = '\\';
+ debug_printf ("remote drive");
+ }
+
+
+ debug_printf ("derived path '%s'", w32);
+ cygwin_conv_to_full_posix_path (w32, posix_fn);
+ return posix_fn;
+}
diff --git a/winsup/cygwin/dtable.h b/winsup/cygwin/dtable.h
new file mode 100644
index 00000000000..19de60e6377
--- /dev/null
+++ b/winsup/cygwin/dtable.h
@@ -0,0 +1,97 @@
+/* dtable.h: fd table definition.
+
+ Copyright 2000, 2001, 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. */
+
+/* Initial and increment values for cygwin's fd table */
+#define NOFILE_INCR 32
+
+#include "thread.h"
+
+class suffix_info;
+class fhandler_fifo;
+
+#define BFH_OPTS (PC_NULLEMPTY | PC_FULL | PC_POSIX)
+class dtable
+{
+ CRITICAL_SECTION lock_cs;
+ fhandler_base **fds;
+ fhandler_base **fds_on_hold;
+ fhandler_base **archetypes;
+ unsigned narchetypes;
+ unsigned farchetype;
+ static const int initial_archetype_size = 8;
+ int first_fd_for_open;
+ int cnt_need_fixup_before;
+ void lock () {/*EnterCriticalSection (&lock_cs); */spf ("%u locked, tid %u\n", GetCurrentProcessId (), GetCurrentThreadId ());}
+ void unlock () {/*LeaveCriticalSection (&lock_cs);*/ spf ("%u ulocked, tid %u\n", GetCurrentProcessId (), GetCurrentThreadId ());}
+ void init_lock () {/*InitializeCriticalSection (&lock_cs); */spf ("%u initialized, tid %u\n", GetCurrentProcessId (), GetCurrentThreadId ());}
+public:
+ size_t size;
+
+ dtable () : archetypes (NULL), narchetypes (0), farchetype (0), first_fd_for_open(3), cnt_need_fixup_before(0) {}
+ void init () {first_fd_for_open = 3;}
+
+ void dec_need_fixup_before ()
+ { if (cnt_need_fixup_before > 0) --cnt_need_fixup_before; }
+ void inc_need_fixup_before ()
+ { cnt_need_fixup_before++; }
+ bool need_fixup_before ()
+ { return cnt_need_fixup_before > 0; }
+
+ int vfork_child_dup ();
+ void vfork_parent_restore ();
+ void vfork_child_fixup ();
+ fhandler_base *dup_worker (fhandler_base *oldfh);
+ int extend (int howmuch);
+ void fixup_before_exec (DWORD win_proc_id);
+ void fixup_before_fork (DWORD win_proc_id);
+ void fixup_after_fork (HANDLE);
+ inline int not_open (int fd)
+ {
+ lock ();
+ int res = fd < 0 || fd >= (int) size || fds[fd] == NULL;
+ unlock ();
+ return res;
+ }
+ int find_unused_handle (int start);
+ int find_unused_handle () { return find_unused_handle (first_fd_for_open);}
+ void release (int fd);
+ void init_std_file_from_handle (int fd, HANDLE handle);
+ int dup2 (int oldfd, int newfd);
+ void fixup_after_exec (HANDLE);
+ inline fhandler_base *&operator [](int fd) const { return fds[fd]; }
+ select_record *select_read (int fd, select_record *s);
+ select_record *select_write (int fd, select_record *s);
+ select_record *select_except (int fd, select_record *s);
+ operator fhandler_base **() {return fds;}
+ void stdio_init ();
+ void get_debugger_info ();
+ void set_file_pointers_for_exec ();
+ bool in_vfork_cleanup () {return fds_on_hold == fds;}
+ fhandler_fifo *find_fifo (ATOM);
+ fhandler_base *find_archetype (device& dev);
+ fhandler_base **add_archetype ();
+ void delete_archetype (fhandler_base *);
+ friend void dtable_init ();
+ friend void __stdcall close_all_files ();
+ friend class cygheap_fdmanip;
+ friend class cygheap_fdget;
+ friend class cygheap_fdnew;
+};
+
+fhandler_base *build_fh_dev (const device&, const char * = NULL);
+fhandler_base *build_fh_name (const char *unix_name, HANDLE = NULL, unsigned = 0, suffix_info * = NULL);
+fhandler_base *build_fh_pc (path_conv& pc);
+
+void dtable_init (void);
+void stdio_init (void);
+extern dtable fdtab;
+
+extern "C" int getfdtabsize ();
+extern "C" void setfdtabsize (int);
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
new file mode 100644
index 00000000000..dab839f6ab9
--- /dev/null
+++ b/winsup/cygwin/exceptions.cc
@@ -0,0 +1,1133 @@
+/* exceptions.cc
+
+ Copyright 1996, 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 "winsup.h"
+#include <imagehlp.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <assert.h>
+
+#include "exceptions.h"
+#include "sync.h"
+#include "pinfo.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "cygerrno.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "shared_info.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygthread.h"
+
+#define CALL_HANDLER_RETRY 20
+
+char debugger_command[2 * CYG_MAX_PATH + 20];
+
+extern "C" {
+static int handle_exceptions (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
+extern void sigdelayed ();
+};
+
+extern DWORD sigtid;
+
+extern HANDLE hExeced;
+extern DWORD dwExeced;
+
+static BOOL WINAPI ctrl_c_handler (DWORD);
+static void signal_exit (int) __attribute__ ((noreturn));
+static char windows_system_directory[1024];
+static size_t windows_system_directory_length;
+
+/* This is set to indicate that we have already exited. */
+
+static NO_COPY int exit_already = 0;
+static NO_COPY muto *mask_sync = NULL;
+
+NO_COPY static struct
+{
+ unsigned int code;
+ const char *name;
+} status_info[] =
+{
+#define X(s) s, #s
+ { X (STATUS_ABANDONED_WAIT_0) },
+ { X (STATUS_ACCESS_VIOLATION) },
+ { X (STATUS_ARRAY_BOUNDS_EXCEEDED) },
+ { X (STATUS_BREAKPOINT) },
+ { X (STATUS_CONTROL_C_EXIT) },
+ { X (STATUS_DATATYPE_MISALIGNMENT) },
+ { X (STATUS_FLOAT_DENORMAL_OPERAND) },
+ { X (STATUS_FLOAT_DIVIDE_BY_ZERO) },
+ { X (STATUS_FLOAT_INEXACT_RESULT) },
+ { X (STATUS_FLOAT_INVALID_OPERATION) },
+ { X (STATUS_FLOAT_OVERFLOW) },
+ { X (STATUS_FLOAT_STACK_CHECK) },
+ { X (STATUS_FLOAT_UNDERFLOW) },
+ { X (STATUS_GUARD_PAGE_VIOLATION) },
+ { X (STATUS_ILLEGAL_INSTRUCTION) },
+ { X (STATUS_INTEGER_DIVIDE_BY_ZERO) },
+ { X (STATUS_INTEGER_OVERFLOW) },
+ { X (STATUS_INVALID_DISPOSITION) },
+ { X (STATUS_IN_PAGE_ERROR) },
+ { X (STATUS_NONCONTINUABLE_EXCEPTION) },
+ { X (STATUS_NO_MEMORY) },
+ { X (STATUS_PENDING) },
+ { X (STATUS_PRIVILEGED_INSTRUCTION) },
+ { X (STATUS_SINGLE_STEP) },
+ { X (STATUS_STACK_OVERFLOW) },
+ { X (STATUS_TIMEOUT) },
+ { X (STATUS_USER_APC) },
+ { X (STATUS_WAIT_0) },
+ { 0, 0 }
+#undef X
+};
+
+/* Initialization code. */
+
+// Set up the exception handler for the current thread. The PowerPC & Mips
+// use compiler generated tables to set up the exception handlers for each
+// region of code, and the kernel walks the call list until it finds a region
+// of code that handles exceptions. The x86 on the other hand uses segment
+// register fs, offset 0 to point to the current exception handler.
+
+extern exception_list *_except_list asm ("%fs:0");
+
+void
+init_exception_handler (exception_list *el, exception_handler *eh)
+{
+ el->handler = eh;
+ el->prev = _except_list;
+ _except_list = el;
+}
+
+extern "C" void
+init_exceptions (exception_list *el)
+{
+ init_exception_handler (el, handle_exceptions);
+}
+
+void
+init_console_handler ()
+{
+ (void) SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
+ if (!SetConsoleCtrlHandler (ctrl_c_handler, TRUE))
+ system_printf ("SetConsoleCtrlHandler failed, %E");
+}
+
+extern "C" void
+error_start_init (const char *buf)
+{
+ if (!buf || !*buf)
+ {
+ debugger_command[0] = '\0';
+ return;
+ }
+
+ char pgm[CYG_MAX_PATH + 1];
+ if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH))
+ strcpy (pgm, "cygwin1.dll");
+ for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\'))
+ *p = '/';
+
+ __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm);
+}
+
+static void
+open_stackdumpfile ()
+{
+ if (myself->progname[0])
+ {
+ const char *p;
+ /* write to progname.stackdump if possible */
+ if (!myself->progname[0])
+ p = "unknown";
+ else if ((p = strrchr (myself->progname, '\\')))
+ p++;
+ else
+ p = myself->progname;
+ char corefile[strlen (p) + sizeof (".stackdump")];
+ __small_sprintf (corefile, "%s.stackdump", p);
+ HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
+ CREATE_ALWAYS, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (!myself->ppid_handle)
+ system_printf ("Dumping stack trace to %s", corefile);
+ else
+ debug_printf ("Dumping stack trace to %s", corefile);
+ SetStdHandle (STD_ERROR_HANDLE, h);
+ }
+ }
+}
+
+/* Utilities for dumping the stack, etc. */
+
+static void
+exception (EXCEPTION_RECORD *e, CONTEXT *in)
+{
+ const char *exception_name = NULL;
+
+ if (e)
+ {
+ for (int i = 0; status_info[i].name; i++)
+ {
+ if (status_info[i].code == e->ExceptionCode)
+ {
+ exception_name = status_info[i].name;
+ break;
+ }
+ }
+ }
+
+#ifdef __i386__
+#define HAVE_STATUS
+ if (exception_name)
+ small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip);
+ else
+ small_printf ("Exception %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
+ small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
+ in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
+ small_printf ("ebp=%08x esp=%08x program=%s\r\n",
+ in->Ebp, in->Esp, myself->progname);
+ small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
+ in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs);
+#endif
+
+#ifndef HAVE_STATUS
+ system_printf ("Had an exception");
+#endif
+}
+
+#ifdef __i386__
+/* Print a stack backtrace. */
+
+#define HAVE_STACK_TRACE
+
+/* A class for manipulating the stack. */
+class stack_info
+{
+ int walk (); /* Uses the "old" method */
+ char *next_offset () {return *((char **) sf.AddrFrame.Offset);}
+ bool needargs;
+ DWORD dummy_frame;
+public:
+ STACKFRAME sf; /* For storing the stack information */
+ void init (DWORD, bool, bool); /* Called the first time that stack info is needed */
+
+ /* Postfix ++ iterates over the stack, returning zero when nothing is left. */
+ int operator ++(int) { return walk (); }
+};
+
+/* The number of parameters used in STACKFRAME */
+#define NPARAMS (sizeof (thestack.sf.Params) / sizeof (thestack.sf.Params[0]))
+
+/* This is the main stack frame info for this process. */
+static NO_COPY stack_info thestack;
+
+/* Initialize everything needed to start iterating. */
+void
+stack_info::init (DWORD ebp, bool wantargs, bool goodframe)
+{
+# define debp ((DWORD *) ebp)
+ memset (&sf, 0, sizeof (sf));
+ if (!goodframe)
+ sf.AddrFrame.Offset = ebp;
+ else
+ {
+ dummy_frame = ebp;
+ sf.AddrFrame.Offset = (DWORD) &dummy_frame;
+ }
+ sf.AddrReturn.Offset = debp[1];
+ sf.AddrFrame.Mode = AddrModeFlat;
+ needargs = wantargs;
+# undef debp
+}
+
+/* Walk the stack by looking at successive stored 'bp' frames.
+ This is not foolproof. */
+int
+stack_info::walk ()
+{
+ char **ebp;
+ if ((ebp = (char **) next_offset ()) == NULL)
+ return 0;
+
+ sf.AddrFrame.Offset = (DWORD) ebp;
+ sf.AddrPC.Offset = sf.AddrReturn.Offset;
+
+ if (!sf.AddrPC.Offset)
+ return 0; /* stack frames are exhausted */
+
+ /* The return address always follows the stack pointer */
+ sf.AddrReturn.Offset = (DWORD) *++ebp;
+
+ if (needargs)
+ /* The arguments follow the return address */
+ for (unsigned i = 0; i < NPARAMS; i++)
+ sf.Params[i] = (DWORD) *++ebp;
+
+ return 1;
+}
+
+static void
+stackdump (DWORD ebp, int open_file, bool isexception)
+{
+ extern unsigned long rlim_core;
+
+ if (rlim_core == 0UL)
+ return;
+
+ if (open_file)
+ open_stackdumpfile ();
+
+ int i;
+
+ thestack.init (ebp, 1, !isexception); /* Initialize from the input CONTEXT */
+ small_printf ("Stack trace:\r\nFrame Function Args\r\n");
+ for (i = 0; i < 16 && thestack++; i++)
+ {
+ small_printf ("%08x %08x ", thestack.sf.AddrFrame.Offset,
+ thestack.sf.AddrPC.Offset);
+ for (unsigned j = 0; j < NPARAMS; j++)
+ small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]);
+ small_printf (")\r\n");
+ }
+ small_printf ("End of stack trace%s",
+ i == 16 ? " (more stack frames may be present)" : "");
+}
+
+/* Temporary (?) function for external callers to get a stack dump */
+extern "C" void
+cygwin_stackdump ()
+{
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (GetCurrentThread (), &c);
+ stackdump (c.Ebp, 0, 0);
+}
+
+#define TIME_TO_WAIT_FOR_DEBUGGER 10000
+
+int keep_looping = 1;
+
+extern "C" int
+try_to_debug (bool waitloop)
+{
+ debug_printf ("debugger_command '%s'", debugger_command);
+ if (*debugger_command == '\0' || being_debugged ())
+ return 0;
+
+ __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ());
+
+ LONG prio = GetThreadPriority (GetCurrentThread ());
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
+ PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
+
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = 0;
+ si.cb = sizeof (si);
+
+ /* FIXME: need to know handles of all running threads to
+ suspend_all_threads_except (current_thread_id);
+ */
+
+ /* if any of these mutexes is owned, we will fail to start any cygwin app
+ until trapped app exits */
+
+ ReleaseMutex (title_mutex);
+
+ /* prevent recursive exception handling */
+ char* rawenv = GetEnvironmentStrings () ;
+ for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1)
+ {
+ if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0)
+ {
+ char* q = strstr (p, "error_start") ;
+ /* replace 'error_start=...' with '_rror_start=...' */
+ if (q)
+ {
+ *q = '_' ;
+ SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ;
+ }
+ break ;
+ }
+ }
+
+ small_printf ("*** starting debugger for pid %u\n",
+ cygwin_pid (GetCurrentProcessId ()));
+ BOOL dbg;
+ dbg = CreateProcess (NULL,
+ debugger_command,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL,
+ NULL,
+ &si,
+ &pi);
+
+ if (!dbg)
+ system_printf ("Failed to start debugger: %E");
+ else
+ {
+ if (!waitloop)
+ return 1;
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
+ while (!being_debugged ())
+ low_priority_sleep (0);
+ small_printf ("*** continuing pid %u from debugger call\n",
+ cygwin_pid (GetCurrentProcessId ()));
+ }
+
+ SetThreadPriority (GetCurrentThread (), prio);
+ return 0;
+}
+
+/* Main exception handler. */
+
+extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
+static int
+handle_exceptions (EXCEPTION_RECORD *e0, void *frame, CONTEXT *in0, void *)
+{
+ static bool NO_COPY debugging = false;
+ static int NO_COPY recursed = 0;
+
+ if (debugging && ++debugging < 500000)
+ {
+ SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
+ return 0;
+ }
+
+ /* If we've already exited, don't do anything here. Returning 1
+ tells Windows to keep looking for an exception handler. */
+ if (exit_already)
+ return 1;
+
+ EXCEPTION_RECORD e = *e0;
+ CONTEXT in = *in0;
+
+ extern DWORD ret_here[];
+ RtlUnwind (frame, ret_here, e0, 0);
+ __asm__ volatile (".equ _ret_here,.");
+
+ int sig;
+ /* Coerce win32 value to posix value. */
+ switch (e.ExceptionCode)
+ {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INEXACT_RESULT:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_OVERFLOW:
+ case STATUS_FLOAT_STACK_CHECK:
+ case STATUS_FLOAT_UNDERFLOW:
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ case STATUS_INTEGER_OVERFLOW:
+ sig = SIGFPE;
+ break;
+
+ case STATUS_ILLEGAL_INSTRUCTION:
+ case STATUS_PRIVILEGED_INSTRUCTION:
+ case STATUS_NONCONTINUABLE_EXCEPTION:
+ sig = SIGILL;
+ break;
+
+ case STATUS_TIMEOUT:
+ sig = SIGALRM;
+ break;
+
+ case STATUS_ACCESS_VIOLATION:
+ case STATUS_DATATYPE_MISALIGNMENT:
+ case STATUS_ARRAY_BOUNDS_EXCEEDED:
+ case STATUS_GUARD_PAGE_VIOLATION:
+ case STATUS_IN_PAGE_ERROR:
+ case STATUS_NO_MEMORY:
+ case STATUS_INVALID_DISPOSITION:
+ case STATUS_STACK_OVERFLOW:
+ sig = SIGSEGV;
+ break;
+
+ case STATUS_CONTROL_C_EXIT:
+ sig = SIGINT;
+ break;
+
+ case STATUS_INVALID_HANDLE:
+ /* CloseHandle will throw this exception if it is given an
+ invalid handle. We don't care about the exception; we just
+ want CloseHandle to return an error. This can be revisited
+ if gcc ever supports Windows style structured exception
+ handling. */
+ return 0;
+
+ default:
+ /* If we don't recognize the exception, we have to assume that
+ we are doing structured exception handling, and we let
+ something else handle it. */
+ return 1;
+ }
+
+ debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e.ExceptionCode, in.Eip, in.Esp);
+ debug_printf ("In cygwin_except_handler sig = %d at %p", sig, in.Eip);
+
+ if (global_sigs[sig].sa_mask & SIGTOMASK (sig))
+ syscall_printf ("signal %d, masked %p", sig, global_sigs[sig].sa_mask);
+
+ debug_printf ("In cygwin_except_handler calling %p",
+ global_sigs[sig].sa_handler);
+
+ DWORD *ebp = (DWORD *)in.Esp;
+ for (DWORD *bpend = (DWORD *) __builtin_frame_address (0); ebp > bpend; ebp--)
+ if (*ebp == in.SegCs && ebp[-1] == in.Eip)
+ {
+ ebp -= 2;
+ break;
+ }
+
+ if (!myself->progname[0]
+ || GetCurrentThreadId () == sigtid
+ || (void *) global_sigs[sig].sa_handler == (void *) SIG_DFL
+ || (void *) global_sigs[sig].sa_handler == (void *) SIG_IGN
+ || (void *) global_sigs[sig].sa_handler == (void *) SIG_ERR)
+ {
+ /* Print the exception to the console */
+ if (1)
+ {
+ for (int i = 0; status_info[i].name; i++)
+ {
+ if (status_info[i].code == e.ExceptionCode)
+ {
+ if (!myself->ppid_handle)
+ system_printf ("Exception: %s", status_info[i].name);
+ break;
+ }
+ }
+ }
+
+ /* Another exception could happen while tracing or while exiting.
+ Only do this once. */
+ if (recursed++)
+ system_printf ("Error while dumping state (probably corrupted stack)");
+ else
+ {
+ if (try_to_debug (0))
+ {
+ debugging = true;
+ return 0;
+ }
+
+ open_stackdumpfile ();
+ exception (&e, &in);
+ stackdump ((DWORD) ebp, 0, 1);
+ }
+
+ signal_exit (0x80 | sig); // Flag signal + core dump
+ }
+
+ _my_tls.push ((__stack_t) ebp, true);
+ sig_send (NULL, sig, &_my_tls); // Signal myself
+ return 1;
+}
+#endif /* __i386__ */
+
+#ifndef HAVE_STACK_TRACE
+void
+stack (void)
+{
+ system_printf ("Stack trace not yet supported on this machine.");
+}
+#endif
+
+/* Utilities to call a user supplied exception handler. */
+
+#define SIG_NONMASKABLE (SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP))
+
+#ifdef __i386__
+#define HAVE_CALL_HANDLER
+
+/* Non-raceable sigsuspend
+ * Note: This implementation is based on the Single UNIX Specification
+ * man page. This indicates that sigsuspend always returns -1 and that
+ * attempts to block unblockable signals will be silently ignored.
+ * This is counter to what appears to be documented in some UNIX
+ * man pages, e.g. Linux.
+ */
+int __stdcall
+handle_sigsuspend (sigset_t tempmask)
+{
+ sig_dispatch_pending ();
+ sigset_t oldmask = myself->getsigmask (); // Remember for restoration
+
+ set_signal_mask (tempmask &= ~SIG_NONMASKABLE, oldmask);// Let signals we're
+ // interested in through.
+ sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
+
+ pthread_testcancel ();
+ pthread::cancelable_wait (signal_arrived, INFINITE);
+
+ set_sig_errno (EINTR); // Per POSIX
+
+ /* A signal dispatch function will have been added to our stack and will
+ be hit eventually. Set the old mask to be restored when the signal
+ handler returns. */
+
+ _my_tls.oldmask = oldmask; // Will be restored by signal handler
+ return -1;
+}
+
+extern DWORD exec_exit; // Possible exit value for exec
+
+extern "C" {
+static void
+sig_handle_tty_stop (int sig)
+{
+ /* Silently ignore attempts to suspend if there is no accomodating
+ cygwin parent to deal with this behavior. */
+ if (!myself->ppid_handle)
+ {
+ myself->process_state &= ~PID_STOPPED;
+ return;
+ }
+
+ myself->stopsig = sig;
+ /* See if we have a living parent. If so, send it a special signal.
+ It will figure out exactly which pid has stopped by scanning
+ its list of subprocesses. */
+ if (my_parent_is_alive ())
+ {
+ pinfo parent (myself->ppid);
+ if (ISSTATE (parent, PID_NOCLDSTOP))
+ sig_send (parent, SIGCHLD);
+ }
+ sigproc_printf ("process %d stopped by signal %d, myself->ppid_handle %p",
+ myself->pid, sig, myself->ppid_handle);
+ if (WaitForSingleObject (sigCONT, INFINITE) != WAIT_OBJECT_0)
+ api_fatal ("WaitSingleObject failed, %E");
+ return;
+}
+}
+
+int
+interruptible (DWORD pc)
+{
+ int res;
+ MEMORY_BASIC_INFORMATION m;
+
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) pc, &m, sizeof m))
+ sigproc_printf ("couldn't get memory info, pc %p, %E", pc);
+
+ char *checkdir = (char *) alloca (windows_system_directory_length + 4);
+ memset (checkdir, 0, sizeof (checkdir));
+
+# define h ((HMODULE) m.AllocationBase)
+ /* Apparently Windows 95 can sometimes return bogus addresses from
+ GetThreadContext. These resolve to a strange allocation base.
+ These should *never* be treated as interruptible. */
+ if (!h || m.State != MEM_COMMIT)
+ res = 0;
+ else if (h == user_data->hmodule)
+ res = 1;
+ else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2))
+ res = 0;
+ else
+ res = !strncasematch (windows_system_directory, checkdir,
+ windows_system_directory_length);
+ sigproc_printf ("pc %p, h %p, interruptible %d", pc, h, res);
+# undef h
+ return res;
+}
+void __stdcall
+_threadinfo::interrupt_setup (int sig, void *handler,
+ struct sigaction& siga, __stack_t retaddr)
+{
+ __stack_t *retaddr_in_tls = stackptr - 1;
+ push ((__stack_t) sigdelayed);
+ oldmask = myself->getsigmask ();
+ newmask = oldmask | siga.sa_mask | SIGTOMASK (sig);
+ sa_flags = siga.sa_flags;
+ func = (void (*) (int)) handler;
+ saved_errno = -1; // Flag: no errno to save
+ if (handler == sig_handle_tty_stop)
+ {
+ myself->stopsig = 0;
+ myself->process_state |= PID_STOPPED;
+ }
+ this->sig = sig; // Should ALWAYS be second to last setting set to avoid a race
+ *retaddr_in_tls = retaddr;
+ /* Clear any waiting threads prior to dispatching to handler function */
+ int res = SetEvent (signal_arrived); // For an EINTR case
+ proc_subproc (PROC_CLEARWAIT, 1);
+ sigproc_printf ("armed signal_arrived %p, sig %d, res %d", signal_arrived,
+ sig, res);
+}
+
+bool
+_threadinfo::interrupt_now (CONTEXT *ctx, int sig, void *handler,
+ struct sigaction& siga)
+{
+ push (0);
+ interrupt_setup (sig, handler, siga, (__stack_t) ctx->Eip);
+ ctx->Eip = pop ();
+ SetThreadContext (*this, ctx); /* Restart the thread in a new location */
+ return 1;
+}
+
+void __stdcall
+signal_fixup_after_fork ()
+{
+ if (_my_tls.sig)
+ {
+ _my_tls.sig = 0;
+ _my_tls.stackptr = _my_tls.stack + 1; // FIXME?
+ set_signal_mask (_my_tls.oldmask);
+ }
+ sigproc_init ();
+}
+
+extern "C" void __stdcall
+set_sig_errno (int e)
+{
+ *_my_tls.errno_addr = e;
+ _my_tls.saved_errno = e;
+ // sigproc_printf ("errno %d", e);
+}
+
+static int setup_handler (int, void *, struct sigaction&, _threadinfo *tls)
+ __attribute__((regparm(3)));
+static int
+setup_handler (int sig, void *handler, struct sigaction& siga, _threadinfo *tls)
+{
+ CONTEXT cx;
+ bool interrupted = false;
+
+ if (tls->sig)
+ goto out;
+
+ for (int i = 0; i < CALL_HANDLER_RETRY; i++)
+ {
+ __stack_t *retaddr_on_stack = tls->stackptr - 1;
+ if (retaddr_on_stack >= tls->stack)
+ {
+ __stack_t retaddr = InterlockedExchange ((LONG *) retaddr_on_stack, 0);
+ if (!retaddr)
+ continue;
+ tls->reset_exception ();
+ tls->interrupt_setup (sig, handler, siga, retaddr);
+ sigproc_printf ("interrupted known cygwin routine");
+ interrupted = true;
+ break;
+ }
+
+ DWORD res;
+ HANDLE hth = (HANDLE) *tls;
+
+ /* Suspend the thread which will receive the signal. But first ensure that
+ this thread doesn't have any mutos. (FIXME: Someday we should just grab
+ all of the mutos rather than checking for them)
+ For Windows 95, we also have to ensure that the addresses returned by GetThreadContext
+ are valid.
+ If one of these conditions is not true we loop for a fixed number of times
+ since we don't want to stall the signal handler. FIXME: Will this result in
+ noticeable delays?
+ If the thread is already suspended (which can occur when a program has called
+ SuspendThread on itself then just queue the signal. */
+
+#ifndef DEBUGGING
+ sigproc_printf ("suspending mainthread");
+#else
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ memset (&cx, 0, sizeof cx);
+ sigproc_printf ("suspending mainthread PC %p", cx.Eip);
+#endif
+ res = SuspendThread (hth);
+ /* Just release the lock now since we hav suspended the main thread and it
+ definitely can't be grabbing it now. This will have to change, of course,
+ if/when we can send signals to other than the main thread. */
+
+ /* Just set pending if thread is already suspended */
+ if (res)
+ {
+ (void) ResumeThread (hth);
+ break;
+ }
+
+ // FIXME - add check for reentering of DLL here
+
+ muto *m;
+ /* FIXME: Make multi-thread aware */
+ for (m = muto_start.next; m != NULL; m = m->next)
+ if (m->unstable () || m->owner () == cygthread::main_thread_id)
+ {
+ sigproc_printf ("suspended thread owns a muto (%s)", m->name);
+ goto resume_thread;
+ }
+
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ system_printf ("couldn't get context of main thread, %E");
+ else if (interruptible (cx.Eip))
+ interrupted = tls->interrupt_now (&cx, sig, handler, siga);
+
+ resume_thread:
+ res = ResumeThread (hth);
+
+ if (interrupted)
+ break;
+ sigproc_printf ("couldn't interrupt. trying again.");
+ low_priority_sleep (0);
+ }
+
+out:
+ sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not ");
+ return interrupted;
+}
+#endif /* i386 */
+
+#ifndef HAVE_CALL_HANDLER
+#error "Need to supply machine dependent setup_handler"
+#endif
+
+/* Keyboard interrupt handler. */
+static BOOL WINAPI
+ctrl_c_handler (DWORD type)
+{
+ static bool saw_close;
+ _my_tls.remove (INFINITE);
+
+ /* Return FALSE to prevent an "End task" dialog box from appearing
+ for each Cygwin process window that's open when the computer
+ is shut down or console window is closed. */
+
+ if (type == CTRL_SHUTDOWN_EVENT)
+ {
+#if 0
+ /* Don't send a signal. Only NT service applications and their child
+ processes will receive this event and the services typically already
+ handle the shutdown action when getting the SERVICE_CONTROL_SHUTDOWN
+ control message. */
+ sig_send (NULL, SIGTERM);
+#endif
+ return FALSE;
+ }
+
+ if (myself->ctty != -1)
+ {
+ if (type == CTRL_CLOSE_EVENT)
+ {
+ saw_close = true;
+ sig_send (NULL, SIGHUP);
+ return FALSE;
+ }
+ if (!saw_close && type == CTRL_LOGOFF_EVENT)
+ {
+ /* Check if the process is actually associated with a visible
+ window station, one which actually represents a visible desktop.
+ If not, the CTRL_LOGOFF_EVENT doesn't concern this process. */
+ if (has_visible_window_station ())
+ sig_send (myself_nowait, SIGHUP);
+ return FALSE;
+ }
+ }
+
+ /* If we are a stub and the new process has a pinfo structure, let it
+ handle this signal. */
+ if (dwExeced && pinfo (dwExeced))
+ return TRUE;
+
+ /* We're only the process group leader when we have a valid pinfo structure.
+ If we don't have one, then the parent "stub" will handle the signal. */
+ if (!pinfo (cygwin_pid (GetCurrentProcessId ())))
+ return TRUE;
+
+ tty_min *t = cygwin_shared->tty.get_tty (myself->ctty);
+ /* Ignore this if we're not the process group leader since it should be handled
+ *by* the process group leader. */
+ if (myself->ctty != -1 && t->getpgid () == myself->pid &&
+ (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP)
+ /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
+ that we have handled the signal). At this point, type should be
+ a CTRL_C_EVENT or CTRL_BREAK_EVENT. */
+ {
+ t->last_ctrl_c = GetTickCount ();
+ kill (-myself->pid, SIGINT);
+ t->last_ctrl_c = GetTickCount ();
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+/* Function used by low level sig wrappers. */
+extern "C" void __stdcall
+set_process_mask (sigset_t newmask)
+{
+ set_signal_mask (newmask);
+}
+
+/* Set the signal mask for this process.
+ Note that some signals are unmaskable, as in UNIX. */
+extern "C" void __stdcall
+set_signal_mask (sigset_t newmask, sigset_t oldmask)
+{
+ mask_sync->acquire (INFINITE);
+ newmask &= ~SIG_NONMASKABLE;
+ sigset_t mask_bits = oldmask & ~newmask;
+ sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask,
+ mask_bits);
+ myself->setsigmask (newmask); // Set a new mask
+ mask_sync->release ();
+ if (mask_bits)
+ sig_dispatch_pending ();
+ else
+ sigproc_printf ("not calling sig_dispatch_pending");
+ return;
+}
+
+int __stdcall
+sig_handle (int sig, sigset_t mask, int pid, _threadinfo *tls)
+{
+ if (sig == SIGCONT)
+ {
+ DWORD stopped = myself->process_state & PID_STOPPED;
+ myself->stopsig = 0;
+ myself->process_state &= ~PID_STOPPED;
+ /* Clear pending stop signals */
+ sig_clear (SIGSTOP);
+ sig_clear (SIGTSTP);
+ sig_clear (SIGTTIN);
+ sig_clear (SIGTTOU);
+ if (stopped)
+ SetEvent (sigCONT);
+ }
+
+ int rc = 1;
+ bool insigwait_mask = tls ? sigismember (&tls->sigwait_mask, sig) : false;
+ bool special_case = ISSTATE (myself, PID_STOPPED) || main_vfork->pid;
+ bool masked = sigismember (&mask, sig);
+ if (sig != SIGKILL && sig != SIGSTOP
+ && (special_case || main_vfork->pid || masked || insigwait_mask
+ || (tls && sigismember (&tls->sigmask, sig))))
+ {
+ sigproc_printf ("signal %d blocked", sig);
+ if ((!special_case && !masked)
+ && (insigwait_mask || (tls = _threadinfo::find_tls (sig)) != NULL))
+ goto thread_specific;
+ rc = -1;
+ goto done;
+ }
+
+ /* Clear pending SIGCONT on stop signals */
+ if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
+ sig_clear (SIGCONT);
+
+ sigproc_printf ("signal %d processing", sig);
+ struct sigaction thissig = global_sigs[sig];
+ void *handler;
+ handler = (void *) thissig.sa_handler;
+
+ myself->rusage_self.ru_nsignals++;
+
+ if (sig == SIGKILL)
+ goto exit_sig;
+
+ if (sig == SIGSTOP)
+ goto stop;
+
+#if 0
+ char sigmsg[24];
+ __small_sprintf (sigmsg, "cygwin: signal %d\n", sig);
+ OutputDebugString (sigmsg);
+#endif
+
+ if (handler == (void *) SIG_DFL)
+ {
+ if (insigwait_mask)
+ goto thread_specific;
+ if (sig == SIGCHLD || sig == SIGIO || sig == SIGCONT || sig == SIGWINCH
+ || sig == SIGURG)
+ {
+ sigproc_printf ("default signal %d ignored", sig);
+ goto done;
+ }
+
+ if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
+ goto stop;
+
+ goto exit_sig;
+ }
+
+ if (handler == (void *) SIG_IGN)
+ {
+ sigproc_printf ("signal %d ignored", sig);
+ goto done;
+ }
+
+ if (handler == (void *) SIG_ERR)
+ goto exit_sig;
+
+ goto dosig;
+
+stop:
+ /* Eat multiple attempts to STOP */
+ if (ISSTATE (myself, PID_STOPPED))
+ goto done;
+ handler = (void *) sig_handle_tty_stop;
+ thissig = global_sigs[SIGSTOP];
+
+dosig:
+ /* Dispatch to the appropriate function. */
+ sigproc_printf ("signal %d, about to call %p", sig, handler);
+ rc = setup_handler (sig, handler, thissig, tls ?: _main_tls);
+
+done:
+ sigproc_printf ("returning %d", rc);
+ return rc;
+
+thread_specific:
+ tls->sig = sig;
+ sigproc_printf ("releasing sigwait for thread");
+ SetEvent (tls->event);
+ goto done;
+
+exit_sig:
+ if (sig == SIGQUIT || sig == SIGABRT)
+ {
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (hMainThread, &c);
+ if (!try_to_debug ())
+ stackdump (c.Ebp, 1, 1);
+ sig |= 0x80;
+ }
+ sigproc_printf ("signal %d, about to call do_exit", sig);
+ signal_exit (sig);
+ /* Never returns */
+}
+
+CRITICAL_SECTION NO_COPY exit_lock;
+
+/* Cover function to `do_exit' to handle exiting even in presence of more
+ exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit
+ routines to run. */
+static void
+signal_exit (int rc)
+{
+ EnterCriticalSection (&exit_lock);
+ rc = EXIT_SIGNAL | (rc << 8);
+ if (exit_already++)
+ myself->exit (rc);
+
+ /* We'd like to stop the main thread from executing but when we do that it
+ causes random, inexplicable hangs. So, instead, we set up the priority
+ of this thread really high so that it should do its thing and then exit. */
+ (void) SetThreadPriority (hMainThread, THREAD_PRIORITY_IDLE);
+ (void) SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+
+ /* Unlock any main thread mutos since we're executing with prejudice. */
+ muto *m;
+ for (m = muto_start.next; m != NULL; m = m->next)
+ if (m->unstable () || m->owner () == cygthread::main_thread_id)
+ m->reset ();
+
+ user_data->resourcelocks->Delete ();
+ user_data->resourcelocks->Init ();
+
+ if (hExeced)
+ {
+ sigproc_printf ("terminating captive process");
+ TerminateProcess (hExeced, rc);
+ }
+
+ sigproc_printf ("about to call do_exit (%x)", rc);
+ (void) SetEvent (signal_arrived);
+ do_exit (rc);
+}
+
+HANDLE NO_COPY title_mutex = NULL;
+
+void
+events_init (void)
+{
+ char *name;
+ char mutex_name[CYG_MAX_PATH];
+ /* title_mutex protects modification of console title. It's necessary
+ while finding console window handle */
+
+ if (!(title_mutex = CreateMutex (&sec_all_nih, FALSE,
+ name = shared_name (mutex_name,
+ "title_mutex", 0))))
+ api_fatal ("can't create title mutex '%s', %E", name);
+
+ ProtectHandle (title_mutex);
+ new_muto (mask_sync);
+ windows_system_directory[0] = '\0';
+ (void) GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2);
+ char *end = strchr (windows_system_directory, '\0');
+ if (end == windows_system_directory)
+ api_fatal ("can't find windows system directory");
+ if (end[-1] != '\\')
+ {
+ *end++ = '\\';
+ *end = '\0';
+ }
+ windows_system_directory_length = end - windows_system_directory;
+ debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
+ windows_system_directory, windows_system_directory_length);
+ InitializeCriticalSection (&exit_lock);
+}
+
+void
+events_terminate (void)
+{
+ exit_already = 1;
+}
+
+extern "C" {
+int __stdcall
+call_signal_handler_now ()
+{
+ int sa_flags = 0;
+ while (_my_tls.sig && _my_tls.stackptr > _my_tls.stack)
+ {
+ sa_flags = _my_tls.sa_flags;
+ int sig = _my_tls.sig;
+ void (*sigfunc) (int) = _my_tls.func;
+
+ (void) _my_tls.pop ();
+#ifdef DEBUGGING
+ if (_my_tls.stackptr > (_my_tls.stack + 1))
+ try_to_debug ();
+#endif
+ reset_signal_arrived ();
+ sigset_t oldmask = _my_tls.oldmask;
+ int this_errno = _my_tls.saved_errno;
+ set_process_mask (_my_tls.newmask);
+ _my_tls.sig = 0;
+ sigfunc (sig);
+ set_process_mask (oldmask);
+ if (this_errno >= 0)
+ set_errno (this_errno);
+ }
+
+ return sa_flags & SA_RESTART;
+}
+
+void __stdcall
+reset_signal_arrived ()
+{
+ (void) ResetEvent (signal_arrived);
+ sigproc_printf ("reset signal_arrived");
+}
+}
diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc
new file mode 100644
index 00000000000..b637d2fbd74
--- /dev/null
+++ b/winsup/cygwin/external.cc
@@ -0,0 +1,295 @@
+/* external.cc: Interface to Cygwin internals from external programs.
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+ Written by Christopher Faylor <cgf@cygnus.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. */
+
+#include "winsup.h"
+#include "security.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include <exceptions.h>
+#include "shared_info.h"
+#include "cygwin_version.h"
+#include "perprocess.h"
+#include "cygerrno.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "wincap.h"
+#include "heap.h"
+#include "cygthread.h"
+#include "pwdgrp.h"
+#include "cygtls.h"
+
+static external_pinfo *
+fillout_pinfo (pid_t pid, int winpid)
+{
+ BOOL nextpid;
+ static external_pinfo ep;
+
+ if ((nextpid = !!(pid & CW_NEXTPID)))
+ pid ^= CW_NEXTPID;
+
+ static winpids pids (0);
+
+ static unsigned int i;
+ if (!pids.npids || !nextpid)
+ {
+ pids.set (winpid);
+ i = 0;
+ }
+
+ if (!pid)
+ i = 0;
+
+ memset (&ep, 0, sizeof ep);
+ while (i < pids.npids)
+ {
+ DWORD thispid = pids.winpid (i);
+ _pinfo *p = pids[i];
+ i++;
+
+ if (!p)
+ {
+ if (!nextpid && thispid != (DWORD) pid)
+ continue;
+ ep.pid = cygwin_pid (thispid);
+ ep.dwProcessId = thispid;
+ ep.process_state = PID_IN_USE;
+ ep.ctty = -1;
+ break;
+ }
+ else if (nextpid || p->pid == pid || (winpid && thispid == (DWORD) pid))
+ {
+ ep.ctty = p->ctty;
+ ep.pid = p->pid;
+ ep.ppid = p->ppid;
+ ep.hProcess = p->hProcess;
+ ep.dwProcessId = p->dwProcessId;
+ ep.uid = p->uid;
+ ep.gid = p->gid;
+ ep.pgid = p->pgid;
+ ep.sid = p->sid;
+ ep.umask = 0;
+ ep.start_time = p->start_time;
+ ep.rusage_self = p->rusage_self;
+ ep.rusage_children = p->rusage_children;
+ strcpy (ep.progname, p->progname);
+ ep.strace_mask = 0;
+ ep.version = EXTERNAL_PINFO_VERSION;
+
+ ep.process_state = p->process_state;
+
+ ep.uid32 = p->uid;
+ ep.gid32 = p->gid;
+ break;
+ }
+ }
+
+ if (!ep.pid)
+ {
+ i = 0;
+ pids.reset ();
+ return 0;
+ }
+ return &ep;
+}
+
+static DWORD
+get_cygdrive_info (char *user, char *system, char *user_flags,
+ char *system_flags)
+{
+ int res = mount_table->get_cygdrive_info (user, system, user_flags,
+ system_flags);
+ return (res == ERROR_SUCCESS) ? 1 : 0;
+}
+
+static DWORD
+get_cygdrive_prefixes (char *user, char *system)
+{
+ char user_flags[CYG_MAX_PATH];
+ char system_flags[CYG_MAX_PATH];
+ DWORD res = get_cygdrive_info (user, system, user_flags, system_flags);
+ return res;
+}
+
+static DWORD
+check_ntsec (const char *filename)
+{
+ if (!filename)
+ return wincap.has_security () && allow_ntsec;
+ path_conv pc (filename);
+ return wincap.has_security () && allow_ntsec && pc.has_acls ();
+}
+
+extern "C" unsigned long
+cygwin_internal (cygwin_getinfo_types t, ...)
+{
+ va_list arg;
+ va_start (arg, t);
+
+ switch (t)
+ {
+ case CW_LOCK_PINFO:
+ return 1;
+
+ case CW_UNLOCK_PINFO:
+ return 1;
+
+ case CW_GETTHREADNAME:
+ return (DWORD) cygthread::name (va_arg (arg, DWORD));
+
+ case CW_SETTHREADNAME:
+ {
+ set_errno (ENOSYS);
+ return 0;
+ }
+
+ case CW_GETPINFO:
+ return (DWORD) fillout_pinfo (va_arg (arg, DWORD), 0);
+
+ case CW_GETVERSIONINFO:
+ return (DWORD) cygwin_version_strings;
+
+ case CW_READ_V1_MOUNT_TABLES:
+ set_errno (ENOSYS);
+ return 1;
+
+ case CW_USER_DATA:
+ return (DWORD) &__cygwin_user_data;
+
+ case CW_PERFILE:
+ perfile_table = va_arg (arg, struct __cygwin_perfile *);
+ return 0;
+
+ case CW_GET_CYGDRIVE_PREFIXES:
+ {
+ char *user = va_arg (arg, char *);
+ char *system = va_arg (arg, char *);
+ return get_cygdrive_prefixes (user, system);
+ }
+
+ case CW_GETPINFO_FULL:
+ return (DWORD) fillout_pinfo (va_arg (arg, pid_t), 1);
+
+ case CW_INIT_EXCEPTIONS:
+ init_exceptions (va_arg (arg, exception_list *));
+ return 0;
+
+ case CW_GET_CYGDRIVE_INFO:
+ {
+ char *user = va_arg (arg, char *);
+ char *system = va_arg (arg, char *);
+ char *user_flags = va_arg (arg, char *);
+ char *system_flags = va_arg (arg, char *);
+ return get_cygdrive_info (user, system, user_flags, system_flags);
+ }
+
+ case CW_SET_CYGWIN_REGISTRY_NAME:
+ {
+ const char *cr = va_arg (arg, char *);
+ if (check_null_empty_str_errno (cr))
+ return (DWORD) NULL;
+ cygheap->cygwin_regname = (char *) crealloc (cygheap->cygwin_regname,
+ strlen (cr) + 1);
+ strcpy (cygheap->cygwin_regname, cr);
+ }
+ case CW_GET_CYGWIN_REGISTRY_NAME:
+ return (DWORD) cygheap->cygwin_regname;
+
+ case CW_STRACE_TOGGLE:
+ {
+ pid_t pid = va_arg (arg, pid_t);
+ pinfo p (pid);
+ if (p)
+ {
+ sig_send (p, __SIGSTRACE);
+ return 0;
+ }
+ else
+ {
+ set_errno (ESRCH);
+ return (DWORD) -1;
+ }
+ }
+
+ case CW_STRACE_ACTIVE:
+ {
+ return strace.active;
+ }
+
+ case CW_CYGWIN_PID_TO_WINPID:
+ {
+ pinfo p (va_arg (arg, pid_t));
+ return p ? p->dwProcessId : 0;
+ }
+ case CW_EXTRACT_DOMAIN_AND_USER:
+ {
+ struct passwd *pw = va_arg (arg, struct passwd *);
+ char *domain = va_arg (arg, char *);
+ char *user = va_arg (arg, char *);
+ extract_nt_dom_user (pw, domain, user);
+ return 0;
+ }
+ case CW_CMDLINE:
+ {
+ size_t n;
+ pid_t pid = va_arg (arg, pid_t);
+ pinfo p (pid);
+ return (DWORD) p->cmdline (n);
+ }
+ case CW_CHECK_NTSEC:
+ {
+ char *filename = va_arg (arg, char *);
+ return check_ntsec (filename);
+ }
+ case CW_GET_ERRNO_FROM_WINERROR:
+ {
+ int error = va_arg (arg, int);
+ int deferrno = va_arg (arg, int);
+ return geterrno_from_win_error (error, deferrno);
+ }
+ case CW_GET_POSIX_SECURITY_ATTRIBUTE:
+ {
+ security_descriptor sd;
+ int attribute = va_arg (arg, int);
+ PSECURITY_ATTRIBUTES psa = va_arg (arg, PSECURITY_ATTRIBUTES);
+ void *sd_buf = va_arg (arg, void *);
+ DWORD sd_buf_size = va_arg (arg, DWORD);
+ set_security_attribute (attribute, psa, sd);
+ if (!psa->lpSecurityDescriptor || sd.size () > sd_buf_size)
+ return sd.size ();
+ memcpy (sd_buf, sd, sd.size ());
+ psa->lpSecurityDescriptor = sd_buf;
+ return 0;
+ }
+ case CW_GET_SHMLBA:
+ {
+ return getshmlba ();
+ }
+ case CW_GET_UID_FROM_SID:
+ {
+ PSID psid = va_arg (arg, PSID);
+ cygsid sid (psid);
+ struct passwd *pw = internal_getpwsid (sid);
+ return pw ? pw->pw_uid : (__uid32_t)-1;
+ }
+ case CW_GET_GID_FROM_SID:
+ {
+ PSID psid = va_arg (arg, PSID);
+ cygsid sid (psid);
+ struct __group32 *gr = internal_getgrsid (sid);
+ return gr ? gr->gr_gid : (__gid32_t)-1;
+ }
+ default:
+ return (DWORD) -1;
+ }
+}
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
new file mode 100644
index 00000000000..407a47dddbc
--- /dev/null
+++ b/winsup/cygwin/fork.cc
@@ -0,0 +1,763 @@
+/* fork.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 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 "winsup.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "child_info.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "perprocess.h"
+#include "dll_init.h"
+#include "sync.h"
+#include "shared_info.h"
+#include "cygmalloc.h"
+#include "cygthread.h"
+
+#ifdef DEBUGGING
+static int npid;
+static int npid_max;
+static pid_t fork_pids[100];
+#endif
+
+/* Timeout to wait for child to start, parent to init child, etc. */
+/* FIXME: Once things stabilize, bump up to a few minutes. */
+#define FORK_WAIT_TIMEOUT (300 * 1000) /* 300 seconds */
+
+#define dll_data_start &_data_start__
+#define dll_data_end &_data_end__
+#define dll_bss_start &_bss_start__
+#define dll_bss_end &_bss_end__
+
+void
+per_thread::set (void *s)
+{
+ if (s == PER_THREAD_FORK_CLEAR)
+ {
+ tls = TlsAlloc ();
+ s = NULL;
+ }
+ TlsSetValue (get_tls (), s);
+}
+
+static void
+stack_base (child_info_fork &ch)
+{
+ MEMORY_BASIC_INFORMATION m;
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) &m, &m, sizeof m))
+ system_printf ("couldn't get memory info, %E");
+
+ ch.stacktop = m.AllocationBase;
+ ch.stackbottom = (LPBYTE) m.BaseAddress + m.RegionSize;
+ ch.stacksize = (DWORD) ch.stackbottom - (DWORD) &m;
+ debug_printf ("bottom %p, top %p, stack %p, size %d, reserve %d",
+ ch.stackbottom, ch.stacktop, &m, ch.stacksize,
+ (DWORD) ch.stackbottom - (DWORD) ch.stacktop);
+}
+
+/* Copy memory from parent to child.
+ The result is a boolean indicating success. */
+
+static int
+fork_copy (PROCESS_INFORMATION &pi, const char *what, ...)
+{
+ va_list args;
+ char *low;
+ int pass = 0;
+
+ va_start (args, what);
+
+ while ((low = va_arg (args, char *)))
+ {
+ char *high = va_arg (args, char *);
+ DWORD todo = wincap.chunksize () ?: high - low;
+ char *here;
+
+ for (here = low; here < high; here += todo)
+ {
+ DWORD done = 0;
+ if (here + todo > high)
+ todo = high - here;
+ int res = WriteProcessMemory (pi.hProcess, here, here, todo, &done);
+ debug_printf ("child handle %p, low %p, high %p, res %d", pi.hProcess,
+ low, high, res);
+ if (!res || todo != done)
+ {
+ if (!res)
+ __seterrno ();
+ /* If this happens then there is a bug in our fork
+ implementation somewhere. */
+ system_printf ("%s pass %d failed, %p..%p, done %d, windows pid %u, %E",
+ what, pass, low, high, done, pi.dwProcessId);
+ goto err;
+ }
+ }
+
+ pass++;
+ }
+
+ debug_printf ("done");
+ return 1;
+
+ err:
+ TerminateProcess (pi.hProcess, 1);
+ set_errno (EAGAIN);
+ return 0;
+}
+
+/* Wait for child to finish what it's doing and signal us.
+ We don't want to wait forever here.If there's a problem somewhere
+ it'll hang the entire system (since all forks are mutex'd). If we
+ time out, set errno = EAGAIN and hope the app tries again. */
+static int
+sync_with_child (PROCESS_INFORMATION &pi, HANDLE subproc_ready,
+ bool hang_child, const char *s)
+{
+ /* We also add the child process handle to the wait. If the child fails
+ to initialize (eg. because of a missing dll). Then this
+ handle will become signalled. This stops a *looong* timeout wait.
+ */
+ HANDLE w4[2];
+
+ debug_printf ("waiting for child. reason: %s, hang_child %d", s,
+ hang_child);
+ w4[1] = pi.hProcess;
+ w4[0] = subproc_ready;
+ DWORD rc = WaitForMultipleObjects (2, w4, FALSE, FORK_WAIT_TIMEOUT);
+
+ if (rc == WAIT_OBJECT_0 ||
+ WaitForSingleObject (subproc_ready, 0) == WAIT_OBJECT_0)
+ /* That's ok */;
+ else if (rc == WAIT_FAILED || rc == WAIT_TIMEOUT)
+ {
+ if (rc != WAIT_FAILED)
+ system_printf ("WaitForMultipleObjects timed out");
+ else
+ system_printf ("WaitForMultipleObjects failed, %E");
+ set_errno (EAGAIN);
+ syscall_printf ("-1 = fork(), WaitForMultipleObjects failed");
+ TerminateProcess (pi.hProcess, 1);
+ return 0;
+ }
+ else
+ {
+ /* Child died. Clean up and exit. */
+ DWORD errcode;
+ GetExitCodeProcess (pi.hProcess, &errcode);
+ /* Fix me. This is not enough. The fork should not be considered
+ * to have failed if the process was essentially killed by a signal.
+ */
+ if (errcode != STATUS_CONTROL_C_EXIT)
+ {
+ system_printf ("child %u(%p) died before initialization with status code %p",
+ cygwin_pid (pi.dwProcessId), pi.hProcess, errcode);
+ system_printf ("*** child state %s", s);
+#ifdef DEBUGGING
+ abort ();
+#endif
+ }
+ set_errno (EAGAIN);
+ syscall_printf ("Child died before subproc_ready signalled");
+ return 0;
+ }
+
+ debug_printf ("child signalled me");
+ return 1;
+}
+
+static int
+resume_child (PROCESS_INFORMATION &pi, HANDLE forker_finished)
+{
+ SetEvent (forker_finished);
+ debug_printf ("signalled child");
+ return 1;
+}
+
+/* Notify parent that it is time for the next step.
+ Note that this has to be a macro since the parent may be messing with
+ our stack. */
+static void __stdcall
+sync_with_parent (const char *s, bool hang_self)
+{
+ debug_printf ("signalling parent: %s", s);
+ /* Tell our parent we're waiting. */
+ if (!SetEvent (fork_info->subproc_ready))
+ api_fatal ("fork child - SetEvent for %s failed, %E", s);
+ if (hang_self)
+ {
+ HANDLE h = fork_info->forker_finished;
+ /* Wait for the parent to fill in our stack and heap.
+ Don't wait forever here. If our parent dies we don't want to clog
+ the system. If the wait fails, we really can't continue so exit. */
+ DWORD psync_rc = WaitForSingleObject (h, FORK_WAIT_TIMEOUT);
+ debug_printf ("awake");
+ switch (psync_rc)
+ {
+ case WAIT_TIMEOUT:
+ api_fatal ("WFSO timed out for %s", s);
+ break;
+ case WAIT_FAILED:
+ if (GetLastError () == ERROR_INVALID_HANDLE &&
+ WaitForSingleObject (fork_info->forker_finished, 1) != WAIT_FAILED)
+ break;
+ api_fatal ("WFSO failed for %s, fork_finished %p, %E", s,
+ fork_info->forker_finished);
+ break;
+ default:
+ debug_printf ("no problems");
+ break;
+ }
+ }
+}
+
+static int __stdcall
+fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls)
+{
+ debug_printf ("child is running. pid %d, ppid %d, stack here %p",
+ myself->pid, myself->ppid, __builtin_frame_address (0));
+
+ /* Restore the inheritance state as in parent
+ Don't call setuid here! The flags are already set. */
+ cygheap->user.reimpersonate ();
+
+ sync_with_parent ("after longjmp", true);
+ sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent,
+ first_dll, load_dlls);
+
+#ifdef DEBUGGING
+ char c;
+ if (GetEnvironmentVariable ("FORKDEBUG", &c, 1))
+ try_to_debug ();
+ char buf[80];
+ /* This is useful for debugging fork problems. Use gdb to attach to
+ the pid reported here. */
+ if (GetEnvironmentVariable ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
+ {
+ small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
+ Sleep (atoi (buf));
+ }
+#endif
+
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ when the "main" thread exits it calls do_exit, like a normal process.
+ Exit with a status code of 0. */
+ if (fork_info->stacksize)
+ {
+ _main_tls = &_my_tls;
+ _main_tls->init_thread (NULL, NULL);
+ _main_tls->local_clib = *_impure_ptr;
+ _impure_ptr = &_main_tls->local_clib;
+ }
+
+ set_file_api_mode (current_codepage);
+
+ MALLOC_CHECK;
+
+ if (fixup_mmaps_after_fork (hParent))
+ api_fatal ("recreate_mmaps_after_fork_failed");
+
+
+ MALLOC_CHECK;
+
+ /* If we haven't dynamically loaded any dlls, just signal
+ the parent. Otherwise, load all the dlls, tell the parent
+ that we're done, and wait for the parent to fill in the.
+ loaded dlls' data/bss. */
+ if (!load_dlls)
+ {
+ cygheap->fdtab.fixup_after_fork (hParent);
+ ProtectHandleINH (hParent);
+ sync_with_parent ("performed fork fixup", false);
+ }
+ else
+ {
+ dlls.load_after_fork (hParent, first_dll);
+ cygheap->fdtab.fixup_after_fork (hParent);
+ ProtectHandleINH (hParent);
+ sync_with_parent ("loaded dlls", true);
+ }
+
+ ForceCloseHandle (hParent);
+ (void) ForceCloseHandle1 (fork_info->subproc_ready, subproc_ready);
+ (void) ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
+
+#ifdef USE_SERVER
+ if (fixup_shms_after_fork ())
+ api_fatal ("recreate_shm areas after fork failed");
+#endif
+
+ pinfo_fixup_after_fork ();
+ signal_fixup_after_fork ();
+
+ /* Set thread local stuff to zero. Under Windows 95/98 this is sometimes
+ non-zero, for some reason.
+ FIXME: There is a memory leak here after a fork. */
+ for (per_thread **t = threadstuff; *t; t++)
+ if ((*t)->clear_on_fork ())
+ (*t)->set ();
+
+ pthread::atforkchild ();
+ wait_for_sigthread ();
+ cygbench ("fork-child");
+ return 0;
+}
+
+#ifndef NO_SLOW_PID_REUSE
+static void
+slow_pid_reuse (HANDLE h)
+{
+ static NO_COPY HANDLE last_fork_procs[4] = {0};
+ static NO_COPY unsigned nfork_procs = 0;
+
+ if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0])))
+ nfork_procs = 0;
+ /* Keep a list of handles to forked processes sitting around to prevent
+ Windows from reusing the same pid n times in a row. Having the same pids
+ close in succesion confuses bash. Keeping a handle open will stop
+ windows from reusing the same pid. */
+ if (last_fork_procs[nfork_procs])
+ ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
+ if (DuplicateHandle (hMainProc, h, hMainProc, &last_fork_procs[nfork_procs],
+ 0, FALSE, DUPLICATE_SAME_ACCESS))
+ ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
+ else
+ {
+ last_fork_procs[nfork_procs] = NULL;
+ system_printf ("couldn't create last_fork_proc, %E");
+ }
+ nfork_procs++;
+}
+#endif
+
+static int __stdcall
+fork_parent (HANDLE& hParent, dll *&first_dll,
+ bool& load_dlls, void *stack_here, child_info_fork &ch)
+{
+ HANDLE subproc_ready, forker_finished;
+ DWORD rc;
+ PROCESS_INFORMATION pi = {0, NULL, 0, 0};
+
+ pthread::atforkprepare ();
+
+ subproc_init ();
+
+ int c_flags = GetPriorityClass (hMainProc) /*|
+ CREATE_NEW_PROCESS_GROUP*/;
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+
+ /* If we don't have a console, then don't create a console for the
+ child either. */
+ HANDLE console_handle = CreateFile ("CONOUT$", GENERIC_WRITE,
+ FILE_SHARE_WRITE, &sec_none_nih,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (console_handle != INVALID_HANDLE_VALUE)
+ CloseHandle (console_handle);
+ else
+ c_flags |= DETACHED_PROCESS;
+
+ /* Some file types (currently only sockets) need extra effort in the
+ parent after CreateProcess and before copying the datastructures
+ to the child. So we have to start the child in suspend state,
+ unfortunately, to avoid a race condition. */
+ if (cygheap->fdtab.need_fixup_before ())
+ c_flags |= CREATE_SUSPENDED;
+
+ /* Create an inheritable handle to pass to the child process. This will
+ allow the child to duplicate handles from the parent to itself. */
+ hParent = NULL;
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hParent, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("couldn't create handle to myself for child, %E");
+ return -1;
+ }
+
+ /* Remember the address of the first loaded dll and decide
+ if we need to load dlls. We do this here so that this
+ information will be available in the parent and, when
+ the stack is copied, in the child. */
+ first_dll = dlls.start.next;
+ load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
+
+ /* This will help some of the confusion. */
+ fflush (stdout);
+
+ subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL);
+ if (subproc_ready == NULL)
+ {
+ CloseHandle (hParent);
+ system_printf ("unable to allocate subproc_ready event, %E");
+ return -1;
+ }
+ forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
+ if (forker_finished == NULL)
+ {
+ CloseHandle (hParent);
+ CloseHandle (subproc_ready);
+ system_printf ("unable to allocate forker_finished event, %E");
+ return -1;
+ }
+
+ ProtectHandleINH (subproc_ready);
+ ProtectHandleINH (forker_finished);
+
+ init_child_info (PROC_FORK, &ch, 1, subproc_ready);
+
+ ch.forker_finished = forker_finished;
+
+ stack_base (ch);
+
+ si.cb = sizeof (STARTUPINFO);
+ si.lpReserved2 = (LPBYTE)&ch;
+ si.cbReserved2 = sizeof (ch);
+
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+
+ ch.parent = hParent;
+#ifdef DEBUGGING
+ if (npid_max)
+ {
+ for (int pass = 0; pass < 2; pass++)
+ {
+ pid_t pid;
+ while ((pid = fork_pids[npid++]))
+ if (!pinfo (pid))
+ {
+ ch.cygpid = pid;
+ goto out;
+ }
+ npid = 0;
+ }
+ }
+ out:
+#endif
+
+ char sa_buf[1024];
+ PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf);
+ syscall_printf ("CreateProcess (%s, %s, 0, 0, 1, %x, 0, 0, %p, %p)",
+ myself->progname, myself->progname, c_flags, &si, &pi);
+ __malloc_lock ();
+ void *newheap;
+ newheap = cygheap_setup_for_child (&ch, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcess (myself->progname, /* image to run */
+ myself->progname, /* what we send in arg0 */
+ sec_attribs,
+ sec_attribs,
+ TRUE, /* inherit handles from parent */
+ c_flags,
+ NULL, /* environment filled in later */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+
+ CloseHandle (hParent);
+
+ if (!rc)
+ {
+ __seterrno ();
+ syscall_printf ("CreateProcessA failed, %E");
+ ForceCloseHandle (subproc_ready);
+ ForceCloseHandle (forker_finished);
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+ cygheap_setup_for_child_cleanup (newheap, &ch, 0);
+ return -1;
+ }
+
+ /* Fixup the parent datastructure if needed and resume the child's
+ main thread. */
+ if (!cygheap->fdtab.need_fixup_before ())
+ cygheap_setup_for_child_cleanup (newheap, &ch, 0);
+ else
+ {
+ cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
+ cygheap_setup_for_child_cleanup (newheap, &ch, 1);
+ ResumeThread (pi.hThread);
+ }
+
+#ifdef DEBUGGING
+ pinfo forked ((ch.cygpid != 1 ? ch.cygpid : cygwin_pid (pi.dwProcessId)), 1);
+#else
+ pinfo forked (cygwin_pid (pi.dwProcessId), 1);
+#endif
+ if (!forked)
+ {
+ syscall_printf ("pinfo failed");
+ if (get_errno () != ENOMEM)
+ set_errno (EAGAIN);
+ goto cleanup;
+ }
+
+ /* Initialize things that are done later in dll_crt0_1 that aren't done
+ for the forkee. */
+ strcpy (forked->progname, myself->progname);
+
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+
+ ProtectHandle (pi.hThread);
+ /* Protect the handle but name it similarly to the way it will
+ be called in subproc handling. */
+ ProtectHandle1 (pi.hProcess, childhProc);
+
+ /* Fill in fields in the child's process table entry. */
+ forked->hProcess = pi.hProcess;
+ forked->dwProcessId = pi.dwProcessId;
+
+ /* Hopefully, this will succeed. The alternative to doing things this
+ way is to reserve space prior to calling CreateProcess and then fill
+ it in afterwards. This requires more bookkeeping than I like, though,
+ so we'll just do it the easy way. So, terminate any child process if
+ we can't actually record the pid in the internal table. */
+ if (!forked.remember ())
+ {
+ TerminateProcess (pi.hProcess, 1);
+ set_errno (EAGAIN);
+ goto cleanup;
+ }
+
+#ifndef NO_SLOW_PID_REUSE
+ slow_pid_reuse (pi.hProcess);
+#endif
+
+ /* Wait for subproc to initialize itself. */
+ if (!sync_with_child (pi, subproc_ready, true, "waiting for longjmp"))
+ goto cleanup;
+
+ /* CHILD IS STOPPED */
+ debug_printf ("child is alive (but stopped)");
+
+ /* Initialize, in order: data, bss, heap, stack, dll data, dll bss
+ Note: variables marked as NO_COPY will not be copied
+ since they are placed in a protected segment. */
+
+
+ MALLOC_CHECK;
+ void *impure_beg;
+ void *impure_end;
+ if (&_my_tls == _main_tls)
+ impure_beg = impure_end = NULL;
+ else
+ {
+ impure_beg = _impure_ptr;
+ impure_end = _impure_ptr + 1;
+ }
+ rc = fork_copy (pi, "user/cygwin data",
+ user_data->data_start, user_data->data_end,
+ user_data->bss_start, user_data->bss_end,
+ cygheap->user_heap.base, cygheap->user_heap.ptr,
+ stack_here, ch.stackbottom,
+ dll_data_start, dll_data_end,
+ dll_bss_start, dll_bss_end, impure_beg, impure_end, NULL);
+
+ __malloc_unlock ();
+ MALLOC_CHECK;
+ if (!rc)
+ goto cleanup;
+
+ /* Now fill data/bss of any DLLs that were linked into the program. */
+ for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ())
+ {
+ debug_printf ("copying data/bss of a linked dll");
+ if (!fork_copy (pi, "linked dll data/bss", d->p.data_start, d->p.data_end,
+ d->p.bss_start, d->p.bss_end,
+ NULL))
+ goto cleanup;
+ }
+
+ /* Start thread, and wait for it to reload dlls. */
+ if (!resume_child (pi, forker_finished) ||
+ !sync_with_child (pi, subproc_ready, load_dlls, "child loading dlls"))
+ goto cleanup;
+
+ /* If DLLs were loaded in the parent, then the child has reloaded all
+ of them and is now waiting to have all of the individual data and
+ bss sections filled in. */
+ if (load_dlls)
+ {
+ /* CHILD IS STOPPED */
+ /* write memory of reloaded dlls */
+ for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
+ {
+ debug_printf ("copying data/bss for a loaded dll");
+ if (!fork_copy (pi, "loaded dll data/bss", d->p.data_start, d->p.data_end,
+ d->p.bss_start, d->p.bss_end,
+ NULL))
+ goto cleanup;
+ }
+ /* Start the child up again. */
+ (void) resume_child (pi, forker_finished);
+ }
+
+ ForceCloseHandle (subproc_ready);
+ ForceCloseHandle (pi.hThread);
+ ForceCloseHandle (forker_finished);
+ forker_finished = NULL;
+ pi.hThread = NULL;
+ pthread::atforkparent ();
+
+ return forked->pid;
+
+/* Common cleanup code for failure cases */
+ cleanup:
+ /* Remember to de-allocate the fd table. */
+ if (pi.hProcess)
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+ if (pi.hThread)
+ ForceCloseHandle (pi.hThread);
+ if (subproc_ready)
+ ForceCloseHandle (subproc_ready);
+ if (forker_finished)
+ ForceCloseHandle (forker_finished);
+ return -1;
+}
+
+extern "C" int
+fork ()
+{
+ struct
+ {
+ HANDLE hParent;
+ dll *first_dll;
+ bool load_dlls;
+ } grouped;
+
+ MALLOC_CHECK;
+
+ debug_printf ("entering");
+ grouped.hParent = grouped.first_dll = NULL;
+ grouped.load_dlls = 0;
+
+ void *esp;
+ __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+
+ myself->set_has_pgid_children ();
+
+ child_info_fork ch;
+
+ int res = setjmp (ch.jmp);
+
+ if (res)
+ res = fork_child (grouped.hParent, grouped.first_dll, grouped.load_dlls);
+ else
+ res = fork_parent (grouped.hParent, grouped.first_dll, grouped.load_dlls, esp, ch);
+
+ MALLOC_CHECK;
+ syscall_printf ("%d = fork()", res);
+ return res;
+}
+#ifdef DEBUGGING
+void
+fork_init ()
+{
+ char buf[1024];
+ if (!GetEnvironmentVariable ("CYGWIN_FORK_PIDS", buf, 1024))
+ return;
+ pid_t pid;
+ char *p, *pe;
+ for (p = buf; (pid = strtol (p, &pe, 10)); p = pe)
+ fork_pids[npid_max++] = pid;
+}
+#endif /*DEBUGGING*/
+
+#ifdef NEWVFORK
+/* Dummy function to force second assignment below to actually be
+ carried out */
+static vfork_save *
+get_vfork_val ()
+{
+ return vfork_storage.val ();
+}
+#endif
+
+extern "C" int
+vfork ()
+{
+#ifndef NEWVFORK
+ return fork ();
+#else
+ vfork_save *vf = get_vfork_val ();
+ char **esp, **pp;
+
+ if (vf == NULL)
+ vf = vfork_storage.create ();
+ else if (vf->pid)
+ return fork ();
+
+ // FIXME the tls stuff could introduce a signal race if a child process
+ // exits quickly.
+ if (!setjmp (vf->j))
+ {
+ vf->pid = -1;
+ __asm__ volatile ("movl %%esp,%0": "=r" (vf->vfork_esp):);
+ __asm__ volatile ("movl %%ebp,%0": "=r" (vf->vfork_ebp):);
+ for (pp = (char **) vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *pp = *esp;
+ vf->ctty = myself->ctty;
+ vf->sid = myself->sid;
+ vf->pgid = myself->pgid;
+ cygheap->ctty_on_hold = cygheap->ctty;
+ vf->open_fhs = cygheap->open_fhs;
+ debug_printf ("cygheap->ctty_on_hold %p, cygheap->open_fhs %d", cygheap->ctty_on_hold, cygheap->open_fhs);
+ int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
+ debug_printf ("%d = vfork()", res);
+ call_signal_handler_now (); // FIXME: racy
+ vf->tls = _my_tls;
+ return res;
+ }
+
+ vf = get_vfork_val ();
+
+ for (pp = (char **) vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *esp = *pp;
+
+ cygheap->fdtab.vfork_parent_restore ();
+
+ myself->ctty = vf->ctty;
+ myself->sid = vf->sid;
+ myself->pgid = vf->pgid;
+ termios_printf ("cygheap->ctty %p, cygheap->ctty_on_hold %p", cygheap->ctty, cygheap->ctty_on_hold);
+ cygheap->open_fhs = vf->open_fhs;
+
+ if (vf->pid < 0)
+ {
+ int exitval = vf->exitval;
+ vf->pid = 0;
+ if ((vf->pid = fork ()) == 0)
+ exit (exitval);
+ }
+
+ int pid = vf->pid;
+ vf->pid = 0;
+ debug_printf ("exiting vfork, pid %d", pid);
+ sig_dispatch_pending ();
+
+ call_signal_handler_now (); // FIXME: racy
+ _my_tls = vf->tls;
+ return pid;
+#endif
+}
diff --git a/winsup/cygwin/heap.cc b/winsup/cygwin/heap.cc
new file mode 100644
index 00000000000..af5ec2fe5f9
--- /dev/null
+++ b/winsup/cygwin/heap.cc
@@ -0,0 +1,164 @@
+/* heap.cc: Cygwin heap manager.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 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 "winsup.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "heap.h"
+#include "shared_info.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "registry.h"
+#include "cygwin_version.h"
+
+#define assert(x)
+
+static unsigned page_const;
+
+extern "C" size_t getpagesize ();
+
+#define MINHEAP_SIZE (4 * 1024 * 1024)
+
+/* Initialize the heap at process start up. */
+void
+heap_init ()
+{
+ /* If we're the forkee, we must allocate the heap at exactly the same place
+ as our parent. If not, we don't care where it ends up. */
+
+ page_const = system_info.dwPageSize;
+ if (!cygheap->user_heap.base)
+ {
+ cygheap->user_heap.chunk = cygwin_shared->heap_chunk_size ();
+ while (cygheap->user_heap.chunk >= MINHEAP_SIZE)
+ {
+ /* Initialize page mask and default heap size. Preallocate a heap
+ * to assure contiguous memory. */
+ cygheap->user_heap.ptr = cygheap->user_heap.top =
+ cygheap->user_heap.base =
+ VirtualAlloc (NULL, cygheap->user_heap.chunk, MEM_RESERVE, PAGE_NOACCESS);
+ if (cygheap->user_heap.base)
+ break;
+ cygheap->user_heap.chunk -= 1 * 1024 * 1024;
+ }
+ if (cygheap->user_heap.base == NULL)
+ api_fatal ("unable to allocate heap, heap_chunk_size %d, %E",
+ cygheap->user_heap.chunk);
+ cygheap->user_heap.max = (char *) cygheap->user_heap.base + cygheap->user_heap.chunk;
+ }
+ else
+ {
+ DWORD chunk = cygheap->user_heap.chunk; /* allocation chunk */
+ /* total size commited in parent */
+ DWORD allocsize = (char *) cygheap->user_heap.top -
+ (char *) cygheap->user_heap.base;
+
+ /* Loop until we've managed to reserve an adequate amount of memory. */
+ char *p;
+ DWORD reserve_size = chunk * ((allocsize + (chunk - 1)) / chunk);
+ while (1)
+ {
+ p = (char *) VirtualAlloc (cygheap->user_heap.base, reserve_size,
+ MEM_RESERVE, PAGE_READWRITE);
+ if (p)
+ break;
+ if ((reserve_size -= page_const) <= allocsize)
+ break;
+ }
+ if (!p)
+ api_fatal ("couldn't allocate cygwin heap, %E, base %p, top %p, "
+ "reserve_size %d, allocsize %d, page_const %d",
+ cygheap->user_heap.base, cygheap->user_heap.top,
+ reserve_size, allocsize, page_const);
+ if (p != cygheap->user_heap.base)
+ api_fatal ("heap allocated at wrong address %p (mapped) != %p (expected)", p, cygheap->user_heap.base);
+ if (!VirtualAlloc (cygheap->user_heap.base, allocsize, MEM_COMMIT, PAGE_READWRITE))
+ api_fatal ("MEM_COMMIT failed, %E");
+ }
+
+ debug_printf ("heap base %p, heap top %p", cygheap->user_heap.base,
+ cygheap->user_heap.top);
+ page_const--;
+ // malloc_init ();
+}
+
+#define pround(n) (((size_t)(n) + page_const) & ~page_const)
+
+/* FIXME: This function no longer handles "split heaps". */
+
+extern "C" void *
+sbrk (int n)
+{
+ char *newtop, *newbrk;
+ unsigned commitbytes, newbrksize;
+
+ if (n == 0)
+ return cygheap->user_heap.ptr; /* Just wanted to find current cygheap->user_heap.ptr address */
+
+ newbrk = (char *) cygheap->user_heap.ptr + n; /* Where new cygheap->user_heap.ptr will be */
+ newtop = (char *) pround (newbrk); /* Actual top of allocated memory -
+ on page boundary */
+
+ if (newtop == cygheap->user_heap.top)
+ goto good;
+
+ if (n < 0)
+ { /* Freeing memory */
+ assert (newtop < cygheap->user_heap.top);
+ n = (char *) cygheap->user_heap.top - newtop;
+ if (VirtualFree (newtop, n, MEM_DECOMMIT)) /* Give it back to OS */
+ goto good; /* Didn't take */
+ else
+ goto err;
+ }
+
+ assert (newtop > cygheap->user_heap.top);
+
+ /* Find the number of bytes to commit, rounded up to the nearest page. */
+ commitbytes = pround (newtop - (char *) cygheap->user_heap.top);
+
+ /* Need to grab more pages from the OS. If this fails it may be because
+ we have used up previously reserved memory. Or, we're just plumb out
+ of memory. Only attempt to commit memory that we know we've previously
+ reserved. */
+ if (newtop <= cygheap->user_heap.max)
+ {
+ if (VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL)
+ goto good;
+ }
+
+ /* Couldn't allocate memory. Maybe we can reserve some more.
+ Reserve either the maximum of the standard cygwin_shared->heap_chunk_size ()
+ or the requested amount. Then attempt to actually allocate it. */
+ if ((newbrksize = cygheap->user_heap.chunk) < commitbytes)
+ newbrksize = commitbytes;
+
+ if ((VirtualAlloc (cygheap->user_heap.top, newbrksize, MEM_RESERVE, PAGE_NOACCESS)
+ || VirtualAlloc (cygheap->user_heap.top, newbrksize = commitbytes, MEM_RESERVE, PAGE_NOACCESS))
+ && VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL)
+ {
+ cygheap->user_heap.max = (char *) cygheap->user_heap.max + pround (newbrksize);
+ goto good;
+ }
+
+err:
+ set_errno (ENOMEM);
+ return (void *) -1;
+
+good:
+ void *oldbrk = cygheap->user_heap.ptr;
+ cygheap->user_heap.ptr = newbrk;
+ cygheap->user_heap.top = newtop;
+ return oldbrk;
+}
diff --git a/winsup/cygwin/init.cc b/winsup/cygwin/init.cc
new file mode 100644
index 00000000000..5ca52949a0f
--- /dev/null
+++ b/winsup/cygwin/init.cc
@@ -0,0 +1,111 @@
+/* init.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 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 "winsup.h"
+#include <stdlib.h>
+#include "thread.h"
+#include "perprocess.h"
+#include "cygthread.h"
+#include "cygtls.h"
+
+int NO_COPY dynamically_loaded;
+static char *search_for = (char *) cygthread::stub;
+static unsigned threadfunc_ix __attribute__((section ("cygwin_dll_common"), shared)) = 0;
+DWORD tls_func;
+
+HANDLE sync_startup;
+
+#define OLDFUNC_OFFSET -3
+
+static void WINAPI
+threadfunc_fe (VOID *arg)
+{
+ void *threadfunc = (void *) TlsGetValue (tls_func);
+ TlsFree (tls_func);
+ // _threadinfo::call ((DWORD (*) (void *, void *)) (((char **) _tlsbase)[OLDFUNC_OFFSET]));
+ _threadinfo::call ((DWORD (*) (void *, void *)) (threadfunc), arg);
+}
+
+static DWORD WINAPI
+calibration_thread (VOID *arg)
+{
+ ExitThread (0);
+}
+
+static void
+munge_threadfunc (HANDLE cygwin_hmodule)
+{
+ char **ebp = (char **) __builtin_frame_address (0);
+ if (!threadfunc_ix)
+ {
+ for (char **peb = ebp; peb < (char **) _tlsbase; peb++)
+ if (*peb == search_for)
+ {
+ threadfunc_ix = peb - ebp;
+ goto foundit;
+ }
+#ifdef DEBUGGING
+ system_printf ("non-fatal warning: unknown thread! search_for %p, cygthread::stub %p, calibration_thread %p, possible func offset %p",
+ search_for, cygthread::stub, calibration_thread, ebp[137]);
+#endif
+ try_to_debug ();
+ return;
+ }
+
+foundit:
+ char *threadfunc = ebp[threadfunc_ix];
+ if (0 & (((DWORD) threadfunc & 0x80000000) == 0x80000000 || threadfunc == (char *) calibration_thread))
+ /*nothing*/;
+ else
+ {
+ ebp[threadfunc_ix] = (char *) threadfunc_fe;
+ // ((char **) _tlsbase)[OLDFUNC_OFFSET] = threadfunc;
+ TlsSetValue (tls_func, (void *) threadfunc);
+ }
+}
+
+void
+prime_threads ()
+{
+ tls_func = TlsAlloc ();
+ if (!threadfunc_ix)
+ {
+ DWORD id;
+ search_for = (char *) calibration_thread;
+ sync_startup = CreateThread (NULL, 0, calibration_thread, 0, 0, &id);
+ }
+}
+
+extern void __stdcall dll_crt0_0 ();
+
+extern "C" int WINAPI
+dll_entry (HANDLE h, DWORD reason, void *static_load)
+{
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ dynamically_loaded = (static_load == NULL);
+ // __cygwin_user_data.impure_ptr = &_my_tls.local_clib;
+ prime_threads ();
+ dll_crt0_0 ();
+ // small_printf ("%u, %p, %p\n", cygwin_pid (GetCurrentProcessId ()), _tlstop, _tlsbase);
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ case DLL_THREAD_ATTACH:
+ munge_threadfunc (h);
+ // small_printf ("%u, %p, %p\n", cygwin_pid (GetCurrentProcessId ()), _tlstop, _tlsbase);
+ break;
+ case DLL_THREAD_DETACH:
+ _my_tls.remove (0);
+ break;
+ }
+ return 1;
+}
diff --git a/winsup/cygwin/malloc_wrapper.cc b/winsup/cygwin/malloc_wrapper.cc
new file mode 100644
index 00000000000..7bcd181c5d6
--- /dev/null
+++ b/winsup/cygwin/malloc_wrapper.cc
@@ -0,0 +1,334 @@
+/* malloc_wrapper.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+ Originally written by Steve Chamberlain of Cygnus Support
+ sac@cygnus.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. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include <assert.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "cygheap.h"
+#include "heap.h"
+#include "sync.h"
+#include "perprocess.h"
+#include "cygmalloc.h"
+#include <malloc.h>
+extern "C" struct mallinfo dlmallinfo ();
+
+/* we provide these stubs to call into a user's
+ provided malloc if there is one - otherwise
+ functions we provide - like strdup will cause
+ problems if malloced on our heap and free'd on theirs.
+*/
+
+static int export_malloc_called;
+static int use_internal_malloc = 1;
+
+#ifdef MALLOC_DEBUG
+extern "C" void * _sbrk (size_t incr_arg);
+
+#if 0
+extern "C" void *
+_sbrk_r (struct _reent *, size_t incr_arg)
+{
+ return _sbrk (incr_arg);
+}
+#endif
+
+extern "C" void *
+_malloc_r (struct _reent *, size_t size)
+{
+ export_malloc_called = 1;
+ return malloc (size);
+}
+#undef malloc
+
+extern "C" void *
+_calloc_r (struct _reent *, size_t nmemb, size_t size)
+{
+ export_malloc_called = 1;
+ return calloc (nmemb, size);
+}
+#undef calloc
+
+extern "C" void
+_free_r (struct _reent *, void *p)
+{
+ export_malloc_called = 1;
+ assert (!incygheap (p));
+ assert (inheap (p));
+ free (p);
+}
+#undef free
+
+extern "C" void *
+_realloc_r (struct _reent *, void *p, size_t size)
+{
+ export_malloc_called = 1;
+ assert (!incygheap (p));
+ assert (inheap (p));
+ return realloc (p, size);
+}
+#undef realloc
+
+extern "C" char *
+strdup_dbg (const char *s, const char *file, int line)
+{
+ char *p;
+ export_malloc_called = 1;
+ if ((p = (char *) malloc_dbg (strlen (s) + 1, file, line)) != NULL)
+ strcpy (p, s);
+ return p;
+}
+
+#undef strdup
+extern "C" char *
+strdup (const char *s)
+{
+ return strdup_dbg (s, __FILE__, __LINE__);
+}
+#else
+#endif
+/* These routines are used by the application if it
+ doesn't provide its own malloc. */
+
+extern "C" void
+free (void *p)
+{
+ malloc_printf ("(%p), called by %p", p, __builtin_return_address (0));
+ if (!use_internal_malloc)
+ user_data->free (p);
+ else
+ {
+ __malloc_lock ();
+ dlfree (p);
+ __malloc_unlock ();
+ }
+}
+
+extern "C" void *
+malloc (size_t size)
+{
+ void *res;
+ export_malloc_called = 1;
+ if (!use_internal_malloc)
+ res = user_data->malloc (size);
+ else
+ {
+ __malloc_lock ();
+ res = dlmalloc (size);
+ __malloc_unlock ();
+ }
+ malloc_printf ("(%d) = %x, called by %p", size, res, __builtin_return_address (0));
+ return res;
+}
+
+extern "C" void *
+realloc (void *p, size_t size)
+{
+ void *res;
+ if (!use_internal_malloc)
+ res = user_data->realloc (p, size);
+ else
+ {
+ __malloc_lock ();
+ res = dlrealloc (p, size);
+ __malloc_unlock ();
+ }
+ malloc_printf ("(%x, %d) = %x, called by %x", p, size, res, __builtin_return_address (0));
+ return res;
+}
+
+extern "C" void *
+calloc (size_t nmemb, size_t size)
+{
+ void *res;
+ if (!use_internal_malloc)
+ res = user_data->calloc (nmemb, size);
+ else
+ {
+ __malloc_lock ();
+ res = dlcalloc (nmemb, size);
+ __malloc_unlock ();
+ }
+ malloc_printf ("(%d, %d) = %x, called by %x", nmemb, size, res, __builtin_return_address (0));
+ return res;
+}
+
+extern "C" void *
+memalign (size_t alignment, size_t bytes)
+{
+ void *res;
+ if (!use_internal_malloc)
+ {
+ set_errno (ENOSYS);
+ res = NULL;
+ }
+ else
+ {
+ __malloc_lock ();
+ res = dlmemalign (alignment, bytes);
+ __malloc_unlock ();
+ }
+
+ return res;
+}
+
+extern "C" void *
+valloc (size_t bytes)
+{
+ void *res;
+ if (!use_internal_malloc)
+ {
+ set_errno (ENOSYS);
+ res = NULL;
+ }
+ else
+ {
+ __malloc_lock ();
+ res = dlvalloc (bytes);
+ __malloc_unlock ();
+ }
+
+ return res;
+}
+
+extern "C" size_t
+malloc_usable_size (void *p)
+{
+ size_t res;
+ if (!use_internal_malloc)
+ {
+ set_errno (ENOSYS);
+ res = 0;
+ }
+ else
+ {
+ __malloc_lock ();
+ res = dlmalloc_usable_size (p);
+ __malloc_unlock ();
+ }
+
+ return res;
+}
+
+extern "C" int
+malloc_trim (size_t pad)
+{
+ size_t res;
+ if (!use_internal_malloc)
+ {
+ set_errno (ENOSYS);
+ res = 0;
+ }
+ else
+ {
+ __malloc_lock ();
+ res = dlmalloc_trim (pad);
+ __malloc_unlock ();
+ }
+
+ return res;
+}
+
+extern "C" int
+mallopt (int p, int v)
+{
+ int res;
+ if (!use_internal_malloc)
+ {
+ set_errno (ENOSYS);
+ res = 0;
+ }
+ else
+ {
+ __malloc_lock ();
+ res = dlmallopt (p, v);
+ __malloc_unlock ();
+ }
+
+ return res;
+}
+
+extern "C" void
+malloc_stats ()
+{
+ if (!use_internal_malloc)
+ set_errno (ENOSYS);
+ else
+ {
+ __malloc_lock ();
+ dlmalloc_stats ();
+ __malloc_unlock ();
+ }
+
+ return;
+}
+
+extern "C" struct mallinfo
+mallinfo ()
+{
+ struct mallinfo m;
+ if (!use_internal_malloc)
+ set_errno (ENOSYS);
+ else
+ {
+ __malloc_lock ();
+ m = dlmallinfo ();
+ __malloc_unlock ();
+ }
+
+ return m;
+}
+
+extern "C" char *
+strdup (const char *s)
+{
+ char *p;
+ size_t len = strlen (s) + 1;
+ if ((p = (char *) malloc (len)) != NULL)
+ memcpy (p, s, len);
+ return p;
+}
+
+/* We use a critical section to lock access to the malloc data
+ structures. This permits malloc to be called from different
+ threads. Note that it does not make malloc reentrant, and it does
+ not permit a signal handler to call malloc. The malloc code in
+ newlib will call __malloc_lock and __malloc_unlock at appropriate
+ times. */
+
+NO_COPY muto *mallock = NULL;
+
+void
+malloc_init ()
+{
+ new_muto (mallock);
+
+ /* Check if mallock is provided by application. If so, redirect all
+ calls to malloc/free/realloc to application provided. This may
+ happen if some other dll calls cygwin's malloc, but main code provides
+ its own malloc */
+ if (!user_data->forkee)
+ {
+#ifdef MALLOC_DEBUG
+ _free_r (NULL, _malloc_r (NULL, 16));
+#else
+ user_data->free (user_data->malloc (16));
+#endif
+ if (!export_malloc_called)
+ use_internal_malloc = 0;
+ }
+}
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
new file mode 100644
index 00000000000..79912b981df
--- /dev/null
+++ b/winsup/cygwin/sigproc.cc
@@ -0,0 +1,1263 @@
+/* sigproc.cc: inter/intra signal and sub process handler
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
+
+ Written by Christopher Faylor
+
+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 "winsup.h"
+#include <stdlib.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <sys/signal.h>
+#include "cygerrno.h"
+#include "sync.h"
+#include "pinfo.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info_magic.h"
+#include "shared_info.h"
+#include "cygthread.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "perthread.h"
+#include "exceptions.h"
+
+/*
+ * Convenience defines
+ */
+#define WSSC 60000 // Wait for signal completion
+#define WPSP 40000 // Wait for proc_subproc mutex
+
+#define PSIZE 63 // Number of processes
+
+#define wake_wait_subproc() SetEvent (events[0])
+
+#define no_signals_available() (!hwait_sig || (myself->sendsig == INVALID_HANDLE_VALUE) || exit_state)
+
+#define NZOMBIES 256
+
+struct sigelem
+{
+ int sig;
+ int pid;
+ _threadinfo *tls;
+ class sigelem *next;
+ friend class pending_signals;
+ friend int __stdcall sig_dispatch_pending ();
+};
+
+class pending_signals
+{
+ sigelem sigs[NSIG + 1];
+ sigelem start;
+ sigelem *end;
+ sigelem *prev;
+ sigelem *curr;
+ int empty;
+public:
+ void reset () {curr = &start; prev = &start;}
+ void add (int sig, int pid, _threadinfo *tls);
+ void del ();
+ sigelem *next ();
+ friend int __stdcall sig_dispatch_pending ();
+};
+
+struct sigpacket
+{
+ int sig;
+ pid_t pid;
+ HANDLE wakeup;
+ sigset_t *mask;
+ _threadinfo *tls;
+};
+
+static pending_signals sigqueue;
+
+struct sigaction *global_sigs;
+
+void __stdcall
+sigalloc ()
+{
+ cygheap->sigs = global_sigs =
+ (struct sigaction *) ccalloc (HEAP_SIGS, NSIG, sizeof (struct sigaction));
+}
+
+void __stdcall
+signal_fixup_after_exec ()
+{
+ global_sigs = cygheap->sigs;
+ /* Set up child's signal handlers */
+ for (int i = 0; i < NSIG; i++)
+ {
+ global_sigs[i].sa_mask = 0;
+ if (global_sigs[i].sa_handler != SIG_IGN)
+ global_sigs[i].sa_handler = SIG_DFL;
+ }
+}
+
+/*
+ * Global variables
+ */
+const char *__sp_fn ;
+int __sp_ln;
+
+char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to
+ // current process but no wait is required
+HANDLE NO_COPY signal_arrived; // Event signaled when a signal has
+ // resulted in a user-specified
+ // function call
+/*
+ * Common variables
+ */
+
+
+/* How long to wait for message/signals. Normally this is infinite.
+ * On termination, however, these are set to zero as a flag to exit.
+ */
+
+#define Static static NO_COPY
+
+Static DWORD proc_loop_wait = 1000; // Wait for subprocesses to exit
+
+Static HANDLE sigcomplete_main; // Event signaled when a signal has
+ // finished processing for the main
+ // thread
+HANDLE NO_COPY sigCONT; // Used to "STOP" a process
+Static cygthread *hwait_sig; // Handle of wait_sig thread
+Static cygthread *hwait_subproc; // Handle of sig_subproc thread
+
+Static HANDLE wait_sig_inited; // Control synchronization of
+ // message queue startup
+
+/* Used by WaitForMultipleObjects. These are handles to child processes.
+ */
+Static HANDLE events[PSIZE + 1]; // All my children's handles++
+#define hchildren (events + 1) // Where the children handles begin
+Static char cpchildren[PSIZE * sizeof (pinfo)]; // All my children info
+Static int nchildren; // Number of active children
+Static char czombies[(NZOMBIES + 1) * sizeof (pinfo)]; // All my deceased children info
+Static int nzombies; // Number of deceased children
+
+#define pchildren ((pinfo *) cpchildren)
+#define zombies ((pinfo *) czombies)
+
+Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads
+Static waitq waitq_main; // Storage for main thread
+
+muto NO_COPY *sync_proc_subproc = NULL; // Control access to subproc stuff
+
+DWORD NO_COPY sigtid = 0; // ID of the signal thread
+
+/* Functions
+ */
+static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1)));
+static __inline__ bool get_proc_lock (DWORD, DWORD);
+static void __stdcall remove_zombie (int);
+static DWORD WINAPI wait_sig (VOID *arg);
+static int __stdcall stopped_or_terminated (waitq *, _pinfo *);
+static DWORD WINAPI wait_subproc (VOID *);
+
+/* Determine if the parent process is alive.
+ */
+
+bool __stdcall
+my_parent_is_alive ()
+{
+ bool res;
+ if (!myself->ppid_handle)
+ {
+ debug_printf ("No myself->ppid_handle");
+ res = false;
+ }
+ else
+ for (int i = 0; i < 2; i++)
+ switch (res = WaitForSingleObject (myself->ppid_handle, 0))
+ {
+ case WAIT_OBJECT_0:
+ debug_printf ("parent dead.");
+ res = false;
+ goto out;
+ case WAIT_TIMEOUT:
+ debug_printf ("parent still alive");
+ res = true;
+ goto out;
+ case WAIT_FAILED:
+ DWORD werr = GetLastError ();
+ if (werr == ERROR_INVALID_HANDLE && i == 0)
+ continue;
+ system_printf ("WFSO for myself->ppid_handle(%p) failed, error %d",
+ myself->ppid_handle, werr);
+ res = false;
+ goto out;
+ }
+out:
+ return res;
+}
+
+void __stdcall
+wait_for_sigthread ()
+{
+ sigproc_printf ("wait_sig_inited %p", wait_sig_inited);
+ HANDLE hsig_inited = wait_sig_inited;
+ (void) WaitForSingleObject (hsig_inited, INFINITE);
+ wait_sig_inited = NULL;
+ (void) ForceCloseHandle1 (hsig_inited, wait_sig_inited);
+}
+
+/* Get the sync_proc_subproc muto to control access to
+ * children, zombie arrays.
+ * Attempt to handle case where process is exiting as we try to grab
+ * the mutex.
+ */
+static bool
+get_proc_lock (DWORD what, DWORD val)
+{
+ Static int lastwhat = -1;
+ if (!sync_proc_subproc)
+ {
+ sigproc_printf ("sync_proc_subproc is NULL (1)");
+ return false;
+ }
+ if (sync_proc_subproc->acquire (WPSP))
+ {
+ lastwhat = what;
+ return true;
+ }
+ if (!sync_proc_subproc)
+ {
+ sigproc_printf ("sync_proc_subproc is NULL (2)");
+ return false;
+ }
+ system_printf ("Couldn't aquire sync_proc_subproc for(%d,%d), %E, last %d",
+ what, val, lastwhat);
+ return true;
+}
+
+static bool __stdcall
+proc_can_be_signalled (_pinfo *p)
+{
+ if (p->sendsig == INVALID_HANDLE_VALUE)
+ {
+ set_errno (EPERM);
+ return false;
+ }
+
+ if (p == myself_nowait || p == myself)
+ return hwait_sig;
+
+ if (ISSTATE (p, PID_INITIALIZING) ||
+ (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) ==
+ (PID_ACTIVE | PID_IN_USE)))
+ return true;
+
+ set_errno (ESRCH);
+ return false;
+}
+
+bool __stdcall
+pid_exists (pid_t pid)
+{
+ pinfo p (pid);
+ return proc_exists (p);
+}
+
+/* Test to determine if a process really exists and is processing signals.
+ */
+bool __stdcall
+proc_exists (_pinfo *p)
+{
+ return p && !(p->process_state & (PID_EXITED | PID_ZOMBIE));
+}
+
+/* Return 1 if this is one of our children, zero otherwise.
+ FIXME: This really should be integrated with the rest of the proc_subproc
+ testing. Scanning these lists twice is inefficient. */
+int __stdcall
+mychild (int pid)
+{
+ for (int i = 0; i < nchildren; i++)
+ if (pchildren[i]->pid == pid)
+ return 1;
+ for (int i = 0; i < nzombies; i++)
+ if (zombies[i]->pid == pid)
+ return 1;
+ return 0;
+}
+
+/* Handle all subprocess requests
+ */
+#define vchild (*((pinfo *) val))
+int __stdcall
+proc_subproc (DWORD what, DWORD val)
+{
+ int rc = 1;
+ int potential_match;
+ _pinfo *child;
+ int clearing;
+ waitq *w;
+ int thiszombie;
+
+#define wval ((waitq *) val)
+
+ sigproc_printf ("args: %x, %d", what, val);
+
+ if (!get_proc_lock (what, val)) // Serialize access to this function
+ {
+ system_printf ("couldn't get proc lock. what %d, val %d", what, val);
+ goto out1;
+ }
+
+ switch (what)
+ {
+ /* Add a new subprocess to the children arrays.
+ * (usually called from the main thread)
+ */
+ case PROC_ADDCHILD:
+ if (nchildren >= PSIZE - 1)
+ {
+ rc = 0;
+ break;
+ }
+ pchildren[nchildren] = vchild;
+ hchildren[nchildren] = vchild->hProcess;
+ if (!DuplicateHandle (hMainProc, vchild->hProcess, hMainProc, &vchild->pid_handle,
+ 0, 0, DUPLICATE_SAME_ACCESS))
+ system_printf ("Couldn't duplicate child handle for pid %d, %E", vchild->pid);
+ ProtectHandle1 (vchild->pid_handle, pid_handle);
+
+ if (!DuplicateHandle (hMainProc, hMainProc, vchild->hProcess, &vchild->ppid_handle,
+ SYNCHRONIZE | PROCESS_DUP_HANDLE, TRUE, 0))
+ system_printf ("Couldn't duplicate my handle<%p> for pid %d, %E", hMainProc, vchild->pid);
+ vchild->ppid = myself->pid;
+ vchild->uid = myself->uid;
+ vchild->gid = myself->gid;
+ vchild->pgid = myself->pgid;
+ vchild->sid = myself->sid;
+ vchild->ctty = myself->ctty;
+ vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY);
+
+ sigproc_printf ("added pid %d to wait list, slot %d, winpid %p, handle %p",
+ vchild->pid, nchildren, vchild->dwProcessId,
+ vchild->hProcess);
+ nchildren++;
+
+ wake_wait_subproc ();
+ break;
+
+ /* A child process had terminated.
+ Possibly this is just due to an exec(). Cygwin implements an exec()
+ as a "handoff" from one windows process to another. If child->hProcess
+ is different from what is recorded in hchildren, then this is an exec().
+ Otherwise this is a normal child termination event.
+ (called from wait_subproc thread) */
+ case PROC_CHILDTERMINATED:
+ if (hchildren[val] != pchildren[val]->hProcess)
+ {
+ sigproc_printf ("pid %d[%d], reparented old hProcess %p, new %p",
+ pchildren[val]->pid, val, hchildren[val], pchildren[val]->hProcess);
+ HANDLE h = hchildren[val];
+ hchildren[val] = pchildren[val]->hProcess; /* Filled out by child */
+ sync_proc_subproc->release (); // Release the lock ASAP
+ ForceCloseHandle1 (h, childhProc);
+ ProtectHandle1 (pchildren[val]->hProcess, childhProc);
+ rc = 0;
+ goto out; // This was an exec()
+ }
+
+ sigproc_printf ("pid %d[%d] terminated, handle %p, nchildren %d, nzombies %d",
+ pchildren[val]->pid, val, hchildren[val], nchildren, nzombies);
+
+ thiszombie = nzombies;
+ zombies[nzombies] = pchildren[val]; // Add to zombie array
+ zombies[nzombies++]->process_state = PID_ZOMBIE;// Walking dead
+
+ sigproc_printf ("zombifying [%d], pid %d, handle %p, nchildren %d",
+ val, pchildren[val]->pid, hchildren[val], nchildren);
+ if ((int) val < --nchildren)
+ {
+ hchildren[val] = hchildren[nchildren];
+ pchildren[val] = pchildren[nchildren];
+ }
+
+ /* See if we should care about the this terminated process. If we've
+ filled up our table or if we're ignoring SIGCHLD, then we immediately
+ remove the process and move on. Otherwise, this process becomes a zombie
+ which must be reaped by a wait() call. FIXME: This is a very inelegant
+ way to deal with this and could lead to process hangs. */
+ if (nzombies >= NZOMBIES)
+ {
+ sigproc_printf ("zombie table overflow %d", thiszombie);
+ remove_zombie (thiszombie);
+ }
+
+ /* Don't scan the wait queue yet. Caller will send SIGCHLD to this process.
+ This will cause an eventual scan of waiters. */
+ break;
+
+ /* Handle a wait4() operation. Allocates an event for the calling
+ * thread which is signaled when the appropriate pid exits or stops.
+ * (usually called from the main thread)
+ */
+ case PROC_WAIT:
+ wval->ev = NULL; // Don't know event flag yet
+
+ if (wval->pid <= 0)
+ child = NULL; // Not looking for a specific pid
+ else if (!mychild (wval->pid))
+ goto out; // invalid pid. flag no such child
+
+ wval->status = 0; // Don't know status yet
+ sigproc_printf ("wval->pid %d, wval->options %d", wval->pid, wval->options);
+
+ /* If the first time for this thread, create a new event, otherwise
+ * reset the event.
+ */
+ if ((wval->ev = wval->thread_ev) == NULL)
+ {
+ wval->ev = wval->thread_ev = CreateEvent (&sec_none_nih, TRUE,
+ FALSE, NULL);
+ ProtectHandle (wval->ev);
+ }
+
+ ResetEvent (wval->ev);
+ w = waitq_head.next;
+ waitq_head.next = wval; /* Add at the beginning. */
+ wval->next = w; /* Link in rest of the list. */
+ clearing = 0;
+ goto scan_wait;
+
+ /* Clear all waiting threads. Called from exceptions.cc prior to
+ the main thread's dispatch to a signal handler function.
+ (called from wait_sig thread) */
+ case PROC_CLEARWAIT:
+ /* Clear all "wait"ing threads. */
+ if (val)
+ sigproc_printf ("clear waiting threads");
+ else
+ sigproc_printf ("looking for processes to reap");
+ clearing = val;
+
+ scan_wait:
+ /* Scan the linked list of wait()ing threads. If a wait's parameters
+ * match this pid, then activate it.
+ */
+ for (w = &waitq_head; w->next != NULL; w = w->next)
+ {
+ if ((potential_match = checkstate (w)) > 0)
+ sigproc_printf ("released waiting thread");
+ else if (!clearing && !(w->next->options & WNOHANG) && potential_match < 0)
+ sigproc_printf ("only found non-terminated children");
+ else if (potential_match <= 0) // nothing matched
+ {
+ sigproc_printf ("waiting thread found no children");
+ HANDLE oldw = w->next->ev;
+ w->next->pid = 0;
+ if (clearing)
+ w->next->status = -1; /* flag that a signal was received */
+ else if (!potential_match || !(w->next->options & WNOHANG))
+ w->next->ev = NULL;
+ if (!SetEvent (oldw))
+ system_printf ("couldn't wake up wait event %p, %E", oldw);
+ w->next = w->next->next;
+ }
+ if (w->next == NULL)
+ break;
+ }
+
+ if (!clearing)
+ sigproc_printf ("finished processing terminated/stopped child");
+ else
+ {
+ waitq_head.next = NULL;
+ sigproc_printf ("finished clearing");
+ }
+
+ if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN)
+ while (nzombies)
+ remove_zombie (0);
+ break;
+ }
+
+out:
+ sync_proc_subproc->release (); // Release the lock
+out1:
+ sigproc_printf ("returning %d", rc);
+ return rc;
+}
+
+/* Terminate the wait_subproc thread.
+ * Called on process exit.
+ * Also called by spawn_guts to disassociate any subprocesses from this
+ * process. Subprocesses will then know to clean up after themselves and
+ * will not become zombies.
+ */
+void __stdcall
+proc_terminate (void)
+{
+ sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies);
+ /* Signal processing is assumed to be blocked in this routine. */
+ if (hwait_subproc)
+ {
+ proc_loop_wait = 0; // Tell wait_subproc thread to exit
+ sync_proc_subproc->acquire (WPSP);
+ wake_wait_subproc (); // Wake wait_subproc loop
+ hwait_subproc = NULL;
+
+ (void) proc_subproc (PROC_CLEARWAIT, 1);
+
+ /* Clean out zombie processes from the pid list. */
+ int i;
+ for (i = 0; i < nzombies; i++)
+ {
+ if (zombies[i]->hProcess)
+ {
+ ForceCloseHandle1 (zombies[i]->hProcess, childhProc);
+ ForceCloseHandle1 (zombies[i]->pid_handle, pid_handle);
+ }
+ zombies[i]->ppid = 1;
+ zombies[i]->process_state = PID_EXITED; /* CGF FIXME - still needed? */
+ zombies[i].release (); // FIXME: this breaks older gccs for some reason
+ }
+
+ /* Disassociate my subprocesses */
+ for (i = 0; i < nchildren; i++)
+ {
+ if (!pchildren[i]->hProcess)
+ sigproc_printf ("%d(%d) hProcess cleared already?", pchildren[i]->pid,
+ pchildren[i]->dwProcessId);
+ else
+ {
+ ForceCloseHandle1 (pchildren[i]->hProcess, childhProc);
+ sigproc_printf ("%d(%d) closed child handle", pchildren[i]->pid,
+ pchildren[i]->dwProcessId);
+ pchildren[i]->ppid = 1;
+ if (pchildren[i]->pgid == myself->pid)
+ pchildren[i]->process_state |= PID_ORPHANED;
+ }
+ pchildren[i].release ();
+ }
+ nchildren = nzombies = 0;
+ sync_proc_subproc = NULL;
+ }
+ sigproc_printf ("leaving");
+}
+
+/* Clear pending signal */
+void __stdcall
+sig_clear (int target_sig)
+{
+ if (GetCurrentThreadId () != sigtid)
+ sig_send (myself, -target_sig);
+ else
+ {
+ sigqueue.reset ();
+ sigelem *q;
+ while ((q = sigqueue.next ()))
+ if (q->sig == target_sig)
+ {
+ sigqueue.del ();
+ break;
+ }
+ }
+ return;
+}
+
+extern "C" int
+sigpending (sigset_t *mask)
+{
+ sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING);
+ if (outset == SIG_BAD_MASK)
+ return -1;
+ *mask = outset;
+ return 0;
+}
+
+/* Force the wait_sig thread to wake up and scan for pending signals */
+int __stdcall
+sig_dispatch_pending ()
+{
+ if (exit_state || GetCurrentThreadId () == sigtid || !sigqueue.start.next)
+ {
+#ifdef DEBUGGING
+ sigproc_printf ("exit_state %d, GetCurrentThreadId () %p, sigtid %p, sigqueue.start.next %p",
+ exit_state, GetCurrentThreadId (), sigtid, sigqueue.start.next);
+#endif
+ return 0;
+ }
+
+#ifdef DEBUGGING
+ sigproc_printf ("flushing");
+#endif
+ (void) sig_send (myself, __SIGFLUSH);
+ return call_signal_handler_now ();
+}
+
+/* Message initialization. Called from dll_crt0_1
+ *
+ * This routine starts the signal handling thread. The wait_sig_inited
+ * event is used to signal that the thread is ready to handle signals.
+ * We don't wait for this during initialization but instead detect it
+ * in sig_send to gain a little concurrency.
+ */
+void __stdcall
+sigproc_init ()
+{
+ wait_sig_inited = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (wait_sig_inited);
+
+ /* sync_proc_subproc is used by proc_subproc. It serialises
+ * access to the children and zombie arrays.
+ */
+ new_muto (sync_proc_subproc);
+
+ /* local event signaled when main thread has been dispatched
+ to a signal handler function. */
+ signal_arrived = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (signal_arrived);
+
+ hwait_sig = new cygthread (wait_sig, cygself, "sig");
+ hwait_sig->zap_h ();
+
+ /* Initialize waitq structure for main thread. A waitq structure is
+ * allocated for each thread that executes a wait to allow multiple threads
+ * to perform waits. Pre-allocate a waitq structure for the main thread.
+ */
+ waitq *w;
+ if ((w = (waitq *)waitq_storage.get ()) == NULL)
+ {
+ w = &waitq_main;
+ waitq_storage.set (w);
+ }
+ memset (w, 0, sizeof *w); // Just to be safe
+
+ global_sigs[SIGSTOP].sa_flags = SA_RESTART | SA_NODEFER;
+ sigproc_printf ("process/signal handling enabled(%x)", myself->process_state);
+ return;
+}
+
+/* Called on process termination to terminate signal and process threads.
+ */
+void __stdcall
+sigproc_terminate (void)
+{
+ hwait_sig = NULL;
+
+ if (myself->sendsig == INVALID_HANDLE_VALUE)
+ sigproc_printf ("sigproc handling not active");
+ else
+ {
+ sigproc_printf ("entering");
+ // finished with anything it is doing
+ ForceCloseHandle (sigcomplete_main);
+ HANDLE sendsig = myself->sendsig;
+ myself->sendsig = INVALID_HANDLE_VALUE;
+ CloseHandle (sendsig);
+ }
+ proc_terminate (); // Terminate process handling thread
+
+ return;
+}
+
+/* Send a signal to another process by raising its signal semaphore.
+ * If pinfo *p == NULL, send to the current process.
+ * If sending to this process, wait for notification that a signal has
+ * completed before returning.
+ */
+int __stdcall
+sig_send (_pinfo *p, int sig, void *tls)
+{
+ int rc = 1;
+ bool its_me;
+ HANDLE sendsig;
+ sigpacket pack;
+
+ bool wait_for_completion;
+ // FIXMENOW: Avoid using main thread's completion event!
+ if (!(its_me = (p == NULL || p == myself || p == myself_nowait)))
+ wait_for_completion = false;
+ else
+ {
+ if (no_signals_available ())
+ goto out; // Either exiting or not yet initializing
+ if (wait_sig_inited)
+ wait_for_sigthread ();
+ wait_for_completion = p != myself_nowait && _my_tls.isinitialized ();
+ p = myself;
+ }
+
+ /* It is possible that the process is not yet ready to receive messages
+ * or that it has exited. Detect this.
+ */
+ if (!proc_can_be_signalled (p)) /* Is the process accepting messages? */
+ {
+ sigproc_printf ("invalid pid %d(%x), signal %d",
+ p->pid, p->process_state, sig);
+ goto out;
+ }
+
+ sigproc_printf ("pid %d, signal %d, its_me %d", p->pid, sig, its_me);
+
+ if (its_me)
+ {
+ sendsig = myself->sendsig;
+ if (wait_for_completion)
+ pack.wakeup = sigcomplete_main;
+ }
+ else
+ {
+ HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId);
+ if (!hp)
+ {
+ sigproc_printf ("OpenProcess failed, %E");
+ __seterrno ();
+ goto out;
+ }
+ for (int i = 0; !p->sendsig && i < 10000; i++)
+ low_priority_sleep (0);
+ if (!DuplicateHandle (hp, p->sendsig, hMainProc, &sendsig, false, 0,
+ DUPLICATE_SAME_ACCESS) || !sendsig)
+ {
+ sigproc_printf ("DuplicateHandle failed, %E");
+ __seterrno ();
+ goto out;
+ }
+ CloseHandle (hp);
+ pack.wakeup = NULL;
+ }
+
+ sigset_t pending;
+ if (!its_me)
+ pack.mask = NULL;
+ else if (sig == __SIGPENDING)
+ pack.mask = &pending;
+ else if (sig == __SIGFLUSH || sig > 0)
+ pack.mask = &myself->getsigmask ();
+ else
+ pack.mask = NULL;
+
+ pack.sig = sig;
+ pack.pid = myself->pid;
+ pack.tls = (_threadinfo *) tls;
+ DWORD nb;
+ if (!WriteFile (sendsig, &pack, sizeof (pack), &nb, NULL) || nb != sizeof (pack))
+ {
+ /* Couldn't send to the pipe. This probably means that the
+ process is exiting. */
+ if (!its_me)
+ {
+ sigproc_printf ("WriteFile for pipe %p failed, %E", sendsig);
+ __seterrno ();
+ ForceCloseHandle (sendsig);
+ }
+ else
+ {
+ if (no_signals_available ())
+ sigproc_printf ("I'm going away now");
+ else
+ system_printf ("error sending signal %d to pid %d, pipe handle %p, %E",
+ sig, p->pid, sendsig);
+ }
+ goto out;
+ }
+
+
+ /* No need to wait for signal completion unless this was a signal to
+ this process.
+
+ If it was a signal to this process, wait for a dispatched signal.
+ Otherwise just wait for the wait_sig to signal that it has finished
+ processing the signal. */
+ if (wait_for_completion)
+ {
+ sigproc_printf ("Waiting for pack.wakeup %p", pack.wakeup);
+ rc = WaitForSingleObject (pack.wakeup, WSSC);
+ }
+ else
+ {
+ rc = WAIT_OBJECT_0;
+ sigproc_printf ("Not waiting for sigcomplete. its_me %d signal %d", its_me, sig);
+ if (!its_me)
+ ForceCloseHandle (sendsig);
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ rc = 0; // Successful exit
+ else
+ {
+ if (!no_signals_available ())
+ system_printf ("wait for sig_complete event failed, signal %d, rc %d, %E",
+ sig, rc);
+ set_errno (ENOSYS);
+ rc = -1;
+ }
+
+ if (wait_for_completion)
+ call_signal_handler_now ();
+
+out:
+ if (sig != __SIGPENDING)
+ /* nothing */;
+ else if (!rc)
+ rc = (int) pending;
+ else
+ rc = SIG_BAD_MASK;
+ sigproc_printf ("returning %p from sending signal %d", rc, sig);
+ return rc;
+}
+
+/* Initialize the wait_subproc thread.
+ * Called from fork() or spawn() to initialize the handling of subprocesses.
+ */
+void __stdcall
+subproc_init (void)
+{
+ if (hwait_subproc)
+ return;
+
+ /* A "wakeup" handle which can be toggled to make wait_subproc reexamine
+ * the hchildren array.
+ */
+ events[0] = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ hwait_subproc = new cygthread (wait_subproc, NULL, "proc");
+ hwait_subproc->zap_h ();
+ ProtectHandle (events[0]);
+ sigproc_printf ("started wait_subproc thread");
+}
+
+/* Initialize some of the memory block passed to child processes
+ by fork/spawn/exec. */
+
+void __stdcall
+init_child_info (DWORD chtype, child_info *ch, pid_t pid, HANDLE subproc_ready)
+{
+ memset (ch, 0, sizeof *ch);
+ ch->cb = chtype == PROC_FORK ? sizeof (child_info_fork) : sizeof (child_info);
+ ch->intro = PROC_MAGIC_GENERIC;
+ ch->magic = CHILD_INFO_MAGIC;
+ ch->type = chtype;
+ ch->cygpid = pid;
+ ch->subproc_ready = subproc_ready;
+ ch->pppid_handle = myself->ppid_handle;
+ ch->fhandler_union_cb = sizeof (fhandler_union);
+ ch->user_h = cygwin_user_h;
+}
+
+/* Check the state of all of our children to see if any are stopped or
+ * terminated.
+ */
+static int __stdcall
+checkstate (waitq *parent_w)
+{
+ int potential_match = 0;
+
+ sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies);
+
+ /* Check already dead processes first to see if they match the criteria
+ * given in w->next.
+ */
+ for (int i = 0; i < nzombies; i++)
+ switch (stopped_or_terminated (parent_w, zombies[i]))
+ {
+ case -1:
+ potential_match = -1;
+ break;
+ case 1:
+ remove_zombie (i);
+ potential_match = 1;
+ goto out;
+ }
+
+ sigproc_printf ("checking alive children");
+
+ /* No dead terminated children matched. Check for stopped children. */
+ for (int i = 0; i < nchildren; i++)
+ switch (stopped_or_terminated (parent_w, pchildren[i]))
+ {
+ case -1:
+ potential_match = -1;
+ break;
+ case 1:
+ potential_match = 1;
+ goto out;
+ }
+
+out:
+ sigproc_printf ("returning %d", potential_match);
+ return potential_match;
+}
+
+/* Remove a zombie from zombies by swapping it with the last child in the list.
+ */
+static void __stdcall
+remove_zombie (int ci)
+{
+ sigproc_printf ("removing %d, pid %d, nzombies %d", ci, zombies[ci]->pid,
+ nzombies);
+
+ if (zombies[ci])
+ {
+ ForceCloseHandle1 (zombies[ci]->hProcess, childhProc);
+ ForceCloseHandle1 (zombies[ci]->pid_handle, pid_handle);
+ zombies[ci].release ();
+ }
+
+ if (ci < --nzombies)
+ zombies[ci] = zombies[nzombies];
+
+ return;
+}
+
+/* Check status of child process vs. waitq member.
+ *
+ * parent_w is the pointer to the parent of the waitq member in question.
+ * child is the subprocess being considered.
+ *
+ * Returns
+ * 1 if stopped or terminated child matches parent_w->next criteria
+ * -1 if a non-stopped/terminated child matches parent_w->next criteria
+ * 0 if child does not match parent_w->next criteria
+ */
+static int __stdcall
+stopped_or_terminated (waitq *parent_w, _pinfo *child)
+{
+ int potential_match;
+ waitq *w = parent_w->next;
+
+ sigproc_printf ("considering pid %d", child->pid);
+ if (w->pid == -1)
+ potential_match = 1;
+ else if (w->pid == 0)
+ potential_match = child->pgid == myself->pgid;
+ else if (w->pid < 0)
+ potential_match = child->pgid == -w->pid;
+ else
+ potential_match = (w->pid == child->pid);
+
+ if (!potential_match)
+ return 0;
+
+ bool terminated;
+
+ if ((terminated = child->process_state == PID_ZOMBIE) ||
+ ((w->options & WUNTRACED) && child->stopsig))
+ {
+ parent_w->next = w->next; /* successful wait. remove from wait queue */
+ w->pid = child->pid;
+
+ if (!terminated)
+ {
+ sigproc_printf ("stopped child");
+ w->status = (child->stopsig << 8) | 0x7f;
+ child->stopsig = 0;
+ }
+ else /* Should only get here when child has been moved to the zombies array */
+ {
+ DWORD status;
+ if (!GetExitCodeProcess (child->hProcess, &status))
+ status = 0xffff;
+ if (status & EXIT_SIGNAL)
+ w->status = (status >> 8) & 0xff; /* exited due to signal */
+ else
+ w->status = (status & 0xff) << 8; /* exited via "exit ()" */
+
+ add_rusage (&myself->rusage_children, &child->rusage_children);
+ add_rusage (&myself->rusage_children, &child->rusage_self);
+
+ if (w->rusage)
+ {
+ add_rusage ((struct rusage *) w->rusage, &child->rusage_children);
+ add_rusage ((struct rusage *) w->rusage, &child->rusage_self);
+ }
+ }
+
+ if (!SetEvent (w->ev)) /* wake up wait4 () immediately */
+ system_printf ("couldn't wake up wait event %p, %E", w->ev);
+ return 1;
+ }
+
+ return -potential_match;
+}
+
+static void
+talktome ()
+{
+ winpids pids ((DWORD) PID_MAP_RW);
+ for (unsigned i = 0; i < pids.npids; i++)
+ if (pids[i]->hello_pid == myself->pid)
+ if (!IsBadWritePtr (pids[i], sizeof (_pinfo)))
+ pids[i]->commune_recv ();
+}
+
+/* Process signals by waiting for a semaphore to become signaled.
+ Then scan an in-memory array representing queued signals.
+ Executes in a separate thread.
+
+ Signals sent from this process are sent a completion signal so
+ that returns from kill/raise do not occur until the signal has
+ has been handled, as per POSIX. */
+
+void
+pending_signals::add (int sig, int pid, _threadinfo *tls)
+{
+ sigelem *se;
+ for (se = start.next; se; se = se->next)
+ if (se->sig == sig)
+ return;
+ while (sigs[empty].sig)
+ if (++empty == NSIG)
+ empty = 0;
+ se = sigs + empty;
+ se->sig = sig;
+ se->next = NULL;
+ se->tls = tls;
+ se->pid = pid;
+ if (end)
+ end->next = se;
+ end = se;
+ if (!start.next)
+ start.next = se;
+ empty++;
+}
+
+void
+pending_signals::del ()
+{
+ sigelem *next = curr->next;
+ prev->next = next;
+ curr->sig = 0;
+#ifdef DEBUGGING
+ curr->next = NULL;
+#endif
+ if (end == curr)
+ end = prev;
+ empty = curr - sigs;
+ curr = next;
+}
+
+sigelem *
+pending_signals::next ()
+{
+ sigelem *res;
+ prev = curr;
+ if (!curr || !(curr = curr->next))
+ res = NULL;
+ else
+ res = curr;
+ return res;
+}
+
+/* Process signals by waiting for signal data to arrive in a pipe.
+ Set a completion event if one was specified. */
+static DWORD WINAPI
+wait_sig (VOID *self)
+{
+ HANDLE readsig;
+ char sa_buf[1024];
+
+ /* Initialization */
+ (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY);
+
+ /* sigcomplete_main - event used to signal main thread on signal
+ completion */
+ if (!CreatePipe (&readsig, &myself->sendsig, sec_user_nih (sa_buf), 0))
+ api_fatal ("couldn't create signal pipe, %E");
+ sigcomplete_main = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ sigproc_printf ("sigcomplete_main %p", sigcomplete_main);
+ sigCONT = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+
+ /* Setting dwProcessId flags that this process is now capable of receiving
+ signals. Prior to this, dwProcessId was set to the windows pid of
+ of the original windows process which spawned us unless this was a
+ "toplevel" process. */
+ myself->dwProcessId = GetCurrentProcessId ();
+ myself->process_state |= PID_ACTIVE;
+ myself->process_state &= ~PID_INITIALIZING;
+
+ ProtectHandle (sigcomplete_main);
+
+ /* If we've been execed, then there is still a stub left in the previous
+ windows process waiting to see if it's started a cygwin process or not.
+ Signalling subproc_ready indicates that we are a cygwin process. */
+ if (child_proc_info && child_proc_info->type == PROC_EXEC)
+ {
+ debug_printf ("subproc_ready %p", child_proc_info->subproc_ready);
+ if (!SetEvent (child_proc_info->subproc_ready))
+ system_printf ("SetEvent (subproc_ready) failed, %E");
+ ForceCloseHandle1 (child_proc_info->subproc_ready, subproc_ready);
+ /* Initialize an "indirect" pid block so that if someone looks up this
+ process via its Windows PID it will be redirected to the appropriate
+ Cygwin PID shared memory block. */
+ static pinfo NO_COPY myself_identity;
+ myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED);
+ }
+
+ SetEvent (wait_sig_inited);
+ sigtid = GetCurrentThreadId ();
+
+ exception_list el;
+ _my_tls.init_threadlist_exceptions (&el);
+
+ for (;;)
+ {
+ DWORD nb;
+ sigpacket pack;
+ if (!ReadFile (readsig, &pack, sizeof (pack), &nb, NULL))
+ break;
+
+ if (nb != sizeof (pack))
+ {
+ system_printf ("short read from signal pipe: %d != %d", nb,
+ sizeof (pack));
+ continue;
+ }
+
+ if (!pack.sig)
+ {
+#ifdef DEBUGGING
+ system_printf ("zero signal?");
+#endif
+ continue;
+ }
+
+ sigset_t dummy_mask;
+ if (!pack.mask)
+ {
+ dummy_mask = myself->getsigmask ();
+ pack.mask = &dummy_mask;
+ }
+
+ sigelem *q;
+ switch (pack.sig)
+ {
+ case __SIGCOMMUNE:
+ talktome ();
+ break;
+ case __SIGSTRACE:
+ strace.hello ();
+ break;
+ case __SIGPENDING:
+ *pack.mask = 0;
+ unsigned bit;
+ sigqueue.reset ();
+ while ((q = sigqueue.next ()))
+ if (myself->getsigmask () & (bit = SIGTOMASK (q->sig)))
+ *pack.mask |= bit;
+ break;
+ case __SIGFLUSH:
+ sigqueue.reset ();
+ while ((q = sigqueue.next ()))
+ if (sig_handle (q->sig, *pack.mask, q->pid, q->tls) > 0)
+ sigqueue.del ();
+ break;
+ default:
+ if (pack.sig < 0)
+ sig_clear (-pack.sig);
+ else
+ {
+ int sigres = sig_handle (pack.sig, *pack.mask, pack.pid, pack.tls);
+ if (sigres <= 0)
+ {
+#ifdef DEBUGGING2
+ if (!sigres)
+ system_printf ("Failed to arm signal %d from pid %d", pack.sig, pack.pid);
+#endif
+ sigqueue.add (pack.sig, pack.pid, pack.tls);// FIXME: Shouldn't add this in !sh condition
+ }
+ if (pack.sig == SIGCHLD)
+ proc_subproc (PROC_CLEARWAIT, 0);
+ }
+ break;
+ }
+ if (pack.wakeup)
+ SetEvent (pack.wakeup);
+ }
+
+ sigproc_printf ("done");
+ ExitThread (0);
+}
+
+/* Wait for subprocesses to terminate. Executes in a separate thread. */
+static DWORD WINAPI
+wait_subproc (VOID *)
+{
+ sigproc_printf ("starting");
+ int errloop = 0;
+
+ for (;;)
+ {
+ DWORD rc = WaitForMultipleObjects (nchildren + 1, events, FALSE,
+ proc_loop_wait);
+ if (rc == WAIT_TIMEOUT)
+ if (!proc_loop_wait)
+ break; // Exiting
+ else
+ continue;
+
+ if (rc == WAIT_FAILED)
+ {
+ if (!proc_loop_wait)
+ break;
+
+ /* It's ok to get an ERROR_INVALID_HANDLE since another thread may have
+ closed a handle in the children[] array. So, we try looping a couple
+ of times to stabilize. FIXME - this is not foolproof. Probably, this
+ thread should be responsible for closing the children. */
+ if (!errloop++)
+ proc_subproc (PROC_NOTHING, 0); // Just synchronize and continue
+ if (errloop < 10)
+ continue;
+
+ system_printf ("wait failed. nchildren %d, wait %d, %E",
+ nchildren, proc_loop_wait);
+
+ for (int i = 0; i <= nchildren; i++)
+ if ((rc = WaitForSingleObject (events[i], 0)) == WAIT_OBJECT_0 ||
+ rc == WAIT_TIMEOUT)
+ continue;
+ else if (i == 0)
+ system_printf ("nchildren %d, event[%d] %p, %E", nchildren, i, events[i]);
+ else
+ {
+ system_printf ("nchildren %d, event[%d] %p, pchildren[%d] %p, events[0] %p, %E",
+ nchildren, i, events[i], i - 1, (_pinfo *) pchildren[i - 1], events[0]);
+ system_printf ("pid %d, dwProcessId %u, hProcess %p, progname '%s'",
+ pchildren[i - 1]->pid, pchildren[i - 1]->dwProcessId,
+ pchildren[i - 1]->hProcess, pchildren[i - 1]->progname);
+ }
+ break;
+ }
+
+ errloop = 0;
+ rc -= WAIT_OBJECT_0;
+ if (rc-- != 0)
+ {
+ rc = proc_subproc (PROC_CHILDTERMINATED, rc);
+ if (!proc_loop_wait) // Don't bother if wait_subproc is
+ break; // exiting
+
+ /* Send a SIGCHLD to myself. We do this here, rather than in proc_subproc
+ to avoid the proc_subproc lock since the signal thread will eventually
+ be calling proc_subproc and could unnecessarily block. */
+ if (rc)
+ sig_send (myself_nowait, SIGCHLD);
+ }
+ sigproc_printf ("looping");
+ }
+
+ ForceCloseHandle (events[0]);
+ events[0] = NULL;
+ sigproc_printf ("done");
+ ExitThread (0);
+}
diff --git a/winsup/cygwin/sync.cc b/winsup/cygwin/sync.cc
new file mode 100644
index 00000000000..77b9bf4e7b5
--- /dev/null
+++ b/winsup/cygwin/sync.cc
@@ -0,0 +1,155 @@
+/* sync.cc: Synchronization functions for cygwin.
+
+ This file implements the methods for controlling the "muto" class
+ which is intended to operate similarly to a mutex but attempts to
+ avoid making expensive calls to the kernel.
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Christopher Faylor <cgf@cygnus.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. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "sync.h"
+#include "security.h"
+
+muto NO_COPY muto_start;
+
+#undef WaitForSingleObject
+
+/* Constructor */
+muto *
+muto::init (const char *s)
+{
+ waiters = -1;
+ /* Create event which is used in the fallback case when blocking is necessary */
+ if (!(bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL)))
+ {
+ DWORD oerr = GetLastError ();
+ SetLastError (oerr);
+ return NULL;
+ }
+ name = s;
+ next = muto_start.next;
+ muto_start.next = this;
+ return this;
+}
+
+#if 0 /* FIXME: Do we need this? mutos aren't destroyed until process exit */
+/* Destructor (racy?) */
+muto::~muto ()
+{
+ while (visits)
+ release ();
+
+ HANDLE h = bruteforce;
+ bruteforce = NULL;
+ /* Just need to close the event handle */
+ if (h)
+ CloseHandle (h);
+}
+#endif
+
+/* Acquire the lock. Argument is the number of milliseconds to wait for
+ the lock. Multiple visits from the same thread are allowed and should
+ be handled correctly.
+
+ Note: The goal here is to minimize, as much as possible, calls to the
+ OS. Hence the use of InterlockedIncrement, etc., rather than (much) more
+ expensive OS mutexes. */
+int
+muto::acquire (DWORD ms)
+{
+ DWORD this_tid = GetCurrentThreadId ();
+
+ if (tid != this_tid)
+ {
+ /* Increment the waiters part of the class. Need to do this first to
+ avoid potential races. */
+ LONG was_waiting = InterlockedIncrement (&waiters);
+
+ /* This is deceptively simple. Basically, it allows multiple attempts to
+ lock the same muto to succeed without attempting to manipulate sync.
+ If the muto is already locked then this thread will wait for ms until
+ it is signalled by muto::release. Then it will attempt to grab the
+ sync field. If it succeeds, then this thread owns the muto.
+
+ There is a pathological condition where a thread times out waiting for
+ bruteforce but the release code triggers the bruteforce event. In this
+ case, it is possible for a thread which is going to wait for bruteforce
+ to wake up immediately. It will then attempt to grab sync but will fail
+ and go back to waiting. */
+ if (tid != this_tid && (was_waiting || InterlockedExchange (&sync, 1) != 0))
+ {
+ switch (WaitForSingleObject (bruteforce, ms))
+ {
+ case WAIT_OBJECT_0:
+ goto gotit;
+ break;
+ default:
+ InterlockedDecrement (&waiters);
+ return 0; /* failed. */
+ }
+ }
+ }
+
+gotit:
+ tid = this_tid; /* register this thread. */
+ return ++visits; /* Increment visit count. */
+}
+
+/* Return the muto lock. Needs to be called once per every acquire. */
+int
+muto::release ()
+{
+ DWORD this_tid = GetCurrentThreadId ();
+
+ if (tid != this_tid || !visits)
+ {
+ SetLastError (ERROR_NOT_OWNER); /* Didn't have the lock. */
+ return 0; /* failed. */
+ }
+
+ /* FIXME: Need to check that other thread has not exited, too. */
+ if (!--visits)
+ {
+ tid = 0; /* We were the last unlocker. */
+ (void) InterlockedExchange (&sync, 0); /* Reset trigger. */
+ /* This thread had incremented waiters but had never decremented it.
+ Decrement it now. If it is >= 0 then there are possibly other
+ threads waiting for the lock, so trigger bruteforce. */
+ if (InterlockedDecrement (&waiters) >= 0)
+ (void) SetEvent (bruteforce); /* Wake up one of the waiting threads */
+ }
+
+ return 1; /* success. */
+}
+
+bool
+muto::acquired ()
+{
+ return tid == GetCurrentThreadId ();
+}
+
+/* Call only when we're exiting. This is not thread safe. */
+void
+muto::reset ()
+{
+ visits = sync = tid = 0;
+ InterlockedExchange (&waiters, -1);
+ if (bruteforce)
+ {
+ CloseHandle (bruteforce);
+ bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, name);
+ }
+}
diff --git a/winsup/cygwin/sync.h b/winsup/cygwin/sync.h
new file mode 100644
index 00000000000..dfc4ba7faa3
--- /dev/null
+++ b/winsup/cygwin/sync.h
@@ -0,0 +1,57 @@
+/* sync.h: Header file for cygwin synchronization primitives.
+
+ Copyright 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Christopher Faylor <cgf@cygnus.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. */
+
+/* FIXME: Note that currently this class cannot be allocated via `new' since
+ there are issues with malloc and fork. */
+class muto
+{
+ LONG sync; /* Used to serialize access to this class. */
+ LONG visits; /* Count of number of times a thread has called acquire. */
+ LONG waiters; /* Number of threads waiting for lock. */
+ HANDLE bruteforce; /* event handle used to control waiting for lock. */
+ DWORD tid; /* Thread Id of lock owner. */
+public:
+ class muto *next;
+ const char *name;
+
+ /* The real constructor. */
+ muto *init(const char *name) __attribute__ ((regparm (3)));
+
+#if 0 /* FIXME: See comment in sync.cc */
+ ~muto ()
+#endif
+ int acquire (DWORD ms = INFINITE) __attribute__ ((regparm (2))); /* Acquire the lock. */
+ int release () __attribute__ ((regparm (1))); /* Release the lock. */
+
+ /* Return true if caller thread owns the lock. */
+ int ismine () {return tid == GetCurrentThreadId ();}
+ DWORD owner () {return tid;}
+ int unstable () {return !tid && (sync || waiters >= 0);}
+ void reset () __attribute__ ((regparm (1)));
+ bool acquired ();
+};
+
+extern muto muto_start;
+
+/* Use a statically allocated buffer as the storage for a muto */
+#define new_muto(__name) \
+({ \
+ static muto __name##_storage __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy"))); \
+ __name = __name##_storage.init (#__name); \
+})
+
+/* Use a statically allocated buffer as the storage for a muto */
+#define new_muto1(__name, __storage) \
+({ \
+ static muto __storage __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy"))); \
+ __name = __storage.init (#__name); \
+})
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
new file mode 100644
index 00000000000..5abe0caf937
--- /dev/null
+++ b/winsup/cygwin/syscalls.cc
@@ -0,0 +1,2994 @@
+/* syscalls.cc: syscalls
+
+ Copyright 1996, 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. */
+
+#define fstat __FOOfstat__
+#define lstat __FOOlstat__
+#define stat __FOOstat__
+#define _close __FOO_close__
+#define _lseek __FOO_lseek__
+#define _open __FOO_open__
+#define _read __FOO_read__
+#define _write __FOO_write__
+#define _open64 __FOO_open64__
+#define _lseek64 __FOO_lseek64__
+#define _fstat64 __FOO_fstat64__
+
+#include "winsup.h"
+#include <sys/stat.h>
+#include <sys/vfs.h> /* needed for statfs */
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h>
+#include <utmp.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <winnls.h>
+#include <wininet.h>
+#include <lmcons.h> /* for UNLEN */
+#include <rpc.h>
+
+#undef fstat
+#undef lstat
+#undef stat
+
+#include <cygwin/version.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "pwdgrp.h"
+#include "cpuid.h"
+#include "registry.h"
+
+#undef _close
+#undef _lseek
+#undef _open
+#undef _read
+#undef _write
+#undef _open64
+#undef _lseek64
+#undef _fstat64
+
+SYSTEM_INFO system_info;
+
+static int __stdcall mknod_worker (const char *, mode_t, mode_t, _major_t,
+ _minor_t);
+
+static int __stdcall stat_worker (const char *name, struct __stat64 *buf,
+ int nofollow) __attribute__ ((regparm (3)));
+
+/* Close all files and process any queued deletions.
+ Lots of unix style applications will open a tmp file, unlink it,
+ but never call close. This function is called by _exit to
+ ensure we don't leave any such files lying around. */
+
+void __stdcall
+close_all_files (void)
+{
+ cygheap->fdtab.lock ();
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) cygheap->fdtab.size; i++)
+ if ((fh = cygheap->fdtab[i]) != NULL)
+ {
+#ifdef DEBUGGING
+ debug_printf ("closing fd %d", i);
+#endif
+ fh->close ();
+ cygheap->fdtab.release (i);
+ }
+
+ if (cygheap->ctty)
+ cygheap->close_ctty ();
+
+ cygheap->fdtab.unlock ();
+ user_shared->delqueue.process_queue ();
+}
+
+int
+dup (int fd)
+{
+ return cygheap->fdtab.dup2 (fd, cygheap_fdnew ());
+}
+
+int
+dup2 (int oldfd, int newfd)
+{
+ return cygheap->fdtab.dup2 (oldfd, newfd);
+}
+
+extern "C" int
+unlink (const char *ourname)
+{
+ int res = -1;
+ DWORD devn;
+
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ goto done;
+ }
+
+ if ((devn = win32_name.get_devn ()) == FH_PROC || devn == FH_REGISTRY
+ || devn == FH_PROCESS)
+ {
+ set_errno (EROFS);
+ goto done;
+ }
+
+ syscall_printf ("_unlink (%s)", win32_name.get_win32 ());
+
+ if (!win32_name.exists ())
+ {
+ syscall_printf ("unlinking a nonexistent file");
+ set_errno (ENOENT);
+ goto done;
+ }
+ else if (win32_name.isdir ())
+ {
+ syscall_printf ("unlinking a directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ /* Windows won't check the directory mode, so we do that ourselves. */
+ if (!writable_directory (win32_name))
+ {
+ syscall_printf ("non-writable directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ bool setattrs;
+ if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
+ setattrs = false;
+ else
+ {
+ /* Allow us to delete even if read-only */
+ setattrs = SetFileAttributes (win32_name,
+ (DWORD) win32_name
+ & ~(FILE_ATTRIBUTE_READONLY
+ | FILE_ATTRIBUTE_SYSTEM));
+ }
+ /* Attempt to use "delete on close" semantics to handle removing
+ a file which may be open. */
+ if (wincap.has_delete_on_close ())
+ {
+ HANDLE h;
+ h = CreateFile (win32_name, 0, FILE_SHARE_READ, &sec_none_nih,
+ OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (wincap.has_hard_links () && setattrs)
+ (void) SetFileAttributes (win32_name, (DWORD) win32_name);
+ BOOL res = CloseHandle (h);
+ syscall_printf ("%d = CloseHandle (%p)", res, h);
+ if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES
+ || !win32_name.isremote ())
+ {
+ syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) succeeded");
+ goto ok;
+ }
+ else
+ {
+ syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) failed");
+ if (setattrs)
+ SetFileAttributes (win32_name, (DWORD) win32_name & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM));
+ }
+ }
+ }
+
+ /* Try a delete with attributes reset */
+ if (DeleteFile (win32_name))
+ {
+ syscall_printf ("DeleteFile after CreateFile/CloseHandle succeeded");
+ goto ok;
+ }
+
+ DWORD lasterr;
+ lasterr = GetLastError ();
+
+ (void) SetFileAttributes (win32_name, (DWORD) win32_name);
+
+ /* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
+ violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case
+ to simplify tests. */
+ if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
+ && !win32_name.isremote ())
+ lasterr = ERROR_SHARING_VIOLATION;
+
+ /* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing
+ violation, then queue the file for deletion when the process
+ exits. Otherwise, punt. */
+ if (lasterr != ERROR_SHARING_VIOLATION)
+ goto err;
+
+ syscall_printf ("couldn't delete file, err %d", lasterr);
+
+ /* Add file to the "to be deleted" queue. */
+ user_shared->delqueue.queue_file (win32_name);
+
+ /* Success condition. */
+ ok:
+ res = 0;
+ goto done;
+
+ /* Error condition. */
+ err:
+ __seterrno ();
+ res = -1;
+
+ done:
+ syscall_printf ("%d = unlink (%s)", res, ourname);
+ return res;
+}
+
+extern "C" int
+_remove_r (struct _reent *, const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" int
+remove (const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" pid_t
+getpid ()
+{
+ return myself->pid;
+}
+
+extern "C" pid_t
+_getpid_r (struct _reent *)
+{
+ return getpid ();
+}
+
+/* getppid: POSIX 4.1.1.1 */
+extern "C" pid_t
+getppid ()
+{
+ return myself->ppid;
+}
+
+/* setsid: POSIX 4.3.2.1 */
+extern "C" pid_t
+setsid (void)
+{
+ vfork_save *vf = vfork_storage.val ();
+ /* This is a horrible, horrible kludge */
+ if (vf && vf->pid < 0)
+ {
+ pid_t pid = fork ();
+ if (pid > 0)
+ {
+ syscall_printf ("longjmping due to vfork");
+ vf->restore_pid (pid);
+ }
+ /* assuming that fork was successful */
+ }
+
+ if (myself->pgid == myself->pid)
+ syscall_printf ("hmm. pgid %d pid %d", myself->pgid, myself->pid);
+ else
+ {
+ if (myself->ctty >= 0 && cygheap->open_fhs <= 0)
+ {
+ syscall_printf ("freeing console");
+ FreeConsole ();
+ }
+ myself->ctty = -1;
+ myself->sid = getpid ();
+ myself->pgid = getpid ();
+ syscall_printf ("sid %d, pgid %d, ctty %d, open_fhs %d", myself->sid,
+ myself->pgid, myself->ctty, cygheap->open_fhs);
+ if (cygheap->ctty)
+ cygheap->close_ctty ();
+ return myself->sid;
+ }
+
+ set_errno (EPERM);
+ return -1;
+}
+
+extern "C" pid_t
+getsid (pid_t pid)
+{
+ pid_t res;
+ if (!pid)
+ res = myself->sid;
+ else
+ {
+ pinfo p (pid);
+ if (p)
+ res = p->sid;
+ else
+ {
+ set_errno (ESRCH);
+ res = -1;
+ }
+ }
+ return res;
+}
+
+extern "C" ssize_t
+read (int fd, void *ptr, size_t len)
+{
+ const iovec iov =
+ {
+ iov_base: ptr,
+ iov_len: len
+ };
+
+ return readv (fd, &iov, 1);
+}
+
+extern "C" ssize_t _read (int, void *, size_t)
+ __attribute__ ((alias ("read")));
+
+extern "C" ssize_t
+write (int fd, const void *ptr, size_t len)
+{
+ const struct iovec iov =
+ {
+ iov_base: (void *) ptr, // const_cast
+ iov_len: len
+ };
+
+ return writev (fd, &iov, 1);
+}
+
+extern "C" ssize_t _write (int fd, const void *ptr, size_t len)
+ __attribute__ ((alias ("write")));
+
+extern "C" ssize_t
+readv (int fd, const struct iovec *const iov, const int iovcnt)
+{
+ extern int sigcatchers;
+ const int e = get_errno ();
+
+ int res = -1;
+
+ const ssize_t tot = check_iovec_for_read (iov, iovcnt);
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ while (1)
+ {
+ sig_dispatch_pending ();
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ break;
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY)
+ {
+ set_errno (EBADF);
+ break;
+ }
+
+ DWORD wait = cfd->is_nonblocking () ? 0 : INFINITE;
+
+ /* Could block, so let user know we at least got here. */
+ syscall_printf ("readv (%d, %p, %d) %sblocking, sigcatchers %d",
+ fd, iov, iovcnt, wait ? "" : "non", sigcatchers);
+
+ if (wait && (!cfd->is_slow () || cfd->get_r_no_interrupt ()))
+ debug_printf ("no need to call ready_for_read");
+ else if (!cfd->ready_for_read (fd, wait))
+ {
+ res = -1;
+ goto out;
+ }
+
+ /* FIXME: This is not thread safe. We need some method to
+ ensure that an fd, closed in another thread, aborts I/O
+ operations. */
+ if (!cfd.isopen ())
+ break;
+
+ /* Check to see if this is a background read from a "tty",
+ sending a SIGTTIN, if appropriate */
+ res = cfd->bg_check (SIGTTIN);
+
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+
+ if (res > bg_eof)
+ {
+ myself->process_state |= PID_TTYIN;
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+ res = cfd->readv (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYIN;
+ }
+
+ out:
+ if (res >= 0 || get_errno () != EINTR || !call_signal_handler_now ())
+ break;
+ set_errno (e);
+ }
+
+done:
+ syscall_printf ("%d = readv (%d, %p, %d), errno %d", res, fd, iov, iovcnt,
+ get_errno ());
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" ssize_t
+writev (const int fd, const struct iovec *const iov, const int iovcnt)
+{
+ int res = -1;
+ sig_dispatch_pending ();
+ const ssize_t tot = check_iovec_for_write (iov, iovcnt);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto done;
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY)
+ {
+ set_errno (EBADF);
+ goto done;
+ }
+
+ /* Could block, so let user know we at least got here. */
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+ else
+ syscall_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+
+ res = cfd->bg_check (SIGTTOU);
+
+ if (res > (int) bg_eof)
+ {
+ myself->process_state |= PID_TTYOU;
+ res = cfd->writev (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYOU;
+ }
+
+done:
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+ else
+ syscall_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+
+ MALLOC_CHECK;
+ return res;
+}
+
+/* _open */
+/* newlib's fcntl.h defines _open as taking variable args so we must
+ correspond. The third arg if it exists is: mode_t mode. */
+extern "C" int
+open (const char *unix_path, int flags, ...)
+{
+ int res = -1;
+ va_list ap;
+ mode_t mode = 0;
+ sig_dispatch_pending ();
+
+ syscall_printf ("open (%s, %p)", unix_path, flags);
+ if (!check_null_empty_str_errno (unix_path))
+ {
+ /* check for optional mode argument */
+ va_start (ap, flags);
+ mode = va_arg (ap, mode_t);
+ va_end (ap);
+
+ fhandler_base *fh;
+ cygheap_fdnew fd;
+
+ if (fd >= 0)
+ {
+ if (!(fh = build_fh_name (unix_path, NULL, PC_SYM_FOLLOW)))
+ res = -1; // errno already set
+ else if (fh->is_fs_special () && fh->device_access_denied (flags))
+ {
+ delete fh;
+ res = -1;
+ }
+ else if (!fh->open (flags, (mode & 07777) & ~cygheap->umask))
+ {
+ delete fh;
+ res = -1;
+ }
+ else
+ {
+ cygheap->fdtab[fd] = fh;
+ if ((res = fd) <= 2)
+ set_std_handle (res);
+ }
+ }
+ }
+
+ syscall_printf ("%d = open (%s, %p)", res, unix_path, flags);
+ return res;
+}
+
+extern "C" int _open (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" int _open64 (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" _off64_t
+lseek64 (int fd, _off64_t pos, int dir)
+{
+ _off64_t res;
+
+ if (dir != SEEK_SET && dir != SEEK_CUR && dir != SEEK_END)
+ {
+ set_errno (EINVAL);
+ res = -1;
+ }
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = cfd->lseek (pos, dir);
+ else
+ res = -1;
+ }
+ syscall_printf ("%d = lseek (%d, %D, %d)", res, fd, pos, dir);
+
+ return res;
+}
+
+extern "C" int _lseek64 (int fd, _off64_t pos, int dir)
+ __attribute__ ((alias ("lseek64")));
+
+extern "C" _off_t
+lseek (int fd, _off_t pos, int dir)
+{
+ return lseek64 (fd, (_off64_t) pos, dir);
+}
+
+extern "C" _off_t _lseek (int, _off_t, int)
+ __attribute__ ((alias ("lseek")));
+
+extern "C" int
+close (int fd)
+{
+ int res;
+
+ syscall_printf ("close (%d)", fd);
+
+ MALLOC_CHECK;
+ cygheap_fdget cfd (fd, true);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ res = cfd->close ();
+ cfd.release ();
+ }
+
+ syscall_printf ("%d = close (%d)", res, fd);
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" int _close (int) __attribute__ ((alias ("close")));
+
+extern "C" int
+isatty (int fd)
+{
+ int res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = 0;
+ else
+ res = cfd->is_tty ();
+ syscall_printf ("%d = isatty (%d)", res, fd);
+ return res;
+}
+
+/* Under NT, try to make a hard link using backup API. If that
+ fails or we are Win 95, just copy the file.
+ FIXME: We should actually be checking partition type, not OS.
+ Under NTFS, we should support hard links. On FAT partitions,
+ we should just copy the file.
+*/
+
+extern "C" int
+link (const char *a, const char *b)
+{
+ int res = -1;
+ path_conv real_a (a, PC_SYM_NOFOLLOW | PC_FULL);
+ path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL);
+ extern bool allow_winsymlinks;
+
+ if (real_a.error)
+ {
+ set_errno (real_a.error);
+ goto done;
+ }
+
+ if (real_b.error)
+ {
+ set_errno (real_b.case_clash ? ECASECLASH : real_b.error);
+ goto done;
+ }
+
+ if (real_b.exists ())
+ {
+ syscall_printf ("file '%s' exists?", (char *) real_b);
+ set_errno (EEXIST);
+ goto done;
+ }
+
+ if (real_b[strlen (real_b) - 1] == '.')
+ {
+ syscall_printf ("trailing dot, bailing out");
+ set_errno (EINVAL);
+ goto done;
+ }
+
+ /* Shortcut hack. */
+ char new_lnk_buf[CYG_MAX_PATH + 5];
+ if (allow_winsymlinks && real_a.is_lnk_symlink () && !real_b.case_clash)
+ {
+ strcpy (new_lnk_buf, b);
+ strcat (new_lnk_buf, ".lnk");
+ b = new_lnk_buf;
+ real_b.check (b, PC_SYM_NOFOLLOW | PC_FULL);
+ }
+ /* Try to make hard link first on Windows NT */
+ if (wincap.has_hard_links ())
+ {
+ if (CreateHardLinkA (real_b, real_a, NULL))
+ goto success;
+
+ HANDLE hFileSource;
+
+ WIN32_STREAM_ID StreamId;
+ DWORD dwBytesWritten;
+ LPVOID lpContext;
+ DWORD cbPathLen;
+ DWORD StreamSize;
+ WCHAR wbuf[CYG_MAX_PATH];
+
+ BOOL bSuccess;
+
+ hFileSource = CreateFile (real_a, FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE /*| FILE_SHARE_DELETE*/,
+ &sec_none_nih, // sa
+ OPEN_EXISTING, 0, NULL);
+
+ if (hFileSource == INVALID_HANDLE_VALUE)
+ {
+ syscall_printf ("cannot open source, %E");
+ goto docopy;
+ }
+
+ cbPathLen = sys_mbstowcs (wbuf, real_b, CYG_MAX_PATH) * sizeof (WCHAR);
+
+ StreamId.dwStreamId = BACKUP_LINK;
+ StreamId.dwStreamAttributes = 0;
+ StreamId.dwStreamNameSize = 0;
+ StreamId.Size.HighPart = 0;
+ StreamId.Size.LowPart = cbPathLen;
+
+ StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) +
+ StreamId.dwStreamNameSize;
+
+ lpContext = NULL;
+ /* Write the WIN32_STREAM_ID */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) &StreamId, // buffer to write
+ StreamSize, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext);
+
+ if (bSuccess)
+ {
+ /* write the buffer containing the path */
+ /* FIXME: BackupWrite sometimes traps if linkname is invalid.
+ Need to handle. */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) wbuf, // buffer to write
+ cbPathLen, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext
+ );
+
+ if (!bSuccess)
+ syscall_printf ("cannot write linkname, %E");
+
+ /* Free context */
+ BackupWrite (
+ hFileSource,
+ NULL, // buffer to write
+ 0, // number of bytes to write
+ &dwBytesWritten,
+ TRUE, // abort
+ FALSE, // don't process security
+ &lpContext);
+ }
+ else
+ syscall_printf ("cannot write streamId, %E");
+
+ CloseHandle (hFileSource);
+
+ if (!bSuccess)
+ goto docopy;
+
+ success:
+ res = 0;
+ if (!allow_winsymlinks && real_a.is_lnk_symlink ())
+ SetFileAttributes (real_b, (DWORD) real_a
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_READONLY);
+
+ goto done;
+ }
+docopy:
+ /* do this with a copy */
+ if (CopyFileA (real_a, real_b, 1))
+ res = 0;
+ else
+ __seterrno ();
+
+done:
+ syscall_printf ("%d = link (%s, %s)", res, a, b);
+ return res;
+}
+
+/* chown: POSIX 5.6.5.1 */
+/*
+ * chown () is only implemented for Windows NT. Under other operating
+ * systems, it is only a stub that always returns zero.
+ */
+static int
+chown_worker (const char *name, unsigned fmode, __uid32_t uid, __gid32_t gid)
+{
+ int res;
+
+ if (check_null_empty_str_errno (name))
+ return -1;
+
+ if (!wincap.has_security ()) // real chown only works on NT
+ res = 0; // return zero (and do nothing) under Windows 9x
+ else
+ {
+ /* we need Win32 path names because of usage of Win32 API functions */
+ path_conv win32_path (PC_NONULLEMPTY, name, fmode);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ res = -1;
+ goto done;
+ }
+
+ /* FIXME: This makes chown on a device succeed always. Someday we'll want
+ to actually allow chown to work properly on devices. */
+ if (win32_path.is_auto_device () && !win32_path.issocket ())
+ {
+ res = 0;
+ goto done;
+ }
+
+ mode_t attrib = 0;
+ if (win32_path.isdir ())
+ attrib |= S_IFDIR;
+ res = get_file_attribute (win32_path.has_acls (),
+ win32_path.get_win32 (),
+ &attrib);
+ if (!res)
+ res = set_file_attribute (win32_path.has_acls (), win32_path, uid,
+ gid, attrib);
+ if (res != 0 && (!win32_path.has_acls () || !allow_ntsec))
+ {
+ /* fake - if not supported, pretend we're like win95
+ where it just works */
+ res = 0;
+ }
+ }
+
+done:
+ syscall_printf ("%d = %schown (%s,...)",
+ res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name);
+ return res;
+}
+
+extern "C" int
+chown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ return chown_worker (name, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+chown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ return chown_worker (name, PC_SYM_FOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+lchown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid);
+}
+
+extern "C" int
+lchown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ return chown_worker (name, PC_SYM_NOFOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+fchown32 (int fd, __uid32_t uid, __gid32_t gid)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchown (%d,...)", fd);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchown (%d,...) (no name)", fd);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchown (%d,...): calling chown_worker (%s,FOLLOW,...)",
+ fd, path);
+ return chown_worker (path, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+fchown (int fd, __uid16_t uid, __gid16_t gid)
+{
+ return fchown32 (fd, uid16touid32 (uid), gid16togid32 (gid));
+}
+
+/* umask: POSIX 5.3.3.1 */
+extern "C" mode_t
+umask (mode_t mask)
+{
+ mode_t oldmask;
+
+ oldmask = cygheap->umask;
+ cygheap->umask = mask & 0777;
+ return oldmask;
+}
+
+int
+chmod_device (path_conv& pc, mode_t mode)
+{
+ return mknod_worker (pc, pc.dev.mode & S_IFMT, mode, pc.dev.major, pc.dev.minor);
+}
+
+/* chmod: POSIX 5.6.4.1 */
+extern "C" int
+chmod (const char *path, mode_t mode)
+{
+ int res = -1;
+
+ path_conv win32_path (path);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ goto done;
+ }
+
+ /* FIXME: This makes chmod on a device succeed always. Someday we'll want
+ to actually allow chmod to work properly on devices. */
+ if (win32_path.is_auto_device ())
+ {
+ res = 0;
+ goto done;
+ }
+ if (win32_path.is_fs_special ())
+ {
+ res = chmod_device (win32_path, mode);
+ goto done;
+ }
+
+ if (!win32_path.exists ())
+ __seterrno ();
+ else
+ {
+ /* temporary erase read only bit, to be able to set file security */
+ SetFileAttributes (win32_path, (DWORD) win32_path & ~FILE_ATTRIBUTE_READONLY);
+
+ if (win32_path.isdir ())
+ mode |= S_IFDIR;
+ if (!set_file_attribute (win32_path.has_acls (), win32_path,
+ ILLEGAL_UID, ILLEGAL_GID, mode)
+ && allow_ntsec)
+ res = 0;
+
+ /* if the mode we want has any write bits set, we can't
+ be read only. */
+ if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
+ (DWORD) win32_path &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ (DWORD) win32_path |= FILE_ATTRIBUTE_READONLY;
+
+ if (!win32_path.is_lnk_symlink () && S_ISLNK (mode) || S_ISSOCK (mode))
+ (DWORD) win32_path |= FILE_ATTRIBUTE_SYSTEM;
+
+ if (!SetFileAttributes (win32_path, win32_path))
+ __seterrno ();
+ else if (!allow_ntsec)
+ /* Correct NTFS security attributes have higher priority */
+ res = 0;
+ }
+
+done:
+ syscall_printf ("%d = chmod (%s, %p)", res, path, mode);
+ return res;
+}
+
+/* fchmod: P96 5.6.4.1 */
+
+extern "C" int
+fchmod (int fd, mode_t mode)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o) (no name)", fd, mode);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchmod (%d, 0%o): calling chmod (%s, 0%o)",
+ fd, mode, path, mode);
+ return chmod (path, mode);
+}
+
+static void
+stat64_to_stat32 (struct __stat64 *src, struct __stat32 *dst)
+{
+ dst->st_dev = ((src->st_dev >> 8) & 0xff00) | (src->st_dev & 0xff);
+ dst->st_ino = ((unsigned) (src->st_ino >> 32)) | (unsigned) src->st_ino;
+ dst->st_mode = src->st_mode;
+ dst->st_nlink = src->st_nlink;
+ dst->st_uid = src->st_uid;
+ dst->st_gid = src->st_gid;
+ dst->st_rdev = ((src->st_rdev >> 8) & 0xff00) | (src->st_rdev & 0xff);
+ dst->st_size = src->st_size;
+ dst->st_atim = src->st_atim;
+ dst->st_mtim = src->st_mtim;
+ dst->st_ctim = src->st_ctim;
+ dst->st_blksize = src->st_blksize;
+ dst->st_blocks = src->st_blocks;
+}
+
+extern "C" int
+fstat64 (int fd, struct __stat64 *buf)
+{
+ int res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ memset (buf, 0, sizeof (struct __stat64));
+ res = cfd->fstat (buf);
+ if (!res)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, cfd->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = cfd->get_device ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ syscall_printf ("%d = fstat (%d, %p)", res, fd, buf);
+ return res;
+}
+
+extern "C" int
+_fstat64_r (struct _reent *ptr, int fd, struct __stat64 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = fstat64 (fd, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+extern "C" int
+fstat (int fd, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = fstat64 (fd, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+_fstat_r (struct _reent *ptr, int fd, struct __stat32 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = fstat (fd, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+/* fsync: P96 6.6.1.1 */
+extern "C" int
+fsync (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fsync (%d)", fd);
+ return -1;
+ }
+
+ if (FlushFileBuffers (cfd->get_handle ()) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+/* sync: standards? */
+extern "C" int
+sync ()
+{
+ return 0;
+}
+
+suffix_info stat_suffixes[] =
+{
+ suffix_info ("", 1),
+ suffix_info (".exe", 1),
+ suffix_info (NULL)
+};
+
+/* Cygwin internal */
+static int __stdcall
+stat_worker (const char *name, struct __stat64 *buf, int nofollow)
+{
+ int res = -1;
+ fhandler_base *fh = NULL;
+
+ if (check_null_invalid_struct_errno (buf))
+ goto done;
+
+ fh = build_fh_name (name, NULL, (nofollow ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW)
+ | PC_FULL, stat_suffixes);
+
+ if (fh->error ())
+ {
+ debug_printf ("got %d error from build_fh_name", fh->error ());
+ set_errno (fh->error ());
+ }
+ else
+ {
+ debug_printf ("(%s, %p, %d, %p), file_attributes %d", name, buf, nofollow,
+ fh, (DWORD) *fh);
+ memset (buf, 0, sizeof (*buf));
+ res = fh->fstat (buf);
+ if (!res)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, fh->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = fh->get_device ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ done:
+ if (fh)
+ delete fh;
+ MALLOC_CHECK;
+ syscall_printf ("%d = (%s, %p)", res, name, buf);
+ return res;
+}
+
+extern "C" int
+stat64 (const char *name, struct __stat64 *buf)
+{
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 0);
+}
+
+extern "C" int
+_stat64_r (struct _reent *ptr, const char *name, struct __stat64 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = stat64 (name, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+extern "C" int
+stat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = stat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+_stat_r (struct _reent *ptr, const char *name, struct __stat32 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = stat (name, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat64 (const char *name, struct __stat64 *buf)
+{
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 1);
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = lstat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+int
+access_worker (path_conv& real_path, int flags, fhandler_base *fh)
+{
+ if (real_path.error)
+ {
+ set_errno (real_path.error);
+ return -1;
+ }
+
+ if (!real_path.exists ())
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ if (!(flags & (R_OK | W_OK | X_OK)))
+ return 0;
+
+ if (real_path.is_fs_special ())
+ /* short circuit */;
+ else if (real_path.has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK))
+ {
+ set_errno (EACCES);
+ return -1;
+ }
+ else if (real_path.has_acls () && allow_ntsec)
+ return check_file_access (real_path, flags);
+
+ struct __stat64 st;
+ int r = fh ? fh->fstat (&st) : stat_worker (real_path, &st, 0);
+ if (r)
+ return -1;
+ r = -1;
+ if (flags & R_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IRUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IRGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IROTH))
+ goto done;
+ }
+ if (flags & W_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IWUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IWGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IWOTH))
+ goto done;
+ }
+ if (flags & X_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IXUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IXGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IXOTH))
+ goto done;
+ }
+ r = 0;
+done:
+ if (r)
+ set_errno (EACCES);
+ return r;
+}
+
+extern "C" int
+access (const char *fn, int flags)
+{
+ // flags were incorrectly specified
+ if (flags & ~(F_OK|R_OK|W_OK|X_OK))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ path_conv pc (fn, PC_SYM_FOLLOW | PC_FULL, stat_suffixes);
+ return access_worker (pc, flags);
+}
+
+extern "C" int
+rename (const char *oldpath, const char *newpath)
+{
+ int res = 0;
+ char *lnk_suffix = NULL;
+
+ path_conv real_old (oldpath, PC_SYM_NOFOLLOW);
+
+ if (real_old.error)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_old.error);
+ return -1;
+ }
+
+ path_conv real_new (newpath, PC_SYM_NOFOLLOW);
+
+ /* Shortcut hack. */
+ char new_lnk_buf[CYG_MAX_PATH + 5];
+ if (real_old.is_lnk_symlink () && !real_new.error && !real_new.case_clash)
+ {
+ strcpy (new_lnk_buf, newpath);
+ strcat (new_lnk_buf, ".lnk");
+ newpath = new_lnk_buf;
+ real_new.check (newpath, PC_SYM_NOFOLLOW);
+ }
+
+ if (real_new.error || real_new.case_clash)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_new.case_clash ? ECASECLASH : real_new.error);
+ return -1;
+ }
+
+ if (!writable_directory (real_old) || !writable_directory (real_new))
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (EACCES);
+ return -1;
+ }
+
+ if (!real_old.exists ()) /* file to move doesn't exist */
+ {
+ syscall_printf ("file to move doesn't exist");
+ set_errno (ENOENT);
+ return (-1);
+ }
+
+ /* Destination file exists and is read only, change that or else
+ the rename won't work. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY);
+
+ /* Shortcut hack No. 2, part 1 */
+ if (!real_old.issymlink () && !real_new.error && real_new.is_lnk_symlink ()
+ && (lnk_suffix = strrchr (real_new.get_win32 (), '.')))
+ *lnk_suffix = '\0';
+
+ if (!MoveFile (real_old, real_new))
+ res = -1;
+
+ if (res == 0 || (GetLastError () != ERROR_ALREADY_EXISTS
+ && GetLastError () != ERROR_FILE_EXISTS))
+ goto done;
+
+ if (wincap.has_move_file_ex ())
+ {
+ if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
+ MOVEFILE_REPLACE_EXISTING))
+ res = 0;
+ }
+ else
+ {
+ syscall_printf ("try win95 hack");
+ for (int i = 0; i < 2; i++)
+ {
+ if (!DeleteFileA (real_new.get_win32 ()) &&
+ GetLastError () != ERROR_FILE_NOT_FOUND)
+ {
+ syscall_printf ("deleting %s to be paranoid",
+ real_new.get_win32 ());
+ break;
+ }
+ else if (MoveFile (real_old.get_win32 (), real_new.get_win32 ()))
+ {
+ res = 0;
+ break;
+ }
+ }
+ }
+
+done:
+ if (res)
+ {
+ __seterrno ();
+ /* Reset R/O attributes if neccessary. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, real_new);
+ }
+ else
+ {
+ /* make the new file have the permissions of the old one */
+ DWORD attr = real_old;
+#ifdef HIDDEN_DOT_FILES
+ char *c = strrchr (real_old.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_old.get_win32 () == '.')
+ attr &= ~FILE_ATTRIBUTE_HIDDEN;
+ c = strrchr (real_new.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_new.get_win32 () == '.')
+ attr |= FILE_ATTRIBUTE_HIDDEN;
+#endif
+ SetFileAttributes (real_new, attr);
+
+ /* Shortcut hack, No. 2, part 2 */
+ /* if the new filename was an existing shortcut, remove it now if the
+ new filename is equal to the shortcut name without .lnk suffix. */
+ if (lnk_suffix)
+ {
+ *lnk_suffix = '.';
+ DeleteFile (real_new);
+ }
+ }
+
+ syscall_printf ("%d = rename (%s, %s)", res, (char *) real_old,
+ (char *) real_new);
+
+ return res;
+}
+
+extern "C" int
+system (const char *cmdstring)
+{
+ pthread_testcancel ();
+
+ if (check_null_empty_str_errno (cmdstring))
+ return -1;
+
+ int res;
+ const char* command[4];
+
+ if (cmdstring == (const char *) NULL)
+ return 1;
+
+ command[0] = "sh";
+ command[1] = "-c";
+ command[2] = cmdstring;
+ command[3] = (const char *) NULL;
+
+ if ((res = spawnvp (_P_SYSTEM, "sh", command)) == -1)
+ {
+ // when exec fails, return value should be as if shell
+ // executed exit (127)
+ res = 127;
+ }
+
+ return res;
+}
+
+extern "C" int
+setdtablesize (int size)
+{
+ if (size <= (int)cygheap->fdtab.size || cygheap->fdtab.extend (size - cygheap->fdtab.size))
+ return 0;
+
+ return -1;
+}
+
+extern "C" int
+getdtablesize ()
+{
+ return cygheap->fdtab.size > OPEN_MAX ? cygheap->fdtab.size : OPEN_MAX;
+}
+
+extern "C" size_t
+getpagesize ()
+{
+ if (!system_info.dwPageSize)
+ GetSystemInfo (&system_info);
+ return (int) system_info.dwPageSize;
+}
+
+size_t
+getshmlba ()
+{
+ if (!system_info.dwAllocationGranularity)
+ GetSystemInfo (&system_info);
+ return system_info.dwAllocationGranularity;
+}
+
+static int
+check_posix_perm (const char *fname, int v)
+{
+ /* Windows 95/98/ME don't support file system security at all. */
+ if (!wincap.has_security ())
+ return 0;
+
+ /* ntea is ok for supporting permission bits but it doesn't support
+ full POSIX security settings. */
+ if (v == _PC_POSIX_PERMISSIONS && allow_ntea)
+ return 1;
+
+ if (!allow_ntsec)
+ return 0;
+
+ char *root = rootdir (strcpy ((char *)alloca (strlen (fname)), fname));
+
+ if (!allow_smbntsec
+ && ((root[0] == '\\' && root[1] == '\\')
+ || GetDriveType (root) == DRIVE_REMOTE))
+ return 0;
+
+ DWORD vsn, len, flags;
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &len, &flags, NULL, 16))
+ {
+ __seterrno ();
+ return 0;
+ }
+
+ return (flags & FS_PERSISTENT_ACLS) ? 1 : 0;
+}
+
+/* FIXME: not all values are correct... */
+extern "C" long int
+fpathconf (int fd, int v)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ switch (v)
+ {
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ if (isatty (fd))
+ return _POSIX_MAX_CANON;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_NAME_MAX:
+ case _PC_PATH_MAX:
+ return PATH_MAX;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ if (cfd->is_tty ())
+ return -1;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ if (cfd->get_device () == FH_FS)
+ return check_posix_perm (cfd->get_win32_name (), v);
+ set_errno (EINVAL);
+ return -1;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" long int
+pathconf (const char *file, int v)
+{
+ switch (v)
+ {
+ case _PC_PATH_MAX:
+ if (check_null_empty_str_errno (file))
+ return -1;
+ return PATH_MAX - strlen (file);
+ case _PC_NAME_MAX:
+ return PATH_MAX;
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ return _POSIX_MAX_CANON;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ return -1;
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ {
+ path_conv full_path (file, PC_SYM_FOLLOW | PC_FULL);
+ if (full_path.error)
+ {
+ set_errno (full_path.error);
+ return -1;
+ }
+ if (full_path.is_auto_device ())
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ return check_posix_perm (full_path, v);
+ }
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" char *
+ttyname (int fd)
+{
+ char *name;
+ cygheap_fdget cfd (fd);
+ if (cfd < 0 || !cfd->is_tty ())
+ return 0;
+ name = (char *) (cfd->ttyname ());
+ debug_printf ("returning %s", name);
+ return name;
+}
+
+extern "C" char *
+ctermid (char *str)
+{
+ static NO_COPY char buf[16];
+ if (str == NULL)
+ str = buf;
+ if (!real_tty_attached (myself))
+ strcpy (str, "/dev/conin");
+ else
+ __small_sprintf (str, "/dev/tty%d", myself->ctty);
+ return str;
+}
+
+/* Tells stdio if it should do the cr/lf conversion for this file */
+extern "C" int
+_cygwin_istext_for_stdio (int fd)
+{
+ syscall_printf ("_cygwin_istext_for_stdio (%d)", fd);
+ if (CYGWIN_VERSION_OLD_STDIO_CRLF_HANDLING)
+ {
+ syscall_printf (" _cifs: old API");
+ return 0; /* we do it for old apps, due to getc/putc macros */
+ }
+
+ cygheap_fdget cfd (fd, false, false);
+ if (cfd < 0)
+ {
+ syscall_printf (" _cifs: fd not open");
+ return 0;
+ }
+
+ if (cfd->get_device () != FH_FS)
+ {
+ syscall_printf (" _cifs: fd not disk file");
+ return 0;
+ }
+
+ if (cfd->get_w_binary () || cfd->get_r_binary ())
+ {
+ syscall_printf (" _cifs: get_*_binary");
+ return 0;
+ }
+
+ syscall_printf ("_cygwin_istext_for_stdio says yes");
+ return 1;
+}
+
+/* internal newlib function */
+extern "C" int _fwalk (struct _reent *ptr, int (*function) (FILE *));
+
+static int setmode_mode;
+static int setmode_file;
+
+static int
+setmode_helper (FILE *f)
+{
+ if (fileno (f) != setmode_file)
+ return 0;
+ syscall_printf ("setmode: file was %s now %s",
+ f->_flags & __SCLE ? "text" : "raw",
+ setmode_mode & O_TEXT ? "text" : "raw");
+ if (setmode_mode & O_TEXT)
+ f->_flags |= __SCLE;
+ else
+ f->_flags &= ~__SCLE;
+ return 0;
+}
+
+extern "C" int
+getmode (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+
+ return cfd->get_flags () & (O_BINARY | O_TEXT);
+}
+
+/* Set a file descriptor into text or binary mode, returning the
+ previous mode. */
+
+extern "C" int
+setmode (int fd, int mode)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ if (mode != O_BINARY && mode != O_TEXT && mode != 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Note that we have no way to indicate the case that writes are
+ binary but not reads, or vice-versa. These cases can arise when
+ using the tty or console interface. People using those
+ interfaces should not use setmode. */
+
+ int res;
+ if (cfd->get_w_binary () && cfd->get_r_binary ())
+ res = O_BINARY;
+ else if (cfd->get_w_binset () && cfd->get_r_binset ())
+ res = O_TEXT; /* Specifically set O_TEXT */
+ else
+ res = 0;
+
+ if (!mode)
+ cfd->reset_to_open_binmode ();
+ else
+ cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode);
+
+ if (_cygwin_istext_for_stdio (fd))
+ setmode_mode = O_TEXT;
+ else
+ setmode_mode = O_BINARY;
+ setmode_file = fd;
+ _fwalk (_REENT, setmode_helper);
+
+ syscall_printf ("setmode (%d<%s>, %p) returns %s", fd, cfd->get_name (),
+ mode, res & O_TEXT ? "text" : "binary");
+ return res;
+}
+
+extern "C" int
+ftruncate64 (int fd, _off64_t length)
+{
+ int res = -1;
+
+ if (length < 0)
+ set_errno (EINVAL);
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ {
+ HANDLE h = cygheap->fdtab[fd]->get_handle ();
+
+ if (cfd->get_handle ())
+ {
+ /* remember curr file pointer location */
+ _off64_t prev_loc = cfd->lseek (0, SEEK_CUR);
+
+ cfd->lseek (length, SEEK_SET);
+ if (!SetEndOfFile (h))
+ __seterrno ();
+ else
+ res = 0;
+
+ /* restore original file pointer location */
+ cfd->lseek (prev_loc, SEEK_SET);
+ }
+ }
+ }
+
+ syscall_printf ("%d = ftruncate (%d, %D)", res, fd, length);
+ return res;
+}
+
+/* ftruncate: P96 5.6.7.1 */
+extern "C" int
+ftruncate (int fd, _off_t length)
+{
+ return ftruncate64 (fd, (_off64_t)length);
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate64 (const char *pathname, _off64_t length)
+{
+ int fd;
+ int res = -1;
+
+ fd = open (pathname, O_RDWR);
+
+ if (fd == -1)
+ set_errno (EBADF);
+ else
+ {
+ res = ftruncate64 (fd, length);
+ close (fd);
+ }
+ syscall_printf ("%d = truncate (%s, %d)", res, pathname, length);
+
+ return res;
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate (const char *pathname, _off_t length)
+{
+ return truncate64 (pathname, (_off64_t)length);
+}
+
+extern "C" long
+get_osfhandle (int fd)
+{
+ long res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = (long) cfd->get_handle ();
+ else
+ res = -1;
+
+ syscall_printf ("%d = get_osfhandle (%d)", res, fd);
+ return res;
+}
+
+extern "C" int
+statfs (const char *fname, struct statfs *sfs)
+{
+ if (!sfs)
+ {
+ set_errno (EFAULT);
+ return -1;
+ }
+
+ path_conv full_path (fname, PC_SYM_FOLLOW | PC_FULL);
+ const char *root = full_path.root_dir ();
+
+ syscall_printf ("statfs %s", root);
+
+ /* GetDiskFreeSpaceEx must be called before GetDiskFreeSpace on
+ WinME, to avoid the MS KB 314417 bug */
+ ULARGE_INTEGER availb, freeb, totalb;
+ BOOL status = GetDiskFreeSpaceEx (root, &availb, &totalb, &freeb);
+
+ DWORD spc, bps, availc, freec, totalc;
+
+ if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ if (status)
+ {
+ availc = availb.QuadPart / (spc*bps);
+ totalc = totalb.QuadPart / (spc*bps);
+ freec = freeb.QuadPart / (spc*bps);
+ }
+ else
+ availc = freec;
+
+ DWORD vsn, maxlen, flags;
+
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0))
+ {
+ __seterrno ();
+ return -1;
+ }
+ sfs->f_type = flags;
+ sfs->f_bsize = spc*bps;
+ sfs->f_blocks = totalc;
+ sfs->f_bavail = availc;
+ sfs->f_bfree = freec;
+ sfs->f_files = -1;
+ sfs->f_ffree = -1;
+ sfs->f_fsid = vsn;
+ sfs->f_namelen = maxlen;
+ return 0;
+}
+
+extern "C" int
+fstatfs (int fd, struct statfs *sfs)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ return statfs (cfd->get_name (), sfs);
+}
+
+/* setpgid: POSIX 4.3.3.1 */
+extern "C" int
+setpgid (pid_t pid, pid_t pgid)
+{
+ int res = -1;
+ if (pid == 0)
+ pid = getpid ();
+ if (pgid == 0)
+ pgid = pid;
+
+ if (pgid < 0)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ else
+ {
+ pinfo p (pid, PID_MAP_RW);
+ if (!p)
+ {
+ set_errno (ESRCH);
+ goto out;
+ }
+ /* A process may only change the process group of itself and its children */
+ if (p == myself || p->ppid == myself->pid)
+ {
+ p->pgid = pgid;
+ if (p->pid != p->pgid)
+ p->set_has_pgid_children (0);
+ res = 0;
+ }
+ else
+ {
+ set_errno (EPERM);
+ goto out;
+ }
+ }
+out:
+ syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res);
+ return res;
+}
+
+extern "C" pid_t
+getpgid (pid_t pid)
+{
+ if (pid == 0)
+ pid = getpid ();
+
+ pinfo p (pid);
+ if (p == 0)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+ return p->pgid;
+}
+
+extern "C" int
+setpgrp (void)
+{
+ return setpgid (0, 0);
+}
+
+extern "C" pid_t
+getpgrp (void)
+{
+ return getpgid (0);
+}
+
+extern "C" char *
+ptsname (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return 0;
+ return (char *) (cfd->ptsname ());
+}
+
+/* FIXME: what is this? */
+extern "C" int __declspec(dllexport)
+regfree ()
+{
+ return 0;
+}
+
+static int __stdcall
+mknod_worker (const char *path, mode_t type, mode_t mode, _major_t major,
+ _minor_t minor)
+{
+ char buf[sizeof (":\\00000000:00000000:00000000") + CYG_MAX_PATH];
+ sprintf (buf, ":\\%x:%x:%x", major, minor,
+ type | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ return symlink_worker (buf, path, true, true);
+}
+
+extern "C" int
+mknod32 (const char *path, mode_t mode, __dev32_t dev)
+{
+ if (check_null_empty_str_errno (path))
+ return -1;
+
+ if (strlen (path) >= CYG_MAX_PATH)
+ return -1;
+
+ path_conv w32path (path, PC_SYM_NOFOLLOW | PC_FULL);
+ if (w32path.exists ())
+ {
+ set_errno (EEXIST);
+ return -1;
+ }
+
+ mode_t type = mode & S_IFMT;
+ _major_t major = _major (dev);
+ _minor_t minor = _minor (dev);
+ switch (type)
+ {
+ case S_IFCHR:
+ case S_IFBLK:
+ break;
+
+ case S_IFIFO:
+ major = _major (FH_FIFO);
+ minor = _minor (FH_FIFO);
+ break;
+
+ case 0:
+ case S_IFREG:
+ {
+ int fd = open (path, O_CREAT, mode);
+ if (fd < 0)
+ return -1;
+ close (fd);
+ return 0;
+ }
+
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return mknod_worker (w32path, type, mode, major, minor);
+}
+
+extern "C" int
+mknod (const char *_path, mode_t mode, __dev16_t dev)
+{
+ return mknod32 (_path, mode, (__dev32_t) dev);
+}
+
+extern "C" int
+mkfifo (const char *_path, mode_t mode)
+{
+ set_errno (ENOSYS); // FIXME
+ return -1;
+}
+
+/* seteuid: standards? */
+extern "C" int
+seteuid32 (__uid32_t uid)
+{
+ debug_printf ("uid: %u myself->gid: %u", uid, myself->gid);
+
+ if (uid == myself->uid && !cygheap->user.groups.ischanged)
+ {
+ debug_printf ("Nothing happens");
+ return 0;
+ }
+
+ cygsid usersid;
+ user_groups &groups = cygheap->user.groups;
+ HANDLE ptok, new_token = INVALID_HANDLE_VALUE;
+ struct passwd * pw_new;
+ bool token_is_internal, issamesid;
+ char dacl_buf[MAX_DACL_LEN (5)];
+ TOKEN_DEFAULT_DACL tdacl = {};
+
+ pw_new = internal_getpwuid (uid);
+ if (!wincap.has_security () && pw_new)
+ goto success_9x;
+ if (!usersid.getfrompw (pw_new))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ RevertToSelf ();
+ if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok))
+ {
+ __seterrno ();
+ goto failed_ptok;;
+ }
+
+ /* Verify if the process token is suitable. */
+ if (verify_token (ptok, usersid, groups))
+ new_token = ptok;
+ /* Verify if the external token is suitable */
+ else if (cygheap->user.external_token != INVALID_HANDLE_VALUE
+ && verify_token (cygheap->user.external_token, usersid, groups))
+ new_token = cygheap->user.external_token;
+ /* Verify if the current token (internal or former external) is suitable */
+ else if (cygheap->user.current_token != INVALID_HANDLE_VALUE
+ && cygheap->user.current_token != cygheap->user.external_token
+ && verify_token (cygheap->user.current_token, usersid, groups,
+ &token_is_internal))
+ new_token = cygheap->user.current_token;
+ /* Verify if the internal token is suitable */
+ else if (cygheap->user.internal_token != INVALID_HANDLE_VALUE
+ && cygheap->user.internal_token != cygheap->user.current_token
+ && verify_token (cygheap->user.internal_token, usersid, groups,
+ &token_is_internal))
+ new_token = cygheap->user.internal_token;
+
+ debug_printf ("Found token %d", new_token);
+
+ /* Set process def dacl to allow access to impersonated token */
+ if (sec_acl ((PACL) dacl_buf, true, true, usersid))
+ {
+ tdacl.DefaultDacl = (PACL) dacl_buf;
+ if (!SetTokenInformation (ptok, TokenDefaultDacl,
+ &tdacl, sizeof dacl_buf))
+ debug_printf ("SetTokenInformation"
+ "(TokenDefaultDacl): %E");
+ }
+
+ /* If no impersonation token is available, try to
+ authenticate using NtCreateToken () or subauthentication. */
+ if (new_token == INVALID_HANDLE_VALUE)
+ {
+ new_token = create_token (usersid, groups, pw_new);
+ if (new_token == INVALID_HANDLE_VALUE)
+ {
+ /* create_token failed. Try subauthentication. */
+ debug_printf ("create token failed, try subauthentication.");
+ new_token = subauth (pw_new);
+ if (new_token == INVALID_HANDLE_VALUE)
+ goto failed;
+ }
+ /* Keep at most one internal token */
+ if (cygheap->user.internal_token != INVALID_HANDLE_VALUE)
+ CloseHandle (cygheap->user.internal_token);
+ cygheap->user.internal_token = new_token;
+ }
+ if (new_token != ptok)
+ {
+ /* Avoid having HKCU use default user */
+ load_registry_hive (usersid);
+
+ /* Try setting owner to same value as user. */
+ if (!SetTokenInformation (new_token, TokenOwner,
+ &usersid, sizeof usersid))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenOwner): %E");
+ /* Try setting primary group in token to current group */
+ if (!SetTokenInformation (new_token, TokenPrimaryGroup,
+ &groups.pgsid, sizeof (cygsid)))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenPrimaryGroup): %E");
+ /* Try setting default DACL */
+ if (tdacl.DefaultDacl
+ && !SetTokenInformation (new_token, TokenDefaultDacl,
+ &tdacl, sizeof (tdacl)))
+ debug_printf ("SetTokenInformation (TokenDefaultDacl): %E");
+ }
+
+ CloseHandle (ptok);
+ issamesid = (usersid == cygheap->user.sid ());
+ cygheap->user.set_sid (usersid);
+ cygheap->user.current_token = new_token == ptok ? INVALID_HANDLE_VALUE
+ : new_token;
+ if (!issamesid) /* MS KB 199190 */
+ RegCloseKey (HKEY_CURRENT_USER);
+ cygheap->user.reimpersonate ();
+ if (!issamesid)
+ user_shared_initialize (true);
+
+success_9x:
+ cygheap->user.set_name (pw_new->pw_name);
+ myself->uid = uid;
+ groups.ischanged = FALSE;
+ return 0;
+
+failed:
+ CloseHandle (ptok);
+failed_ptok:
+ cygheap->user.reimpersonate ();
+ return -1;
+}
+
+extern "C" int
+seteuid (__uid16_t uid)
+{
+ return seteuid32 (uid16touid32 (uid));
+}
+
+/* setuid: POSIX 4.2.2.1 */
+extern "C" int
+setuid32 (__uid32_t uid)
+{
+ int ret = seteuid32 (uid);
+ if (!ret)
+ cygheap->user.real_uid = myself->uid;
+ debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setuid (__uid16_t uid)
+{
+ return setuid32 (uid16touid32 (uid));
+}
+
+extern "C" int
+setreuid32 (__uid32_t ruid, __uid32_t euid)
+{
+ int ret = 0;
+ bool tried = false;
+ __uid32_t old_euid = myself->uid;
+
+ if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid)
+ tried = !(ret = seteuid32 (ruid));
+ if (!ret && euid != ILLEGAL_UID)
+ ret = seteuid32 (euid);
+ if (tried && (ret || euid == ILLEGAL_UID) && seteuid32 (old_euid))
+ system_printf ("Cannot restore original euid %u", old_euid);
+ if (!ret && ruid != ILLEGAL_UID)
+ cygheap->user.real_uid = ruid;
+ debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setreuid (__uid16_t ruid, __uid16_t euid)
+{
+ return setreuid32 (uid16touid32 (ruid), uid16touid32 (euid));
+}
+
+/* setegid: from System V. */
+extern "C" int
+setegid32 (__gid32_t gid)
+{
+ debug_printf ("new egid: %u current: %u", gid, myself->gid);
+
+ if (gid == myself->gid || !wincap.has_security ())
+ {
+ myself->gid = gid;
+ return 0;
+ }
+
+ user_groups * groups = &cygheap->user.groups;
+ cygsid gsid;
+ HANDLE ptok;
+ struct __group32 * gr = internal_getgrgid (gid);
+
+ if (!gsid.getfromgr (gr))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ myself->gid = gid;
+
+ groups->update_pgrp (gsid);
+ /* If impersonated, update primary group and revert */
+ if (cygheap->user.issetuid ())
+ {
+ if (!SetTokenInformation (cygheap->user.token (),
+ TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(thread, "
+ "TokenPrimaryGroup): %E");
+ RevertToSelf ();
+ }
+ if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok))
+ debug_printf ("OpenProcessToken(): %E");
+ else
+ {
+ if (!SetTokenInformation (ptok, TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(process, "
+ "TokenPrimaryGroup): %E");
+ CloseHandle (ptok);
+ }
+ if (cygheap->user.issetuid ()
+ && !ImpersonateLoggedOnUser (cygheap->user.token ()))
+ system_printf ("Impersonating in setegid failed: %E");
+ return 0;
+}
+
+extern "C" int
+setegid (__gid16_t gid)
+{
+ return setegid32 (gid16togid32 (gid));
+}
+
+/* setgid: POSIX 4.2.2.1 */
+extern "C" int
+setgid32 (__gid32_t gid)
+{
+ int ret = setegid32 (gid);
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setgid (__gid16_t gid)
+{
+ int ret = setegid32 (gid16togid32 (gid));
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setregid32 (__gid32_t rgid, __gid32_t egid)
+{
+ int ret = 0;
+ bool tried = false;
+ __gid32_t old_egid = myself->gid;
+
+ if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid)
+ tried = !(ret = setegid32 (rgid));
+ if (!ret && egid != ILLEGAL_GID)
+ ret = setegid32 (egid);
+ if (tried && (ret || egid == ILLEGAL_GID) && setegid32 (old_egid))
+ system_printf ("Cannot restore original egid %u", old_egid);
+ if (!ret && rgid != ILLEGAL_GID)
+ cygheap->user.real_gid = rgid;
+ debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid);
+ return ret;
+}
+
+extern "C" int
+setregid (__gid16_t rgid, __gid16_t egid)
+{
+ return setregid32 (gid16togid32 (rgid), gid16togid32 (egid));
+}
+
+/* chroot: privileged Unix system call. */
+/* FIXME: Not privileged here. How should this be done? */
+extern "C" int
+chroot (const char *newroot)
+{
+ path_conv path (newroot, PC_SYM_FOLLOW | PC_FULL | PC_POSIX);
+
+ int ret;
+ if (path.error)
+ ret = -1;
+ else if (!path.exists ())
+ {
+ set_errno (ENOENT);
+ ret = -1;
+ }
+ else if (!path.isdir ())
+ {
+ set_errno (ENOTDIR);
+ ret = -1;
+ }
+ else
+ {
+ cygheap->root.set (path.normalized_path, path);
+ ret = 0;
+ }
+
+ syscall_printf ("%d = chroot (%s)", ret ? get_errno () : 0,
+ newroot ? newroot : "NULL");
+ if (!path.normalized_path_size && path.normalized_path)
+ cfree (path.normalized_path);
+ return ret;
+}
+
+extern "C" int
+creat (const char *path, mode_t mode)
+{
+ return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+extern "C" void
+__assertfail ()
+{
+ exit (99);
+}
+
+extern "C" int
+getw (FILE *fp)
+{
+ int w, ret;
+ ret = fread (&w, sizeof (int), 1, fp);
+ return ret != 1 ? EOF : w;
+}
+
+extern "C" int
+putw (int w, FILE *fp)
+{
+ int ret;
+ ret = fwrite (&w, sizeof (int), 1, fp);
+ if (feof (fp) || ferror (fp))
+ return -1;
+ return 0;
+}
+
+extern "C" int
+wcscmp (const wchar_t *s1, const wchar_t *s2)
+{
+ while (*s1 && *s1 == *s2)
+ {
+ s1++;
+ s2++;
+ }
+
+ return (* (unsigned short *) s1) - (* (unsigned short *) s2);
+}
+
+extern "C" size_t
+wcslen (const wchar_t *s1)
+{
+ int l = 0;
+ while (s1[l])
+ l++;
+ return l;
+}
+
+/* FIXME: to do this right, maybe work out the usoft va_list machine
+ and use wsvprintfW instead?
+*/
+extern "C" int
+wprintf (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = vprintf (fmt, ap);
+ va_end (ap);
+ return ret;
+}
+
+extern "C" int
+vhangup ()
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" _PTR
+memccpy (_PTR out, const _PTR in, int c, size_t len)
+{
+ const char *inc = (char *) in;
+ char *outc = (char *) out;
+
+ while (len)
+ {
+ char x = *inc++;
+ *outc++ = x;
+ if (x == c)
+ return outc;
+ len --;
+ }
+ return 0;
+}
+
+extern "C" int
+nice (int incr)
+{
+ DWORD priority[] =
+ {
+ IDLE_PRIORITY_CLASS,
+ IDLE_PRIORITY_CLASS,
+ NORMAL_PRIORITY_CLASS,
+ HIGH_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS
+ };
+ int curr = 2;
+
+ switch (GetPriorityClass (hMainProc))
+ {
+ case IDLE_PRIORITY_CLASS:
+ curr = 1;
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ curr = 2;
+ break;
+ case HIGH_PRIORITY_CLASS:
+ curr = 3;
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ curr = 4;
+ break;
+ }
+ if (incr > 0)
+ incr = -1;
+ else if (incr < 0)
+ incr = 1;
+
+ if (SetPriorityClass (hMainProc, priority[curr + incr]) == FALSE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Find the first bit set in I.
+ */
+
+extern "C" int
+ffs (int i)
+{
+ static const unsigned char table[] =
+ {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ unsigned long int a;
+ unsigned long int x = i & -i;
+
+ a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24);
+
+ return table[x >> a] + a;
+}
+
+static void
+locked_append (int fd, const void * buf, size_t size)
+{
+ struct __flock64 lock_buffer = {F_WRLCK, SEEK_SET, 0, 0, 0};
+ int count = 0;
+
+ do
+ if ((lock_buffer.l_start = lseek64 (fd, 0, SEEK_END)) != (_off64_t)-1
+ && fcntl_worker (fd, F_SETLKW, &lock_buffer) != -1)
+ {
+ if (lseek64 (fd, 0, SEEK_END) != (_off64_t)-1)
+ write (fd, buf, size);
+ lock_buffer.l_type = F_UNLCK;
+ fcntl_worker (fd, F_SETLK, &lock_buffer);
+ break;
+ }
+ while (count++ < 1000
+ && (errno == EACCES || errno == EAGAIN)
+ && !usleep (1000));
+}
+
+extern "C" void
+updwtmp (const char *wtmp_file, const struct utmp *ut)
+{
+ int fd;
+
+ if ((fd = open (wtmp_file, O_WRONLY | O_BINARY, 0)) >= 0)
+ {
+ locked_append (fd, ut, sizeof *ut);
+ close (fd);
+ }
+}
+
+extern "C" void
+logwtmp (const char *line, const char *user, const char *host)
+{
+ struct utmp ut;
+ memset (&ut, 0, sizeof ut);
+ ut.ut_type = USER_PROCESS;
+ ut.ut_pid = getpid ();
+ if (line)
+ strncpy (ut.ut_line, line, sizeof ut.ut_line);
+ time (&ut.ut_time);
+ if (user)
+ strncpy (ut.ut_user, user, sizeof ut.ut_user);
+ if (host)
+ strncpy (ut.ut_host, host, sizeof ut.ut_host);
+ updwtmp (_PATH_WTMP, &ut);
+}
+
+extern "C" void
+login (struct utmp *ut)
+{
+ pututline (ut);
+ endutent ();
+ updwtmp (_PATH_WTMP, ut);
+}
+
+extern "C" int
+logout (char *line)
+{
+ struct utmp ut_buf, *ut;
+
+ memset (&ut_buf, 0, sizeof ut_buf);
+ strncpy (ut_buf.ut_line, line, sizeof ut_buf.ut_line);
+ setutent ();
+ ut = getutline (&ut_buf);
+
+ if (ut)
+ {
+ ut->ut_type = DEAD_PROCESS;
+ memset (ut->ut_user, 0, sizeof ut->ut_user);
+ memset (ut->ut_host, 0, sizeof ut->ut_host);
+ time (&ut->ut_time);
+ debug_printf ("set logout time for %s", line);
+ pututline (ut);
+ endutent ();
+ return 1;
+ }
+ return 0;
+}
+
+static int utmp_fd = -1;
+static bool utmp_readonly = false;
+static char *utmp_file = (char *) _PATH_UTMP;
+
+static void
+internal_setutent (bool force_readwrite)
+{
+ if (force_readwrite && utmp_readonly)
+ endutent ();
+ if (utmp_fd < 0)
+ {
+ utmp_fd = open (utmp_file, O_RDWR | O_BINARY);
+ /* If open fails, we assume an unprivileged process (who?). In this
+ case we try again for reading only unless the process calls
+ pututline() (==force_readwrite) in which case opening just fails. */
+ if (utmp_fd < 0 && !force_readwrite)
+ {
+ utmp_fd = open (utmp_file, O_RDONLY | O_BINARY);
+ if (utmp_fd >= 0)
+ utmp_readonly = true;
+ }
+ }
+ else
+ lseek (utmp_fd, 0, SEEK_SET);
+}
+
+extern "C" void
+setutent ()
+{
+ internal_setutent (false);
+}
+
+extern "C" void
+endutent ()
+{
+ if (utmp_fd >= 0)
+ {
+ close (utmp_fd);
+ utmp_fd = -1;
+ utmp_readonly = false;
+ }
+}
+
+extern "C" void
+utmpname (_CONST char *file)
+{
+ if (check_null_empty_str (file))
+ {
+ debug_printf ("Invalid file");
+ return;
+ }
+ endutent ();
+ utmp_file = strdup (file);
+ debug_printf ("New UTMP file: %s", utmp_file);
+}
+
+/* Note: do not make NO_COPY */
+static struct utmp utmp_data_buf[16];
+static unsigned utix = 0;
+#define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0]))
+#define utmp_data ({ \
+ if (utix > nutdbuf) \
+ utix = 0; \
+ utmp_data_buf + utix++; \
+})
+
+extern "C" struct utmp *
+getutent ()
+{
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut)
+ return NULL;
+ return ut;
+}
+
+extern "C" struct utmp *
+getutid (struct utmp *id)
+{
+ if (check_null_invalid_struct_errno (id))
+ return NULL;
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
+ {
+ switch (id->ut_type)
+ {
+ case RUN_LVL:
+ case BOOT_TIME:
+ case OLD_TIME:
+ case NEW_TIME:
+ if (id->ut_type == ut->ut_type)
+ return ut;
+ break;
+ case INIT_PROCESS:
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ case DEAD_PROCESS:
+ if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0)
+ return ut;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+extern "C" struct utmp *
+getutline (struct utmp *line)
+{
+ if (check_null_invalid_struct_errno (line))
+ return NULL;
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
+ if ((ut->ut_type == LOGIN_PROCESS ||
+ ut->ut_type == USER_PROCESS) &&
+ !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line)))
+ return ut;
+
+ return NULL;
+}
+
+extern "C" void
+pututline (struct utmp *ut)
+{
+ if (check_null_invalid_struct (ut))
+ return;
+ internal_setutent (true);
+ if (utmp_fd < 0)
+ {
+ debug_printf ("error: utmp_fd %d", utmp_fd);
+ return;
+ }
+ debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n",
+ ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id);
+ debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n",
+ ut->ut_user, ut->ut_host);
+
+ struct utmp *u;
+ if ((u = getutid (ut)))
+ {
+ lseek (utmp_fd, -sizeof *ut, SEEK_CUR);
+ write (utmp_fd, ut, sizeof *ut);
+ }
+ else
+ locked_append (utmp_fd, ut, sizeof *ut);
+}
+
+extern "C"
+long gethostid (void)
+{
+ unsigned data[13] = {0x92895012,
+ 0x10293412,
+ 0x29602018,
+ 0x81928167,
+ 0x34601329,
+ 0x75630198,
+ 0x89860395,
+ 0x62897564,
+ 0x00194362,
+ 0x20548593,
+ 0x96839102,
+ 0x12219854,
+ 0x00290012};
+
+ bool has_cpuid = false;
+
+ DWORD opmask = SetThreadAffinityMask (GetCurrentThread (), 1);
+ if (!opmask)
+ debug_printf ("SetThreadAffinityMask to 1 failed, %E");
+
+ if (!can_set_flag (0x00040000))
+ debug_printf ("386 processor - no cpuid");
+ else
+ {
+ debug_printf ("486 processor");
+ if (can_set_flag (0x00200000))
+ {
+ debug_printf ("processor supports CPUID instruction");
+ has_cpuid = true;
+ }
+ else
+ debug_printf ("processor does not support CPUID instruction");
+ }
+ if (has_cpuid)
+ {
+ unsigned maxf, unused[3];
+ cpuid (&maxf, &unused[0], &unused[1], &unused[2], 0);
+ maxf &= 0xffff;
+ if (maxf >= 1)
+ {
+ unsigned features;
+ cpuid (&data[0], &unused[0], &unused[1], &features, 1);
+ if (features & (1 << 18))
+ {
+ debug_printf ("processor has psn");
+ if (maxf >= 3)
+ {
+ cpuid (&unused[0], &unused[1], &data[1], &data[2], 3);
+ debug_printf ("Processor PSN: %04x-%04x-%04x-%04x-%04x-%04x",
+ data[0] >> 16, data[0] & 0xffff, data[2] >> 16, data[2] & 0xffff, data[1] >> 16, data[1] & 0xffff);
+ }
+ }
+ else
+ debug_printf ("processor does not have psn");
+ }
+ }
+
+ UUID Uuid;
+ RPC_STATUS status = UuidCreateSequential (&Uuid);
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ status = UuidCreate (&Uuid);
+ if (status == RPC_S_OK)
+ {
+ data[4] = *(unsigned *)&Uuid.Data4[2];
+ data[5] = *(unsigned short *)&Uuid.Data4[6];
+ // Unfortunately Windows will sometimes pick a virtual Ethernet card
+ // e.g. VMWare Virtual Ethernet Adaptor
+ debug_printf ("MAC address of first Ethernet card: %02x:%02x:%02x:%02x:%02x:%02x",
+ Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4],
+ Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]);
+ }
+ else
+ {
+ debug_printf ("no Ethernet card installed");
+ }
+
+ reg_key key (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", "Microsoft", "Windows", "CurrentVersion", NULL);
+ key.get_string ("ProductId", (char *)&data[6], 24, "00000-000-0000000-00000");
+ debug_printf ("Windows Product ID: %s", (char *)&data[6]);
+
+ /* Contrary to MSDN, NT4 requires the second argument
+ or a STATUS_ACCESS_VIOLATION is generated */
+ ULARGE_INTEGER availb;
+ GetDiskFreeSpaceEx ("C:\\", &availb, (PULARGE_INTEGER) &data[11], NULL);
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ GetDiskFreeSpace ("C:\\", NULL, NULL, NULL, (DWORD *)&data[11]);
+
+ debug_printf ("hostid entropy: %08x %08x %08x %08x "
+ "%08x %08x %08x %08x "
+ "%08x %08x %08x %08x "
+ "%08x",
+ data[0], data[1],
+ data[2], data[3],
+ data[4], data[5],
+ data[6], data[7],
+ data[8], data[9],
+ data[10], data[11],
+ data[12]);
+
+ long hostid = 0x40291372;
+ // a random hashing algorithm
+ // dependancy on md5 is probably too costly
+ for (int i=0;i<13;i++)
+ hostid ^= ((data[i] << (i << 2)) | (data[i] >> (32 - (i << 2))));
+
+ if (opmask && !SetThreadAffinityMask (GetCurrentThread (), opmask))
+ debug_printf ("SetThreadAffinityMask to %p failed, %E", opmask);
+
+ debug_printf ("hostid: %08x", hostid);
+
+ return hostid;
+}
+
+#define ETC_SHELLS "/etc/shells"
+static int shell_index;
+static struct __sFILE64 *shell_fp;
+
+extern "C" char *
+getusershell ()
+{
+ /* List of default shells if no /etc/shells exists, defined as on Linux.
+ FIXME: SunOS has a far longer list, containing all shells which
+ might be shipped with the OS. Should we do the same for the Cygwin
+ distro, adding bash, tcsh, ksh, pdksh and zsh? */
+ static NO_COPY const char *def_shells[] = {
+ "/bin/sh",
+ "/bin/csh",
+ "/usr/bin/sh",
+ "/usr/bin/csh",
+ NULL
+ };
+ static char buf[CYG_MAX_PATH];
+ int ch, buf_idx;
+
+ if (!shell_fp && !(shell_fp = fopen64 (ETC_SHELLS, "rt")))
+ {
+ if (def_shells[shell_index])
+ return strcpy (buf, def_shells[shell_index++]);
+ return NULL;
+ }
+ /* Skip white space characters. */
+ while ((ch = getc (shell_fp)) != EOF && isspace (ch))
+ ;
+ /* Get each non-whitespace character as part of the shell path as long as
+ it fits in buf. */
+ for (buf_idx = 0;
+ ch != EOF && !isspace (ch) && buf_idx < CYG_MAX_PATH;
+ buf_idx++, ch = getc (shell_fp))
+ buf[buf_idx] = ch;
+ /* Skip any trailing non-whitespace character not fitting in buf. If the
+ path is longer than CYG_MAX_PATH, it's invalid anyway. */
+ while (ch != EOF && !isspace (ch))
+ ch = getc (shell_fp);
+ if (buf_idx)
+ {
+ buf[buf_idx] = '\0';
+ return buf;
+ }
+ return NULL;
+}
+
+extern "C" void
+setusershell ()
+{
+ if (shell_fp)
+ fseek (shell_fp, 0L, SEEK_SET);
+ shell_index = 0;
+}
+
+extern "C" void
+endusershell ()
+{
+ if (shell_fp)
+ fclose (shell_fp);
+ shell_index = 0;
+}
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
new file mode 100644
index 00000000000..ae03dc6266b
--- /dev/null
+++ b/winsup/cygwin/thread.cc
@@ -0,0 +1,3223 @@
+/* thread.cc: Locking and threading module functions
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+ Originally written by Marco Fuykschot <marco@ddi.nl>
+ Substantialy enhanced by Robert Collins <rbtcollins@hotmail.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. */
+
+/* Implementation overview and caveats:
+
+ Win32 puts some contraints on what can and cannot be implemented. Where
+ possible we work around those contrainsts. Where we cannot work around
+ the constraints we either pretend to be conformant, or return an error
+ code.
+
+ Some caveats: PROCESS_SHARED objects while they pretend to be process
+ shared, may not actually work. Some test cases are needed to determine
+ win32's behaviour. My suspicion is that the win32 handle needs to be
+ opened with different flags for proper operation.
+
+ R.Collins, April 2001. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "winsup.h"
+#include <limits.h>
+#include "cygerrno.h"
+#include <assert.h>
+#include <stdlib.h>
+#include "pinfo.h"
+#include "sigproc.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygtls.h"
+#include <semaphore.h>
+#include <stdio.h>
+#include <sys/timeb.h>
+#include <exceptions.h>
+#include <sys/fcntl.h>
+
+extern int threadsafe;
+
+#undef __getreent
+extern "C" struct _reent *
+__getreent ()
+{
+ return &_my_tls.local_clib;
+}
+
+inline LPCRITICAL_SECTION
+ResourceLocks::Lock (int _resid)
+{
+ return &lock;
+}
+
+void
+SetResourceLock (int _res_id, int _mode, const char *_function)
+{
+ EnterCriticalSection (user_data->resourcelocks->Lock (_res_id));
+}
+
+void
+ReleaseResourceLock (int _res_id, int _mode, const char *_function)
+{
+ LeaveCriticalSection (user_data->resourcelocks->Lock (_res_id));
+}
+
+void
+ResourceLocks::Init ()
+{
+ InitializeCriticalSection (&lock);
+ inited = true;
+ thread_printf ("lock %p inited by %p , %d", &lock, user_data, myself->pid);
+}
+
+void
+ResourceLocks::Delete ()
+{
+ if (inited)
+ {
+ thread_printf ("Close Resource Locks %p ", &lock);
+ DeleteCriticalSection (&lock);
+ inited = false;
+ }
+}
+
+void
+MTinterface::Init ()
+{
+ pthread_mutex::init_mutex ();
+ pthread_cond::init_mutex ();
+ pthread_rwlock::init_mutex ();
+}
+
+void
+MTinterface::fixup_before_fork (void)
+{
+ pthread_key::fixup_before_fork ();
+}
+
+/* This function is called from a single threaded process */
+void
+MTinterface::fixup_after_fork (void)
+{
+ pthread_key::fixup_after_fork ();
+
+ threadcount = 0;
+ pthread::init_mainthread ();
+
+ pthread::fixup_after_fork ();
+ pthread_mutex::fixup_after_fork ();
+ pthread_cond::fixup_after_fork ();
+ pthread_rwlock::fixup_after_fork ();
+ semaphore::fixup_after_fork ();
+}
+
+/* pthread calls */
+
+/* static methods */
+void
+pthread::init_mainthread ()
+{
+ pthread *thread = get_tls_self_pointer ();
+ if (!thread)
+ {
+ thread = new pthread ();
+ if (!thread)
+ api_fatal ("failed to create mainthread object");
+ }
+
+ thread->cygtls = &_my_tls;
+ _my_tls.tid = thread;
+ thread->thread_id = GetCurrentThreadId ();
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+ GetCurrentProcess (), &thread->win32_obj_id,
+ 0, FALSE, DUPLICATE_SAME_ACCESS))
+ api_fatal ("failed to create mainthread handle");
+ if (!thread->create_cancel_event ())
+ api_fatal ("couldn't create cancel event for main thread");
+ thread->postcreate ();
+}
+
+pthread *
+pthread::self ()
+{
+ pthread *thread = get_tls_self_pointer ();
+ if (thread)
+ return thread;
+ return pthread_null::get_null_pthread ();
+}
+
+pthread *
+pthread::get_tls_self_pointer ()
+{
+ return _my_tls.tid;
+}
+
+List<pthread> pthread::threads;
+
+/* member methods */
+pthread::pthread ():verifyable_object (PTHREAD_MAGIC), win32_obj_id (0),
+ valid (false), suspended (false),
+ cancelstate (0), canceltype (0), cancel_event (0),
+ joiner (NULL), next (NULL), cleanup_stack (NULL)
+{
+ if (this != pthread_null::get_null_pthread ())
+ threads.insert (this);
+}
+
+pthread::~pthread ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+ if (cancel_event)
+ CloseHandle (cancel_event);
+
+ if (this != pthread_null::get_null_pthread ())
+ threads.remove (this);
+}
+
+bool
+pthread::create_cancel_event ()
+{
+ cancel_event = ::CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ if (!cancel_event)
+ {
+ system_printf ("couldn't create cancel event, %E");
+ /* we need the event for correct behaviour */
+ return false;
+ }
+ return true;
+}
+
+void
+pthread::precreate (pthread_attr *newattr)
+{
+ pthread_mutex *verifyable_mutex_obj = &mutex;
+
+ /* already running ? */
+ if (win32_obj_id)
+ return;
+
+ if (newattr)
+ {
+ attr.joinable = newattr->joinable;
+ attr.contentionscope = newattr->contentionscope;
+ attr.inheritsched = newattr->inheritsched;
+ attr.stacksize = newattr->stacksize;
+ }
+
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("New thread object access mutex is not valid. this %p",
+ this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mutex.type = PTHREAD_MUTEX_NORMAL;
+ if (!create_cancel_event ())
+ magic = 0;
+}
+
+void
+pthread::create (void *(*func) (void *), pthread_attr *newattr,
+ void *threadarg)
+{
+ precreate (newattr);
+ if (!magic)
+ return;
+
+ function = func;
+ arg = threadarg;
+
+ win32_obj_id = ::CreateThread (&sec_none_nih, attr.stacksize,
+ thread_init_wrapper, this, CREATE_SUSPENDED,
+ &thread_id);
+
+ if (!win32_obj_id)
+ {
+ thread_printf ("CreateThread failed: this %p, %E", this);
+ magic = 0;
+ }
+ else
+ {
+ postcreate ();
+ ResumeThread (win32_obj_id);
+ }
+}
+
+void
+pthread::postcreate ()
+{
+ valid = true;
+
+ InterlockedIncrement (&MT_INTERFACE->threadcount);
+ /* FIXME: set the priority appropriately for system contention scope */
+ if (attr.inheritsched == PTHREAD_EXPLICIT_SCHED)
+ {
+ /* FIXME: set the scheduling settings for the new thread */
+ /* sched_thread_setparam (win32_obj_id, attr.schedparam); */
+ }
+}
+
+void
+pthread::exit (void *value_ptr)
+{
+ class pthread *thread = this;
+
+ // run cleanup handlers
+ pop_all_cleanup_handlers ();
+
+ pthread_key::run_all_destructors ();
+
+ mutex.lock ();
+ // cleanup if thread is in detached state and not joined
+ if (equal (joiner, thread))
+ delete this;
+ else
+ {
+ valid = false;
+ return_ptr = value_ptr;
+ mutex.unlock ();
+ }
+
+ (_reclaim_reent) (_REENT);
+
+
+ if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0)
+ ::exit (0);
+ else
+ {
+ _my_tls.remove (INFINITE);
+ ExitThread (0);
+ }
+}
+
+int
+pthread::cancel (void)
+{
+ class pthread *thread = this;
+ class pthread *self = pthread::self ();
+
+ mutex.lock ();
+
+ if (!valid)
+ {
+ mutex.unlock ();
+ return 0;
+ }
+
+ if (canceltype == PTHREAD_CANCEL_DEFERRED ||
+ cancelstate == PTHREAD_CANCEL_DISABLE)
+ {
+ // cancel deferred
+ mutex.unlock ();
+ SetEvent (cancel_event);
+ return 0;
+ }
+ else if (equal (thread, self))
+ {
+ mutex.unlock ();
+ cancel_self ();
+ return 0; // Never reached
+ }
+
+ // cancel asynchronous
+ SuspendThread (win32_obj_id);
+ if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_CONTROL;
+ GetThreadContext (win32_obj_id, &context);
+ context.Eip = (DWORD) pthread::static_cancel_self;
+ SetThreadContext (win32_obj_id, &context);
+ }
+ mutex.unlock ();
+ ResumeThread (win32_obj_id);
+
+ return 0;
+/*
+ TODO: insert pthread_testcancel into the required functions
+ the required function list is: *indicates done, X indicates not present in cygwin.
+aio_suspend ()
+*close ()
+*creat ()
+fcntl ()
+fsync ()
+getmsg ()
+getpmsg ()
+lockf ()
+mq_receive ()
+mq_send ()
+msgrcv ()
+msgsnd ()
+msync ()
+nanosleep ()
+open ()
+*pause ()
+poll ()
+pread ()
+*pthread_cond_timedwait ()
+*pthread_cond_wait ()
+*pthread_join ()
+*pthread_testcancel ()
+putmsg ()
+putpmsg ()
+pwrite ()
+read ()
+readv ()
+select ()
+*sem_wait ()
+*sigpause ()
+*sigsuspend ()
+sigtimedwait ()
+sigwait ()
+sigwaitinfo ()
+*sleep ()
+*system ()
+tcdrain ()
+*usleep ()
+*wait ()
+*wait3()
+waitid ()
+*waitpid ()
+write ()
+writev ()
+
+the optional list is:
+catclose ()
+catgets ()
+catopen ()
+closedir ()
+closelog ()
+ctermid ()
+dbm_close ()
+dbm_delete ()
+dbm_fetch ()
+dbm_nextkey ()
+dbm_open ()
+dbm_store ()
+dlclose ()
+dlopen ()
+endgrent ()
+endpwent ()
+endutxent ()
+fclose ()
+fcntl ()
+fflush ()
+fgetc ()
+fgetpos ()
+fgets ()
+fgetwc ()
+fgetws ()
+fopen ()
+fprintf ()
+fputc ()
+fputs ()
+fputwc ()
+fputws ()
+fread ()
+freopen ()
+fscanf ()
+fseek ()
+fseeko ()
+fsetpos ()
+ftell ()
+ftello ()
+ftw ()
+fwprintf ()
+fwrite ()
+fwscanf ()
+getc ()
+getc_unlocked ()
+getchar ()
+getchar_unlocked ()
+getcwd ()
+getdate ()
+getgrent ()
+getgrgid ()
+getgrgid_r ()
+getgrnam ()
+getgrnam_r ()
+getlogin ()
+getlogin_r ()
+getpwent ()
+*getpwnam ()
+*getpwnam_r ()
+*getpwuid ()
+*getpwuid_r ()
+gets ()
+getutxent ()
+getutxid ()
+getutxline ()
+getw ()
+getwc ()
+getwchar ()
+getwd ()
+glob ()
+iconv_close ()
+iconv_open ()
+ioctl ()
+lseek ()
+mkstemp ()
+nftw ()
+opendir ()
+openlog ()
+pclose ()
+perror ()
+popen ()
+printf ()
+putc ()
+putc_unlocked ()
+putchar ()
+putchar_unlocked ()
+puts ()
+pututxline ()
+putw ()
+putwc ()
+putwchar ()
+readdir ()
+readdir_r ()
+remove ()
+rename ()
+rewind ()
+rewinddir ()
+scanf ()
+seekdir ()
+semop ()
+setgrent ()
+setpwent ()
+setutxent ()
+strerror ()
+syslog ()
+tmpfile ()
+tmpnam ()
+ttyname ()
+ttyname_r ()
+ungetc ()
+ungetwc ()
+unlink ()
+vfprintf ()
+vfwprintf ()
+vprintf ()
+vwprintf ()
+wprintf ()
+wscanf ()
+
+Note, that for fcntl (), for any value of the cmd argument.
+
+And we must not introduce cancellation points anywhere else that's part of the posix or
+opengroup specs.
+ */
+}
+
+void
+pthread::testcancel (void)
+{
+ if (cancelstate == PTHREAD_CANCEL_DISABLE)
+ return;
+
+ if (WaitForSingleObject (cancel_event, 0) == WAIT_OBJECT_0)
+ cancel_self ();
+}
+
+void
+pthread::static_cancel_self (void)
+{
+ pthread::self ()->cancel_self ();
+}
+
+
+DWORD
+pthread::cancelable_wait (HANDLE object, DWORD timeout, const bool do_cancel)
+{
+ DWORD res;
+ HANDLE wait_objects[2];
+ pthread_t thread = self ();
+
+ if (!is_good_object (&thread) || thread->cancelstate == PTHREAD_CANCEL_DISABLE)
+ return WaitForSingleObject (object, timeout);
+
+ // Do not change the wait order
+ // The object must have higher priority than the cancel event,
+ // because WaitForMultipleObjects will return the smallest index
+ // if both objects are signaled
+ wait_objects[0] = object;
+ wait_objects[1] = thread->cancel_event;
+
+ res = WaitForMultipleObjects (2, wait_objects, FALSE, timeout);
+ if (do_cancel && res == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ return res;
+}
+
+int
+pthread::setcancelstate (int state, int *oldstate)
+{
+ int result = 0;
+
+ mutex.lock ();
+
+ if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE)
+ result = EINVAL;
+ else
+ {
+ if (oldstate)
+ *oldstate = cancelstate;
+ cancelstate = state;
+ }
+
+ mutex.unlock ();
+
+ return result;
+}
+
+int
+pthread::setcanceltype (int type, int *oldtype)
+{
+ int result = 0;
+
+ mutex.lock ();
+
+ if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS)
+ result = EINVAL;
+ else
+ {
+ if (oldtype)
+ *oldtype = canceltype;
+ canceltype = type;
+ }
+
+ mutex.unlock ();
+
+ return result;
+}
+
+void
+pthread::push_cleanup_handler (__pthread_cleanup_handler *handler)
+{
+ if (this != self ())
+ // TODO: do it?
+ api_fatal ("Attempt to push a cleanup handler across threads");
+ handler->next = cleanup_stack;
+ cleanup_stack = handler;
+}
+
+void
+pthread::pop_cleanup_handler (int const execute)
+{
+ if (this != self ())
+ // TODO: send a signal or something to the thread ?
+ api_fatal ("Attempt to execute a cleanup handler across threads");
+
+ mutex.lock ();
+
+ if (cleanup_stack != NULL)
+ {
+ __pthread_cleanup_handler *handler = cleanup_stack;
+
+ if (execute)
+ (*handler->function) (handler->arg);
+ cleanup_stack = handler->next;
+ }
+
+ mutex.unlock ();
+}
+
+void
+pthread::pop_all_cleanup_handlers ()
+{
+ while (cleanup_stack != NULL)
+ pop_cleanup_handler (1);
+}
+
+void
+pthread::cancel_self ()
+{
+ exit (PTHREAD_CANCELED);
+}
+
+DWORD
+pthread::get_thread_id ()
+{
+ return thread_id;
+}
+
+void
+pthread::_fixup_after_fork ()
+{
+ /* set thread to not running if it is not the forking thread */
+ if (this != pthread::self ())
+ {
+ magic = 0;
+ valid = false;
+ win32_obj_id = NULL;
+ cancel_event = NULL;
+ }
+}
+
+void
+pthread::suspend_except_self ()
+{
+ if (valid && this != pthread::self ())
+ SuspendThread (win32_obj_id);
+}
+
+void
+pthread::resume ()
+{
+ if (valid)
+ ResumeThread (win32_obj_id);
+}
+
+/* static members */
+bool
+pthread_attr::is_good_object (pthread_attr_t const *attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_ATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+/* instance members */
+
+pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC),
+joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS),
+inheritsched (PTHREAD_INHERIT_SCHED), stacksize (0)
+{
+ schedparam.sched_priority = 0;
+}
+
+pthread_attr::~pthread_attr ()
+{
+}
+
+bool
+pthread_condattr::is_good_object (pthread_condattr_t const *attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_CONDATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+pthread_condattr::pthread_condattr ():verifyable_object
+ (PTHREAD_CONDATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE)
+{
+}
+
+pthread_condattr::~pthread_condattr ()
+{
+}
+
+List<pthread_cond> pthread_cond::conds;
+
+/* This is used for cond creation protection within a single process only */
+fast_mutex NO_COPY pthread_cond::cond_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_cond::init_mutex ()
+{
+ if (!cond_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread cond static initializer support.");
+}
+
+pthread_cond::pthread_cond (pthread_condattr *attr) :
+ verifyable_object (PTHREAD_COND_MAGIC),
+ shared (0), waiting (0), pending (0), sem_wait (NULL),
+ mtx_cond(NULL), next (NULL)
+{
+ pthread_mutex *verifyable_mutex_obj;
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ verifyable_mutex_obj = &mtx_in;
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /*
+ * Change the mutex type to NORMAL.
+ * This mutex MUST be of type normal
+ */
+ mtx_in.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_mutex_obj = &mtx_out;
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtx_out.type = PTHREAD_MUTEX_NORMAL;
+
+ sem_wait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!sem_wait)
+ {
+ debug_printf ("CreateSemaphore failed. %E");
+ magic = 0;
+ return;
+ }
+
+ conds.insert (this);
+}
+
+pthread_cond::~pthread_cond ()
+{
+ if (sem_wait)
+ CloseHandle (sem_wait);
+
+ conds.remove (this);
+}
+
+void
+pthread_cond::unblock (const bool all)
+{
+ unsigned long releaseable;
+
+ /*
+ * Block outgoing threads (and avoid simultanous unblocks)
+ */
+ mtx_out.lock ();
+
+ releaseable = waiting - pending;
+ if (releaseable)
+ {
+ unsigned long released;
+
+ if (!pending)
+ {
+ /*
+ * Block incoming threads until all waiting threads are released.
+ */
+ mtx_in.lock ();
+
+ /*
+ * Calculate releaseable again because threads can enter until
+ * the semaphore has been taken, but they can not leave, therefore pending
+ * is unchanged and releaseable can only get higher
+ */
+ releaseable = waiting - pending;
+ }
+
+ released = all ? releaseable : 1;
+ pending += released;
+ /*
+ * Signal threads
+ */
+ ::ReleaseSemaphore (sem_wait, released, NULL);
+ }
+
+ /*
+ * And let the threads release.
+ */
+ mtx_out.unlock ();
+}
+
+int
+pthread_cond::wait (pthread_mutex_t mutex, DWORD dwMilliseconds)
+{
+ DWORD rv;
+
+ mtx_in.lock ();
+ if (InterlockedIncrement ((long *)&waiting) == 1)
+ mtx_cond = mutex;
+ else if (mtx_cond != mutex)
+ {
+ InterlockedDecrement ((long *)&waiting);
+ mtx_in.unlock ();
+ return EINVAL;
+ }
+ mtx_in.unlock ();
+
+ /*
+ * Release the mutex and wait on semaphore
+ */
+ ++mutex->condwaits;
+ mutex->unlock ();
+
+ rv = pthread::cancelable_wait (sem_wait, dwMilliseconds, false);
+
+ mtx_out.lock ();
+
+ if (rv != WAIT_OBJECT_0)
+ {
+ /*
+ * It might happen that a signal is sent while the thread got canceled
+ * or timed out. Try to take one.
+ * If the thread gets one than a signal|broadcast is in progress.
+ */
+ if (WaitForSingleObject (sem_wait, 0) == WAIT_OBJECT_0)
+ /*
+ * thread got cancelled ot timed out while a signalling is in progress.
+ * Set wait result back to signaled
+ */
+ rv = WAIT_OBJECT_0;
+ }
+
+ InterlockedDecrement ((long *)&waiting);
+
+ if (rv == WAIT_OBJECT_0 && --pending == 0)
+ /*
+ * All signaled threads are released,
+ * new threads can enter Wait
+ */
+ mtx_in.unlock ();
+
+ mtx_out.unlock ();
+
+ mutex->lock ();
+ --mutex->condwaits;
+
+ if (rv == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ else if (rv == WAIT_TIMEOUT)
+ return ETIMEDOUT;
+
+ return 0;
+}
+
+void
+pthread_cond::_fixup_after_fork ()
+{
+ waiting = pending = 0;
+ mtx_cond = NULL;
+
+ /* Unlock eventually locked mutexes */
+ mtx_in.unlock ();
+ mtx_out.unlock ();
+
+ sem_wait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!sem_wait)
+ api_fatal ("pthread_cond::_fixup_after_fork () failed to recreate win32 semaphore");
+}
+
+bool
+pthread_rwlockattr::is_good_object (pthread_rwlockattr_t const *attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_RWLOCKATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+pthread_rwlockattr::pthread_rwlockattr ():verifyable_object
+ (PTHREAD_RWLOCKATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE)
+{
+}
+
+pthread_rwlockattr::~pthread_rwlockattr ()
+{
+}
+
+List<pthread_rwlock> pthread_rwlock::rwlocks;
+
+/* This is used for rwlock creation protection within a single process only */
+fast_mutex NO_COPY pthread_rwlock::rwlock_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_rwlock::init_mutex ()
+{
+ if (!rwlock_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread rwlock static initializer support.");
+}
+
+pthread_rwlock::pthread_rwlock (pthread_rwlockattr *attr) :
+ verifyable_object (PTHREAD_RWLOCK_MAGIC),
+ shared (0), waiting_readers (0), waiting_writers (0), writer (NULL),
+ readers (NULL), readers_mx (), mtx (NULL), cond_readers (NULL), cond_writers (NULL),
+ next (NULL)
+{
+ pthread_mutex *verifyable_mutex_obj = &mtx;
+ pthread_cond *verifyable_cond_obj;
+
+ if (!readers_mx.init ())
+ {
+ thread_printf ("Internal rwlock synchronisation mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal rwlock mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtx.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_cond_obj = &cond_readers;
+ if (!pthread_cond::is_good_object (&verifyable_cond_obj))
+ {
+ thread_printf ("Internal rwlock readers cond is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+ verifyable_cond_obj = &cond_writers;
+ if (!pthread_cond::is_good_object (&verifyable_cond_obj))
+ {
+ thread_printf ("Internal rwlock writers cond is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+
+ rwlocks.insert (this);
+}
+
+pthread_rwlock::~pthread_rwlock ()
+{
+ rwlocks.remove (this);
+}
+
+int
+pthread_rwlock::rdlock ()
+{
+ int result = 0;
+ struct RWLOCK_READER *reader;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (lookup_reader (self))
+ {
+ result = EDEADLK;
+ goto DONE;
+ }
+
+ reader = new struct RWLOCK_READER;
+ if (!reader)
+ {
+ result = EAGAIN;
+ goto DONE;
+ }
+
+ while (writer || waiting_writers)
+ {
+ pthread_cleanup_push (pthread_rwlock::rdlock_cleanup, this);
+
+ ++waiting_readers;
+ cond_readers.wait (&mtx);
+ --waiting_readers;
+
+ pthread_cleanup_pop (0);
+ }
+
+ reader->thread = self;
+ add_reader (reader);
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::tryrdlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer || waiting_writers || lookup_reader (self))
+ result = EBUSY;
+ else
+ {
+ struct RWLOCK_READER *reader = new struct RWLOCK_READER;
+ if (reader)
+ {
+ reader->thread = self;
+ add_reader (reader);
+ }
+ else
+ result = EAGAIN;
+ }
+
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::wrlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer == self || lookup_reader (self))
+ {
+ result = EDEADLK;
+ goto DONE;
+ }
+
+ while (writer || readers)
+ {
+ pthread_cleanup_push (pthread_rwlock::wrlock_cleanup, this);
+
+ ++waiting_writers;
+ cond_writers.wait (&mtx);
+ --waiting_writers;
+
+ pthread_cleanup_pop (0);
+ }
+
+ writer = self;
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::trywrlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer || readers)
+ result = EBUSY;
+ else
+ writer = self;
+
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::unlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer)
+ {
+ if (writer != self)
+ {
+ result = EPERM;
+ goto DONE;
+ }
+
+ writer = NULL;
+ }
+ else
+ {
+ struct RWLOCK_READER *reader = lookup_reader (self);
+
+ if (!reader)
+ {
+ result = EPERM;
+ goto DONE;
+ }
+
+ remove_reader (reader);
+ delete reader;
+ }
+
+ release ();
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+void
+pthread_rwlock::add_reader (struct RWLOCK_READER *rd)
+{
+ List_insert (readers, rd);
+}
+
+void
+pthread_rwlock::remove_reader (struct RWLOCK_READER *rd)
+{
+ List_remove (readers_mx, readers, rd);
+}
+
+struct pthread_rwlock::RWLOCK_READER *
+pthread_rwlock::lookup_reader (pthread_t thread)
+{
+ readers_mx.lock ();
+
+ struct RWLOCK_READER *cur = readers;
+
+ while (cur && cur->thread != thread)
+ cur = cur->next;
+
+ readers_mx.unlock ();
+
+ return cur;
+}
+
+void
+pthread_rwlock::rdlock_cleanup (void *arg)
+{
+ pthread_rwlock *rwlock = (pthread_rwlock *) arg;
+
+ --(rwlock->waiting_readers);
+ rwlock->release ();
+ rwlock->mtx.unlock ();
+}
+
+void
+pthread_rwlock::wrlock_cleanup (void *arg)
+{
+ pthread_rwlock *rwlock = (pthread_rwlock *) arg;
+
+ --(rwlock->waiting_writers);
+ rwlock->release ();
+ rwlock->mtx.unlock ();
+}
+
+void
+pthread_rwlock::_fixup_after_fork ()
+{
+ pthread_t self = pthread::self ();
+ struct RWLOCK_READER **temp = &readers;
+
+ waiting_readers = 0;
+ waiting_writers = 0;
+
+ if (!readers_mx.init ())
+ api_fatal ("pthread_rwlock::_fixup_after_fork () failed to recreate mutex");
+
+ /* Unlock eventually locked mutex */
+ mtx.unlock ();
+ /*
+ * Remove all readers except self
+ */
+ while (*temp)
+ {
+ if ((*temp)->thread == self)
+ temp = &((*temp)->next);
+ else
+ {
+ struct RWLOCK_READER *cur = *temp;
+ *temp = (*temp)->next;
+ delete cur;
+ }
+ }
+}
+
+/* pthread_key */
+/* static members */
+/* This stores pthread_key information across fork() boundaries */
+List<pthread_key> pthread_key::keys;
+
+bool
+pthread_key::is_good_object (pthread_key_t const *key)
+{
+ if (verifyable_object_isvalid (key, PTHREAD_KEY_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+/* non-static members */
+
+pthread_key::pthread_key (void (*aDestructor) (void *)):verifyable_object (PTHREAD_KEY_MAGIC), destructor (aDestructor)
+{
+ tls_index = TlsAlloc ();
+ if (tls_index == TLS_OUT_OF_INDEXES)
+ magic = 0;
+ else
+ keys.insert (this);
+}
+
+pthread_key::~pthread_key ()
+{
+ /* We may need to make the list code lock the list during operations
+ */
+ if (magic != 0)
+ {
+ keys.remove (this);
+ TlsFree (tls_index);
+ }
+}
+
+int
+pthread_key::set (const void *value)
+{
+ /* the OS function doesn't perform error checking */
+ TlsSetValue (tls_index, (void *) value);
+ return 0;
+}
+
+void *
+pthread_key::get () const
+{
+ int saved_error = ::GetLastError ();
+ void *result = TlsGetValue (tls_index);
+ ::SetLastError (saved_error);
+ return result;
+}
+
+void
+pthread_key::_fixup_before_fork ()
+{
+ fork_buf = get ();
+}
+
+void
+pthread_key::_fixup_after_fork ()
+{
+ tls_index = TlsAlloc ();
+ if (tls_index == TLS_OUT_OF_INDEXES)
+ api_fatal ("pthread_key::recreate_key_from_buffer () failed to reallocate Tls storage");
+ set (fork_buf);
+}
+
+void
+pthread_key::run_destructor ()
+{
+ if (destructor)
+ {
+ void *oldValue = get ();
+ if (oldValue)
+ {
+ set (NULL);
+ destructor (oldValue);
+ }
+ }
+}
+
+/* pshared mutexs:
+
+ REMOVED FROM CURRENT. These can be reinstated with the daemon, when all the
+ gymnastics can be a lot easier.
+
+ the mutex_t (size 4) is not used as a verifyable object because we cannot
+ guarantee the same address space for all processes.
+ we use the following:
+ high bit set (never a valid address).
+ second byte is reserved for the priority.
+ third byte is reserved
+ fourth byte is the mutex id. (max 255 cygwin mutexs system wide).
+ creating mutex's does get slower and slower, but as creation is a one time
+ job, it should never become an issue
+
+ And if you're looking at this and thinking, why not an array in cygwin for all mutexs,
+ - you incur a penalty on _every_ mutex call and you have toserialise them all.
+ ... Bad karma.
+
+ option 2? put everything in userspace and update the ABI?
+ - bad karma as well - the HANDLE, while identical across process's,
+ Isn't duplicated, it's reopened. */
+
+/* static members */
+bool
+pthread_mutex::is_good_object (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_mutex::is_good_initializer (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_mutex::is_good_initializer_or_object (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_mutex::is_good_initializer_or_bad_object (pthread_mutex_t const *mutex)
+{
+ verifyable_object_state objectState = verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER);
+ if (objectState == VALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_mutex::can_be_unlocked (pthread_mutex_t const *mutex)
+{
+ pthread_t self = pthread::self ();
+
+ if (!is_good_object (mutex))
+ return false;
+ /*
+ * Check if the mutex is owned by the current thread and can be unlocked
+ */
+ return ((*mutex)->recursion_counter == 1 && pthread::equal ((*mutex)->owner, self));
+}
+
+List<pthread_mutex> pthread_mutex::mutexes;
+
+/* This is used for mutex creation protection within a single process only */
+fast_mutex NO_COPY pthread_mutex::mutex_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_mutex::init_mutex ()
+{
+ if (!mutex_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread mutex static initializer support.");
+}
+
+pthread_mutex::pthread_mutex (pthread_mutexattr *attr) :
+ verifyable_object (PTHREAD_MUTEX_MAGIC),
+ lock_counter (0),
+ win32_obj_id (NULL), recursion_counter (0),
+ condwaits (0), owner (NULL), type (PTHREAD_MUTEX_DEFAULT),
+ pshared (PTHREAD_PROCESS_PRIVATE)
+{
+ win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!win32_obj_id)
+ {
+ magic = 0;
+ return;
+ }
+ /*attr checked in the C call */
+ if (attr)
+ {
+ if (attr->pshared == PTHREAD_PROCESS_SHARED)
+ {
+ // fail
+ magic = 0;
+ return;
+ }
+
+ type = attr->mutextype;
+ }
+
+ mutexes.insert (this);
+}
+
+pthread_mutex::~pthread_mutex ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+
+ mutexes.remove (this);
+}
+
+int
+pthread_mutex::_lock (pthread_t self)
+{
+ int result = 0;
+
+ if (InterlockedIncrement ((long *)&lock_counter) == 1)
+ set_owner (self);
+ else if (type != PTHREAD_MUTEX_NORMAL && pthread::equal (owner, self))
+ {
+ InterlockedDecrement ((long *) &lock_counter);
+ if (type == PTHREAD_MUTEX_RECURSIVE)
+ result = lock_recursive ();
+ else
+ result = EDEADLK;
+ }
+ else
+ {
+ WaitForSingleObject (win32_obj_id, INFINITE);
+ set_owner (self);
+ }
+
+ return result;
+}
+
+int
+pthread_mutex::_trylock (pthread_t self)
+{
+ int result = 0;
+
+ if (InterlockedCompareExchange ((long *) &lock_counter, 1, 0) == 0)
+ set_owner (self);
+ else if (type == PTHREAD_MUTEX_RECURSIVE && pthread::equal (owner, self))
+ result = lock_recursive ();
+ else
+ result = EBUSY;
+
+ return result;
+}
+
+int
+pthread_mutex::_unlock (pthread_t self)
+{
+ if (!pthread::equal (owner, self))
+ return EPERM;
+
+ if (--recursion_counter == 0)
+ {
+ owner = NULL;
+ if (InterlockedDecrement ((long *)&lock_counter))
+ // Another thread is waiting
+ ::ReleaseSemaphore (win32_obj_id, 1, NULL);
+ }
+
+ return 0;
+}
+
+int
+pthread_mutex::_destroy (pthread_t self)
+{
+ if (condwaits || _trylock (self))
+ // Do not destroy a condwaited or locked mutex
+ return EBUSY;
+ else if (recursion_counter != 1)
+ {
+ // Do not destroy a recursive locked mutex
+ --recursion_counter;
+ return EBUSY;
+ }
+
+ delete this;
+ return 0;
+}
+
+void
+pthread_mutex::_fixup_after_fork ()
+{
+ debug_printf ("mutex %x in _fixup_after_fork", this);
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ api_fatal ("pthread_mutex::_fixup_after_fork () doesn'tunderstand PROCESS_SHARED mutex's");
+
+ if (owner == NULL)
+ /* mutex has no owner, reset to initial */
+ lock_counter = 0;
+ else if (lock_counter != 0)
+ /* All waiting threads are gone after a fork */
+ lock_counter = 1;
+
+ win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!win32_obj_id)
+ api_fatal ("pthread_mutex::_fixup_after_fork () failed to recreate win32 semaphore for mutex");
+
+ condwaits = 0;
+}
+
+bool
+pthread_mutexattr::is_good_object (pthread_mutexattr_t const * attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_MUTEXATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+pthread_mutexattr::pthread_mutexattr ():verifyable_object (PTHREAD_MUTEXATTR_MAGIC),
+pshared (PTHREAD_PROCESS_PRIVATE), mutextype (PTHREAD_MUTEX_DEFAULT)
+{
+}
+
+pthread_mutexattr::~pthread_mutexattr ()
+{
+}
+
+List<semaphore> semaphore::semaphores;
+
+semaphore::semaphore (int pshared, unsigned int value)
+: verifyable_object (SEM_MAGIC),
+ shared (pshared),
+ currentvalue (value),
+ name (NULL)
+{
+ SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE)
+ ? sec_all : sec_none_nih;
+ this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, NULL);
+ if (!this->win32_obj_id)
+ magic = 0;
+
+ semaphores.insert (this);
+}
+
+semaphore::semaphore (const char *sem_name, int oflag, mode_t mode,
+ unsigned int value)
+: verifyable_object (SEM_MAGIC),
+ shared (PTHREAD_PROCESS_SHARED),
+ currentvalue (value), /* Unused for named semaphores. */
+ name (NULL)
+{
+ if (oflag & O_CREAT)
+ {
+ SECURITY_ATTRIBUTES sa = sec_all;
+ security_descriptor sd;
+ if (allow_ntsec)
+ set_security_attribute (mode, &sa, sd);
+ this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, sem_name);
+ if (!this->win32_obj_id)
+ magic = 0;
+ if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL))
+ {
+ __seterrno ();
+ CloseHandle (this->win32_obj_id);
+ magic = 0;
+ }
+ }
+ else
+ {
+ this->win32_obj_id = ::OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
+ sem_name);
+ if (!this->win32_obj_id)
+ {
+ __seterrno ();
+ magic = 0;
+ }
+ }
+ if (magic)
+ {
+ name = new char [strlen (sem_name + 1)];
+ if (!name)
+ {
+ set_errno (ENOSPC);
+ CloseHandle (this->win32_obj_id);
+ magic = 0;
+ }
+ else
+ strcpy (name, sem_name);
+ }
+
+ semaphores.insert (this);
+}
+
+semaphore::~semaphore ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+
+ delete [] name;
+
+ semaphores.remove (this);
+}
+
+void
+semaphore::_post ()
+{
+ if (ReleaseSemaphore (win32_obj_id, 1, &currentvalue))
+ currentvalue++;
+}
+
+int
+semaphore::_getvalue (int *sval)
+{
+ long val;
+
+ switch (WaitForSingleObject (win32_obj_id, 0))
+ {
+ case WAIT_OBJECT_0:
+ ReleaseSemaphore (win32_obj_id, 1, &val);
+ *sval = val + 1;
+ break;
+ case WAIT_TIMEOUT:
+ *sval = 0;
+ break;
+ default:
+ set_errno (EAGAIN);
+ return -1;
+ }
+ return 0;
+}
+
+int
+semaphore::_trywait ()
+{
+ /* FIXME: signals should be able to interrupt semaphores...
+ *We probably need WaitForMultipleObjects here.
+ */
+ if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ set_errno (EAGAIN);
+ return -1;
+ }
+ currentvalue--;
+ return 0;
+}
+
+int
+semaphore::_timedwait (const struct timespec *abstime)
+{
+ struct timeval tv;
+ long waitlength;
+
+ if (__check_invalid_read_ptr (abstime, sizeof *abstime))
+ {
+ /* According to SUSv3, abstime need not be checked for validity,
+ if the semaphore can be locked immediately. */
+ if (!_trywait ())
+ return 0;
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ gettimeofday (&tv, NULL);
+ waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000);
+ waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ if (waitlength < 0)
+ waitlength = 0;
+ switch (pthread::cancelable_wait (win32_obj_id, waitlength))
+ {
+ case WAIT_OBJECT_0:
+ currentvalue--;
+ break;
+ case WAIT_TIMEOUT:
+ set_errno (ETIMEDOUT);
+ return -1;
+ default:
+ debug_printf ("cancelable_wait failed. %E");
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+void
+semaphore::_wait ()
+{
+ switch (pthread::cancelable_wait (win32_obj_id, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ currentvalue--;
+ break;
+ default:
+ debug_printf ("cancelable_wait failed. %E");
+ return;
+ }
+}
+
+void
+semaphore::_fixup_after_fork ()
+{
+ if (shared == PTHREAD_PROCESS_PRIVATE)
+ {
+ debug_printf ("sem %x in _fixup_after_fork", this);
+ /* FIXME: duplicate code here and in the constructor. */
+ this->win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue,
+ LONG_MAX, NULL);
+ if (!win32_obj_id)
+ api_fatal ("failed to create new win32 semaphore, error %d");
+ }
+}
+
+verifyable_object::verifyable_object (long verifyer):
+magic (verifyer)
+{
+}
+
+verifyable_object::~verifyable_object ()
+{
+ magic = 0;
+}
+
+/* Generic memory acccess routine - where should it live ? */
+int __stdcall
+check_valid_pointer (void const *pointer)
+{
+ if (!pointer || IsBadWritePtr ((void *) pointer, sizeof (verifyable_object)))
+ return EFAULT;
+ return 0;
+}
+
+verifyable_object_state
+verifyable_object_isvalid (void const * objectptr, long magic, void *static_ptr)
+{
+ verifyable_object **object = (verifyable_object **)objectptr;
+ if (check_valid_pointer (object))
+ return INVALID_OBJECT;
+ if (static_ptr && *object == static_ptr)
+ return VALID_STATIC_OBJECT;
+ if (!*object)
+ return INVALID_OBJECT;
+ if (check_valid_pointer (*object))
+ return INVALID_OBJECT;
+ if ((*object)->magic != magic)
+ return INVALID_OBJECT;
+ return VALID_OBJECT;
+}
+
+verifyable_object_state
+verifyable_object_isvalid (void const * objectptr, long magic)
+{
+ return verifyable_object_isvalid (objectptr, magic, NULL);
+}
+
+DWORD WINAPI
+pthread::thread_init_wrapper (void *arg)
+{
+ pthread *thread = (pthread *) arg;
+ _my_tls.tid = thread;
+
+ thread->mutex.lock ();
+
+ // if thread is detached force cleanup on exit
+ if (thread->attr.joinable == PTHREAD_CREATE_DETACHED && thread->joiner == NULL)
+ thread->joiner = thread;
+ thread->mutex.unlock ();
+
+ thread_printf ("started thread %p %p %p %p %p %p", arg, &_my_tls.local_clib,
+ _impure_ptr, thread, thread->function, thread->arg);
+
+ // call the user's thread
+ void *ret = thread->function (thread->arg);
+
+ thread->exit (ret);
+
+ return 0; // just for show. Never returns.
+}
+
+bool
+pthread::is_good_object (pthread_t const *thread)
+{
+ if (verifyable_object_isvalid (thread, PTHREAD_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+unsigned long
+pthread::getsequence_np ()
+{
+ return get_thread_id ();
+}
+
+int
+pthread::create (pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ if (attr && !pthread_attr::is_good_object (attr))
+ return EINVAL;
+
+ *thread = new pthread ();
+ (*thread)->create (start_routine, attr ? *attr : NULL, arg);
+ if (!is_good_object (thread))
+ {
+ delete (*thread);
+ *thread = NULL;
+ return EAGAIN;
+ }
+
+ return 0;
+}
+
+int
+pthread::once (pthread_once_t *once_control, void (*init_routine) (void))
+{
+ // already done ?
+ if (once_control->state)
+ return 0;
+
+ pthread_mutex_lock (&once_control->mutex);
+ /* Here we must set a cancellation handler to unlock the mutex if needed */
+ /* but a cancellation handler is not the right thing. We need this in the thread
+ *cleanup routine. Assumption: a thread can only be in one pthread_once routine
+ *at a time. Stote a mutex_t *in the pthread_structure. if that's non null unlock
+ *on pthread_exit ();
+ */
+ if (!once_control->state)
+ {
+ init_routine ();
+ once_control->state = 1;
+ }
+ /* Here we must remove our cancellation handler */
+ pthread_mutex_unlock (&once_control->mutex);
+ return 0;
+}
+
+int
+pthread::cancel (pthread_t thread)
+{
+ if (!is_good_object (&thread))
+ return ESRCH;
+
+ return thread->cancel ();
+}
+
+void
+pthread::atforkprepare (void)
+{
+ MT_INTERFACE->fixup_before_fork ();
+
+ callback *cb = MT_INTERFACE->pthread_prepare;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+void
+pthread::atforkparent (void)
+{
+ callback *cb = MT_INTERFACE->pthread_parent;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+void
+pthread::atforkchild (void)
+{
+ MT_INTERFACE->fixup_after_fork ();
+
+ callback *cb = MT_INTERFACE->pthread_child;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+/* Register a set of functions to run before and after fork.
+ prepare calls are called in LI-FC order.
+ parent and child calls are called in FI-FC order. */
+int
+pthread::atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void))
+{
+ callback *prepcb = NULL, *parentcb = NULL, *childcb = NULL;
+ if (prepare)
+ {
+ prepcb = new callback;
+ if (!prepcb)
+ return ENOMEM;
+ }
+ if (parent)
+ {
+ parentcb = new callback;
+ if (!parentcb)
+ {
+ if (prepcb)
+ delete prepcb;
+ return ENOMEM;
+ }
+ }
+ if (child)
+ {
+ childcb = new callback;
+ if (!childcb)
+ {
+ if (prepcb)
+ delete prepcb;
+ if (parentcb)
+ delete parentcb;
+ return ENOMEM;
+ }
+ }
+
+ if (prepcb)
+ {
+ prepcb->cb = prepare;
+ List_insert (MT_INTERFACE->pthread_prepare, prepcb);
+ }
+ if (parentcb)
+ {
+ parentcb->cb = parent;
+ callback **t = &MT_INTERFACE->pthread_parent;
+ while (*t)
+ t = &(*t)->next;
+ /* t = pointer to last next in the list */
+ List_insert (*t, parentcb);
+ }
+ if (childcb)
+ {
+ childcb->cb = child;
+ callback **t = &MT_INTERFACE->pthread_child;
+ while (*t)
+ t = &(*t)->next;
+ /* t = pointer to last next in the list */
+ List_insert (*t, childcb);
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_attr_init (pthread_attr_t *attr)
+{
+ if (pthread_attr::is_good_object (attr))
+ return EBUSY;
+
+ *attr = new pthread_attr;
+ if (!pthread_attr::is_good_object (attr))
+ {
+ delete (*attr);
+ *attr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getinheritsched (const pthread_attr_t *attr,
+ int *inheritsched)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *inheritsched = (*attr)->inheritsched;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getschedparam (const pthread_attr_t *attr,
+ struct sched_param *param)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *param = (*attr)->schedparam;
+ return 0;
+}
+
+/* From a pure code point of view, this should call a helper in sched.cc,
+ to allow for someone adding scheduler policy changes to win32 in the future.
+ However that's extremely unlikely, so short and sweet will do us */
+extern "C" int
+pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *policy = SCHED_FIFO;
+ return 0;
+}
+
+
+extern "C" int
+pthread_attr_getscope (const pthread_attr_t *attr, int *contentionscope)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *contentionscope = (*attr)->contentionscope;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (detachstate < 0 || detachstate > 1)
+ return EINVAL;
+ (*attr)->joinable = detachstate;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *detachstate = (*attr)->joinable;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setinheritsched (pthread_attr_t *attr, int inheritsched)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (inheritsched != PTHREAD_INHERIT_SCHED
+ && inheritsched != PTHREAD_EXPLICIT_SCHED)
+ return ENOTSUP;
+ (*attr)->inheritsched = inheritsched;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setschedparam (pthread_attr_t *attr,
+ const struct sched_param *param)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (!valid_sched_parameters (param))
+ return ENOTSUP;
+ (*attr)->schedparam = *param;
+ return 0;
+}
+
+/* See __pthread_attr_getschedpolicy for some notes */
+extern "C" int
+pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (policy != SCHED_FIFO)
+ return ENOTSUP;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setscope (pthread_attr_t *attr, int contentionscope)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (contentionscope != PTHREAD_SCOPE_SYSTEM
+ && contentionscope != PTHREAD_SCOPE_PROCESS)
+ return EINVAL;
+ /* In future, we may be able to support system scope by escalating the thread
+ priority to exceed the priority class. For now we only support PROCESS scope. */
+ if (contentionscope != PTHREAD_SCOPE_PROCESS)
+ return ENOTSUP;
+ (*attr)->contentionscope = contentionscope;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setstacksize (pthread_attr_t *attr, size_t size)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ (*attr)->stacksize = size;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *size = (*attr)->stacksize;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_destroy (pthread_attr_t *attr)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ delete (*attr);
+ *attr = NULL;
+ return 0;
+}
+
+int
+pthread::join (pthread_t *thread, void **return_val)
+{
+ pthread_t joiner = self ();
+
+ joiner->testcancel ();
+
+ // Initialize return val with NULL
+ if (return_val)
+ *return_val = NULL;
+
+ if (!is_good_object (&joiner))
+ return EINVAL;
+
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ if (equal (*thread,joiner))
+ return EDEADLK;
+
+ (*thread)->mutex.lock ();
+
+ if ((*thread)->attr.joinable == PTHREAD_CREATE_DETACHED)
+ {
+ (*thread)->mutex.unlock ();
+ return EINVAL;
+ }
+ else
+ {
+ (*thread)->joiner = joiner;
+ (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED;
+ (*thread)->mutex.unlock ();
+
+ switch (cancelable_wait ((*thread)->win32_obj_id, INFINITE, false))
+ {
+ case WAIT_OBJECT_0:
+ if (return_val)
+ *return_val = (*thread)->return_ptr;
+ delete (*thread);
+ break;
+ case WAIT_CANCELED:
+ // set joined thread back to joinable since we got canceled
+ (*thread)->joiner = NULL;
+ (*thread)->attr.joinable = PTHREAD_CREATE_JOINABLE;
+ joiner->cancel_self ();
+ // never reached
+ break;
+ default:
+ // should never happen
+ return EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int
+pthread::detach (pthread_t *thread)
+{
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ (*thread)->mutex.lock ();
+ if ((*thread)->attr.joinable == PTHREAD_CREATE_DETACHED)
+ {
+ (*thread)->mutex.unlock ();
+ return EINVAL;
+ }
+
+ // check if thread is still alive
+ if ((*thread)->valid && WaitForSingleObject ((*thread)->win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ // force cleanup on exit
+ (*thread)->joiner = *thread;
+ (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED;
+ (*thread)->mutex.unlock ();
+ }
+ else
+ {
+ // thread has already terminated.
+ (*thread)->mutex.unlock ();
+ delete (*thread);
+ }
+
+ return 0;
+}
+
+int
+pthread::suspend (pthread_t *thread)
+{
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ if ((*thread)->suspended == false)
+ {
+ (*thread)->suspended = true;
+ SuspendThread ((*thread)->win32_obj_id);
+ }
+
+ return 0;
+}
+
+
+int
+pthread::resume (pthread_t *thread)
+{
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ if ((*thread)->suspended == true)
+ ResumeThread ((*thread)->win32_obj_id);
+ (*thread)->suspended = false;
+
+ return 0;
+}
+
+/* provided for source level compatability.
+ See http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html
+*/
+extern "C" int
+pthread_getconcurrency (void)
+{
+ return MT_INTERFACE->concurrency;
+}
+
+/* keep this in sync with sched.cc */
+extern "C" int
+pthread_getschedparam (pthread_t thread, int *policy,
+ struct sched_param *param)
+{
+ if (!pthread::is_good_object (&thread))
+ return ESRCH;
+ *policy = SCHED_FIFO;
+ /* we don't return the current effective priority, we return the current
+ requested priority */
+ *param = thread->attr.schedparam;
+ return 0;
+}
+
+/* Thread Specific Data */
+extern "C" int
+pthread_key_create (pthread_key_t *key, void (*destructor) (void *))
+{
+ /* The opengroup docs don't define if we should check this or not,
+ but creation is relatively rare. */
+ if (pthread_key::is_good_object (key))
+ return EBUSY;
+
+ *key = new pthread_key (destructor);
+
+ if (!pthread_key::is_good_object (key))
+ {
+ delete (*key);
+ *key = NULL;
+ return EAGAIN;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_key_delete (pthread_key_t key)
+{
+ if (!pthread_key::is_good_object (&key))
+ return EINVAL;
+
+ delete (key);
+ return 0;
+}
+
+/* provided for source level compatability. See
+http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html
+*/
+extern "C" int
+pthread_setconcurrency (int new_level)
+{
+ if (new_level < 0)
+ return EINVAL;
+ MT_INTERFACE->concurrency = new_level;
+ return 0;
+}
+
+/* keep syncronised with sched.cc */
+extern "C" int
+pthread_setschedparam (pthread_t thread, int policy,
+ const struct sched_param *param)
+{
+ if (!pthread::is_good_object (&thread))
+ return ESRCH;
+ if (policy != SCHED_FIFO)
+ return ENOTSUP;
+ if (!param)
+ return EINVAL;
+ int rv =
+ sched_set_thread_priority (thread->win32_obj_id, param->sched_priority);
+ if (!rv)
+ thread->attr.schedparam.sched_priority = param->sched_priority;
+ return rv;
+}
+
+
+extern "C" int
+pthread_setspecific (pthread_key_t key, const void *value)
+{
+ if (!pthread_key::is_good_object (&key))
+ return EINVAL;
+ (key)->set (value);
+ return 0;
+}
+
+extern "C" void *
+pthread_getspecific (pthread_key_t key)
+{
+ if (!pthread_key::is_good_object (&key))
+ return NULL;
+
+ return (key)->get ();
+
+}
+
+/* Thread synchronisation */
+bool
+pthread_cond::is_good_object (pthread_cond_t const *cond)
+{
+ if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_cond::is_good_initializer (pthread_cond_t const *cond)
+{
+ if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_cond::is_good_initializer_or_object (pthread_cond_t const *cond)
+{
+ if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_cond::is_good_initializer_or_bad_object (pthread_cond_t const *cond)
+{
+ verifyable_object_state objectState = verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER);
+ if (objectState == VALID_OBJECT)
+ return false;
+ return true;
+}
+
+extern "C" int
+pthread_cond_destroy (pthread_cond_t *cond)
+{
+ if (pthread_cond::is_good_initializer (cond))
+ return 0;
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ /* reads are atomic */
+ if ((*cond)->waiting)
+ return EBUSY;
+
+ delete (*cond);
+ *cond = NULL;
+
+ return 0;
+}
+
+int
+pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr)
+{
+ if (attr && !pthread_condattr::is_good_object (attr))
+ return EINVAL;
+
+ cond_initialization_lock.lock ();
+
+ if (!is_good_initializer_or_bad_object (cond))
+ {
+ cond_initialization_lock.unlock ();
+ return EBUSY;
+ }
+
+ *cond = new pthread_cond (attr ? (*attr) : NULL);
+ if (!is_good_object (cond))
+ {
+ delete (*cond);
+ *cond = NULL;
+ cond_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+ cond_initialization_lock.unlock ();
+ return 0;
+}
+
+extern "C" int
+pthread_cond_broadcast (pthread_cond_t *cond)
+{
+ if (pthread_cond::is_good_initializer (cond))
+ return 0;
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ (*cond)->unblock (true);
+
+ return 0;
+}
+
+extern "C" int
+pthread_cond_signal (pthread_cond_t *cond)
+{
+ if (pthread_cond::is_good_initializer (cond))
+ return 0;
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ (*cond)->unblock (false);
+
+ return 0;
+}
+
+static int
+__pthread_cond_dowait (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ DWORD waitlength)
+{
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+ if (!pthread_mutex::can_be_unlocked (mutex))
+ return EPERM;
+
+ if (pthread_cond::is_good_initializer (cond))
+ pthread_cond::init (cond, NULL);
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ return (*cond)->wait (*mutex, waitlength);
+}
+
+extern "C" int
+pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ const struct timespec *abstime)
+{
+ struct timeval tv;
+ long waitlength;
+
+ pthread_testcancel ();
+
+ if (check_valid_pointer (abstime))
+ return EINVAL;
+
+ gettimeofday (&tv, NULL);
+ waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000);
+ waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ if (waitlength < 0)
+ return ETIMEDOUT;
+ return __pthread_cond_dowait (cond, mutex, waitlength);
+}
+
+extern "C" int
+pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ pthread_testcancel ();
+
+ return __pthread_cond_dowait (cond, mutex, INFINITE);
+}
+
+extern "C" int
+pthread_condattr_init (pthread_condattr_t *condattr)
+{
+ if (pthread_condattr::is_good_object (condattr))
+ return EBUSY;
+
+ *condattr = new pthread_condattr;
+ if (!pthread_condattr::is_good_object (condattr))
+ {
+ delete (*condattr);
+ *condattr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared)
+{
+ if (!pthread_condattr::is_good_object (attr))
+ return EINVAL;
+ *pshared = (*attr)->shared;
+ return 0;
+}
+
+extern "C" int
+pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared)
+{
+ if (!pthread_condattr::is_good_object (attr))
+ return EINVAL;
+ if ((pshared < 0) || (pshared > 1))
+ return EINVAL;
+ /* shared cond vars not currently supported */
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ (*attr)->shared = pshared;
+ return 0;
+}
+
+extern "C" int
+pthread_condattr_destroy (pthread_condattr_t *condattr)
+{
+ if (!pthread_condattr::is_good_object (condattr))
+ return EINVAL;
+ delete (*condattr);
+ *condattr = NULL;
+ return 0;
+}
+
+/* RW locks */
+bool
+pthread_rwlock::is_good_object (pthread_rwlock_t const *rwlock)
+{
+ if (verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_rwlock::is_good_initializer (pthread_rwlock_t const *rwlock)
+{
+ if (verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC, PTHREAD_RWLOCK_INITIALIZER) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_rwlock::is_good_initializer_or_object (pthread_rwlock_t const *rwlock)
+{
+ if (verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC, PTHREAD_RWLOCK_INITIALIZER) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_rwlock::is_good_initializer_or_bad_object (pthread_rwlock_t const *rwlock)
+{
+ verifyable_object_state objectState = verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC, PTHREAD_RWLOCK_INITIALIZER);
+ if (objectState == VALID_OBJECT)
+ return false;
+ return true;
+}
+
+extern "C" int
+pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ return 0;
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ if ((*rwlock)->writer || (*rwlock)->readers ||
+ (*rwlock)->waiting_readers || (*rwlock)->waiting_writers)
+ return EBUSY;
+
+ delete (*rwlock);
+ *rwlock = NULL;
+
+ return 0;
+}
+
+int
+pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
+{
+ if (attr && !pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+
+ rwlock_initialization_lock.lock ();
+
+ if (!is_good_initializer_or_bad_object (rwlock))
+ {
+ rwlock_initialization_lock.unlock ();
+ return EBUSY;
+ }
+
+ *rwlock = new pthread_rwlock (attr ? (*attr) : NULL);
+ if (!is_good_object (rwlock))
+ {
+ delete (*rwlock);
+ *rwlock = NULL;
+ rwlock_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+ rwlock_initialization_lock.unlock ();
+ return 0;
+}
+
+extern "C" int
+pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
+{
+ pthread_testcancel ();
+
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->rdlock ();
+}
+
+extern "C" int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->tryrdlock ();
+}
+
+extern "C" int
+pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
+{
+ pthread_testcancel ();
+
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->wrlock ();
+}
+
+extern "C" int
+pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->trywrlock ();
+}
+
+extern "C" int
+pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ return 0;
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->unlock ();
+}
+
+extern "C" int
+pthread_rwlockattr_init (pthread_rwlockattr_t *rwlockattr)
+{
+ if (pthread_rwlockattr::is_good_object (rwlockattr))
+ return EBUSY;
+
+ *rwlockattr = new pthread_rwlockattr;
+ if (!pthread_rwlockattr::is_good_object (rwlockattr))
+ {
+ delete (*rwlockattr);
+ *rwlockattr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared)
+{
+ if (!pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+ *pshared = (*attr)->shared;
+ return 0;
+}
+
+extern "C" int
+pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared)
+{
+ if (!pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+ if ((pshared < 0) || (pshared > 1))
+ return EINVAL;
+ /* shared rwlock vars not currently supported */
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ (*attr)->shared = pshared;
+ return 0;
+}
+
+extern "C" int
+pthread_rwlockattr_destroy (pthread_rwlockattr_t *rwlockattr)
+{
+ if (!pthread_rwlockattr::is_good_object (rwlockattr))
+ return EINVAL;
+ delete (*rwlockattr);
+ *rwlockattr = NULL;
+ return 0;
+}
+
+/* Thread signal */
+extern "C" int
+pthread_kill (pthread_t thread, int sig)
+{
+ // lock myself, for the use of thread2signal
+ // two different kills might clash: FIXME
+
+ if (!pthread::is_good_object (&thread))
+ return EINVAL;
+
+ int rval = sig ? sig_send (NULL, sig, thread->cygtls) : 0;
+
+ // unlock myself
+ return rval;
+}
+
+extern "C" int
+pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
+{
+ return handle_sigprocmask (operation, set, old_set, _my_tls.sigmask);
+}
+
+/* ID */
+
+extern "C" int
+pthread_equal (pthread_t t1, pthread_t t2)
+{
+ return pthread::equal (t1, t2);
+}
+
+/* Mutexes */
+
+/* FIXME: there's a potential race with PTHREAD_MUTEX_INITALIZER:
+ the mutex is not actually inited until the first use.
+ So two threads trying to lock/trylock may collide.
+ Solution: we need a global mutex on mutex creation, or possibly simply
+ on all constructors that allow INITIALIZER macros.
+ the lock should be very small: only around the init routine, not
+ every test, or all mutex access will be synchronised. */
+
+int
+pthread_mutex::init (pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *attr)
+{
+ if (attr && !pthread_mutexattr::is_good_object (attr) || check_valid_pointer (mutex))
+ return EINVAL;
+
+ mutex_initialization_lock.lock ();
+
+ if (!is_good_initializer_or_bad_object (mutex))
+ {
+ mutex_initialization_lock.unlock ();
+ return EBUSY;
+ }
+
+ *mutex = new pthread_mutex (attr ? (*attr) : NULL);
+ if (!is_good_object (mutex))
+ {
+ delete (*mutex);
+ *mutex = NULL;
+ mutex_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+ mutex_initialization_lock.unlock ();
+ return 0;
+}
+
+extern "C" int
+pthread_mutex_getprioceiling (const pthread_mutex_t *mutex,
+ int *prioceiling)
+{
+ pthread_mutex_t *themutex = (pthread_mutex_t *) mutex;
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init ((pthread_mutex_t *) mutex, NULL);
+ if (!pthread_mutex::is_good_object (themutex))
+ return EINVAL;
+ /* We don't define _POSIX_THREAD_PRIO_PROTECT because we do't currently support
+ mutex priorities.
+
+ We can support mutex priorities in the future though:
+ Store a priority with each mutex.
+ When the mutex is optained, set the thread priority as appropriate
+ When the mutex is released, reset the thread priority. */
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutex_lock (pthread_mutex_t *mutex)
+{
+ pthread_mutex_t *themutex = mutex;
+ /* This could be simplified via is_good_initializer_or_object
+ and is_good_initializer, but in a performance critical call like this....
+ no. */
+ switch (verifyable_object_isvalid (themutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER))
+ {
+ case INVALID_OBJECT:
+ return EINVAL;
+ break;
+ case VALID_STATIC_OBJECT:
+ if (pthread_mutex::is_good_initializer (mutex))
+ {
+ int rv = pthread_mutex::init (mutex, NULL);
+ if (rv && rv != EBUSY)
+ return rv;
+ }
+ /* No else needed. If it's been initialized while we waited,
+ we can just attempt to lock it */
+ break;
+ case VALID_OBJECT:
+ break;
+ }
+ return (*themutex)->lock ();
+}
+
+extern "C" int
+pthread_mutex_trylock (pthread_mutex_t *mutex)
+{
+ pthread_mutex_t *themutex = mutex;
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL);
+ if (!pthread_mutex::is_good_object (themutex))
+ return EINVAL;
+ return (*themutex)->trylock ();
+}
+
+extern "C" int
+pthread_mutex_unlock (pthread_mutex_t *mutex)
+{
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL);
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+ return (*mutex)->unlock ();
+}
+
+extern "C" int
+pthread_mutex_destroy (pthread_mutex_t *mutex)
+{
+ int rv;
+
+ if (pthread_mutex::is_good_initializer (mutex))
+ return 0;
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+
+ rv = (*mutex)->destroy ();
+ if (rv)
+ return rv;
+
+ *mutex = NULL;
+ return 0;
+}
+
+extern "C" int
+pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling,
+ int *old_ceiling)
+{
+ pthread_mutex_t *themutex = mutex;
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL);
+ if (!pthread_mutex::is_good_object (themutex))
+ return EINVAL;
+ return ENOSYS;
+}
+
+/* Win32 doesn't support mutex priorities - see __pthread_mutex_getprioceiling
+ for more detail */
+extern "C" int
+pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr,
+ int *protocol)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr,
+ int *pshared)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ *pshared = (*attr)->pshared;
+ return 0;
+}
+
+extern "C" int
+pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ *type = (*attr)->mutextype;
+ return 0;
+}
+
+/* FIXME: write and test process shared mutex's. */
+extern "C" int
+pthread_mutexattr_init (pthread_mutexattr_t *attr)
+{
+ if (pthread_mutexattr::is_good_object (attr))
+ return EBUSY;
+
+ *attr = new pthread_mutexattr ();
+ if (!pthread_mutexattr::is_good_object (attr))
+ {
+ delete (*attr);
+ *attr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_mutexattr_destroy (pthread_mutexattr_t *attr)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ delete (*attr);
+ *attr = NULL;
+ return 0;
+}
+
+
+/* Win32 doesn't support mutex priorities */
+extern "C" int
+pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+/* Win32 doesn't support mutex priorities */
+extern "C" int
+pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr,
+ int prioceiling)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr,
+ int *prioceiling)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ /* we don't use pshared for anything as yet. We need to test PROCESS_SHARED
+ *functionality
+ */
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ (*attr)->pshared = pshared;
+ return 0;
+}
+
+/* see pthread_mutex_gettype */
+extern "C" int
+pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+
+ switch (type)
+ {
+ case PTHREAD_MUTEX_ERRORCHECK:
+ case PTHREAD_MUTEX_RECURSIVE:
+ case PTHREAD_MUTEX_NORMAL:
+ (*attr)->mutextype = type;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* Semaphores */
+
+/* static members */
+bool
+semaphore::is_good_object (sem_t const * sem)
+{
+ if (verifyable_object_isvalid (sem, SEM_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+int
+semaphore::init (sem_t *sem, int pshared, unsigned int value)
+{
+ /* opengroup calls this undefined */
+ if (is_good_object (sem))
+ return EBUSY;
+
+ if (value > SEM_VALUE_MAX)
+ return EINVAL;
+
+ *sem = new semaphore (pshared, value);
+
+ if (!is_good_object (sem))
+ {
+ delete (*sem);
+ *sem = NULL;
+ return EAGAIN;
+ }
+ return 0;
+}
+
+int
+semaphore::destroy (sem_t *sem)
+{
+ if (!is_good_object (sem))
+ return EINVAL;
+
+ /* FIXME - new feature - test for busy against threads... */
+
+ delete (*sem);
+ *sem = NULL;
+ return 0;
+}
+
+sem_t *
+semaphore::open (const char *name, int oflag, mode_t mode, unsigned int value)
+{
+ if (value > SEM_VALUE_MAX)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+
+ sem_t *sem = new sem_t;
+ if (!sem)
+ {
+ set_errno (ENOMEM);
+ return NULL;
+ }
+
+ *sem = new semaphore (name, oflag, mode, value);
+
+ if (!is_good_object (sem))
+ {
+ delete *sem;
+ delete sem;
+ return NULL;
+ }
+ return sem;
+}
+
+int
+semaphore::wait (sem_t *sem)
+{
+ pthread_testcancel ();
+
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ (*sem)->_wait ();
+ return 0;
+}
+
+int
+semaphore::trywait (sem_t *sem)
+{
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_trywait ();
+}
+
+int
+semaphore::timedwait (sem_t *sem, const struct timespec *abstime)
+{
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_timedwait (abstime);
+}
+
+int
+semaphore::post (sem_t *sem)
+{
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ (*sem)->_post ();
+ return 0;
+}
+
+int
+semaphore::getvalue (sem_t *sem, int *sval)
+{
+
+ if (!is_good_object (sem)
+ || __check_null_invalid_struct (sval, sizeof (int)))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_getvalue (sval);
+}
+
+/* pthread_null */
+pthread *
+pthread_null::get_null_pthread ()
+{
+ /* because of weird entry points */
+ _instance.magic = 0;
+ return &_instance;
+}
+
+pthread_null::pthread_null ()
+{
+ attr.joinable = PTHREAD_CREATE_DETACHED;
+ /* Mark ourselves as invalid */
+ magic = 0;
+}
+
+pthread_null::~pthread_null ()
+{
+}
+
+void
+pthread_null::create (void *(*)(void *), pthread_attr *, void *)
+{
+}
+
+void
+pthread_null::exit (void *value_ptr)
+{
+ ExitThread (0);
+}
+
+int
+pthread_null::cancel ()
+{
+ return 0;
+}
+
+void
+pthread_null::testcancel ()
+{
+}
+
+int
+pthread_null::setcancelstate (int state, int *oldstate)
+{
+ return EINVAL;
+}
+
+int
+pthread_null::setcanceltype (int type, int *oldtype)
+{
+ return EINVAL;
+}
+
+void
+pthread_null::push_cleanup_handler (__pthread_cleanup_handler *handler)
+{
+}
+
+void
+pthread_null::pop_cleanup_handler (int const execute)
+{
+}
+
+unsigned long
+pthread_null::getsequence_np ()
+{
+ return 0;
+}
+
+pthread_null pthread_null::_instance;
diff --git a/winsup/cygwin/thread.h b/winsup/cygwin/thread.h
new file mode 100644
index 00000000000..0e9779a628e
--- /dev/null
+++ b/winsup/cygwin/thread.h
@@ -0,0 +1,691 @@
+/* thread.h: Locking and threading module definitions
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+ Written by Marco Fuykschot <marco@ddi.nl>
+ Major update 2001 Robert Collins <rbtcollins@hotmail.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. */
+
+#ifndef _CYGNUS_THREADS_
+#define _CYGNUS_THREADS_
+
+#define LOCK_MMAP_LIST 1
+
+#define WRITE_LOCK 1
+#define READ_LOCK 2
+
+#include <pthread.h>
+#include <limits.h>
+#include <security.h>
+#include <errno.h>
+
+extern "C"
+{
+void SetResourceLock (int, int, const char *) __attribute__ ((regparm (3)));
+void ReleaseResourceLock (int, int, const char *)
+ __attribute__ ((regparm (3)));
+}
+
+class fast_mutex
+{
+public:
+ fast_mutex () :
+ lock_counter (0), win32_obj_id (0)
+ {
+ }
+
+ ~fast_mutex ()
+ {
+ if(win32_obj_id)
+ CloseHandle (win32_obj_id);
+ }
+
+ bool init ()
+ {
+ lock_counter = 0;
+ win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!win32_obj_id)
+ {
+ debug_printf ("CreateSemaphore failed. %E");
+ return false;
+ }
+ return true;
+ }
+
+ void lock ()
+ {
+ if (InterlockedIncrement ((long *)&lock_counter) != 1)
+ WaitForSingleObject (win32_obj_id, INFINITE);
+ }
+
+ void unlock ()
+ {
+ if (InterlockedDecrement ((long *)&lock_counter))
+ ::ReleaseSemaphore (win32_obj_id, 1, NULL);
+ }
+
+private:
+ unsigned long lock_counter;
+ HANDLE win32_obj_id;
+};
+
+class per_process;
+class pinfo;
+
+class ResourceLocks
+{
+public:
+ LPCRITICAL_SECTION Lock (int);
+ void Init ();
+ void Delete ();
+private:
+ CRITICAL_SECTION lock;
+ bool inited;
+};
+
+#define PTHREAD_MAGIC 0xdf0df045
+#define PTHREAD_MUTEX_MAGIC PTHREAD_MAGIC+1
+#define PTHREAD_KEY_MAGIC PTHREAD_MAGIC+2
+#define PTHREAD_ATTR_MAGIC PTHREAD_MAGIC+3
+#define PTHREAD_MUTEXATTR_MAGIC PTHREAD_MAGIC+4
+#define PTHREAD_COND_MAGIC PTHREAD_MAGIC+5
+#define PTHREAD_CONDATTR_MAGIC PTHREAD_MAGIC+6
+#define SEM_MAGIC PTHREAD_MAGIC+7
+#define PTHREAD_ONCE_MAGIC PTHREAD_MAGIC+8
+#define PTHREAD_RWLOCK_MAGIC PTHREAD_MAGIC+9
+#define PTHREAD_RWLOCKATTR_MAGIC PTHREAD_MAGIC+10
+
+#define MUTEX_OWNER_ANONYMOUS ((pthread_t) -1)
+
+/* verifyable_object should not be defined here - it's a general purpose class */
+
+class verifyable_object
+{
+public:
+ long magic;
+
+ verifyable_object (long);
+ virtual ~verifyable_object ();
+};
+
+typedef enum
+{
+ VALID_OBJECT,
+ INVALID_OBJECT,
+ VALID_STATIC_OBJECT
+} verifyable_object_state;
+
+verifyable_object_state verifyable_object_isvalid (void const *, long);
+verifyable_object_state verifyable_object_isvalid (void const *, long, void *);
+
+template <class list_node> inline void
+List_insert (list_node *&head, list_node *node)
+{
+ if (!node)
+ return;
+ do
+ node->next = head;
+ while (InterlockedCompareExchangePointer (&head, node, node->next) != node->next);
+}
+
+template <class list_node> inline void
+List_remove (fast_mutex &mx, list_node *&head, list_node *node)
+{
+ if (!node)
+ return;
+ mx.lock ();
+ if (head)
+ {
+ if (InterlockedCompareExchangePointer (&head, node->next, node) != node)
+ {
+ list_node *cur = head;
+
+ while (cur->next && node != cur->next)
+ cur = cur->next;
+ if (node == cur->next)
+ cur->next = cur->next->next;
+ }
+ }
+ mx.unlock ();
+}
+
+
+template <class list_node> class List
+{
+ public:
+ List() : head(NULL)
+ {
+ mx_init ();
+ }
+
+ ~List()
+ {
+ }
+
+ void fixup_after_fork ()
+ {
+ mx_init ();
+ }
+
+ void insert (list_node *node)
+ {
+ List_insert (head, node);
+ }
+
+ void remove (list_node *node)
+ {
+ List_remove (mx, head, node);
+ }
+
+ void for_each (void (list_node::*callback) ())
+ {
+ mx.lock ();
+ list_node *cur = head;
+ while (cur)
+ {
+ (cur->*callback) ();
+ cur = cur->next;
+ }
+ mx.unlock ();
+ }
+
+protected:
+ void mx_init ()
+ {
+ if (!mx.init ())
+ api_fatal ("Could not create mutex for list synchronisation.");
+ }
+
+ fast_mutex mx;
+ list_node *head;
+};
+
+class pthread_key: public verifyable_object
+{
+public:
+ static bool is_good_object (pthread_key_t const *);
+ DWORD tls_index;
+
+ int set (const void *);
+ void *get () const;
+
+ pthread_key (void (*)(void *));
+ ~pthread_key ();
+ static void fixup_before_fork ()
+ {
+ keys.for_each (&pthread_key::_fixup_before_fork);
+ }
+
+ static void fixup_after_fork ()
+ {
+ keys.fixup_after_fork ();
+ keys.for_each (&pthread_key::_fixup_after_fork);
+ }
+
+ static void run_all_destructors ()
+ {
+ keys.for_each (&pthread_key::run_destructor);
+ }
+
+ /* List support calls */
+ class pthread_key *next;
+private:
+ static List<pthread_key> keys;
+ void _fixup_before_fork ();
+ void _fixup_after_fork ();
+ void (*destructor) (void *);
+ void run_destructor ();
+ void *fork_buf;
+};
+
+class pthread_attr: public verifyable_object
+{
+public:
+ static bool is_good_object(pthread_attr_t const *);
+ int joinable;
+ int contentionscope;
+ int inheritsched;
+ struct sched_param schedparam;
+ size_t stacksize;
+
+ pthread_attr ();
+ ~pthread_attr ();
+};
+
+class pthread_mutexattr: public verifyable_object
+{
+public:
+ static bool is_good_object(pthread_mutexattr_t const *);
+ int pshared;
+ int mutextype;
+ pthread_mutexattr ();
+ ~pthread_mutexattr ();
+};
+
+class pthread_mutex: public verifyable_object
+{
+public:
+ static bool is_good_object (pthread_mutex_t const *);
+ static bool is_good_initializer (pthread_mutex_t const *);
+ static bool is_good_initializer_or_object (pthread_mutex_t const *);
+ static bool is_good_initializer_or_bad_object (pthread_mutex_t const *mutex);
+ static bool can_be_unlocked (pthread_mutex_t const *mutex);
+ static void init_mutex ();
+ static int init (pthread_mutex_t *, const pthread_mutexattr_t *);
+
+ unsigned long lock_counter;
+ HANDLE win32_obj_id;
+ unsigned int recursion_counter;
+ LONG condwaits;
+ pthread_t owner;
+ int type;
+ int pshared;
+
+ pthread_t get_pthread_self () const
+ {
+ return PTHREAD_MUTEX_NORMAL == type ? MUTEX_OWNER_ANONYMOUS :
+ ::pthread_self ();
+ }
+
+ int lock ()
+ {
+ return _lock (get_pthread_self ());
+ }
+ int trylock ()
+ {
+ return _trylock (get_pthread_self ());
+ }
+ int unlock ()
+ {
+ return _unlock (get_pthread_self ());
+ }
+ int destroy ()
+ {
+ return _destroy (get_pthread_self ());
+ }
+
+ void set_owner (pthread_t self)
+ {
+ recursion_counter = 1;
+ owner = self;
+ }
+
+ int lock_recursive ()
+ {
+ if (UINT_MAX == recursion_counter)
+ return EAGAIN;
+ ++recursion_counter;
+ return 0;
+ }
+
+ pthread_mutex (pthread_mutexattr * = NULL);
+ pthread_mutex (pthread_mutex_t *, pthread_mutexattr *);
+ ~pthread_mutex ();
+
+ class pthread_mutex * next;
+ static void fixup_after_fork ()
+ {
+ mutexes.fixup_after_fork ();
+ mutexes.for_each (&pthread_mutex::_fixup_after_fork);
+ }
+
+private:
+ int _lock (pthread_t self);
+ int _trylock (pthread_t self);
+ int _unlock (pthread_t self);
+ int _destroy (pthread_t self);
+
+ void _fixup_after_fork ();
+
+ static List<pthread_mutex> mutexes;
+ static fast_mutex mutex_initialization_lock;
+};
+
+#define WAIT_CANCELED (WAIT_OBJECT_0 + 1)
+
+class _threadinfo;
+class pthread: public verifyable_object
+{
+public:
+ HANDLE win32_obj_id;
+ class pthread_attr attr;
+ void *(*function) (void *);
+ void *arg;
+ void *return_ptr;
+ bool valid;
+ bool suspended;
+ int cancelstate, canceltype;
+ _threadinfo *cygtls;
+ HANDLE cancel_event;
+ pthread_t joiner;
+
+ virtual void create (void *(*)(void *), pthread_attr *, void *);
+
+ pthread ();
+ virtual ~pthread ();
+
+ static void init_mainthread ();
+ static bool is_good_object(pthread_t const *);
+ static void atforkprepare();
+ static void atforkparent();
+ static void atforkchild();
+
+ /* API calls */
+ static int cancel (pthread_t);
+ static int join (pthread_t * thread, void **return_val);
+ static int detach (pthread_t * thread);
+ static int create (pthread_t * thread, const pthread_attr_t * attr,
+ void *(*start_routine) (void *), void *arg);
+ static int once (pthread_once_t *, void (*)(void));
+ static int atfork(void (*)(void), void (*)(void), void (*)(void));
+ static int suspend (pthread_t * thread);
+ static int resume (pthread_t * thread);
+
+ virtual void exit (void *value_ptr) __attribute__ ((noreturn));
+
+ virtual int cancel ();
+
+ virtual void testcancel ();
+ static void static_cancel_self ();
+
+ static DWORD cancelable_wait (HANDLE object, DWORD timeout, const bool do_cancel = true);
+
+ virtual int setcancelstate (int state, int *oldstate);
+ virtual int setcanceltype (int type, int *oldtype);
+
+ virtual void push_cleanup_handler (__pthread_cleanup_handler *handler);
+ virtual void pop_cleanup_handler (int const execute);
+
+ static pthread* self ();
+ static DWORD WINAPI thread_init_wrapper (void *);
+
+ virtual unsigned long getsequence_np();
+
+ static int equal (pthread_t t1, pthread_t t2)
+ {
+ return t1 == t2;
+ }
+
+ /* List support calls */
+ class pthread *next;
+ static void fixup_after_fork ()
+ {
+ threads.fixup_after_fork ();
+ threads.for_each (&pthread::_fixup_after_fork);
+ }
+
+ static void suspend_all_except_self ()
+ {
+ threads.for_each (&pthread::suspend_except_self);
+ }
+
+ static void resume_all ()
+ {
+ threads.for_each (&pthread::resume);
+ }
+
+private:
+ static List<pthread> threads;
+ DWORD thread_id;
+ __pthread_cleanup_handler *cleanup_stack;
+ pthread_mutex mutex;
+
+ void suspend_except_self ();
+ void resume ();
+
+ void _fixup_after_fork ();
+
+ void pop_all_cleanup_handlers (void);
+ void precreate (pthread_attr *);
+ void postcreate ();
+ void set_tls_self_pointer ();
+ bool create_cancel_event ();
+ static pthread *get_tls_self_pointer ();
+ void cancel_self ();
+ DWORD get_thread_id ();
+};
+
+class pthread_null : public pthread
+{
+ public:
+ static pthread *get_null_pthread();
+ ~pthread_null();
+
+ /* From pthread These should never get called
+ * as the ojbect is not verifyable
+ */
+ void create (void *(*)(void *), pthread_attr *, void *);
+ void exit (void *value_ptr) __attribute__ ((noreturn));
+ int cancel ();
+ void testcancel ();
+ int setcancelstate (int state, int *oldstate);
+ int setcanceltype (int type, int *oldtype);
+ void push_cleanup_handler (__pthread_cleanup_handler *handler);
+ void pop_cleanup_handler (int const execute);
+ unsigned long getsequence_np();
+
+ private:
+ pthread_null ();
+ static pthread_null _instance;
+};
+
+class pthread_condattr: public verifyable_object
+{
+public:
+ static bool is_good_object(pthread_condattr_t const *);
+ int shared;
+
+ pthread_condattr ();
+ ~pthread_condattr ();
+};
+
+class pthread_cond: public verifyable_object
+{
+public:
+ static bool is_good_object (pthread_cond_t const *);
+ static bool is_good_initializer (pthread_cond_t const *);
+ static bool is_good_initializer_or_object (pthread_cond_t const *);
+ static bool is_good_initializer_or_bad_object (pthread_cond_t const *);
+ static void init_mutex ();
+ static int init (pthread_cond_t *, const pthread_condattr_t *);
+
+ int shared;
+
+ unsigned long waiting;
+ unsigned long pending;
+ HANDLE sem_wait;
+
+ pthread_mutex mtx_in;
+ pthread_mutex mtx_out;
+
+ pthread_mutex_t mtx_cond;
+
+ void unblock (const bool all);
+ int wait (pthread_mutex_t mutex, DWORD dwMilliseconds = INFINITE);
+
+ pthread_cond (pthread_condattr *);
+ ~pthread_cond ();
+
+ class pthread_cond * next;
+ static void fixup_after_fork ()
+ {
+ conds.fixup_after_fork ();
+ conds.for_each (&pthread_cond::_fixup_after_fork);
+ }
+
+private:
+ void _fixup_after_fork ();
+
+ static List<pthread_cond> conds;
+ static fast_mutex cond_initialization_lock;
+};
+
+class pthread_rwlockattr: public verifyable_object
+{
+public:
+ static bool is_good_object(pthread_rwlockattr_t const *);
+ int shared;
+
+ pthread_rwlockattr ();
+ ~pthread_rwlockattr ();
+};
+
+class pthread_rwlock: public verifyable_object
+{
+public:
+ static bool is_good_object (pthread_rwlock_t const *);
+ static bool is_good_initializer (pthread_rwlock_t const *);
+ static bool is_good_initializer_or_object (pthread_rwlock_t const *);
+ static bool is_good_initializer_or_bad_object (pthread_rwlock_t const *);
+ static void init_mutex ();
+ static int init (pthread_rwlock_t *, const pthread_rwlockattr_t *);
+
+ int shared;
+
+ unsigned long waiting_readers;
+ unsigned long waiting_writers;
+ pthread_t writer;
+ struct RWLOCK_READER
+ {
+ struct RWLOCK_READER *next;
+ pthread_t thread;
+ } *readers;
+ fast_mutex readers_mx;
+
+ int rdlock ();
+ int tryrdlock ();
+
+ int wrlock ();
+ int trywrlock ();
+
+ int unlock ();
+
+ pthread_mutex mtx;
+ pthread_cond cond_readers;
+ pthread_cond cond_writers;
+
+ pthread_rwlock (pthread_rwlockattr *);
+ ~pthread_rwlock ();
+
+ class pthread_rwlock * next;
+ static void fixup_after_fork ()
+ {
+ rwlocks.fixup_after_fork ();
+ rwlocks.for_each (&pthread_rwlock::_fixup_after_fork);
+ }
+
+private:
+ static List<pthread_rwlock> rwlocks;
+
+ void add_reader (struct RWLOCK_READER *rd);
+ void remove_reader (struct RWLOCK_READER *rd);
+ struct RWLOCK_READER *lookup_reader (pthread_t thread);
+
+ void release ()
+ {
+ if (waiting_writers)
+ {
+ if (!readers)
+ cond_writers.unblock (false);
+ }
+ else if (waiting_readers)
+ cond_readers.unblock (true);
+ }
+
+
+ static void rdlock_cleanup (void *arg);
+ static void wrlock_cleanup (void *arg);
+
+ void _fixup_after_fork ();
+
+ static fast_mutex rwlock_initialization_lock;
+};
+
+class pthread_once
+{
+public:
+ pthread_mutex_t mutex;
+ int state;
+};
+
+/* shouldn't be here */
+class semaphore: public verifyable_object
+{
+public:
+ static bool is_good_object(sem_t const *);
+ /* API calls */
+ static int init (sem_t * sem, int pshared, unsigned int value);
+ static int destroy (sem_t * sem);
+ static sem_t *open (const char *name, int oflag, mode_t mode,
+ unsigned int value);
+ static int wait (sem_t * sem);
+ static int post (sem_t * sem);
+ static int getvalue (sem_t * sem, int *sval);
+ static int trywait (sem_t * sem);
+ static int timedwait (sem_t * sem, const struct timespec *abstime);
+
+ HANDLE win32_obj_id;
+ int shared;
+ long currentvalue;
+ char *name;
+
+ semaphore (int, unsigned int);
+ semaphore (const char *name, int oflag, mode_t mode, unsigned int value);
+ ~semaphore ();
+
+ class semaphore * next;
+ static void fixup_after_fork ()
+ {
+ semaphores.fixup_after_fork ();
+ semaphores.for_each (&semaphore::_fixup_after_fork);
+ }
+
+private:
+ void _wait ();
+ void _post ();
+ int _getvalue (int *sval);
+ int _trywait ();
+ int _timedwait (const struct timespec *abstime);
+
+ void _fixup_after_fork ();
+
+ static List<semaphore> semaphores;
+};
+
+class callback
+{
+public:
+ void (*cb)(void);
+ class callback * next;
+};
+
+struct MTinterface
+{
+ // General
+ int concurrency;
+ long int threadcount;
+
+ callback *pthread_prepare;
+ callback *pthread_child;
+ callback *pthread_parent;
+
+ void Init ();
+ void fixup_before_fork (void);
+ void fixup_after_fork (void);
+
+#if 0 // avoid initialization since zero is implied and
+ MTinterface () :
+ concurrency (0), threadcount (0),
+ pthread_prepare (NULL), pthread_child (NULL), pthread_parent (NULL)
+ {
+ }
+#endif
+};
+
+#define MT_INTERFACE user_data->threadinterface
+#endif // _CYGNUS_THREADS_
diff --git a/winsup/cygwin/tlsoffsets.h b/winsup/cygwin/tlsoffsets.h
new file mode 100644
index 00000000000..c61bad990a0
--- /dev/null
+++ b/winsup/cygwin/tlsoffsets.h
@@ -0,0 +1,46 @@
+//;# autogenerated: Do not edit.
+
+//; $tls::func = -5100;
+//; $tls::saved_errno = -5096;
+//; $tls::sa_flags = -5092;
+//; $tls::oldmask = -5088;
+//; $tls::newmask = -5084;
+//; $tls::event = -5080;
+//; $tls::errno_addr = -5076;
+//; $tls::initialized = -5072;
+//; $tls::sigmask = -5068;
+//; $tls::sigwait_mask = -5064;
+//; $tls::sigwait_info = -5060;
+//; $tls::infodata = -5056;
+//; $tls::tid = -4532;
+//; $tls::local_clib = -4528;
+//; $tls::locals = -3600;
+//; $tls::prev = -2064;
+//; $tls::next = -2060;
+//; $tls::stackptr = -2056;
+//; $tls::sig = -2052;
+//; $tls::stack = -2048;
+//; $tls::padding = -1024;
+//; __DATA__
+
+#define tls_func (-5100)
+#define tls_saved_errno (-5096)
+#define tls_sa_flags (-5092)
+#define tls_oldmask (-5088)
+#define tls_newmask (-5084)
+#define tls_event (-5080)
+#define tls_errno_addr (-5076)
+#define tls_initialized (-5072)
+#define tls_sigmask (-5068)
+#define tls_sigwait_mask (-5064)
+#define tls_sigwait_info (-5060)
+#define tls_infodata (-5056)
+#define tls_tid (-4532)
+#define tls_local_clib (-4528)
+#define tls_locals (-3600)
+#define tls_prev (-2064)
+#define tls_next (-2060)
+#define tls_stackptr (-2056)
+#define tls_sig (-2052)
+#define tls_stack (-2048)
+#define tls_padding (-1024)
diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h
new file mode 100644
index 00000000000..6c2e58fafa6
--- /dev/null
+++ b/winsup/cygwin/winsup.h
@@ -0,0 +1,359 @@
+/* winsup.h: main Cygwin header file.
+
+ Copyright 1996, 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. */
+
+#ifdef DEBUGIT
+#define spf(a, b, c) small_printf (a, b, c)
+#else
+#define spf(a, b, c) do {} while (0)
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define __INSIDE_CYGWIN__
+
+#define strlen __builtin_strlen
+#define strcmp __builtin_strcmp
+#define strcpy __builtin_strcpy
+#define memcpy __builtin_memcpy
+#define memcmp __builtin_memcmp
+#ifdef HAVE_BUILTIN_MEMSET
+# define memset __builtin_memset
+#endif
+
+#define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy")))
+#define NO_COPY_INIT __attribute__((section(".data_cygwin_nocopy")))
+
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L
+#define NEW_MACRO_VARARGS
+#endif
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include <sys/types.h>
+#include <sys/strace.h>
+
+/* Declarations for functions used in C and C++ code. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern __uid32_t getuid32 (void);
+extern __uid32_t geteuid32 (void);
+extern int seteuid32 (__uid32_t);
+extern __gid32_t getegid32 (void);
+extern struct passwd *getpwuid32 (__uid32_t);
+extern struct passwd *getpwnam (const char *);
+extern struct __sFILE64 *fopen64 (const char *, const char *);
+extern struct hostent *cygwin_gethostbyname (const char *name);
+extern unsigned long cygwin_inet_addr (const char *cp);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Note that MAX_PATH is defined in the windows headers */
+/* There is also PATH_MAX and MAXPATHLEN.
+ PATH_MAX is from Posix and does *not* include the trailing NUL.
+ MAXPATHLEN is from Unix.
+
+ Thou shalt use CYG_MAX_PATH throughout. It avoids the NUL vs no-NUL
+ issue and is neither of the Unixy ones [so we can punt on which
+ one is the right one to use].
+
+ Windows ANSI calls are limited to MAX_PATH in length. Cygwin calls that
+ thunk through to Windows Wide calls are limited to 32K. We define
+ CYG_MAX_PATH as a convenient, not to short, not too long 'happy medium'.
+
+ */
+
+#define CYG_MAX_PATH (MAX_PATH)
+
+#ifdef __cplusplus
+
+extern const char case_folded_lower[];
+#define cyg_tolower(c) (case_folded_lower[(unsigned char)(c)])
+extern const char case_folded_upper[];
+#define cyg_toupper(c) (case_folded_upper[(unsigned char)(c)])
+
+#ifndef MALLOC_DEBUG
+#define cfree newlib_cfree_dont_use
+#endif
+
+#define WIN32_LEAN_AND_MEAN 1
+#define _WINGDI_H
+#define _WINUSER_H
+#define _WINNLS_H
+#define _WINVER_H
+#define _WINNETWK_H
+#define _WINSVC_H
+#include <windows.h>
+#include <wincrypt.h>
+#include <lmcons.h>
+#undef _WINGDI_H
+#undef _WINUSER_H
+#undef _WINNLS_H
+#undef _WINVER_H
+#undef _WINNETWK_H
+#undef _WINSVC_H
+
+#include "wincap.h"
+
+/* The one function we use from winuser.h most of the time */
+extern "C" DWORD WINAPI GetLastError (void);
+
+enum codepage_type {ansi_cp, oem_cp};
+extern codepage_type current_codepage;
+
+UINT get_cp ();
+
+int __stdcall sys_wcstombs(char *, const WCHAR *, int)
+ __attribute__ ((regparm(3)));
+
+int __stdcall sys_mbstowcs(WCHAR *, const char *, int)
+ __attribute__ ((regparm(3)));
+
+/* Used to check if Cygwin DLL is dynamically loaded. */
+extern int dynamically_loaded;
+
+extern int cygserver_running;
+
+#define _MT_SAFE // DELTEME someday
+
+#define TITLESIZE 1024
+
+/* status bit manipulation */
+#define __ISSETF(what, x, prefix) \
+ ((what)->status & prefix##_##x)
+#define __SETF(what, x, prefix) \
+ ((what)->status |= prefix##_##x)
+#define __CLEARF(what, x, prefix) \
+ ((what)->status &= ~prefix##_##x)
+#define __CONDSETF(n, what, x, prefix) \
+ ((n) ? __SETF (what, x, prefix) : __CLEARF (what, x, prefix))
+
+#include "debug.h"
+
+/* Events/mutexes */
+extern HANDLE title_mutex;
+
+/**************************** Convenience ******************************/
+
+/* Used when treating / and \ as equivalent. */
+#define isdirsep(ch) \
+ ({ \
+ char __c = (ch); \
+ ((__c) == '/' || (__c) == '\\'); \
+ })
+
+/* Convert a signal to a signal mask */
+#define SIGTOMASK(sig) (1 << ((sig) - signal_shift_subtract))
+extern unsigned int signal_shift_subtract;
+
+#ifdef NEW_MACRO_VARARGS
+# define api_fatal(...) __api_fatal (__VA_ARGS__)
+#else
+# define api_fatal(fmt, args...) __api_fatal ("%P: *** " fmt,## args)
+#endif
+
+#undef issep
+#define issep(ch) (strchr (" \t\n\r", (ch)) != NULL)
+
+#define isabspath(p) \
+ (isdirsep (*(p)) || (isalpha (*(p)) && (p)[1] == ':' && (!(p)[2] || isdirsep ((p)[2]))))
+
+/******************** Initialization/Termination **********************/
+
+class per_process;
+/* cygwin .dll initialization */
+void dll_crt0 (per_process *) __asm__ ("_dll_crt0__FP11per_process");
+extern "C" void __stdcall _dll_crt0 ();
+
+/* dynamically loaded dll initialization */
+extern "C" int dll_dllcrt0 (HMODULE, per_process *);
+
+/* dynamically loaded dll initialization for non-cygwin apps */
+extern "C" int dll_noncygwin_dllcrt0 (HMODULE, per_process *);
+
+/* exit the program */
+
+enum exit_states
+ {
+ ES_NOT_EXITING = 0,
+ ES_EVENTS_TERMINATE,
+ ES_THREADTERM,
+ ES_SIGNAL,
+ ES_CLOSEALL,
+ ES_SIGPROCTERMINATE,
+ ES_TITLE,
+ ES_HUP_PGRP,
+ ES_HUP_SID,
+ ES_TTY_TERMINATE
+ };
+
+extern exit_states exit_state;
+void __stdcall do_exit (int) __attribute__ ((regparm (1), noreturn));
+
+/* UID/GID */
+void uinfo_init (void);
+
+#define ILLEGAL_UID16 ((__uid16_t)-1)
+#define ILLEGAL_UID ((__uid32_t)-1)
+#define ILLEGAL_GID16 ((__gid16_t)-1)
+#define ILLEGAL_GID ((__gid32_t)-1)
+#define ILLEGAL_SEEK ((_off64_t)-1)
+
+#define uid16touid32(u16) ((u16)==ILLEGAL_UID16?ILLEGAL_UID:(__uid32_t)(u16))
+#define gid16togid32(g16) ((g16)==ILLEGAL_GID16?ILLEGAL_GID:(__gid32_t)(g16))
+
+/* various events */
+void events_init (void);
+void events_terminate (void);
+
+void __stdcall close_all_files (void);
+
+/* Invisible window initialization/termination. */
+HWND __stdcall gethwnd (void);
+/* Check if running in a visible window station. */
+extern bool has_visible_window_station (void);
+
+/* Globals that handle initialization of winsock in a child process. */
+extern HANDLE wsock32_handle;
+extern HANDLE ws2_32_handle;
+
+/* Globals that handle initialization of netapi in a child process. */
+extern HANDLE netapi32_handle;
+
+/* debug_on_trap support. see exceptions.cc:try_to_debug() */
+extern "C" void error_start_init (const char*);
+extern "C" int try_to_debug (bool waitloop = 1);
+
+void set_file_api_mode (codepage_type);
+
+extern int cygwin_finished_initializing;
+
+/**************************** Miscellaneous ******************************/
+
+void __stdcall set_std_handle (int);
+int __stdcall writable_directory (const char *file);
+int __stdcall stat_dev (DWORD, int, unsigned long, struct __stat64 *);
+
+__ino64_t __stdcall hash_path_name (__ino64_t hash, const char *name) __attribute__ ((regparm(2)));
+void __stdcall nofinalslash (const char *src, char *dst) __attribute__ ((regparm(2)));
+extern "C" char *__stdcall rootdir (char *full_path) __attribute__ ((regparm(1)));
+
+/* String manipulation */
+extern "C" char *__stdcall strccpy (char *s1, const char **s2, char c);
+extern "C" int __stdcall strcasematch (const char *s1, const char *s2) __attribute__ ((regparm(2)));
+extern "C" int __stdcall strncasematch (const char *s1, const char *s2, size_t n) __attribute__ ((regparm(3)));
+extern "C" char *__stdcall strcasestr (const char *searchee, const char *lookfor) __attribute__ ((regparm(2)));
+
+/* Time related */
+void __stdcall totimeval (struct timeval *dst, FILETIME * src, int sub, int flag);
+long __stdcall to_time_t (FILETIME * ptr);
+void __stdcall to_timestruc_t (FILETIME * ptr, timestruc_t * out);
+void __stdcall time_as_timestruc_t (timestruc_t * out);
+
+void __stdcall set_console_title (char *);
+void init_console_handler ();
+void init_global_security ();
+
+int __stdcall check_null_str (const char *name) __attribute__ ((regparm(1)));
+int __stdcall check_null_empty_str (const char *name) __attribute__ ((regparm(1)));
+int __stdcall check_null_empty_str_errno (const char *name) __attribute__ ((regparm(1)));
+int __stdcall check_null_str_errno (const char *name) __attribute__ ((regparm(1)));
+int __stdcall __check_null_invalid_struct (void *s, unsigned sz) __attribute__ ((regparm(2)));
+int __stdcall __check_null_invalid_struct_errno (void *s, unsigned sz) __attribute__ ((regparm(2)));
+int __stdcall __check_invalid_read_ptr (const void *s, unsigned sz) __attribute__ ((regparm(2)));
+int __stdcall __check_invalid_read_ptr_errno (const void *s, unsigned sz) __attribute__ ((regparm(2)));
+
+#define check_null_invalid_struct(s) \
+ __check_null_invalid_struct ((s), sizeof (*(s)))
+#define check_null_invalid_struct_errno(s) \
+ __check_null_invalid_struct_errno ((s), sizeof (*(s)))
+#define check_invalid_read_struct_errno(s) \
+ __check_invalid_read_ptr_errno ((s), sizeof (*(s)))
+
+struct iovec;
+ssize_t check_iovec_for_read (const struct iovec *, int) __attribute__ ((regparm(2)));
+ssize_t check_iovec_for_write (const struct iovec *, int) __attribute__ ((regparm(2)));
+
+#define set_winsock_errno() __set_winsock_errno (__FUNCTION__, __LINE__)
+void __set_winsock_errno (const char *fn, int ln) __attribute__ ((regparm(2)));
+
+extern bool wsock_started;
+
+/* Printf type functions */
+extern "C" void __api_fatal (const char *, ...) __attribute__ ((noreturn));
+extern "C" int __small_sprintf (char *dst, const char *fmt, ...) /*__attribute__ ((regparm (2)))*/;
+extern "C" int __small_vsprintf (char *dst, const char *fmt, va_list ap) /*__attribute__ ((regparm (3)))*/;
+extern void multiple_cygwin_problem (const char *, unsigned, unsigned);
+
+int symlink_worker (const char *, const char *, bool, bool)
+ __attribute__ ((regparm (3)));
+
+class path_conv;
+int access_worker (path_conv&, int, class fhandler_base * = NULL) __attribute__ ((regparm (3)));
+
+int fcntl_worker (int fd, int cmd, void *arg);
+
+extern "C" int low_priority_sleep (DWORD) __attribute__ ((regparm (1)));
+#define SLEEP_0_STAY_LOW INFINITE
+
+size_t getshmlba (void);
+
+/**************************** Exports ******************************/
+
+extern "C" {
+int cygwin_select (int , fd_set *, fd_set *, fd_set *,
+ struct timeval *to);
+int cygwin_gethostname (char *__name, size_t __len);
+
+int kill_pgrp (pid_t, int);
+int _kill (int, int);
+int _raise (int sig);
+
+extern DWORD binmode;
+extern char _data_start__, _data_end__, _bss_start__, _bss_end__;
+extern void (*__CTOR_LIST__) (void);
+extern void (*__DTOR_LIST__) (void);
+extern SYSTEM_INFO system_info;
+};
+
+/*************************** Unsorted ******************************/
+
+#define WM_ASYNCIO 0x8000 // WM_APP
+
+
+#define STD_RBITS (S_IRUSR | S_IRGRP | S_IROTH)
+#define STD_WBITS (S_IWUSR)
+#define STD_XBITS (S_IXUSR | S_IXGRP | S_IXOTH)
+#define NO_W ~(S_IWUSR | S_IWGRP | S_IWOTH)
+#define NO_R ~(S_IRUSR | S_IRGRP | S_IROTH)
+#define NO_X ~(S_IXUSR | S_IXGRP | S_IXOTH)
+
+/* The title on program start. */
+extern char *old_title;
+extern bool display_title;
+
+extern HANDLE hMainThread;
+extern HANDLE hMainProc;
+
+extern bool cygwin_testing;
+
+extern char almost_null[];
+
+#define winsock2_active (wsadata.wVersion >= 512)
+#define winsock_active (wsadata.wVersion < 512)
+extern struct WSAData wsadata;
+
+#endif /* defined __cplusplus */