diff options
author | Christopher Faylor <cgf@redhat.com> | 2004-01-11 18:13:51 +0000 |
---|---|---|
committer | Christopher Faylor <cgf@redhat.com> | 2004-01-11 18:13:51 +0000 |
commit | 46f9078b6f5ef23c2fcebc7318aed97b3f60482c (patch) | |
tree | 9581d766b006dc62da49033215581ec32a173f9b | |
parent | 69a7b5f79888513741e65a54216d7756474b76c2 (diff) | |
download | gdb-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.
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, ¤tvalue)) + 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 */ |